diff options
author | Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com> | 2018-12-07 06:09:39 -0500 |
---|---|---|
committer | Stephen Boyd <sboyd@kernel.org> | 2019-02-06 13:35:02 -0500 |
commit | 3eee6c7d119cd8563ad25898f94d6c1b514da548 (patch) | |
tree | a78d21a7c0f1ba47021cec1f0730f14036568166 | |
parent | bfeffd155283772bbe78c6a05dec7c0128ee500c (diff) |
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>
-rw-r--r-- | Documentation/driver-model/devres.txt | 1 | ||||
-rw-r--r-- | drivers/clk/clkdev.c | 111 | ||||
-rw-r--r-- | include/linux/clkdev.h | 4 |
3 files changed, 93 insertions, 23 deletions
diff --git a/Documentation/driver-model/devres.txt b/Documentation/driver-model/devres.txt index b277cafce71e..805b7bf5d98f 100644 --- a/Documentation/driver-model/devres.txt +++ b/Documentation/driver-model/devres.txt | |||
@@ -245,6 +245,7 @@ CLOCK | |||
245 | devm_clk_put() | 245 | devm_clk_put() |
246 | devm_clk_hw_register() | 246 | devm_clk_hw_register() |
247 | devm_of_clk_add_hw_provider() | 247 | devm_of_clk_add_hw_provider() |
248 | devm_clk_hw_register_clkdev() | ||
248 | 249 | ||
249 | DMA | 250 | DMA |
250 | dmaenginem_async_device_register() | 251 | dmaenginem_async_device_register() |
diff --git a/drivers/clk/clkdev.c b/drivers/clk/clkdev.c index 9ab3db8b3988..4621f8a91fc0 100644 --- a/drivers/clk/clkdev.c +++ b/drivers/clk/clkdev.c | |||
@@ -401,6 +401,23 @@ static struct clk_lookup *__clk_register_clkdev(struct clk_hw *hw, | |||
401 | return cl; | 401 | return cl; |
402 | } | 402 | } |
403 | 403 | ||
404 | static int do_clk_register_clkdev(struct clk_hw *hw, | ||
405 | struct clk_lookup **cl, const char *con_id, const char *dev_id) | ||
406 | { | ||
407 | if (IS_ERR(hw)) | ||
408 | return PTR_ERR(hw); | ||
409 | /* | ||
410 | * Since dev_id can be NULL, and NULL is handled specially, we must | ||
411 | * pass it as either a NULL format string, or with "%s". | ||
412 | */ | ||
413 | if (dev_id) | ||
414 | *cl = __clk_register_clkdev(hw, con_id, "%s", dev_id); | ||
415 | else | ||
416 | *cl = __clk_register_clkdev(hw, con_id, NULL); | ||
417 | |||
418 | return *cl ? 0 : -ENOMEM; | ||
419 | } | ||
420 | |||
404 | /** | 421 | /** |
405 | * clk_register_clkdev - register one clock lookup for a struct clk | 422 | * clk_register_clkdev - register one clock lookup for a struct clk |
406 | * @clk: struct clk to associate with all clk_lookups | 423 | * @clk: struct clk to associate with all clk_lookups |
@@ -423,17 +440,8 @@ int clk_register_clkdev(struct clk *clk, const char *con_id, | |||
423 | if (IS_ERR(clk)) | 440 | if (IS_ERR(clk)) |
424 | return PTR_ERR(clk); | 441 | return PTR_ERR(clk); |
425 | 442 | ||
426 | /* | 443 | return do_clk_register_clkdev(__clk_get_hw(clk), &cl, con_id, |
427 | * Since dev_id can be NULL, and NULL is handled specially, we must | 444 | dev_id); |
428 | * pass it as either a NULL format string, or with "%s". | ||
429 | */ | ||
430 | if (dev_id) | ||
431 | cl = __clk_register_clkdev(__clk_get_hw(clk), con_id, "%s", | ||
432 | dev_id); | ||
433 | else | ||
434 | cl = __clk_register_clkdev(__clk_get_hw(clk), con_id, NULL); | ||
435 | |||
436 | return cl ? 0 : -ENOMEM; | ||
437 | } | 445 | } |
438 | EXPORT_SYMBOL(clk_register_clkdev); | 446 | EXPORT_SYMBOL(clk_register_clkdev); |
439 | 447 | ||
@@ -456,18 +464,75 @@ int clk_hw_register_clkdev(struct clk_hw *hw, const char *con_id, | |||
456 | { | 464 | { |
457 | struct clk_lookup *cl; | 465 | struct clk_lookup *cl; |
458 | 466 | ||
459 | if (IS_ERR(hw)) | 467 | return do_clk_register_clkdev(hw, &cl, con_id, dev_id); |
460 | return PTR_ERR(hw); | 468 | } |
469 | EXPORT_SYMBOL(clk_hw_register_clkdev); | ||
461 | 470 | ||
462 | /* | 471 | static void devm_clkdev_release(struct device *dev, void *res) |
463 | * Since dev_id can be NULL, and NULL is handled specially, we must | 472 | { |
464 | * pass it as either a NULL format string, or with "%s". | 473 | clkdev_drop(*(struct clk_lookup **)res); |
465 | */ | 474 | } |
466 | if (dev_id) | ||
467 | cl = __clk_register_clkdev(hw, con_id, "%s", dev_id); | ||
468 | else | ||
469 | cl = __clk_register_clkdev(hw, con_id, NULL); | ||
470 | 475 | ||
471 | return cl ? 0 : -ENOMEM; | 476 | static int devm_clk_match_clkdev(struct device *dev, void *res, void *data) |
477 | { | ||
478 | struct clk_lookup **l = res; | ||
479 | |||
480 | return *l == data; | ||
472 | } | 481 | } |
473 | EXPORT_SYMBOL(clk_hw_register_clkdev); | 482 | |
483 | /** | ||
484 | * devm_clk_release_clkdev - Resource managed clkdev lookup release | ||
485 | * @dev: device this lookup is bound | ||
486 | * @con_id: connection ID string on device | ||
487 | * @dev_id: format string describing device name | ||
488 | * | ||
489 | * Drop the clkdev lookup created with devm_clk_hw_register_clkdev. | ||
490 | * Normally this function will not need to be called and the resource | ||
491 | * management code will ensure that the resource is freed. | ||
492 | */ | ||
493 | void devm_clk_release_clkdev(struct device *dev, const char *con_id, | ||
494 | const char *dev_id) | ||
495 | { | ||
496 | struct clk_lookup *cl; | ||
497 | int rval; | ||
498 | |||
499 | cl = clk_find(dev_id, con_id); | ||
500 | WARN_ON(!cl); | ||
501 | rval = devres_release(dev, devm_clkdev_release, | ||
502 | devm_clk_match_clkdev, cl); | ||
503 | WARN_ON(rval); | ||
504 | } | ||
505 | EXPORT_SYMBOL(devm_clk_release_clkdev); | ||
506 | |||
507 | /** | ||
508 | * devm_clk_hw_register_clkdev - managed clk lookup registration for clk_hw | ||
509 | * @dev: device this lookup is bound | ||
510 | * @hw: struct clk_hw to associate with all clk_lookups | ||
511 | * @con_id: connection ID string on device | ||
512 | * @dev_id: format string describing device name | ||
513 | * | ||
514 | * con_id or dev_id may be NULL as a wildcard, just as in the rest of | ||
515 | * clkdev. | ||
516 | * | ||
517 | * To make things easier for mass registration, we detect error clk_hws | ||
518 | * from a previous clk_hw_register_*() call, and return the error code for | ||
519 | * those. This is to permit this function to be called immediately | ||
520 | * after clk_hw_register_*(). | ||
521 | */ | ||
522 | int devm_clk_hw_register_clkdev(struct device *dev, struct clk_hw *hw, | ||
523 | const char *con_id, const char *dev_id) | ||
524 | { | ||
525 | int rval = -ENOMEM; | ||
526 | struct clk_lookup **cl; | ||
527 | |||
528 | cl = devres_alloc(devm_clkdev_release, sizeof(*cl), GFP_KERNEL); | ||
529 | if (cl) { | ||
530 | rval = do_clk_register_clkdev(hw, cl, con_id, dev_id); | ||
531 | if (!rval) | ||
532 | devres_add(dev, cl); | ||
533 | else | ||
534 | devres_free(cl); | ||
535 | } | ||
536 | return rval; | ||
537 | } | ||
538 | EXPORT_SYMBOL(devm_clk_hw_register_clkdev); | ||
diff --git a/include/linux/clkdev.h b/include/linux/clkdev.h index 4890ff033220..ccb32af5848b 100644 --- a/include/linux/clkdev.h +++ b/include/linux/clkdev.h | |||
@@ -52,4 +52,8 @@ int clk_add_alias(const char *, const char *, const char *, struct device *); | |||
52 | int clk_register_clkdev(struct clk *, const char *, const char *); | 52 | int clk_register_clkdev(struct clk *, const char *, const char *); |
53 | int clk_hw_register_clkdev(struct clk_hw *, const char *, const char *); | 53 | int clk_hw_register_clkdev(struct clk_hw *, const char *, const char *); |
54 | 54 | ||
55 | int devm_clk_hw_register_clkdev(struct device *dev, struct clk_hw *hw, | ||
56 | const char *con_id, const char *dev_id); | ||
57 | void devm_clk_release_clkdev(struct device *dev, const char *con_id, | ||
58 | const char *dev_id); | ||
55 | #endif | 59 | #endif |