diff options
Diffstat (limited to 'drivers/clk/clkdev.c')
| -rw-r--r-- | drivers/clk/clkdev.c | 117 |
1 files changed, 94 insertions, 23 deletions
diff --git a/drivers/clk/clkdev.c b/drivers/clk/clkdev.c index 9ab3db8b3988..4cfe39636105 100644 --- a/drivers/clk/clkdev.c +++ b/drivers/clk/clkdev.c | |||
| @@ -52,6 +52,12 @@ struct clk *of_clk_get(struct device_node *np, int index) | |||
| 52 | } | 52 | } |
| 53 | EXPORT_SYMBOL(of_clk_get); | 53 | EXPORT_SYMBOL(of_clk_get); |
| 54 | 54 | ||
| 55 | /* | ||
| 56 | * Beware the return values when np is valid, but no clock provider is found. | ||
| 57 | * If name == NULL, the function returns -ENOENT. | ||
| 58 | * If name != NULL, the function returns -EINVAL. This is because __of_clk_get() | ||
| 59 | * is called even if of_property_match_string() returns an error. | ||
| 60 | */ | ||
| 55 | static struct clk *__of_clk_get_by_name(struct device_node *np, | 61 | static struct clk *__of_clk_get_by_name(struct device_node *np, |
| 56 | const char *dev_id, | 62 | const char *dev_id, |
| 57 | const char *name) | 63 | const char *name) |
| @@ -401,6 +407,23 @@ static struct clk_lookup *__clk_register_clkdev(struct clk_hw *hw, | |||
| 401 | return cl; | 407 | return cl; |
| 402 | } | 408 | } |
| 403 | 409 | ||
| 410 | static int do_clk_register_clkdev(struct clk_hw *hw, | ||
| 411 | struct clk_lookup **cl, const char *con_id, const char *dev_id) | ||
| 412 | { | ||
| 413 | if (IS_ERR(hw)) | ||
| 414 | return PTR_ERR(hw); | ||
| 415 | /* | ||
| 416 | * Since dev_id can be NULL, and NULL is handled specially, we must | ||
| 417 | * pass it as either a NULL format string, or with "%s". | ||
| 418 | */ | ||
| 419 | if (dev_id) | ||
| 420 | *cl = __clk_register_clkdev(hw, con_id, "%s", dev_id); | ||
| 421 | else | ||
| 422 | *cl = __clk_register_clkdev(hw, con_id, NULL); | ||
| 423 | |||
| 424 | return *cl ? 0 : -ENOMEM; | ||
| 425 | } | ||
| 426 | |||
| 404 | /** | 427 | /** |
| 405 | * clk_register_clkdev - register one clock lookup for a struct clk | 428 | * clk_register_clkdev - register one clock lookup for a struct clk |
| 406 | * @clk: struct clk to associate with all clk_lookups | 429 | * @clk: struct clk to associate with all clk_lookups |
| @@ -423,17 +446,8 @@ int clk_register_clkdev(struct clk *clk, const char *con_id, | |||
| 423 | if (IS_ERR(clk)) | 446 | if (IS_ERR(clk)) |
| 424 | return PTR_ERR(clk); | 447 | return PTR_ERR(clk); |
| 425 | 448 | ||
| 426 | /* | 449 | 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 | 450 | 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 | } | 451 | } |
| 438 | EXPORT_SYMBOL(clk_register_clkdev); | 452 | EXPORT_SYMBOL(clk_register_clkdev); |
| 439 | 453 | ||
| @@ -456,18 +470,75 @@ int clk_hw_register_clkdev(struct clk_hw *hw, const char *con_id, | |||
| 456 | { | 470 | { |
| 457 | struct clk_lookup *cl; | 471 | struct clk_lookup *cl; |
| 458 | 472 | ||
| 459 | if (IS_ERR(hw)) | 473 | return do_clk_register_clkdev(hw, &cl, con_id, dev_id); |
| 460 | return PTR_ERR(hw); | 474 | } |
| 475 | EXPORT_SYMBOL(clk_hw_register_clkdev); | ||
| 461 | 476 | ||
| 462 | /* | 477 | static void devm_clkdev_release(struct device *dev, void *res) |
| 463 | * Since dev_id can be NULL, and NULL is handled specially, we must | 478 | { |
| 464 | * pass it as either a NULL format string, or with "%s". | 479 | clkdev_drop(*(struct clk_lookup **)res); |
| 465 | */ | 480 | } |
| 466 | if (dev_id) | 481 | |
| 467 | cl = __clk_register_clkdev(hw, con_id, "%s", dev_id); | 482 | static int devm_clk_match_clkdev(struct device *dev, void *res, void *data) |
| 468 | else | 483 | { |
| 469 | cl = __clk_register_clkdev(hw, con_id, NULL); | 484 | struct clk_lookup **l = res; |
| 470 | 485 | ||
| 471 | return cl ? 0 : -ENOMEM; | 486 | return *l == data; |
| 472 | } | 487 | } |
| 473 | EXPORT_SYMBOL(clk_hw_register_clkdev); | 488 | |
| 489 | /** | ||
| 490 | * devm_clk_release_clkdev - Resource managed clkdev lookup release | ||
| 491 | * @dev: device this lookup is bound | ||
| 492 | * @con_id: connection ID string on device | ||
| 493 | * @dev_id: format string describing device name | ||
| 494 | * | ||
| 495 | * Drop the clkdev lookup created with devm_clk_hw_register_clkdev. | ||
| 496 | * Normally this function will not need to be called and the resource | ||
| 497 | * management code will ensure that the resource is freed. | ||
| 498 | */ | ||
| 499 | void devm_clk_release_clkdev(struct device *dev, const char *con_id, | ||
| 500 | const char *dev_id) | ||
| 501 | { | ||
| 502 | struct clk_lookup *cl; | ||
| 503 | int rval; | ||
| 504 | |||
| 505 | cl = clk_find(dev_id, con_id); | ||
| 506 | WARN_ON(!cl); | ||
| 507 | rval = devres_release(dev, devm_clkdev_release, | ||
| 508 | devm_clk_match_clkdev, cl); | ||
| 509 | WARN_ON(rval); | ||
| 510 | } | ||
| 511 | EXPORT_SYMBOL(devm_clk_release_clkdev); | ||
| 512 | |||
| 513 | /** | ||
| 514 | * devm_clk_hw_register_clkdev - managed clk lookup registration for clk_hw | ||
| 515 | * @dev: device this lookup is bound | ||
| 516 | * @hw: struct clk_hw to associate with all clk_lookups | ||
| 517 | * @con_id: connection ID string on device | ||
| 518 | * @dev_id: format string describing device name | ||
| 519 | * | ||
| 520 | * con_id or dev_id may be NULL as a wildcard, just as in the rest of | ||
| 521 | * clkdev. | ||
| 522 | * | ||
| 523 | * To make things easier for mass registration, we detect error clk_hws | ||
| 524 | * from a previous clk_hw_register_*() call, and return the error code for | ||
| 525 | * those. This is to permit this function to be called immediately | ||
| 526 | * after clk_hw_register_*(). | ||
| 527 | */ | ||
| 528 | int devm_clk_hw_register_clkdev(struct device *dev, struct clk_hw *hw, | ||
| 529 | const char *con_id, const char *dev_id) | ||
| 530 | { | ||
| 531 | int rval = -ENOMEM; | ||
| 532 | struct clk_lookup **cl; | ||
| 533 | |||
| 534 | cl = devres_alloc(devm_clkdev_release, sizeof(*cl), GFP_KERNEL); | ||
| 535 | if (cl) { | ||
| 536 | rval = do_clk_register_clkdev(hw, cl, con_id, dev_id); | ||
| 537 | if (!rval) | ||
| 538 | devres_add(dev, cl); | ||
| 539 | else | ||
| 540 | devres_free(cl); | ||
| 541 | } | ||
| 542 | return rval; | ||
| 543 | } | ||
| 544 | EXPORT_SYMBOL(devm_clk_hw_register_clkdev); | ||
