Skip to content

Commit

Permalink
clk: Add devm_clk_{register,unregister}()
Browse files Browse the repository at this point in the history
Some clock drivers can be simplified if devres takes care of
unregistering any registered clocks along error paths. Introduce
devm_clk_register() so that clock drivers get unregistration for
free along with simplified error paths.

Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
Signed-off-by: Mike Turquette <mturquette@linaro.org>
  • Loading branch information
Stephen Boyd authored and Mike Turquette committed Oct 29, 2012
1 parent 980f58a commit 46c8773
Show file tree
Hide file tree
Showing 2 changed files with 92 additions and 21 deletions.
111 changes: 90 additions & 21 deletions drivers/clk/clk.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include <linux/list.h>
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/device.h>

static DEFINE_SPINLOCK(enable_lock);
static DEFINE_MUTEX(prepare_lock);
Expand Down Expand Up @@ -1361,28 +1362,9 @@ struct clk *__clk_register(struct device *dev, struct clk_hw *hw)
}
EXPORT_SYMBOL_GPL(__clk_register);

/**
* clk_register - allocate a new clock, register it and return an opaque cookie
* @dev: device that is registering this clock
* @hw: link to hardware-specific clock data
*
* clk_register is the primary interface for populating the clock tree with new
* clock nodes. It returns a pointer to the newly allocated struct clk which
* cannot be dereferenced by driver code but may be used in conjuction with the
* rest of the clock API. In the event of an error clk_register will return an
* error code; drivers must test for an error code after calling clk_register.
*/
struct clk *clk_register(struct device *dev, struct clk_hw *hw)
static int _clk_register(struct device *dev, struct clk_hw *hw, struct clk *clk)
{
int i, ret;
struct clk *clk;

clk = kzalloc(sizeof(*clk), GFP_KERNEL);
if (!clk) {
pr_err("%s: could not allocate clk\n", __func__);
ret = -ENOMEM;
goto fail_out;
}

clk->name = kstrdup(hw->init->name, GFP_KERNEL);
if (!clk->name) {
Expand Down Expand Up @@ -1420,7 +1402,7 @@ struct clk *clk_register(struct device *dev, struct clk_hw *hw)

ret = __clk_init(dev, clk);
if (!ret)
return clk;
return 0;

fail_parent_names_copy:
while (--i >= 0)
Expand All @@ -1429,6 +1411,36 @@ struct clk *clk_register(struct device *dev, struct clk_hw *hw)
fail_parent_names:
kfree(clk->name);
fail_name:
return ret;
}

/**
* clk_register - allocate a new clock, register it and return an opaque cookie
* @dev: device that is registering this clock
* @hw: link to hardware-specific clock data
*
* clk_register is the primary interface for populating the clock tree with new
* clock nodes. It returns a pointer to the newly allocated struct clk which
* cannot be dereferenced by driver code but may be used in conjuction with the
* rest of the clock API. In the event of an error clk_register will return an
* error code; drivers must test for an error code after calling clk_register.
*/
struct clk *clk_register(struct device *dev, struct clk_hw *hw)
{
int ret;
struct clk *clk;

clk = kzalloc(sizeof(*clk), GFP_KERNEL);
if (!clk) {
pr_err("%s: could not allocate clk\n", __func__);
ret = -ENOMEM;
goto fail_out;
}

ret = _clk_register(dev, hw, clk);
if (!ret)
return clk;

kfree(clk);
fail_out:
return ERR_PTR(ret);
Expand All @@ -1444,6 +1456,63 @@ EXPORT_SYMBOL_GPL(clk_register);
void clk_unregister(struct clk *clk) {}
EXPORT_SYMBOL_GPL(clk_unregister);

static void devm_clk_release(struct device *dev, void *res)
{
clk_unregister(res);
}

/**
* devm_clk_register - resource managed clk_register()
* @dev: device that is registering this clock
* @hw: link to hardware-specific clock data
*
* Managed clk_register(). Clocks returned from this function are
* automatically clk_unregister()ed on driver detach. See clk_register() for
* more information.
*/
struct clk *devm_clk_register(struct device *dev, struct clk_hw *hw)
{
struct clk *clk;
int ret;

clk = devres_alloc(devm_clk_release, sizeof(*clk), GFP_KERNEL);
if (!clk)
return ERR_PTR(-ENOMEM);

ret = _clk_register(dev, hw, clk);
if (!ret) {
devres_add(dev, clk);
} else {
devres_free(clk);
clk = ERR_PTR(ret);
}

return clk;
}
EXPORT_SYMBOL_GPL(devm_clk_register);

static int devm_clk_match(struct device *dev, void *res, void *data)
{
struct clk *c = res;
if (WARN_ON(!c))
return 0;
return c == data;
}

/**
* devm_clk_unregister - resource managed clk_unregister()
* @clk: clock to unregister
*
* Deallocate a clock allocated with devm_clk_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_unregister(struct device *dev, struct clk *clk)
{
WARN_ON(devres_release(dev, devm_clk_release, devm_clk_match, clk));
}
EXPORT_SYMBOL_GPL(devm_clk_unregister);

/*** clk rate change notifiers ***/

/**
Expand Down
2 changes: 2 additions & 0 deletions include/linux/clk-provider.h
Original file line number Diff line number Diff line change
Expand Up @@ -331,8 +331,10 @@ struct clk *clk_register_fixed_factor(struct device *dev, const char *name,
* error code; drivers must test for an error code after calling clk_register.
*/
struct clk *clk_register(struct device *dev, struct clk_hw *hw);
struct clk *devm_clk_register(struct device *dev, struct clk_hw *hw);

void clk_unregister(struct clk *clk);
void devm_clk_unregister(struct device *dev, struct clk *clk);

/* helper functions */
const char *__clk_get_name(struct clk *clk);
Expand Down

0 comments on commit 46c8773

Please sign in to comment.