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/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);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user