From 266e4e9d9150e98141b85c7400f8aa3cd57a7f9b Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Fri, 19 May 2017 21:49:04 +0800 Subject: clk: add clk_bulk_get accessories These helper function allows drivers to get several clk consumers in one operation. If any of the clk cannot be acquired then any clks that were got will be put before returning to the caller. This can relieve the driver owners' life who needs to handle many clocks, as well as each clock error reporting. Cc: Michael Turquette Cc: Stephen Boyd Cc: Russell King Cc: Geert Uytterhoeven Cc: "Rafael J. Wysocki" Cc: Viresh Kumar Cc: Mark Brown Cc: Shawn Guo Cc: Fabio Estevam Cc: Sascha Hauer Cc: Anson Huang Cc: Robin Gong Cc: Bai Ping Cc: Leonard Crestez Cc: Octavian Purdila Signed-off-by: Dong Aisheng Signed-off-by: Stephen Boyd --- drivers/clk/Makefile | 2 +- drivers/clk/clk-bulk.c | 157 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 158 insertions(+), 1 deletion(-) create mode 100644 drivers/clk/clk-bulk.c (limited to 'drivers/clk') diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index c19983afcb81..ed1e99f19ec3 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -1,5 +1,5 @@ # common clock types -obj-$(CONFIG_HAVE_CLK) += clk-devres.o +obj-$(CONFIG_HAVE_CLK) += clk-devres.o clk-bulk.o obj-$(CONFIG_CLKDEV_LOOKUP) += clkdev.o obj-$(CONFIG_COMMON_CLK) += clk.o obj-$(CONFIG_COMMON_CLK) += clk-divider.o diff --git a/drivers/clk/clk-bulk.c b/drivers/clk/clk-bulk.c new file mode 100644 index 000000000000..c834f5abfc49 --- /dev/null +++ b/drivers/clk/clk-bulk.c @@ -0,0 +1,157 @@ +/* + * Copyright 2017 NXP + * + * Dong Aisheng + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include + +void clk_bulk_put(int num_clks, struct clk_bulk_data *clks) +{ + while (--num_clks >= 0) { + clk_put(clks[num_clks].clk); + clks[num_clks].clk = NULL; + } +} +EXPORT_SYMBOL_GPL(clk_bulk_put); + +int __must_check clk_bulk_get(struct device *dev, int num_clks, + struct clk_bulk_data *clks) +{ + int ret; + int i; + + for (i = 0; i < num_clks; i++) + clks[i].clk = NULL; + + for (i = 0; i < num_clks; i++) { + clks[i].clk = clk_get(dev, clks[i].id); + if (IS_ERR(clks[i].clk)) { + ret = PTR_ERR(clks[i].clk); + dev_err(dev, "Failed to get clk '%s': %d\n", + clks[i].id, ret); + clks[i].clk = NULL; + goto err; + } + } + + return 0; + +err: + clk_bulk_put(i, clks); + + return ret; +} +EXPORT_SYMBOL(clk_bulk_get); + +#ifdef CONFIG_HAVE_CLK_PREPARE + +/** + * clk_bulk_unprepare - undo preparation of a set of clock sources + * @num_clks: the number of clk_bulk_data + * @clks: the clk_bulk_data table being unprepared + * + * clk_bulk_unprepare may sleep, which differentiates it from clk_bulk_disable. + * Returns 0 on success, -EERROR otherwise. + */ +void clk_bulk_unprepare(int num_clks, const struct clk_bulk_data *clks) +{ + while (--num_clks >= 0) + clk_unprepare(clks[num_clks].clk); +} +EXPORT_SYMBOL_GPL(clk_bulk_unprepare); + +/** + * clk_bulk_prepare - prepare a set of clocks + * @num_clks: the number of clk_bulk_data + * @clks: the clk_bulk_data table being prepared + * + * clk_bulk_prepare may sleep, which differentiates it from clk_bulk_enable. + * Returns 0 on success, -EERROR otherwise. + */ +int __must_check clk_bulk_prepare(int num_clks, + const struct clk_bulk_data *clks) +{ + int ret; + int i; + + for (i = 0; i < num_clks; i++) { + ret = clk_prepare(clks[i].clk); + if (ret) { + pr_err("Failed to prepare clk '%s': %d\n", + clks[i].id, ret); + goto err; + } + } + + return 0; + +err: + clk_bulk_unprepare(i, clks); + + return ret; +} + +#endif /* CONFIG_HAVE_CLK_PREPARE */ + +/** + * clk_bulk_disable - gate a set of clocks + * @num_clks: the number of clk_bulk_data + * @clks: the clk_bulk_data table being gated + * + * clk_bulk_disable must not sleep, which differentiates it from + * clk_bulk_unprepare. clk_bulk_disable must be called before + * clk_bulk_unprepare. + */ +void clk_bulk_disable(int num_clks, const struct clk_bulk_data *clks) +{ + + while (--num_clks >= 0) + clk_disable(clks[num_clks].clk); +} +EXPORT_SYMBOL_GPL(clk_bulk_disable); + +/** + * clk_bulk_enable - ungate a set of clocks + * @num_clks: the number of clk_bulk_data + * @clks: the clk_bulk_data table being ungated + * + * clk_bulk_enable must not sleep + * Returns 0 on success, -EERROR otherwise. + */ +int __must_check clk_bulk_enable(int num_clks, const struct clk_bulk_data *clks) +{ + int ret; + int i; + + for (i = 0; i < num_clks; i++) { + ret = clk_enable(clks[i].clk); + if (ret) { + pr_err("Failed to enable clk '%s': %d\n", + clks[i].id, ret); + goto err; + } + } + + return 0; + +err: + clk_bulk_disable(i, clks); + + return ret; +} +EXPORT_SYMBOL_GPL(clk_bulk_enable); -- cgit v1.2.3 From 618aee02e2f57042f4cdeab228caf631e524b281 Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Fri, 19 May 2017 21:49:05 +0800 Subject: clk: add managed version of clk_bulk_get This patch introduces the managed version of clk_bulk_get. Cc: Michael Turquette Cc: Stephen Boyd Cc: Russell King Cc: Geert Uytterhoeven Cc: "Rafael J. Wysocki" Cc: Viresh Kumar Cc: Mark Brown Cc: Shawn Guo Cc: Fabio Estevam Cc: Sascha Hauer Cc: Anson Huang Cc: Robin Gong Cc: Bai Ping Cc: Leonard Crestez Cc: Octavian Purdila Signed-off-by: Dong Aisheng Signed-off-by: Stephen Boyd --- drivers/clk/clk-devres.c | 36 ++++++++++++++++++++++++++++++++++++ include/linux/clk.h | 21 +++++++++++++++++++++ 2 files changed, 57 insertions(+) (limited to 'drivers/clk') diff --git a/drivers/clk/clk-devres.c b/drivers/clk/clk-devres.c index 3a218c3a06ae..d854e26a8ddb 100644 --- a/drivers/clk/clk-devres.c +++ b/drivers/clk/clk-devres.c @@ -34,6 +34,42 @@ struct clk *devm_clk_get(struct device *dev, const char *id) } EXPORT_SYMBOL(devm_clk_get); +struct clk_bulk_devres { + struct clk_bulk_data *clks; + int num_clks; +}; + +static void devm_clk_bulk_release(struct device *dev, void *res) +{ + struct clk_bulk_devres *devres = res; + + clk_bulk_put(devres->num_clks, devres->clks); +} + +int __must_check devm_clk_bulk_get(struct device *dev, int num_clks, + struct clk_bulk_data *clks) +{ + struct clk_bulk_devres *devres; + int ret; + + devres = devres_alloc(devm_clk_bulk_release, + sizeof(*devres), GFP_KERNEL); + if (!devres) + return -ENOMEM; + + ret = clk_bulk_get(dev, num_clks, clks); + if (!ret) { + devres->clks = clks; + devres->num_clks = num_clks; + devres_add(dev, devres); + } else { + devres_free(devres); + } + + return ret; +} +EXPORT_SYMBOL_GPL(devm_clk_bulk_get); + static int devm_clk_match(struct device *dev, void *res, void *data) { struct clk **c = res; diff --git a/include/linux/clk.h b/include/linux/clk.h index 72b0cfce9165..c673f0b91751 100644 --- a/include/linux/clk.h +++ b/include/linux/clk.h @@ -280,6 +280,21 @@ struct clk *clk_get(struct device *dev, const char *id); int __must_check clk_bulk_get(struct device *dev, int num_clks, struct clk_bulk_data *clks); +/** + * devm_clk_bulk_get - managed get multiple clk consumers + * @dev: device for clock "consumer" + * @num_clks: the number of clk_bulk_data + * @clks: the clk_bulk_data table of consumer + * + * Return 0 on success, an errno on failure. + * + * This helper function allows drivers to get several clk + * consumers in one operation with management, the clks will + * automatically be freed when the device is unbound. + */ +int __must_check devm_clk_bulk_get(struct device *dev, int num_clks, + struct clk_bulk_data *clks); + /** * devm_clk_get - lookup and obtain a managed reference to a clock producer. * @dev: device for clock "consumer" @@ -550,6 +565,12 @@ static inline struct clk *devm_clk_get(struct device *dev, const char *id) return NULL; } +static inline int devm_clk_bulk_get(struct device *dev, int num_clks, + struct clk_bulk_data *clks) +{ + return 0; +} + static inline struct clk *devm_get_clk_from_child(struct device *dev, struct device_node *np, const char *con_id) { -- cgit v1.2.3