diff options
author | Arnd Bergmann <arnd@arndb.de> | 2012-05-11 19:13:00 +0400 |
---|---|---|
committer | Arnd Bergmann <arnd@arndb.de> | 2012-05-11 19:13:00 +0400 |
commit | e12ff34402bd3a6cbeab0423012066874bb10f4b (patch) | |
tree | 026668e3c64638ff8cc367d5a43269b2949a2549 /drivers | |
parent | bef945965884365cdc49985169977a51390253f1 (diff) | |
parent | e9d7f4065408e92338a41b809e437c6e043da090 (diff) | |
download | linux-e12ff34402bd3a6cbeab0423012066874bb10f4b.tar.xz |
Merge branch 'depends/rmk/clkdev' into next/clock
* depends/rmk/clkdev:
CLKDEV: provide helpers for common clock framework
ARM: 7392/1: CLKDEV: Optimize clk_find()
ARM: 7376/1: clkdev: Implement managed clk_get()
Diffstat (limited to 'drivers')
-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); |