diff options
Diffstat (limited to 'drivers/clk/clkdev.c')
| -rw-r--r-- | drivers/clk/clkdev.c | 142 | 
1 files changed, 132 insertions, 10 deletions
diff --git a/drivers/clk/clkdev.c b/drivers/clk/clkdev.c index 6db161f64ae0..c535cf8c5770 100644 --- a/drivers/clk/clkdev.c +++ b/drivers/clk/clkdev.c @@ -35,7 +35,12 @@ static DEFINE_MUTEX(clocks_mutex);  static struct clk_lookup *clk_find(const char *dev_id, const char *con_id)  {  	struct clk_lookup *p, *cl = NULL; -	int match, best = 0; +	int match, best_found = 0, best_possible = 0; + +	if (dev_id) +		best_possible += 2; +	if (con_id) +		best_possible += 1;  	list_for_each_entry(p, &clocks, node) {  		match = 0; @@ -50,10 +55,10 @@ static struct clk_lookup *clk_find(const char *dev_id, const char *con_id)  			match += 1;  		} -		if (match > best) { +		if (match > best_found) {  			cl = p; -			if (match != 3) -				best = match; +			if (match != best_possible) +				best_found = match;  			else  				break;  		} @@ -89,6 +94,51 @@ void clk_put(struct clk *clk)  }  EXPORT_SYMBOL(clk_put); +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); + +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_destroy(dev, devm_clk_release, devm_clk_match, clk); + +	WARN_ON(ret); +} +EXPORT_SYMBOL(devm_clk_put); +  void clkdev_add(struct clk_lookup *cl)  {  	mutex_lock(&clocks_mutex); @@ -116,8 +166,9 @@ struct clk_lookup_alloc {  	char	con_id[MAX_CON_ID];  }; -struct clk_lookup * __init_refok -clkdev_alloc(struct clk *clk, const char *con_id, const char *dev_fmt, ...) +static struct clk_lookup * __init_refok +vclkdev_alloc(struct clk *clk, const char *con_id, const char *dev_fmt, +	va_list ap)  {  	struct clk_lookup_alloc *cla; @@ -132,16 +183,25 @@ clkdev_alloc(struct clk *clk, const char *con_id, const char *dev_fmt, ...)  	}  	if (dev_fmt) { -		va_list ap; - -		va_start(ap, dev_fmt);  		vscnprintf(cla->dev_id, sizeof(cla->dev_id), dev_fmt, ap);  		cla->cl.dev_id = cla->dev_id; -		va_end(ap);  	}  	return &cla->cl;  } + +struct clk_lookup * __init_refok +clkdev_alloc(struct clk *clk, const char *con_id, const char *dev_fmt, ...) +{ +	struct clk_lookup *cl; +	va_list ap; + +	va_start(ap, dev_fmt); +	cl = vclkdev_alloc(clk, con_id, dev_fmt, ap); +	va_end(ap); + +	return cl; +}  EXPORT_SYMBOL(clkdev_alloc);  int clk_add_alias(const char *alias, const char *alias_dev_name, char *id, @@ -173,3 +233,65 @@ void clkdev_drop(struct clk_lookup *cl)  	kfree(cl);  }  EXPORT_SYMBOL(clkdev_drop); + +/** + * clk_register_clkdev - register one clock lookup for a struct clk + * @clk: struct clk to associate with all clk_lookups + * @con_id: connection ID string on device + * @dev_id: format string describing device name + * + * con_id or dev_id may be NULL as a wildcard, just as in the rest of + * clkdev. + * + * To make things easier for mass registration, we detect error clks + * from a previous clk_register() call, and return the error code for + * those.  This is to permit this function to be called immediately + * after clk_register(). + */ +int clk_register_clkdev(struct clk *clk, const char *con_id, +	const char *dev_fmt, ...) +{ +	struct clk_lookup *cl; +	va_list ap; + +	if (IS_ERR(clk)) +		return PTR_ERR(clk); + +	va_start(ap, dev_fmt); +	cl = vclkdev_alloc(clk, con_id, dev_fmt, ap); +	va_end(ap); + +	if (!cl) +		return -ENOMEM; + +	clkdev_add(cl); + +	return 0; +} + +/** + * clk_register_clkdevs - register a set of clk_lookup for a struct clk + * @clk: struct clk to associate with all clk_lookups + * @cl: array of clk_lookup structures with con_id and dev_id pre-initialized + * @num: number of clk_lookup structures to register + * + * To make things easier for mass registration, we detect error clks + * from a previous clk_register() call, and return the error code for + * those.  This is to permit this function to be called immediately + * after clk_register(). + */ +int clk_register_clkdevs(struct clk *clk, struct clk_lookup *cl, size_t num) +{ +	unsigned i; + +	if (IS_ERR(clk)) +		return PTR_ERR(clk); + +	for (i = 0; i < num; i++, cl++) { +		cl->clk = clk; +		clkdev_add(cl); +	} + +	return 0; +} +EXPORT_SYMBOL(clk_register_clkdevs);  | 
