diff options
Diffstat (limited to 'drivers')
| -rw-r--r-- | drivers/base/power/power.h | 1 | ||||
| -rw-r--r-- | drivers/base/power/runtime.c | 54 | ||||
| -rw-r--r-- | drivers/base/power/sysfs.c | 56 |
3 files changed, 103 insertions, 8 deletions
diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h index 8b2745c69e2a..698dde742587 100644 --- a/drivers/base/power/power.h +++ b/drivers/base/power/power.h | |||
| @@ -60,6 +60,7 @@ static inline void device_pm_move_last(struct device *dev) {} | |||
| 60 | 60 | ||
| 61 | extern int dpm_sysfs_add(struct device *); | 61 | extern int dpm_sysfs_add(struct device *); |
| 62 | extern void dpm_sysfs_remove(struct device *); | 62 | extern void dpm_sysfs_remove(struct device *); |
| 63 | extern void rpm_sysfs_remove(struct device *); | ||
| 63 | 64 | ||
| 64 | #else /* CONFIG_PM */ | 65 | #else /* CONFIG_PM */ |
| 65 | 66 | ||
diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index ed227b7c1bb5..5bd4daa93ef1 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c | |||
| @@ -10,8 +10,10 @@ | |||
| 10 | #include <linux/sched.h> | 10 | #include <linux/sched.h> |
| 11 | #include <linux/pm_runtime.h> | 11 | #include <linux/pm_runtime.h> |
| 12 | #include <linux/jiffies.h> | 12 | #include <linux/jiffies.h> |
| 13 | #include "power.h" | ||
| 13 | 14 | ||
| 14 | static int rpm_resume(struct device *dev, int rpmflags); | 15 | static int rpm_resume(struct device *dev, int rpmflags); |
| 16 | static int rpm_suspend(struct device *dev, int rpmflags); | ||
| 15 | 17 | ||
| 16 | /** | 18 | /** |
| 17 | * update_pm_runtime_accounting - Update the time accounting of power states | 19 | * update_pm_runtime_accounting - Update the time accounting of power states |
| @@ -148,6 +150,12 @@ static int rpm_idle(struct device *dev, int rpmflags) | |||
| 148 | /* Pending requests need to be canceled. */ | 150 | /* Pending requests need to be canceled. */ |
| 149 | dev->power.request = RPM_REQ_NONE; | 151 | dev->power.request = RPM_REQ_NONE; |
| 150 | 152 | ||
| 153 | if (dev->power.no_callbacks) { | ||
| 154 | /* Assume ->runtime_idle() callback would have suspended. */ | ||
| 155 | retval = rpm_suspend(dev, rpmflags); | ||
| 156 | goto out; | ||
| 157 | } | ||
| 158 | |||
| 151 | /* Carry out an asynchronous or a synchronous idle notification. */ | 159 | /* Carry out an asynchronous or a synchronous idle notification. */ |
| 152 | if (rpmflags & RPM_ASYNC) { | 160 | if (rpmflags & RPM_ASYNC) { |
| 153 | dev->power.request = RPM_REQ_IDLE; | 161 | dev->power.request = RPM_REQ_IDLE; |
| @@ -254,6 +262,10 @@ static int rpm_suspend(struct device *dev, int rpmflags) | |||
| 254 | goto repeat; | 262 | goto repeat; |
| 255 | } | 263 | } |
| 256 | 264 | ||
| 265 | dev->power.deferred_resume = false; | ||
| 266 | if (dev->power.no_callbacks) | ||
| 267 | goto no_callback; /* Assume success. */ | ||
| 268 | |||
| 257 | /* Carry out an asynchronous or a synchronous suspend. */ | 269 | /* Carry out an asynchronous or a synchronous suspend. */ |
| 258 | if (rpmflags & RPM_ASYNC) { | 270 | if (rpmflags & RPM_ASYNC) { |
| 259 | dev->power.request = RPM_REQ_SUSPEND; | 271 | dev->power.request = RPM_REQ_SUSPEND; |
| @@ -265,7 +277,6 @@ static int rpm_suspend(struct device *dev, int rpmflags) | |||
| 265 | } | 277 | } |
| 266 | 278 | ||
| 267 | __update_runtime_status(dev, RPM_SUSPENDING); | 279 | __update_runtime_status(dev, RPM_SUSPENDING); |
| 268 | dev->power.deferred_resume = false; | ||
| 269 | 280 | ||
| 270 | if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_suspend) { | 281 | if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_suspend) { |
| 271 | spin_unlock_irq(&dev->power.lock); | 282 | spin_unlock_irq(&dev->power.lock); |
| @@ -305,6 +316,7 @@ static int rpm_suspend(struct device *dev, int rpmflags) | |||
| 305 | pm_runtime_cancel_pending(dev); | 316 | pm_runtime_cancel_pending(dev); |
| 306 | } | 317 | } |
| 307 | } else { | 318 | } else { |
| 319 | no_callback: | ||
| 308 | __update_runtime_status(dev, RPM_SUSPENDED); | 320 | __update_runtime_status(dev, RPM_SUSPENDED); |
| 309 | pm_runtime_deactivate_timer(dev); | 321 | pm_runtime_deactivate_timer(dev); |
| 310 | 322 | ||
| @@ -409,6 +421,23 @@ static int rpm_resume(struct device *dev, int rpmflags) | |||
| 409 | goto repeat; | 421 | goto repeat; |
| 410 | } | 422 | } |
| 411 | 423 | ||
| 424 | /* | ||
| 425 | * See if we can skip waking up the parent. This is safe only if | ||
| 426 | * power.no_callbacks is set, because otherwise we don't know whether | ||
| 427 | * the resume will actually succeed. | ||
| 428 | */ | ||
| 429 | if (dev->power.no_callbacks && !parent && dev->parent) { | ||
| 430 | spin_lock(&dev->parent->power.lock); | ||
| 431 | if (dev->parent->power.disable_depth > 0 | ||
| 432 | || dev->parent->power.ignore_children | ||
| 433 | || dev->parent->power.runtime_status == RPM_ACTIVE) { | ||
| 434 | atomic_inc(&dev->parent->power.child_count); | ||
| 435 | spin_unlock(&dev->parent->power.lock); | ||
| 436 | goto no_callback; /* Assume success. */ | ||
| 437 | } | ||
| 438 | spin_unlock(&dev->parent->power.lock); | ||
| 439 | } | ||
| 440 | |||
| 412 | /* Carry out an asynchronous or a synchronous resume. */ | 441 | /* Carry out an asynchronous or a synchronous resume. */ |
| 413 | if (rpmflags & RPM_ASYNC) { | 442 | if (rpmflags & RPM_ASYNC) { |
| 414 | dev->power.request = RPM_REQ_RESUME; | 443 | dev->power.request = RPM_REQ_RESUME; |
| @@ -449,6 +478,9 @@ static int rpm_resume(struct device *dev, int rpmflags) | |||
| 449 | goto repeat; | 478 | goto repeat; |
| 450 | } | 479 | } |
| 451 | 480 | ||
| 481 | if (dev->power.no_callbacks) | ||
| 482 | goto no_callback; /* Assume success. */ | ||
| 483 | |||
| 452 | __update_runtime_status(dev, RPM_RESUMING); | 484 | __update_runtime_status(dev, RPM_RESUMING); |
| 453 | 485 | ||
| 454 | if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_resume) { | 486 | if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_resume) { |
| @@ -482,6 +514,7 @@ static int rpm_resume(struct device *dev, int rpmflags) | |||
| 482 | __update_runtime_status(dev, RPM_SUSPENDED); | 514 | __update_runtime_status(dev, RPM_SUSPENDED); |
| 483 | pm_runtime_cancel_pending(dev); | 515 | pm_runtime_cancel_pending(dev); |
| 484 | } else { | 516 | } else { |
| 517 | no_callback: | ||
| 485 | __update_runtime_status(dev, RPM_ACTIVE); | 518 | __update_runtime_status(dev, RPM_ACTIVE); |
| 486 | if (parent) | 519 | if (parent) |
| 487 | atomic_inc(&parent->power.child_count); | 520 | atomic_inc(&parent->power.child_count); |
| @@ -955,6 +988,25 @@ void pm_runtime_allow(struct device *dev) | |||
| 955 | EXPORT_SYMBOL_GPL(pm_runtime_allow); | 988 | EXPORT_SYMBOL_GPL(pm_runtime_allow); |
| 956 | 989 | ||
| 957 | /** | 990 | /** |
| 991 | * pm_runtime_no_callbacks - Ignore run-time PM callbacks for a device. | ||
| 992 | * @dev: Device to handle. | ||
| 993 | * | ||
| 994 | * Set the power.no_callbacks flag, which tells the PM core that this | ||
| 995 | * device is power-managed through its parent and has no run-time PM | ||
| 996 | * callbacks of its own. The run-time sysfs attributes will be removed. | ||
| 997 | * | ||
| 998 | */ | ||
| 999 | void pm_runtime_no_callbacks(struct device *dev) | ||
| 1000 | { | ||
| 1001 | spin_lock_irq(&dev->power.lock); | ||
| 1002 | dev->power.no_callbacks = 1; | ||
| 1003 | spin_unlock_irq(&dev->power.lock); | ||
| 1004 | if (device_is_registered(dev)) | ||
| 1005 | rpm_sysfs_remove(dev); | ||
| 1006 | } | ||
| 1007 | EXPORT_SYMBOL_GPL(pm_runtime_no_callbacks); | ||
| 1008 | |||
| 1009 | /** | ||
| 958 | * pm_runtime_init - Initialize run-time PM fields in given device object. | 1010 | * pm_runtime_init - Initialize run-time PM fields in given device object. |
| 959 | * @dev: Device object to initialize. | 1011 | * @dev: Device object to initialize. |
| 960 | */ | 1012 | */ |
diff --git a/drivers/base/power/sysfs.c b/drivers/base/power/sysfs.c index 8859780817e1..b5708c47ce2d 100644 --- a/drivers/base/power/sysfs.c +++ b/drivers/base/power/sysfs.c | |||
| @@ -81,6 +81,9 @@ | |||
| 81 | static const char enabled[] = "enabled"; | 81 | static const char enabled[] = "enabled"; |
| 82 | static const char disabled[] = "disabled"; | 82 | static const char disabled[] = "disabled"; |
| 83 | 83 | ||
| 84 | const char power_group_name[] = "power"; | ||
| 85 | EXPORT_SYMBOL_GPL(power_group_name); | ||
| 86 | |||
| 84 | #ifdef CONFIG_PM_RUNTIME | 87 | #ifdef CONFIG_PM_RUNTIME |
| 85 | static const char ctrl_auto[] = "auto"; | 88 | static const char ctrl_auto[] = "auto"; |
| 86 | static const char ctrl_on[] = "on"; | 89 | static const char ctrl_on[] = "on"; |
| @@ -390,12 +393,6 @@ static DEVICE_ATTR(async, 0644, async_show, async_store); | |||
| 390 | #endif /* CONFIG_PM_ADVANCED_DEBUG */ | 393 | #endif /* CONFIG_PM_ADVANCED_DEBUG */ |
| 391 | 394 | ||
| 392 | static struct attribute * power_attrs[] = { | 395 | static struct attribute * power_attrs[] = { |
| 393 | #ifdef CONFIG_PM_RUNTIME | ||
| 394 | &dev_attr_control.attr, | ||
| 395 | &dev_attr_runtime_status.attr, | ||
| 396 | &dev_attr_runtime_suspended_time.attr, | ||
| 397 | &dev_attr_runtime_active_time.attr, | ||
| 398 | #endif | ||
| 399 | &dev_attr_wakeup.attr, | 396 | &dev_attr_wakeup.attr, |
| 400 | #ifdef CONFIG_PM_SLEEP | 397 | #ifdef CONFIG_PM_SLEEP |
| 401 | &dev_attr_wakeup_count.attr, | 398 | &dev_attr_wakeup_count.attr, |
| @@ -409,6 +406,7 @@ static struct attribute * power_attrs[] = { | |||
| 409 | #ifdef CONFIG_PM_ADVANCED_DEBUG | 406 | #ifdef CONFIG_PM_ADVANCED_DEBUG |
| 410 | &dev_attr_async.attr, | 407 | &dev_attr_async.attr, |
| 411 | #ifdef CONFIG_PM_RUNTIME | 408 | #ifdef CONFIG_PM_RUNTIME |
| 409 | &dev_attr_runtime_status.attr, | ||
| 412 | &dev_attr_runtime_usage.attr, | 410 | &dev_attr_runtime_usage.attr, |
| 413 | &dev_attr_runtime_active_kids.attr, | 411 | &dev_attr_runtime_active_kids.attr, |
| 414 | &dev_attr_runtime_enabled.attr, | 412 | &dev_attr_runtime_enabled.attr, |
| @@ -417,10 +415,52 @@ static struct attribute * power_attrs[] = { | |||
| 417 | NULL, | 415 | NULL, |
| 418 | }; | 416 | }; |
| 419 | static struct attribute_group pm_attr_group = { | 417 | static struct attribute_group pm_attr_group = { |
| 420 | .name = "power", | 418 | .name = power_group_name, |
| 421 | .attrs = power_attrs, | 419 | .attrs = power_attrs, |
| 422 | }; | 420 | }; |
| 423 | 421 | ||
| 422 | #ifdef CONFIG_PM_RUNTIME | ||
| 423 | |||
| 424 | static struct attribute *runtime_attrs[] = { | ||
| 425 | #ifndef CONFIG_PM_ADVANCED_DEBUG | ||
| 426 | &dev_attr_runtime_status.attr, | ||
| 427 | #endif | ||
| 428 | &dev_attr_control.attr, | ||
| 429 | &dev_attr_runtime_suspended_time.attr, | ||
| 430 | &dev_attr_runtime_active_time.attr, | ||
| 431 | NULL, | ||
| 432 | }; | ||
| 433 | static struct attribute_group pm_runtime_attr_group = { | ||
| 434 | .name = power_group_name, | ||
| 435 | .attrs = runtime_attrs, | ||
| 436 | }; | ||
| 437 | |||
| 438 | int dpm_sysfs_add(struct device *dev) | ||
| 439 | { | ||
| 440 | int rc; | ||
| 441 | |||
| 442 | rc = sysfs_create_group(&dev->kobj, &pm_attr_group); | ||
| 443 | if (rc == 0 && !dev->power.no_callbacks) { | ||
| 444 | rc = sysfs_merge_group(&dev->kobj, &pm_runtime_attr_group); | ||
| 445 | if (rc) | ||
| 446 | sysfs_remove_group(&dev->kobj, &pm_attr_group); | ||
| 447 | } | ||
| 448 | return rc; | ||
| 449 | } | ||
| 450 | |||
| 451 | void rpm_sysfs_remove(struct device *dev) | ||
| 452 | { | ||
| 453 | sysfs_unmerge_group(&dev->kobj, &pm_runtime_attr_group); | ||
| 454 | } | ||
| 455 | |||
| 456 | void dpm_sysfs_remove(struct device *dev) | ||
| 457 | { | ||
| 458 | rpm_sysfs_remove(dev); | ||
| 459 | sysfs_remove_group(&dev->kobj, &pm_attr_group); | ||
| 460 | } | ||
| 461 | |||
| 462 | #else /* CONFIG_PM_RUNTIME */ | ||
| 463 | |||
| 424 | int dpm_sysfs_add(struct device * dev) | 464 | int dpm_sysfs_add(struct device * dev) |
| 425 | { | 465 | { |
| 426 | return sysfs_create_group(&dev->kobj, &pm_attr_group); | 466 | return sysfs_create_group(&dev->kobj, &pm_attr_group); |
| @@ -430,3 +470,5 @@ void dpm_sysfs_remove(struct device * dev) | |||
| 430 | { | 470 | { |
| 431 | sysfs_remove_group(&dev->kobj, &pm_attr_group); | 471 | sysfs_remove_group(&dev->kobj, &pm_attr_group); |
| 432 | } | 472 | } |
| 473 | |||
| 474 | #endif | ||
