diff options
Diffstat (limited to 'drivers/devfreq/devfreq.c')
| -rw-r--r-- | drivers/devfreq/devfreq.c | 125 |
1 files changed, 115 insertions, 10 deletions
diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index 2042ec3656ba..9f90369dd6bd 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c | |||
| @@ -394,7 +394,7 @@ static int devfreq_notifier_call(struct notifier_block *nb, unsigned long type, | |||
| 394 | * @devfreq: the devfreq struct | 394 | * @devfreq: the devfreq struct |
| 395 | * @skip: skip calling device_unregister(). | 395 | * @skip: skip calling device_unregister(). |
| 396 | */ | 396 | */ |
| 397 | static void _remove_devfreq(struct devfreq *devfreq, bool skip) | 397 | static void _remove_devfreq(struct devfreq *devfreq) |
| 398 | { | 398 | { |
| 399 | mutex_lock(&devfreq_list_lock); | 399 | mutex_lock(&devfreq_list_lock); |
| 400 | if (IS_ERR(find_device_devfreq(devfreq->dev.parent))) { | 400 | if (IS_ERR(find_device_devfreq(devfreq->dev.parent))) { |
| @@ -412,11 +412,6 @@ static void _remove_devfreq(struct devfreq *devfreq, bool skip) | |||
| 412 | if (devfreq->profile->exit) | 412 | if (devfreq->profile->exit) |
| 413 | devfreq->profile->exit(devfreq->dev.parent); | 413 | devfreq->profile->exit(devfreq->dev.parent); |
| 414 | 414 | ||
| 415 | if (!skip && get_device(&devfreq->dev)) { | ||
| 416 | device_unregister(&devfreq->dev); | ||
| 417 | put_device(&devfreq->dev); | ||
| 418 | } | ||
| 419 | |||
| 420 | mutex_destroy(&devfreq->lock); | 415 | mutex_destroy(&devfreq->lock); |
| 421 | kfree(devfreq); | 416 | kfree(devfreq); |
| 422 | } | 417 | } |
| @@ -426,14 +421,12 @@ static void _remove_devfreq(struct devfreq *devfreq, bool skip) | |||
| 426 | * @dev: the devfreq device | 421 | * @dev: the devfreq device |
| 427 | * | 422 | * |
| 428 | * This calls _remove_devfreq() if _remove_devfreq() is not called. | 423 | * This calls _remove_devfreq() if _remove_devfreq() is not called. |
| 429 | * Note that devfreq_dev_release() could be called by _remove_devfreq() as | ||
| 430 | * well as by others unregistering the device. | ||
| 431 | */ | 424 | */ |
| 432 | static void devfreq_dev_release(struct device *dev) | 425 | static void devfreq_dev_release(struct device *dev) |
| 433 | { | 426 | { |
| 434 | struct devfreq *devfreq = to_devfreq(dev); | 427 | struct devfreq *devfreq = to_devfreq(dev); |
| 435 | 428 | ||
| 436 | _remove_devfreq(devfreq, true); | 429 | _remove_devfreq(devfreq); |
| 437 | } | 430 | } |
| 438 | 431 | ||
| 439 | /** | 432 | /** |
| @@ -544,12 +537,76 @@ int devfreq_remove_device(struct devfreq *devfreq) | |||
| 544 | if (!devfreq) | 537 | if (!devfreq) |
| 545 | return -EINVAL; | 538 | return -EINVAL; |
| 546 | 539 | ||
| 547 | _remove_devfreq(devfreq, false); | 540 | device_unregister(&devfreq->dev); |
| 541 | put_device(&devfreq->dev); | ||
| 548 | 542 | ||
| 549 | return 0; | 543 | return 0; |
| 550 | } | 544 | } |
| 551 | EXPORT_SYMBOL(devfreq_remove_device); | 545 | EXPORT_SYMBOL(devfreq_remove_device); |
| 552 | 546 | ||
| 547 | static int devm_devfreq_dev_match(struct device *dev, void *res, void *data) | ||
| 548 | { | ||
| 549 | struct devfreq **r = res; | ||
| 550 | |||
| 551 | if (WARN_ON(!r || !*r)) | ||
| 552 | return 0; | ||
| 553 | |||
| 554 | return *r == data; | ||
| 555 | } | ||
| 556 | |||
| 557 | static void devm_devfreq_dev_release(struct device *dev, void *res) | ||
| 558 | { | ||
| 559 | devfreq_remove_device(*(struct devfreq **)res); | ||
| 560 | } | ||
| 561 | |||
| 562 | /** | ||
| 563 | * devm_devfreq_add_device() - Resource-managed devfreq_add_device() | ||
| 564 | * @dev: the device to add devfreq feature. | ||
| 565 | * @profile: device-specific profile to run devfreq. | ||
| 566 | * @governor_name: name of the policy to choose frequency. | ||
| 567 | * @data: private data for the governor. The devfreq framework does not | ||
| 568 | * touch this value. | ||
| 569 | * | ||
| 570 | * This function manages automatically the memory of devfreq device using device | ||
| 571 | * resource management and simplify the free operation for memory of devfreq | ||
| 572 | * device. | ||
| 573 | */ | ||
| 574 | struct devfreq *devm_devfreq_add_device(struct device *dev, | ||
| 575 | struct devfreq_dev_profile *profile, | ||
| 576 | const char *governor_name, | ||
| 577 | void *data) | ||
| 578 | { | ||
| 579 | struct devfreq **ptr, *devfreq; | ||
| 580 | |||
| 581 | ptr = devres_alloc(devm_devfreq_dev_release, sizeof(*ptr), GFP_KERNEL); | ||
| 582 | if (!ptr) | ||
| 583 | return ERR_PTR(-ENOMEM); | ||
| 584 | |||
| 585 | devfreq = devfreq_add_device(dev, profile, governor_name, data); | ||
| 586 | if (IS_ERR(devfreq)) { | ||
| 587 | devres_free(ptr); | ||
| 588 | return ERR_PTR(-ENOMEM); | ||
| 589 | } | ||
| 590 | |||
| 591 | *ptr = devfreq; | ||
| 592 | devres_add(dev, ptr); | ||
| 593 | |||
| 594 | return devfreq; | ||
| 595 | } | ||
| 596 | EXPORT_SYMBOL(devm_devfreq_add_device); | ||
| 597 | |||
| 598 | /** | ||
| 599 | * devm_devfreq_remove_device() - Resource-managed devfreq_remove_device() | ||
| 600 | * @dev: the device to add devfreq feature. | ||
| 601 | * @devfreq: the devfreq instance to be removed | ||
| 602 | */ | ||
| 603 | void devm_devfreq_remove_device(struct device *dev, struct devfreq *devfreq) | ||
| 604 | { | ||
| 605 | WARN_ON(devres_release(dev, devm_devfreq_dev_release, | ||
| 606 | devm_devfreq_dev_match, devfreq)); | ||
| 607 | } | ||
| 608 | EXPORT_SYMBOL(devm_devfreq_remove_device); | ||
| 609 | |||
| 553 | /** | 610 | /** |
| 554 | * devfreq_suspend_device() - Suspend devfreq of a device. | 611 | * devfreq_suspend_device() - Suspend devfreq of a device. |
| 555 | * @devfreq: the devfreq instance to be suspended | 612 | * @devfreq: the devfreq instance to be suspended |
| @@ -1112,6 +1169,54 @@ int devfreq_unregister_opp_notifier(struct device *dev, struct devfreq *devfreq) | |||
| 1112 | return ret; | 1169 | return ret; |
| 1113 | } | 1170 | } |
| 1114 | 1171 | ||
| 1172 | static void devm_devfreq_opp_release(struct device *dev, void *res) | ||
| 1173 | { | ||
| 1174 | devfreq_unregister_opp_notifier(dev, *(struct devfreq **)res); | ||
| 1175 | } | ||
| 1176 | |||
| 1177 | /** | ||
| 1178 | * devm_ devfreq_register_opp_notifier() | ||
| 1179 | * - Resource-managed devfreq_register_opp_notifier() | ||
| 1180 | * @dev: The devfreq user device. (parent of devfreq) | ||
| 1181 | * @devfreq: The devfreq object. | ||
| 1182 | */ | ||
| 1183 | int devm_devfreq_register_opp_notifier(struct device *dev, | ||
| 1184 | struct devfreq *devfreq) | ||
| 1185 | { | ||
| 1186 | struct devfreq **ptr; | ||
| 1187 | int ret; | ||
| 1188 | |||
| 1189 | ptr = devres_alloc(devm_devfreq_opp_release, sizeof(*ptr), GFP_KERNEL); | ||
| 1190 | if (!ptr) | ||
| 1191 | return -ENOMEM; | ||
| 1192 | |||
| 1193 | ret = devfreq_register_opp_notifier(dev, devfreq); | ||
| 1194 | if (ret) { | ||
| 1195 | devres_free(ptr); | ||
| 1196 | return ret; | ||
| 1197 | } | ||
| 1198 | |||
| 1199 | *ptr = devfreq; | ||
| 1200 | devres_add(dev, ptr); | ||
| 1201 | |||
| 1202 | return 0; | ||
| 1203 | } | ||
| 1204 | EXPORT_SYMBOL(devm_devfreq_register_opp_notifier); | ||
| 1205 | |||
| 1206 | /** | ||
| 1207 | * devm_devfreq_unregister_opp_notifier() | ||
| 1208 | * - Resource-managed devfreq_unregister_opp_notifier() | ||
| 1209 | * @dev: The devfreq user device. (parent of devfreq) | ||
| 1210 | * @devfreq: The devfreq object. | ||
| 1211 | */ | ||
| 1212 | void devm_devfreq_unregister_opp_notifier(struct device *dev, | ||
| 1213 | struct devfreq *devfreq) | ||
| 1214 | { | ||
| 1215 | WARN_ON(devres_release(dev, devm_devfreq_opp_release, | ||
| 1216 | devm_devfreq_dev_match, devfreq)); | ||
| 1217 | } | ||
| 1218 | EXPORT_SYMBOL(devm_devfreq_unregister_opp_notifier); | ||
| 1219 | |||
| 1115 | MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>"); | 1220 | MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>"); |
| 1116 | MODULE_DESCRIPTION("devfreq class support"); | 1221 | MODULE_DESCRIPTION("devfreq class support"); |
| 1117 | MODULE_LICENSE("GPL"); | 1222 | MODULE_LICENSE("GPL"); |
