diff options
Diffstat (limited to 'drivers/base/power/main.c')
| -rw-r--r-- | drivers/base/power/main.c | 106 |
1 files changed, 28 insertions, 78 deletions
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index d887d5cb5bef..c4568b82875b 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c | |||
| @@ -50,26 +50,40 @@ | |||
| 50 | LIST_HEAD(dpm_active); | 50 | LIST_HEAD(dpm_active); |
| 51 | static LIST_HEAD(dpm_off); | 51 | static LIST_HEAD(dpm_off); |
| 52 | static LIST_HEAD(dpm_off_irq); | 52 | static LIST_HEAD(dpm_off_irq); |
| 53 | static LIST_HEAD(dpm_destroy); | ||
| 54 | 53 | ||
| 55 | static DEFINE_MUTEX(dpm_list_mtx); | 54 | static DEFINE_MUTEX(dpm_list_mtx); |
| 56 | 55 | ||
| 57 | static DECLARE_RWSEM(pm_sleep_rwsem); | 56 | /* 'true' if all devices have been suspended, protected by dpm_list_mtx */ |
| 58 | 57 | static bool all_sleeping; | |
| 59 | int (*platform_enable_wakeup)(struct device *dev, int is_on); | ||
| 60 | 58 | ||
| 61 | /** | 59 | /** |
| 62 | * device_pm_add - add a device to the list of active devices | 60 | * device_pm_add - add a device to the list of active devices |
| 63 | * @dev: Device to be added to the list | 61 | * @dev: Device to be added to the list |
| 64 | */ | 62 | */ |
| 65 | void device_pm_add(struct device *dev) | 63 | int device_pm_add(struct device *dev) |
| 66 | { | 64 | { |
| 65 | int error = 0; | ||
| 66 | |||
| 67 | pr_debug("PM: Adding info for %s:%s\n", | 67 | pr_debug("PM: Adding info for %s:%s\n", |
| 68 | dev->bus ? dev->bus->name : "No Bus", | 68 | dev->bus ? dev->bus->name : "No Bus", |
| 69 | kobject_name(&dev->kobj)); | 69 | kobject_name(&dev->kobj)); |
| 70 | mutex_lock(&dpm_list_mtx); | 70 | mutex_lock(&dpm_list_mtx); |
| 71 | list_add_tail(&dev->power.entry, &dpm_active); | 71 | if ((dev->parent && dev->parent->power.sleeping) || all_sleeping) { |
| 72 | if (dev->parent->power.sleeping) | ||
| 73 | dev_warn(dev, | ||
| 74 | "parent %s is sleeping, will not add\n", | ||
| 75 | dev->parent->bus_id); | ||
| 76 | else | ||
| 77 | dev_warn(dev, "devices are sleeping, will not add\n"); | ||
| 78 | WARN_ON(true); | ||
| 79 | error = -EBUSY; | ||
| 80 | } else { | ||
| 81 | error = dpm_sysfs_add(dev); | ||
| 82 | if (!error) | ||
| 83 | list_add_tail(&dev->power.entry, &dpm_active); | ||
| 84 | } | ||
| 72 | mutex_unlock(&dpm_list_mtx); | 85 | mutex_unlock(&dpm_list_mtx); |
| 86 | return error; | ||
| 73 | } | 87 | } |
| 74 | 88 | ||
| 75 | /** | 89 | /** |
| @@ -89,50 +103,6 @@ void device_pm_remove(struct device *dev) | |||
| 89 | mutex_unlock(&dpm_list_mtx); | 103 | mutex_unlock(&dpm_list_mtx); |
| 90 | } | 104 | } |
| 91 | 105 | ||
| 92 | /** | ||
| 93 | * device_pm_schedule_removal - schedule the removal of a suspended device | ||
| 94 | * @dev: Device to destroy | ||
| 95 | * | ||
| 96 | * Moves the device to the dpm_destroy list for further processing by | ||
| 97 | * unregister_dropped_devices(). | ||
| 98 | */ | ||
| 99 | void device_pm_schedule_removal(struct device *dev) | ||
| 100 | { | ||
| 101 | pr_debug("PM: Preparing for removal: %s:%s\n", | ||
| 102 | dev->bus ? dev->bus->name : "No Bus", | ||
| 103 | kobject_name(&dev->kobj)); | ||
| 104 | mutex_lock(&dpm_list_mtx); | ||
| 105 | list_move_tail(&dev->power.entry, &dpm_destroy); | ||
| 106 | mutex_unlock(&dpm_list_mtx); | ||
| 107 | } | ||
| 108 | EXPORT_SYMBOL_GPL(device_pm_schedule_removal); | ||
| 109 | |||
| 110 | /** | ||
| 111 | * pm_sleep_lock - mutual exclusion for registration and suspend | ||
| 112 | * | ||
| 113 | * Returns 0 if no suspend is underway and device registration | ||
| 114 | * may proceed, otherwise -EBUSY. | ||
| 115 | */ | ||
| 116 | int pm_sleep_lock(void) | ||
| 117 | { | ||
| 118 | if (down_read_trylock(&pm_sleep_rwsem)) | ||
| 119 | return 0; | ||
| 120 | |||
| 121 | return -EBUSY; | ||
| 122 | } | ||
| 123 | |||
| 124 | /** | ||
| 125 | * pm_sleep_unlock - mutual exclusion for registration and suspend | ||
| 126 | * | ||
| 127 | * This routine undoes the effect of device_pm_add_lock | ||
| 128 | * when a device's registration is complete. | ||
| 129 | */ | ||
| 130 | void pm_sleep_unlock(void) | ||
| 131 | { | ||
| 132 | up_read(&pm_sleep_rwsem); | ||
| 133 | } | ||
| 134 | |||
| 135 | |||
| 136 | /*------------------------- Resume routines -------------------------*/ | 106 | /*------------------------- Resume routines -------------------------*/ |
| 137 | 107 | ||
| 138 | /** | 108 | /** |
| @@ -242,11 +212,13 @@ static int resume_device(struct device *dev) | |||
| 242 | static void dpm_resume(void) | 212 | static void dpm_resume(void) |
| 243 | { | 213 | { |
| 244 | mutex_lock(&dpm_list_mtx); | 214 | mutex_lock(&dpm_list_mtx); |
| 215 | all_sleeping = false; | ||
| 245 | while(!list_empty(&dpm_off)) { | 216 | while(!list_empty(&dpm_off)) { |
| 246 | struct list_head *entry = dpm_off.next; | 217 | struct list_head *entry = dpm_off.next; |
| 247 | struct device *dev = to_device(entry); | 218 | struct device *dev = to_device(entry); |
| 248 | 219 | ||
| 249 | list_move_tail(entry, &dpm_active); | 220 | list_move_tail(entry, &dpm_active); |
| 221 | dev->power.sleeping = false; | ||
| 250 | mutex_unlock(&dpm_list_mtx); | 222 | mutex_unlock(&dpm_list_mtx); |
| 251 | resume_device(dev); | 223 | resume_device(dev); |
| 252 | mutex_lock(&dpm_list_mtx); | 224 | mutex_lock(&dpm_list_mtx); |
| @@ -255,26 +227,6 @@ static void dpm_resume(void) | |||
| 255 | } | 227 | } |
| 256 | 228 | ||
| 257 | /** | 229 | /** |
| 258 | * unregister_dropped_devices - Unregister devices scheduled for removal | ||
| 259 | * | ||
| 260 | * Unregister all devices on the dpm_destroy list. | ||
| 261 | */ | ||
| 262 | static void unregister_dropped_devices(void) | ||
| 263 | { | ||
| 264 | mutex_lock(&dpm_list_mtx); | ||
| 265 | while (!list_empty(&dpm_destroy)) { | ||
| 266 | struct list_head *entry = dpm_destroy.next; | ||
| 267 | struct device *dev = to_device(entry); | ||
| 268 | |||
| 269 | mutex_unlock(&dpm_list_mtx); | ||
| 270 | /* This also removes the device from the list */ | ||
| 271 | device_unregister(dev); | ||
| 272 | mutex_lock(&dpm_list_mtx); | ||
| 273 | } | ||
| 274 | mutex_unlock(&dpm_list_mtx); | ||
| 275 | } | ||
| 276 | |||
| 277 | /** | ||
| 278 | * device_resume - Restore state of each device in system. | 230 | * device_resume - Restore state of each device in system. |
| 279 | * | 231 | * |
| 280 | * Resume all the devices, unlock them all, and allow new | 232 | * Resume all the devices, unlock them all, and allow new |
| @@ -284,8 +236,6 @@ void device_resume(void) | |||
| 284 | { | 236 | { |
| 285 | might_sleep(); | 237 | might_sleep(); |
| 286 | dpm_resume(); | 238 | dpm_resume(); |
| 287 | unregister_dropped_devices(); | ||
| 288 | up_write(&pm_sleep_rwsem); | ||
| 289 | } | 239 | } |
| 290 | EXPORT_SYMBOL_GPL(device_resume); | 240 | EXPORT_SYMBOL_GPL(device_resume); |
| 291 | 241 | ||
| @@ -377,11 +327,6 @@ static int suspend_device(struct device *dev, pm_message_t state) | |||
| 377 | 327 | ||
| 378 | down(&dev->sem); | 328 | down(&dev->sem); |
| 379 | 329 | ||
| 380 | if (dev->power.power_state.event) { | ||
| 381 | dev_dbg(dev, "PM: suspend %d-->%d\n", | ||
| 382 | dev->power.power_state.event, state.event); | ||
| 383 | } | ||
| 384 | |||
| 385 | if (dev->class && dev->class->suspend) { | 330 | if (dev->class && dev->class->suspend) { |
| 386 | suspend_device_dbg(dev, state, "class "); | 331 | suspend_device_dbg(dev, state, "class "); |
| 387 | error = dev->class->suspend(dev, state); | 332 | error = dev->class->suspend(dev, state); |
| @@ -426,6 +371,9 @@ static int dpm_suspend(pm_message_t state) | |||
| 426 | struct list_head *entry = dpm_active.prev; | 371 | struct list_head *entry = dpm_active.prev; |
| 427 | struct device *dev = to_device(entry); | 372 | struct device *dev = to_device(entry); |
| 428 | 373 | ||
| 374 | WARN_ON(dev->parent && dev->parent->power.sleeping); | ||
| 375 | |||
| 376 | dev->power.sleeping = true; | ||
| 429 | mutex_unlock(&dpm_list_mtx); | 377 | mutex_unlock(&dpm_list_mtx); |
| 430 | error = suspend_device(dev, state); | 378 | error = suspend_device(dev, state); |
| 431 | mutex_lock(&dpm_list_mtx); | 379 | mutex_lock(&dpm_list_mtx); |
| @@ -437,11 +385,14 @@ static int dpm_suspend(pm_message_t state) | |||
| 437 | (error == -EAGAIN ? | 385 | (error == -EAGAIN ? |
| 438 | " (please convert to suspend_late)" : | 386 | " (please convert to suspend_late)" : |
| 439 | "")); | 387 | "")); |
| 388 | dev->power.sleeping = false; | ||
| 440 | break; | 389 | break; |
| 441 | } | 390 | } |
| 442 | if (!list_empty(&dev->power.entry)) | 391 | if (!list_empty(&dev->power.entry)) |
| 443 | list_move(&dev->power.entry, &dpm_off); | 392 | list_move(&dev->power.entry, &dpm_off); |
| 444 | } | 393 | } |
| 394 | if (!error) | ||
| 395 | all_sleeping = true; | ||
| 445 | mutex_unlock(&dpm_list_mtx); | 396 | mutex_unlock(&dpm_list_mtx); |
| 446 | 397 | ||
| 447 | return error; | 398 | return error; |
| @@ -459,7 +410,6 @@ int device_suspend(pm_message_t state) | |||
| 459 | int error; | 410 | int error; |
| 460 | 411 | ||
| 461 | might_sleep(); | 412 | might_sleep(); |
| 462 | down_write(&pm_sleep_rwsem); | ||
| 463 | error = dpm_suspend(state); | 413 | error = dpm_suspend(state); |
| 464 | if (error) | 414 | if (error) |
| 465 | device_resume(); | 415 | device_resume(); |
