Add a new managed clock framework helper function that combines getting optional bulk clocks and enabling them in a single operation. The devm_clk_bulk_get_optional_enable() function simplifies the common pattern where drivers need to get optional bulk clocks, prepare and enable them, and have them automatically disabled/unprepared and freed when the device is unbound. This new API follows the established pattern of devm_clk_bulk_get_all_enabled() and reduces boilerplate code in drivers that manage multiple optional clocks. Suggested-by: Andrew Lunn Signed-off-by: Suraj Gupta Reviewed-by: Brian Masney --- drivers/clk/clk-devres.c | 50 ++++++++++++++++++++++++++++++++++++++++ include/linux/clk.h | 23 ++++++++++++++++++ 2 files changed, 73 insertions(+) diff --git a/drivers/clk/clk-devres.c b/drivers/clk/clk-devres.c index 5368d92d9b39..994d5bc5168b 100644 --- a/drivers/clk/clk-devres.c +++ b/drivers/clk/clk-devres.c @@ -179,6 +179,56 @@ int __must_check devm_clk_bulk_get_optional(struct device *dev, int num_clks, } EXPORT_SYMBOL_GPL(devm_clk_bulk_get_optional); +static void devm_clk_bulk_release_enable(struct device *dev, void *res) +{ + struct clk_bulk_devres *devres = res; + + clk_bulk_disable_unprepare(devres->num_clks, devres->clks); + clk_bulk_put(devres->num_clks, devres->clks); +} + +static int __devm_clk_bulk_get_enable(struct device *dev, int num_clks, + struct clk_bulk_data *clks, bool optional) +{ + struct clk_bulk_devres *devres; + int ret; + + devres = devres_alloc(devm_clk_bulk_release_enable, + sizeof(*devres), GFP_KERNEL); + if (!devres) + return -ENOMEM; + + if (optional) + ret = clk_bulk_get_optional(dev, num_clks, clks); + else + ret = clk_bulk_get(dev, num_clks, clks); + if (ret) + goto err_clk_get; + + ret = clk_bulk_prepare_enable(num_clks, clks); + if (ret) + goto err_clk_prepare; + + devres->clks = clks; + devres->num_clks = num_clks; + devres_add(dev, devres); + + return 0; + +err_clk_prepare: + clk_bulk_put(num_clks, clks); +err_clk_get: + devres_free(devres); + return ret; +} + +int __must_check devm_clk_bulk_get_optional_enable(struct device *dev, int num_clks, + struct clk_bulk_data *clks) +{ + return __devm_clk_bulk_get_enable(dev, num_clks, clks, true); +} +EXPORT_SYMBOL_GPL(devm_clk_bulk_get_optional_enable); + static void devm_clk_bulk_release_all(struct device *dev, void *res) { struct clk_bulk_devres *devres = res; diff --git a/include/linux/clk.h b/include/linux/clk.h index b607482ca77e..ac0affa16c8a 100644 --- a/include/linux/clk.h +++ b/include/linux/clk.h @@ -478,6 +478,22 @@ int __must_check devm_clk_bulk_get(struct device *dev, int num_clks, */ int __must_check devm_clk_bulk_get_optional(struct device *dev, int num_clks, struct clk_bulk_data *clks); +/** + * devm_clk_bulk_get_optional_enable - Get and enable optional bulk clocks (managed) + * @dev: device for clock "consumer" + * @num_clks: the number of clk_bulk_data + * @clks: pointer to the clk_bulk_data table of consumer + * + * Behaves the same as devm_clk_bulk_get_optional() but also prepares and enables + * the clocks in one operation with management. The clks will automatically be + * disabled, unprepared and freed when the device is unbound. + * + * Returns 0 if all clocks specified in clk_bulk_data table are obtained + * and enabled successfully, or for any clk there was no clk provider available. + * Otherwise returns valid IS_ERR() condition containing errno. + */ +int __must_check devm_clk_bulk_get_optional_enable(struct device *dev, int num_clks, + struct clk_bulk_data *clks); /** * devm_clk_bulk_get_all - managed get multiple clk consumers * @dev: device for clock "consumer" @@ -1029,6 +1045,13 @@ static inline int __must_check devm_clk_bulk_get_optional(struct device *dev, return 0; } +static inline int __must_check devm_clk_bulk_get_optional_enable(struct device *dev, + int num_clks, + struct clk_bulk_data *clks) +{ + return 0; +} + static inline int __must_check devm_clk_bulk_get_all(struct device *dev, struct clk_bulk_data **clks) { -- 2.25.1