clk: generalize devm_clk_get() a bit
authorUwe Kleine-König <u.kleine-koenig@pengutronix.de>
Fri, 20 May 2022 07:57:35 +0000 (09:57 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 14 Dec 2022 10:37:13 +0000 (11:37 +0100)
[ Upstream commit abae8e57e49aa75f6db76aa866c775721523908f ]

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>
Stable-dep-of: c61bfb1cb63d ("mmc: mtk-sd: Fix missing clk_disable_unprepare in msdc_of_clock_parse()")
Signed-off-by: Sasha Levin <sashal@kernel.org>
drivers/clk/clk-devres.c

index f9d5b73..c822f4e 100644 (file)
@@ -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);
 }
 
-struct clk *devm_clk_get(struct device *dev, const char *id)
+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 clk **ptr, *clk;
+       struct devm_clk_state *state;
+       struct clk *clk;
+       int ret;
 
-       ptr = devres_alloc(devm_clk_release, sizeof(*ptr), GFP_KERNEL);
-       if (!ptr)
+       state = devres_alloc(devm_clk_release, sizeof(*state), GFP_KERNEL);
+       if (!state)
                return ERR_PTR(-ENOMEM);
 
-       clk = clk_get(dev, id);
-       if (!IS_ERR(clk)) {
-               *ptr = clk;
-               devres_add(dev, ptr);
-       } else {
-               devres_free(ptr);
+       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)
+{
+       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);