clkdev: add managed clkdev lookup registration

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>
This commit is contained in:
Matti Vaittinen 2018-12-07 13:09:39 +02:00 committed by Stephen Boyd
parent bfeffd1552
commit 3eee6c7d11
3 changed files with 94 additions and 24 deletions

View File

@ -245,6 +245,7 @@ CLOCK
devm_clk_put() devm_clk_put()
devm_clk_hw_register() devm_clk_hw_register()
devm_of_clk_add_hw_provider() devm_of_clk_add_hw_provider()
devm_clk_hw_register_clkdev()
DMA DMA
dmaenginem_async_device_register() dmaenginem_async_device_register()

View File

@ -401,6 +401,23 @@ static struct clk_lookup *__clk_register_clkdev(struct clk_hw *hw,
return cl; 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_register_clkdev - register one clock lookup for a struct clk
* @clk: struct clk to associate with all clk_lookups * @clk: struct clk to associate with all clk_lookups
@ -423,17 +440,8 @@ int clk_register_clkdev(struct clk *clk, const char *con_id,
if (IS_ERR(clk)) if (IS_ERR(clk))
return PTR_ERR(clk); return PTR_ERR(clk);
/* return do_clk_register_clkdev(__clk_get_hw(clk), &cl, con_id,
* Since dev_id can be NULL, and NULL is handled specially, we must dev_id);
* 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;
} }
EXPORT_SYMBOL(clk_register_clkdev); EXPORT_SYMBOL(clk_register_clkdev);
@ -456,18 +464,75 @@ int clk_hw_register_clkdev(struct clk_hw *hw, const char *con_id,
{ {
struct clk_lookup *cl; struct clk_lookup *cl;
if (IS_ERR(hw)) return do_clk_register_clkdev(hw, &cl, con_id, dev_id);
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;
} }
EXPORT_SYMBOL(clk_hw_register_clkdev); EXPORT_SYMBOL(clk_hw_register_clkdev);
static void devm_clkdev_release(struct device *dev, void *res)
{
clkdev_drop(*(struct clk_lookup **)res);
}
static int devm_clk_match_clkdev(struct device *dev, void *res, void *data)
{
struct clk_lookup **l = res;
return *l == data;
}
/**
* 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);

View File

@ -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_register_clkdev(struct clk *, const char *, const char *);
int clk_hw_register_clkdev(struct clk_hw *, 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 #endif