From 52720c536ffdbe0e6aece79840e2791d87204cf7 Mon Sep 17 00:00:00 2001 From: Jean-Jacques Hiblot Date: Tue, 22 Oct 2019 14:00:04 +0200 Subject: [PATCH] drivers: clk: Add a managed API to get clocks from the device-tree Add devm_clk_get(), devm_clk_get_optional() to get clocks from the device-tree. The clocks is automatically released and the data structure freed when the device is unbound. Also add devm_clk_put() to release the clock and free the data structure manually. Signed-off-by: Jean-Jacques Hiblot --- drivers/clk/clk-uclass.c | 48 +++++++++++++++++++++++++ drivers/mtd/nand/raw/brcmnand/brcmnand_compat.c | 30 ---------------- drivers/mtd/nand/raw/brcmnand/brcmnand_compat.h | 4 --- include/clk.h | 47 ++++++++++++++++++++++++ 4 files changed, 95 insertions(+), 34 deletions(-) diff --git a/drivers/clk/clk-uclass.c b/drivers/clk/clk-uclass.c index 82a62c0..8034698 100644 --- a/drivers/clk/clk-uclass.c +++ b/drivers/clk/clk-uclass.c @@ -625,6 +625,54 @@ bool clk_is_match(const struct clk *p, const struct clk *q) return false; } +static void devm_clk_release(struct udevice *dev, void *res) +{ + clk_free(res); +} + +static int devm_clk_match(struct udevice *dev, void *res, void *data) +{ + return res == data; +} + +struct clk *devm_clk_get(struct udevice *dev, const char *id) +{ + int rc; + struct clk *clk; + + clk = devres_alloc(devm_clk_release, sizeof(struct clk), __GFP_ZERO); + if (unlikely(!clk)) + return ERR_PTR(-ENOMEM); + + rc = clk_get_by_name(dev, id, clk); + if (rc) + return ERR_PTR(rc); + + devres_add(dev, clk); + return clk; +} + +struct clk *devm_clk_get_optional(struct udevice *dev, const char *id) +{ + struct clk *clk = devm_clk_get(dev, id); + + if (IS_ERR(clk)) + return NULL; + + return clk; +} + +void devm_clk_put(struct udevice *dev, struct clk *clk) +{ + int rc; + + if (!clk) + return; + + rc = devres_release(dev, devm_clk_release, devm_clk_match, clk); + WARN_ON(rc); +} + UCLASS_DRIVER(clk) = { .id = UCLASS_CLK, .name = "clk", diff --git a/drivers/mtd/nand/raw/brcmnand/brcmnand_compat.c b/drivers/mtd/nand/raw/brcmnand/brcmnand_compat.c index 96b27e6..8839483 100644 --- a/drivers/mtd/nand/raw/brcmnand/brcmnand_compat.c +++ b/drivers/mtd/nand/raw/brcmnand/brcmnand_compat.c @@ -3,36 +3,6 @@ #include #include "brcmnand_compat.h" -struct clk *devm_clk_get(struct udevice *dev, const char *id) -{ - struct clk *clk; - int ret; - - clk = devm_kzalloc(dev, sizeof(*clk), GFP_KERNEL); - if (!clk) { - debug("%s: can't allocate clock\n", __func__); - return ERR_PTR(-ENOMEM); - } - - ret = clk_get_by_name(dev, id, clk); - if (ret < 0) { - debug("%s: can't get clock (ret = %d)!\n", __func__, ret); - return ERR_PTR(ret); - } - - return clk; -} - -int clk_prepare_enable(struct clk *clk) -{ - return clk_enable(clk); -} - -void clk_disable_unprepare(struct clk *clk) -{ - clk_disable(clk); -} - static char *devm_kvasprintf(struct udevice *dev, gfp_t gfp, const char *fmt, va_list ap) { diff --git a/drivers/mtd/nand/raw/brcmnand/brcmnand_compat.h b/drivers/mtd/nand/raw/brcmnand/brcmnand_compat.h index 02cab0f..6f9bec7 100644 --- a/drivers/mtd/nand/raw/brcmnand/brcmnand_compat.h +++ b/drivers/mtd/nand/raw/brcmnand/brcmnand_compat.h @@ -6,10 +6,6 @@ #include #include -struct clk *devm_clk_get(struct udevice *dev, const char *id); -int clk_prepare_enable(struct clk *clk); -void clk_disable_unprepare(struct clk *clk); - char *devm_kasprintf(struct udevice *dev, gfp_t gfp, const char *fmt, ...); #endif /* __BRCMNAND_COMPAT_H */ diff --git a/include/clk.h b/include/clk.h index 6568865..9be3264 100644 --- a/include/clk.h +++ b/include/clk.h @@ -155,6 +155,37 @@ int clk_get_bulk(struct udevice *dev, struct clk_bulk *bulk); int clk_get_by_name(struct udevice *dev, const char *name, struct clk *clk); /** + * devm_clk_get - lookup and obtain a managed reference to a clock producer. + * @dev: device for clock "consumer" + * @id: clock consumer ID + * + * Returns a struct clk corresponding to the clock producer, or + * valid IS_ERR() condition containing errno. The implementation + * uses @dev and @id to determine the clock consumer, and thereby + * the clock producer. (IOW, @id may be identical strings, but + * clk_get may return different clock producers depending on @dev.) + * + * Drivers must assume that the clock source is not enabled. + * + * devm_clk_get should not be called from within interrupt context. + * + * The clock will automatically be freed when the device is unbound + * from the bus. + */ +struct clk *devm_clk_get(struct udevice *dev, const char *id); + +/** + * devm_clk_get_optional - lookup and obtain a managed reference to an optional + * clock producer. + * @dev: device for clock "consumer" + * @id: clock consumer ID + * + * Behaves the same as devm_clk_get() except where there is no clock producer. + * In this case, instead of returning -ENOENT, the function returns NULL. + */ +struct clk *devm_clk_get_optional(struct udevice *dev, const char *id); + +/** * clk_release_all() - Disable (turn off)/Free an array of previously * requested clocks. * @@ -168,6 +199,19 @@ int clk_get_by_name(struct udevice *dev, const char *name, struct clk *clk); */ int clk_release_all(struct clk *clk, int count); +/** + * devm_clk_put - "free" a managed clock source + * @dev: device used to acquire the clock + * @clk: clock source acquired with devm_clk_get() + * + * Note: drivers must ensure that all clk_enable calls made on this + * clock source are balanced by clk_disable calls prior to calling + * this function. + * + * clk_put should not be called from within interrupt context. + */ +void devm_clk_put(struct udevice *dev, struct clk *clk); + #else static inline int clk_get_by_index(struct udevice *dev, int index, struct clk *clk) @@ -379,3 +423,6 @@ int clk_get_by_id(ulong id, struct clk **clkp); */ bool clk_dev_binded(struct clk *clk); #endif + +#define clk_prepare_enable(clk) clk_enable(clk) +#define clk_disable_unprepare(clk) clk_disable(clk) -- 2.7.4