forked from mirrors/linux
		
	 60b8f0ddf1
			
		
	
	
		60b8f0ddf1
		
	
	
	
	
		
			
			This adds clk_get_optional() and devm_clk_get_optional() functions to get optional clocks. They behave the same as (devm_)clk_get() except where there is no clock producer. In this case, instead of returning -ENOENT, the function returns NULL. This makes error checking simpler and allows clk_prepare_enable, etc to be called on the returned reference without additional checks. Signed-off-by: Phil Edworthy <phil.edworthy@renesas.com> Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com> Cc: Russell King <linux@armlinux.org.uk> [sboyd@kernel.org: Document in devres.txt] Signed-off-by: Stephen Boyd <sboyd@kernel.org>
		
			
				
	
	
		
			142 lines
		
	
	
	
		
			2.7 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			142 lines
		
	
	
	
		
			2.7 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| // SPDX-License-Identifier: GPL-2.0
 | |
| #include <linux/clk.h>
 | |
| #include <linux/device.h>
 | |
| #include <linux/export.h>
 | |
| #include <linux/gfp.h>
 | |
| 
 | |
| static void devm_clk_release(struct device *dev, void *res)
 | |
| {
 | |
| 	clk_put(*(struct clk **)res);
 | |
| }
 | |
| 
 | |
| 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;
 | |
| }
 | |
| 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;
 | |
| }
 | |
| EXPORT_SYMBOL(devm_clk_get_optional);
 | |
| 
 | |
| struct clk_bulk_devres {
 | |
| 	struct clk_bulk_data *clks;
 | |
| 	int num_clks;
 | |
| };
 | |
| 
 | |
| static void devm_clk_bulk_release(struct device *dev, void *res)
 | |
| {
 | |
| 	struct clk_bulk_devres *devres = res;
 | |
| 
 | |
| 	clk_bulk_put(devres->num_clks, devres->clks);
 | |
| }
 | |
| 
 | |
| int __must_check devm_clk_bulk_get(struct device *dev, int num_clks,
 | |
| 		      struct clk_bulk_data *clks)
 | |
| {
 | |
| 	struct clk_bulk_devres *devres;
 | |
| 	int ret;
 | |
| 
 | |
| 	devres = devres_alloc(devm_clk_bulk_release,
 | |
| 			      sizeof(*devres), GFP_KERNEL);
 | |
| 	if (!devres)
 | |
| 		return -ENOMEM;
 | |
| 
 | |
| 	ret = clk_bulk_get(dev, num_clks, clks);
 | |
| 	if (!ret) {
 | |
| 		devres->clks = clks;
 | |
| 		devres->num_clks = num_clks;
 | |
| 		devres_add(dev, devres);
 | |
| 	} else {
 | |
| 		devres_free(devres);
 | |
| 	}
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| EXPORT_SYMBOL_GPL(devm_clk_bulk_get);
 | |
| 
 | |
| int __must_check devm_clk_bulk_get_all(struct device *dev,
 | |
| 				       struct clk_bulk_data **clks)
 | |
| {
 | |
| 	struct clk_bulk_devres *devres;
 | |
| 	int ret;
 | |
| 
 | |
| 	devres = devres_alloc(devm_clk_bulk_release,
 | |
| 			      sizeof(*devres), GFP_KERNEL);
 | |
| 	if (!devres)
 | |
| 		return -ENOMEM;
 | |
| 
 | |
| 	ret = clk_bulk_get_all(dev, &devres->clks);
 | |
| 	if (ret > 0) {
 | |
| 		*clks = devres->clks;
 | |
| 		devres->num_clks = ret;
 | |
| 		devres_add(dev, devres);
 | |
| 	} else {
 | |
| 		devres_free(devres);
 | |
| 	}
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| EXPORT_SYMBOL_GPL(devm_clk_bulk_get_all);
 | |
| 
 | |
| static int devm_clk_match(struct device *dev, void *res, void *data)
 | |
| {
 | |
| 	struct clk **c = res;
 | |
| 	if (!c || !*c) {
 | |
| 		WARN_ON(!c || !*c);
 | |
| 		return 0;
 | |
| 	}
 | |
| 	return *c == data;
 | |
| }
 | |
| 
 | |
| void devm_clk_put(struct device *dev, struct clk *clk)
 | |
| {
 | |
| 	int ret;
 | |
| 
 | |
| 	ret = devres_release(dev, devm_clk_release, devm_clk_match, clk);
 | |
| 
 | |
| 	WARN_ON(ret);
 | |
| }
 | |
| EXPORT_SYMBOL(devm_clk_put);
 | |
| 
 | |
| struct clk *devm_get_clk_from_child(struct device *dev,
 | |
| 				    struct device_node *np, const char *con_id)
 | |
| {
 | |
| 	struct clk **ptr, *clk;
 | |
| 
 | |
| 	ptr = devres_alloc(devm_clk_release, sizeof(*ptr), GFP_KERNEL);
 | |
| 	if (!ptr)
 | |
| 		return ERR_PTR(-ENOMEM);
 | |
| 
 | |
| 	clk = of_clk_get_by_name(np, con_id);
 | |
| 	if (!IS_ERR(clk)) {
 | |
| 		*ptr = clk;
 | |
| 		devres_add(dev, ptr);
 | |
| 	} else {
 | |
| 		devres_free(ptr);
 | |
| 	}
 | |
| 
 | |
| 	return clk;
 | |
| }
 | |
| EXPORT_SYMBOL(devm_get_clk_from_child);
 |