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:
Uwe Kleine-König 2022-05-20 09:57:35 +02:00 committed by Stephen Boyd
parent af89cd4560
commit abae8e57e4

View File

@ -4,39 +4,71 @@
#include <linux/export.h> #include <linux/export.h>
#include <linux/gfp.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) 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 *devm_clk_get(struct device *dev, const char *id)
{ {
struct clk **ptr, *clk; return __devm_clk_get(dev, id, clk_get, NULL, NULL);
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); EXPORT_SYMBOL(devm_clk_get);
struct clk *devm_clk_get_optional(struct device *dev, const char *id) struct clk *devm_clk_get_optional(struct device *dev, const char *id)
{ {
struct clk *clk = devm_clk_get(dev, id); return __devm_clk_get(dev, id, clk_get_optional, NULL, NULL);
if (clk == ERR_PTR(-ENOENT))
return NULL;
return clk;
} }
EXPORT_SYMBOL(devm_clk_get_optional); EXPORT_SYMBOL(devm_clk_get_optional);