From 8a1661f20e6cdda682820eba9ccc79bff5a3d7f8 Mon Sep 17 00:00:00 2001 From: Jean-Jacques Hiblot Date: Tue, 22 Oct 2019 14:00:03 +0200 Subject: drivers: clk: Handle gracefully NULL pointers Prepare the way for a managed CLK API by handling NULL pointers without crashing nor failing. Signed-off-by: Jean-Jacques Hiblot --- include/clk.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/clk.h b/include/clk.h index 18b2e3ca54c..6568865d40f 100644 --- a/include/clk.h +++ b/include/clk.h @@ -356,7 +356,7 @@ int soc_clk_dump(void); */ static inline bool clk_valid(struct clk *clk) { - return !!clk->dev; + return clk && !!clk->dev; } /** -- cgit v1.3.1 From 52720c536ffdbe0e6aece79840e2791d87204cf7 Mon Sep 17 00:00:00 2001 From: Jean-Jacques Hiblot Date: Tue, 22 Oct 2019 14:00:04 +0200 Subject: 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(-) (limited to 'include') diff --git a/drivers/clk/clk-uclass.c b/drivers/clk/clk-uclass.c index 82a62c02d99..80346987b9d 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 96b27e6e5ad..883948355ca 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 02cab0f828c..6f9bec7085c 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 6568865d40f..9be3264113f 100644 --- a/include/clk.h +++ b/include/clk.h @@ -154,6 +154,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) -- cgit v1.3.1 From fd1ba2965244e7628ac3aa539e51653490e1b4fe Mon Sep 17 00:00:00 2001 From: Jean-Jacques Hiblot Date: Tue, 22 Oct 2019 14:00:06 +0200 Subject: drivers: clk: Fix using assigned-clocks in the node of the clock it sets up This fixes the case where assigned-clocks is used to define a clock defaults inside this same clock's node. This is used sometimes to setup a default parents and/or rate for a clock. example: muxed_clock: muxed_clock { clocks = <&clk_provider 0>, <&clk_provider 1>; #clock-cells = <0>; assigned-clocks = <&muxed_clock>; assigned-clock-parents = <&clk_provider 1>; }; It doesn't work in u-boot because the assigned-clocks are setup *before* the clock is probed. (clk_set_parent() will likely crash or fail if called before the device probe function) Making it work by handling "assigned-clocks" in 2 steps: first before the clk device is probed, and then after the clk device is probed. Signed-off-by: Jean-Jacques Hiblot --- drivers/clk/clk-uclass.c | 48 ++++++++++++++++++++++++++++++++++++++++++------ drivers/core/device.c | 2 +- include/clk.h | 7 +++++-- 3 files changed, 48 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/drivers/clk/clk-uclass.c b/drivers/clk/clk-uclass.c index 80346987b9d..9aa8537004a 100644 --- a/drivers/clk/clk-uclass.c +++ b/drivers/clk/clk-uclass.c @@ -178,7 +178,7 @@ bulk_get_err: return ret; } -static int clk_set_default_parents(struct udevice *dev) +static int clk_set_default_parents(struct udevice *dev, int stage) { struct clk clk, parent_clk; int index; @@ -214,8 +214,18 @@ static int clk_set_default_parents(struct udevice *dev) return ret; } - ret = clk_set_parent(&clk, &parent_clk); + /* This is clk provider device trying to reparent itself + * It cannot be done right now but need to wait after the + * device is probed + */ + if (stage == 0 && clk.dev == dev) + continue; + + if (stage > 0 && clk.dev != dev) + /* do not setup twice the parent clocks */ + continue; + ret = clk_set_parent(&clk, &parent_clk); /* * Not all drivers may support clock-reparenting (as of now). * Ignore errors due to this. @@ -233,7 +243,7 @@ static int clk_set_default_parents(struct udevice *dev) return 0; } -static int clk_set_default_rates(struct udevice *dev) +static int clk_set_default_rates(struct udevice *dev, int stage) { struct clk clk; int index; @@ -268,7 +278,19 @@ static int clk_set_default_rates(struct udevice *dev) continue; } + /* This is clk provider device trying to program itself + * It cannot be done right now but need to wait after the + * device is probed + */ + if (stage == 0 && clk.dev == dev) + continue; + + if (stage > 0 && clk.dev != dev) + /* do not setup twice the parent clocks */ + continue; + ret = clk_set_rate(&clk, rates[index]); + if (ret < 0) { debug("%s: failed to set rate on clock index %d (%ld) for %s\n", __func__, index, clk.id, dev_read_name(dev)); @@ -281,7 +303,7 @@ fail: return ret; } -int clk_set_defaults(struct udevice *dev) +int clk_set_defaults(struct udevice *dev, int stage) { int ret; @@ -294,11 +316,11 @@ int clk_set_defaults(struct udevice *dev) debug("%s(%s)\n", __func__, dev_read_name(dev)); - ret = clk_set_default_parents(dev); + ret = clk_set_default_parents(dev, stage); if (ret) return ret; - ret = clk_set_default_rates(dev); + ret = clk_set_default_rates(dev, stage); if (ret < 0) return ret; @@ -673,7 +695,21 @@ void devm_clk_put(struct udevice *dev, struct clk *clk) WARN_ON(rc); } +int clk_uclass_post_probe(struct udevice *dev) +{ + /* + * when a clock provider is probed. Call clk_set_defaults() + * also after the device is probed. This takes care of cases + * where the DT is used to setup default parents and rates + * using assigned-clocks + */ + clk_set_defaults(dev, 1); + + return 0; +} + UCLASS_DRIVER(clk) = { .id = UCLASS_CLK, .name = "clk", + .post_probe = clk_uclass_post_probe, }; diff --git a/drivers/core/device.c b/drivers/core/device.c index 95f26efdd3b..8eabaf8b553 100644 --- a/drivers/core/device.c +++ b/drivers/core/device.c @@ -423,7 +423,7 @@ int device_probe(struct udevice *dev) * Process 'assigned-{clocks/clock-parents/clock-rates}' * properties */ - ret = clk_set_defaults(dev); + ret = clk_set_defaults(dev, 0); if (ret) goto fail; } diff --git a/include/clk.h b/include/clk.h index 9be3264113f..a5ee53d94aa 100644 --- a/include/clk.h +++ b/include/clk.h @@ -244,10 +244,13 @@ static inline int clk_release_all(struct clk *clk, int count) * * @dev: A device to process (the ofnode associated with this device * will be processed). + * @stage: A integer. 0 indicates that this is called before the device + * is probed. 1 indicates that this is called just after the + * device has been probed */ -int clk_set_defaults(struct udevice *dev); +int clk_set_defaults(struct udevice *dev, int stage); #else -static inline int clk_set_defaults(struct udevice *dev) +static inline int clk_set_defaults(struct udevice *dev, int stage) { return 0; } -- cgit v1.3.1