mirror of
https://github.com/torvalds/linux.git
synced 2024-12-17 16:43:08 +00:00
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
@ -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 New Issue
Block a user