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(); |