forked from mirrors/linux
		
	clk: generalize devm_clk_get() a bit
Allow to add an exit hook to devm managed clocks. Also use clk_get_optional() in devm_clk_get_optional instead of open coding it. The generalisation will be used in the next commit to add some more devm_clk helpers. Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com> Reviewed-by: Alexandru Ardelean <aardelean@deviqon.com> Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de> Link: https://lore.kernel.org/r/20220520075737.758761-3-u.kleine-koenig@pengutronix.de Signed-off-by: Stephen Boyd <sboyd@kernel.org>
This commit is contained in:
		
							parent
							
								
									af89cd4560
								
							
						
					
					
						commit
						abae8e57e4
					
				
					 1 changed files with 54 additions and 22 deletions
				
			
		|  | @ -4,39 +4,71 @@ | |||
| #include <linux/export.h> | ||||
| #include <linux/gfp.h> | ||||
| 
 | ||||
| struct devm_clk_state { | ||||
| 	struct clk *clk; | ||||
| 	void (*exit)(struct clk *clk); | ||||
| }; | ||||
| 
 | ||||
| static void devm_clk_release(struct device *dev, void *res) | ||||
| { | ||||
| 	clk_put(*(struct clk **)res); | ||||
| 	struct devm_clk_state *state = *(struct devm_clk_state **)res; | ||||
| 
 | ||||
| 	if (state->exit) | ||||
| 		state->exit(state->clk); | ||||
| 
 | ||||
| 	clk_put(state->clk); | ||||
| } | ||||
| 
 | ||||
| static struct clk *__devm_clk_get(struct device *dev, const char *id, | ||||
| 				  struct clk *(*get)(struct device *dev, const char *id), | ||||
| 				  int (*init)(struct clk *clk), | ||||
| 				  void (*exit)(struct clk *clk)) | ||||
| { | ||||
| 	struct devm_clk_state *state; | ||||
| 	struct clk *clk; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	state = devres_alloc(devm_clk_release, sizeof(*state), GFP_KERNEL); | ||||
| 	if (!state) | ||||
| 		return ERR_PTR(-ENOMEM); | ||||
| 
 | ||||
| 	clk = get(dev, id); | ||||
| 	if (IS_ERR(clk)) { | ||||
| 		ret = PTR_ERR(clk); | ||||
| 		goto err_clk_get; | ||||
| 	} | ||||
| 
 | ||||
| 	if (init) { | ||||
| 		ret = init(clk); | ||||
| 		if (ret) | ||||
| 			goto err_clk_init; | ||||
| 	} | ||||
| 
 | ||||
| 	state->clk = clk; | ||||
| 	state->exit = exit; | ||||
| 
 | ||||
| 	devres_add(dev, state); | ||||
| 
 | ||||
| 	return clk; | ||||
| 
 | ||||
| err_clk_init: | ||||
| 
 | ||||
| 	clk_put(clk); | ||||
| err_clk_get: | ||||
| 
 | ||||
| 	devres_free(state); | ||||
| 	return ERR_PTR(ret); | ||||
| } | ||||
| 
 | ||||
| struct clk *devm_clk_get(struct device *dev, const char *id) | ||||
| { | ||||
| 	struct clk **ptr, *clk; | ||||
| 
 | ||||
| 	ptr = devres_alloc(devm_clk_release, sizeof(*ptr), GFP_KERNEL); | ||||
| 	if (!ptr) | ||||
| 		return ERR_PTR(-ENOMEM); | ||||
| 
 | ||||
| 	clk = clk_get(dev, id); | ||||
| 	if (!IS_ERR(clk)) { | ||||
| 		*ptr = clk; | ||||
| 		devres_add(dev, ptr); | ||||
| 	} else { | ||||
| 		devres_free(ptr); | ||||
| 	} | ||||
| 
 | ||||
| 	return clk; | ||||
| 	return __devm_clk_get(dev, id, clk_get, NULL, NULL); | ||||
| } | ||||
| EXPORT_SYMBOL(devm_clk_get); | ||||
| 
 | ||||
| struct clk *devm_clk_get_optional(struct device *dev, const char *id) | ||||
| { | ||||
| 	struct clk *clk = devm_clk_get(dev, id); | ||||
| 
 | ||||
| 	if (clk == ERR_PTR(-ENOENT)) | ||||
| 		return NULL; | ||||
| 
 | ||||
| 	return clk; | ||||
| 	return __devm_clk_get(dev, id, clk_get_optional, NULL, NULL); | ||||
| } | ||||
| EXPORT_SYMBOL(devm_clk_get_optional); | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 Uwe Kleine-König
						Uwe Kleine-König