Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c7ef7185 authored by Uwe Kleine-König's avatar Uwe Kleine-König Committed by Greg Kroah-Hartman
Browse files

clk: generalize devm_clk_get() a bit



[ 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: default avatarJonathan Cameron <Jonathan.Cameron@huawei.com>
Reviewed-by: default avatarAlexandru Ardelean <aardelean@deviqon.com>
Signed-off-by: default avatarUwe 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: default avatarStephen Boyd <sboyd@kernel.org>
Stable-dep-of: 340cb392a038 ("memory: atmel-sdramc: Fix missing clk_disable_unprepare in atmel_ramc_probe()")
Signed-off-by: default avatarSasha Levin <sashal@kernel.org>
parent 90245959
Loading
Loading
Loading
Loading
+49 −17
Original line number Original line Diff line number Diff line
@@ -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);
}
}


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);
	state = devres_alloc(devm_clk_release, sizeof(*state), GFP_KERNEL);
	if (!ptr)
	if (!state)
		return ERR_PTR(-ENOMEM);
		return ERR_PTR(-ENOMEM);


	clk = clk_get(dev, id);
	clk = get(dev, id);
	if (!IS_ERR(clk)) {
	if (IS_ERR(clk)) {
		*ptr = clk;
		ret = PTR_ERR(clk);
		devres_add(dev, ptr);
		goto err_clk_get;
	} else {
		devres_free(ptr);
	}
	}


	if (init) {
		ret = init(clk);
		if (ret)
			goto err_clk_init;
	}

	state->clk = clk;
	state->exit = exit;

	devres_add(dev, state);

	return clk;
	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);
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);