Skip to content

Commit

Permalink
clkdev: add managed clkdev lookup registration
Browse files Browse the repository at this point in the history
Clkdev registration lacks of managed registration functions and it
seems few drivers do not drop clkdev lookups at exit. Add
devm_clk_hw_register_clkdev and devm_clk_release_clkdev to ease lookup
releasing at exit.

Signed-off-by: Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>
Signed-off-by: Stephen Boyd <sboyd@kernel.org>
  • Loading branch information
Matti Vaittinen authored and Stephen Boyd committed Feb 6, 2019
1 parent bfeffd1 commit 3eee6c7
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 23 deletions.
1 change: 1 addition & 0 deletions Documentation/driver-model/devres.txt
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,7 @@ CLOCK
devm_clk_put()
devm_clk_hw_register()
devm_of_clk_add_hw_provider()
devm_clk_hw_register_clkdev()

DMA
dmaenginem_async_device_register()
Expand Down
111 changes: 88 additions & 23 deletions drivers/clk/clkdev.c
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,23 @@ static struct clk_lookup *__clk_register_clkdev(struct clk_hw *hw,
return cl;
}

static int do_clk_register_clkdev(struct clk_hw *hw,
struct clk_lookup **cl, const char *con_id, const char *dev_id)
{
if (IS_ERR(hw))
return PTR_ERR(hw);
/*
* Since dev_id can be NULL, and NULL is handled specially, we must
* pass it as either a NULL format string, or with "%s".
*/
if (dev_id)
*cl = __clk_register_clkdev(hw, con_id, "%s", dev_id);
else
*cl = __clk_register_clkdev(hw, con_id, NULL);

return *cl ? 0 : -ENOMEM;
}

/**
* clk_register_clkdev - register one clock lookup for a struct clk
* @clk: struct clk to associate with all clk_lookups
Expand All @@ -423,17 +440,8 @@ int clk_register_clkdev(struct clk *clk, const char *con_id,
if (IS_ERR(clk))
return PTR_ERR(clk);

/*
* Since dev_id can be NULL, and NULL is handled specially, we must
* pass it as either a NULL format string, or with "%s".
*/
if (dev_id)
cl = __clk_register_clkdev(__clk_get_hw(clk), con_id, "%s",
dev_id);
else
cl = __clk_register_clkdev(__clk_get_hw(clk), con_id, NULL);

return cl ? 0 : -ENOMEM;
return do_clk_register_clkdev(__clk_get_hw(clk), &cl, con_id,
dev_id);
}
EXPORT_SYMBOL(clk_register_clkdev);

Expand All @@ -456,18 +464,75 @@ int clk_hw_register_clkdev(struct clk_hw *hw, const char *con_id,
{
struct clk_lookup *cl;

if (IS_ERR(hw))
return PTR_ERR(hw);
return do_clk_register_clkdev(hw, &cl, con_id, dev_id);
}
EXPORT_SYMBOL(clk_hw_register_clkdev);

/*
* Since dev_id can be NULL, and NULL is handled specially, we must
* pass it as either a NULL format string, or with "%s".
*/
if (dev_id)
cl = __clk_register_clkdev(hw, con_id, "%s", dev_id);
else
cl = __clk_register_clkdev(hw, con_id, NULL);
static void devm_clkdev_release(struct device *dev, void *res)
{
clkdev_drop(*(struct clk_lookup **)res);
}

return cl ? 0 : -ENOMEM;
static int devm_clk_match_clkdev(struct device *dev, void *res, void *data)
{
struct clk_lookup **l = res;

return *l == data;
}
EXPORT_SYMBOL(clk_hw_register_clkdev);

/**
* devm_clk_release_clkdev - Resource managed clkdev lookup release
* @dev: device this lookup is bound
* @con_id: connection ID string on device
* @dev_id: format string describing device name
*
* Drop the clkdev lookup created with devm_clk_hw_register_clkdev.
* Normally this function will not need to be called and the resource
* management code will ensure that the resource is freed.
*/
void devm_clk_release_clkdev(struct device *dev, const char *con_id,
const char *dev_id)
{
struct clk_lookup *cl;
int rval;

cl = clk_find(dev_id, con_id);
WARN_ON(!cl);
rval = devres_release(dev, devm_clkdev_release,
devm_clk_match_clkdev, cl);
WARN_ON(rval);
}
EXPORT_SYMBOL(devm_clk_release_clkdev);

/**
* devm_clk_hw_register_clkdev - managed clk lookup registration for clk_hw
* @dev: device this lookup is bound
* @hw: struct clk_hw to associate with all clk_lookups
* @con_id: connection ID string on device
* @dev_id: format string describing device name
*
* con_id or dev_id may be NULL as a wildcard, just as in the rest of
* clkdev.
*
* To make things easier for mass registration, we detect error clk_hws
* from a previous clk_hw_register_*() call, and return the error code for
* those. This is to permit this function to be called immediately
* after clk_hw_register_*().
*/
int devm_clk_hw_register_clkdev(struct device *dev, struct clk_hw *hw,
const char *con_id, const char *dev_id)
{
int rval = -ENOMEM;
struct clk_lookup **cl;

cl = devres_alloc(devm_clkdev_release, sizeof(*cl), GFP_KERNEL);
if (cl) {
rval = do_clk_register_clkdev(hw, cl, con_id, dev_id);
if (!rval)
devres_add(dev, cl);
else
devres_free(cl);
}
return rval;
}
EXPORT_SYMBOL(devm_clk_hw_register_clkdev);
4 changes: 4 additions & 0 deletions include/linux/clkdev.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,8 @@ int clk_add_alias(const char *, const char *, const char *, struct device *);
int clk_register_clkdev(struct clk *, const char *, const char *);
int clk_hw_register_clkdev(struct clk_hw *, const char *, const char *);

int devm_clk_hw_register_clkdev(struct device *dev, struct clk_hw *hw,
const char *con_id, const char *dev_id);
void devm_clk_release_clkdev(struct device *dev, const char *con_id,
const char *dev_id);
#endif

0 comments on commit 3eee6c7

Please sign in to comment.