From 4143804c4fdef40358c654d1fb2271a1a0f1fedf Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Fri, 5 Feb 2016 17:02:52 -0800 Subject: [PATCH] clk: Add {devm_}clk_hw_{register,unregister}() APIs We've largely split the clk consumer and provider APIs along struct clk and struct clk_hw, but clk_register() still returns a struct clk pointer for each struct clk_hw that's registered. Eventually we'd like to only allocate struct clks when there's a user, because struct clk is per-user now, so clk_register() needs to change. Let's add new APIs to register struct clk_hws, but this time we'll hide the struct clk from the caller by returning an int error code. Also add an unregistration API that takes the clk_hw structure that was passed to the registration API. This way provider drivers never have to deal with a struct clk pointer unless they're using the clk consumer APIs. Signed-off-by: Stephen Boyd --- Documentation/driver-model/devres.txt | 1 + drivers/clk/clk.c | 86 +++++++++++++++++++++++++++++++++++ include/linux/clk-provider.h | 6 +++ 3 files changed, 93 insertions(+) --- a/Documentation/driver-model/devres.txt +++ b/Documentation/driver-model/devres.txt @@ -236,6 +236,7 @@ certainly invest a bit more effort into CLOCK devm_clk_get() devm_clk_put() + devm_clk_hw_register() DMA dmam_alloc_coherent() --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -2595,6 +2595,22 @@ fail_out: } EXPORT_SYMBOL_GPL(clk_register); +/** + * clk_hw_register - register a clk_hw and return an error code + * @dev: device that is registering this clock + * @hw: link to hardware-specific clock data + * + * clk_hw_register is the primary interface for populating the clock tree with + * new clock nodes. It returns an integer equal to zero indicating success or + * less than zero indicating failure. Drivers must test for an error code after + * calling clk_hw_register(). + */ +int clk_hw_register(struct device *dev, struct clk_hw *hw) +{ + return PTR_ERR_OR_ZERO(clk_register(dev, hw)); +} +EXPORT_SYMBOL_GPL(clk_hw_register); + /* Free memory allocated for a clock. */ static void __clk_release(struct kref *ref) { @@ -2696,11 +2712,26 @@ void clk_unregister(struct clk *clk) } EXPORT_SYMBOL_GPL(clk_unregister); +/** + * clk_hw_unregister - unregister a currently registered clk_hw + * @hw: hardware-specific clock data to unregister + */ +void clk_hw_unregister(struct clk_hw *hw) +{ + clk_unregister(hw->clk); +} +EXPORT_SYMBOL_GPL(clk_hw_unregister); + static void devm_clk_release(struct device *dev, void *res) { clk_unregister(*(struct clk **)res); } +static void devm_clk_hw_release(struct device *dev, void *res) +{ + clk_hw_unregister(*(struct clk_hw **)res); +} + /** * devm_clk_register - resource managed clk_register() * @dev: device that is registering this clock @@ -2731,6 +2762,36 @@ struct clk *devm_clk_register(struct dev } EXPORT_SYMBOL_GPL(devm_clk_register); +/** + * devm_clk_hw_register - resource managed clk_hw_register() + * @dev: device that is registering this clock + * @hw: link to hardware-specific clock data + * + * Managed clk_hw_register(). Clocks returned from this function are + * automatically clk_hw_unregister()ed on driver detach. See clk_hw_register() + * for more information. + */ +int devm_clk_hw_register(struct device *dev, struct clk_hw *hw) +{ + struct clk_hw **hwp; + int ret; + + hwp = devres_alloc(devm_clk_hw_release, sizeof(*hwp), GFP_KERNEL); + if (!hwp) + return -ENOMEM; + + ret = clk_hw_register(dev, hw); + if (!ret) { + *hwp = hw; + devres_add(dev, hwp); + } else { + devres_free(hwp); + } + + return ret; +} +EXPORT_SYMBOL_GPL(devm_clk_hw_register); + static int devm_clk_match(struct device *dev, void *res, void *data) { struct clk *c = res; @@ -2739,6 +2800,15 @@ static int devm_clk_match(struct device return c == data; } +static int devm_clk_hw_match(struct device *dev, void *res, void *data) +{ + struct clk_hw *hw = res; + + if (WARN_ON(!hw)) + return 0; + return hw == data; +} + /** * devm_clk_unregister - resource managed clk_unregister() * @clk: clock to unregister @@ -2753,6 +2823,22 @@ void devm_clk_unregister(struct device * } EXPORT_SYMBOL_GPL(devm_clk_unregister); +/** + * devm_clk_hw_unregister - resource managed clk_hw_unregister() + * @dev: device that is unregistering the hardware-specific clock data + * @hw: link to hardware-specific clock data + * + * Unregister a clk_hw registered with devm_clk_hw_register(). Normally + * this function will not need to be called and the resource management + * code will ensure that the resource is freed. + */ +void devm_clk_hw_unregister(struct device *dev, struct clk_hw *hw) +{ + WARN_ON(devres_release(dev, devm_clk_hw_release, devm_clk_hw_match, + hw)); +} +EXPORT_SYMBOL_GPL(devm_clk_hw_unregister); + /* * clkdev helpers */ --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -639,9 +639,15 @@ void of_gpio_mux_clk_setup(struct device struct clk *clk_register(struct device *dev, struct clk_hw *hw); struct clk *devm_clk_register(struct device *dev, struct clk_hw *hw); +int __must_check clk_hw_register(struct device *dev, struct clk_hw *hw); +int __must_check devm_clk_hw_register(struct device *dev, struct clk_hw *hw); + void clk_unregister(struct clk *clk); void devm_clk_unregister(struct device *dev, struct clk *clk); +void clk_hw_unregister(struct clk_hw *hw); +void devm_clk_hw_unregister(struct device *dev, struct clk_hw *hw); + /* helper functions */ const char *__clk_get_name(const struct clk *clk); const char *clk_hw_get_name(const struct clk_hw *hw);