diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/base/power/domain.c | 144 |
1 files changed, 99 insertions, 45 deletions
diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index d06f3bb80b2e..7e6cc8a5ce5b 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c | |||
@@ -44,7 +44,8 @@ static void genpd_acquire_lock(struct generic_pm_domain *genpd) | |||
44 | for (;;) { | 44 | for (;;) { |
45 | prepare_to_wait(&genpd->status_wait_queue, &wait, | 45 | prepare_to_wait(&genpd->status_wait_queue, &wait, |
46 | TASK_UNINTERRUPTIBLE); | 46 | TASK_UNINTERRUPTIBLE); |
47 | if (genpd->status != GPD_STATE_BUSY) | 47 | if (genpd->status == GPD_STATE_ACTIVE |
48 | || genpd->status == GPD_STATE_POWER_OFF) | ||
48 | break; | 49 | break; |
49 | mutex_unlock(&genpd->lock); | 50 | mutex_unlock(&genpd->lock); |
50 | 51 | ||
@@ -60,6 +61,12 @@ static void genpd_release_lock(struct generic_pm_domain *genpd) | |||
60 | mutex_unlock(&genpd->lock); | 61 | mutex_unlock(&genpd->lock); |
61 | } | 62 | } |
62 | 63 | ||
64 | static void genpd_set_active(struct generic_pm_domain *genpd) | ||
65 | { | ||
66 | if (genpd->resume_count == 0) | ||
67 | genpd->status = GPD_STATE_ACTIVE; | ||
68 | } | ||
69 | |||
63 | /** | 70 | /** |
64 | * pm_genpd_poweron - Restore power to a given PM domain and its parents. | 71 | * pm_genpd_poweron - Restore power to a given PM domain and its parents. |
65 | * @genpd: PM domain to power up. | 72 | * @genpd: PM domain to power up. |
@@ -75,42 +82,24 @@ int pm_genpd_poweron(struct generic_pm_domain *genpd) | |||
75 | 82 | ||
76 | start: | 83 | start: |
77 | if (parent) { | 84 | if (parent) { |
78 | mutex_lock(&parent->lock); | 85 | genpd_acquire_lock(parent); |
79 | mutex_lock_nested(&genpd->lock, SINGLE_DEPTH_NESTING); | 86 | mutex_lock_nested(&genpd->lock, SINGLE_DEPTH_NESTING); |
80 | } else { | 87 | } else { |
81 | mutex_lock(&genpd->lock); | 88 | mutex_lock(&genpd->lock); |
82 | } | 89 | } |
83 | /* | ||
84 | * Wait for the domain to transition into either the active, | ||
85 | * or the power off state. | ||
86 | */ | ||
87 | for (;;) { | ||
88 | prepare_to_wait(&genpd->status_wait_queue, &wait, | ||
89 | TASK_UNINTERRUPTIBLE); | ||
90 | if (genpd->status != GPD_STATE_BUSY) | ||
91 | break; | ||
92 | mutex_unlock(&genpd->lock); | ||
93 | if (parent) | ||
94 | mutex_unlock(&parent->lock); | ||
95 | |||
96 | schedule(); | ||
97 | |||
98 | if (parent) { | ||
99 | mutex_lock(&parent->lock); | ||
100 | mutex_lock_nested(&genpd->lock, SINGLE_DEPTH_NESTING); | ||
101 | } else { | ||
102 | mutex_lock(&genpd->lock); | ||
103 | } | ||
104 | } | ||
105 | finish_wait(&genpd->status_wait_queue, &wait); | ||
106 | 90 | ||
107 | if (genpd->status == GPD_STATE_ACTIVE | 91 | if (genpd->status == GPD_STATE_ACTIVE |
108 | || (genpd->prepared_count > 0 && genpd->suspend_power_off)) | 92 | || (genpd->prepared_count > 0 && genpd->suspend_power_off)) |
109 | goto out; | 93 | goto out; |
110 | 94 | ||
95 | if (genpd->status != GPD_STATE_POWER_OFF) { | ||
96 | genpd_set_active(genpd); | ||
97 | goto out; | ||
98 | } | ||
99 | |||
111 | if (parent && parent->status != GPD_STATE_ACTIVE) { | 100 | if (parent && parent->status != GPD_STATE_ACTIVE) { |
112 | mutex_unlock(&genpd->lock); | 101 | mutex_unlock(&genpd->lock); |
113 | mutex_unlock(&parent->lock); | 102 | genpd_release_lock(parent); |
114 | 103 | ||
115 | ret = pm_genpd_poweron(parent); | 104 | ret = pm_genpd_poweron(parent); |
116 | if (ret) | 105 | if (ret) |
@@ -125,14 +114,14 @@ int pm_genpd_poweron(struct generic_pm_domain *genpd) | |||
125 | goto out; | 114 | goto out; |
126 | } | 115 | } |
127 | 116 | ||
128 | genpd->status = GPD_STATE_ACTIVE; | 117 | genpd_set_active(genpd); |
129 | if (parent) | 118 | if (parent) |
130 | parent->sd_count++; | 119 | parent->sd_count++; |
131 | 120 | ||
132 | out: | 121 | out: |
133 | mutex_unlock(&genpd->lock); | 122 | mutex_unlock(&genpd->lock); |
134 | if (parent) | 123 | if (parent) |
135 | mutex_unlock(&parent->lock); | 124 | genpd_release_lock(parent); |
136 | 125 | ||
137 | return ret; | 126 | return ret; |
138 | } | 127 | } |
@@ -210,6 +199,20 @@ static void __pm_genpd_restore_device(struct dev_list_entry *dle, | |||
210 | } | 199 | } |
211 | 200 | ||
212 | /** | 201 | /** |
202 | * genpd_abort_poweroff - Check if a PM domain power off should be aborted. | ||
203 | * @genpd: PM domain to check. | ||
204 | * | ||
205 | * Return true if a PM domain's status changed to GPD_STATE_ACTIVE during | ||
206 | * a "power off" operation, which means that a "power on" has occured in the | ||
207 | * meantime, or if its resume_count field is different from zero, which means | ||
208 | * that one of its devices has been resumed in the meantime. | ||
209 | */ | ||
210 | static bool genpd_abort_poweroff(struct generic_pm_domain *genpd) | ||
211 | { | ||
212 | return genpd->status == GPD_STATE_ACTIVE || genpd->resume_count > 0; | ||
213 | } | ||
214 | |||
215 | /** | ||
213 | * pm_genpd_poweroff - Remove power from a given PM domain. | 216 | * pm_genpd_poweroff - Remove power from a given PM domain. |
214 | * @genpd: PM domain to power down. | 217 | * @genpd: PM domain to power down. |
215 | * | 218 | * |
@@ -223,9 +226,17 @@ static int pm_genpd_poweroff(struct generic_pm_domain *genpd) | |||
223 | struct generic_pm_domain *parent; | 226 | struct generic_pm_domain *parent; |
224 | struct dev_list_entry *dle; | 227 | struct dev_list_entry *dle; |
225 | unsigned int not_suspended; | 228 | unsigned int not_suspended; |
226 | int ret; | 229 | int ret = 0; |
227 | 230 | ||
228 | if (genpd->status == GPD_STATE_POWER_OFF || genpd->prepared_count > 0) | 231 | start: |
232 | /* | ||
233 | * Do not try to power off the domain in the following situations: | ||
234 | * (1) The domain is already in the "power off" state. | ||
235 | * (2) System suspend is in progress. | ||
236 | * (3) One of the domain's devices is being resumed right now. | ||
237 | */ | ||
238 | if (genpd->status == GPD_STATE_POWER_OFF || genpd->prepared_count > 0 | ||
239 | || genpd->resume_count > 0) | ||
229 | return 0; | 240 | return 0; |
230 | 241 | ||
231 | if (genpd->sd_count > 0) | 242 | if (genpd->sd_count > 0) |
@@ -239,34 +250,54 @@ static int pm_genpd_poweroff(struct generic_pm_domain *genpd) | |||
239 | if (not_suspended > genpd->in_progress) | 250 | if (not_suspended > genpd->in_progress) |
240 | return -EBUSY; | 251 | return -EBUSY; |
241 | 252 | ||
253 | if (genpd->poweroff_task) { | ||
254 | /* | ||
255 | * Another instance of pm_genpd_poweroff() is executing | ||
256 | * callbacks, so tell it to start over and return. | ||
257 | */ | ||
258 | genpd->status = GPD_STATE_REPEAT; | ||
259 | return 0; | ||
260 | } | ||
261 | |||
242 | if (genpd->gov && genpd->gov->power_down_ok) { | 262 | if (genpd->gov && genpd->gov->power_down_ok) { |
243 | if (!genpd->gov->power_down_ok(&genpd->domain)) | 263 | if (!genpd->gov->power_down_ok(&genpd->domain)) |
244 | return -EAGAIN; | 264 | return -EAGAIN; |
245 | } | 265 | } |
246 | 266 | ||
247 | genpd->status = GPD_STATE_BUSY; | 267 | genpd->status = GPD_STATE_BUSY; |
268 | genpd->poweroff_task = current; | ||
248 | 269 | ||
249 | list_for_each_entry_reverse(dle, &genpd->dev_list, node) { | 270 | list_for_each_entry_reverse(dle, &genpd->dev_list, node) { |
250 | ret = __pm_genpd_save_device(dle, genpd); | 271 | ret = __pm_genpd_save_device(dle, genpd); |
251 | if (ret) | 272 | if (ret) |
252 | goto err_dev; | 273 | goto err_dev; |
253 | } | ||
254 | 274 | ||
255 | mutex_unlock(&genpd->lock); | 275 | if (genpd_abort_poweroff(genpd)) |
276 | goto out; | ||
277 | |||
278 | if (genpd->status == GPD_STATE_REPEAT) { | ||
279 | genpd->poweroff_task = NULL; | ||
280 | goto start; | ||
281 | } | ||
282 | } | ||
256 | 283 | ||
257 | parent = genpd->parent; | 284 | parent = genpd->parent; |
258 | if (parent) { | 285 | if (parent) { |
286 | mutex_unlock(&genpd->lock); | ||
287 | |||
259 | genpd_acquire_lock(parent); | 288 | genpd_acquire_lock(parent); |
260 | mutex_lock_nested(&genpd->lock, SINGLE_DEPTH_NESTING); | 289 | mutex_lock_nested(&genpd->lock, SINGLE_DEPTH_NESTING); |
261 | } else { | 290 | |
262 | mutex_lock(&genpd->lock); | 291 | if (genpd_abort_poweroff(genpd)) { |
292 | genpd_release_lock(parent); | ||
293 | goto out; | ||
294 | } | ||
263 | } | 295 | } |
264 | 296 | ||
265 | if (genpd->power_off) | 297 | if (genpd->power_off) |
266 | genpd->power_off(genpd); | 298 | genpd->power_off(genpd); |
267 | 299 | ||
268 | genpd->status = GPD_STATE_POWER_OFF; | 300 | genpd->status = GPD_STATE_POWER_OFF; |
269 | wake_up_all(&genpd->status_wait_queue); | ||
270 | 301 | ||
271 | if (parent) { | 302 | if (parent) { |
272 | genpd_sd_counter_dec(parent); | 303 | genpd_sd_counter_dec(parent); |
@@ -276,16 +307,17 @@ static int pm_genpd_poweroff(struct generic_pm_domain *genpd) | |||
276 | genpd_release_lock(parent); | 307 | genpd_release_lock(parent); |
277 | } | 308 | } |
278 | 309 | ||
279 | return 0; | 310 | out: |
311 | genpd->poweroff_task = NULL; | ||
312 | wake_up_all(&genpd->status_wait_queue); | ||
313 | return ret; | ||
280 | 314 | ||
281 | err_dev: | 315 | err_dev: |
282 | list_for_each_entry_continue(dle, &genpd->dev_list, node) | 316 | list_for_each_entry_continue(dle, &genpd->dev_list, node) |
283 | __pm_genpd_restore_device(dle, genpd); | 317 | __pm_genpd_restore_device(dle, genpd); |
284 | 318 | ||
285 | genpd->status = GPD_STATE_ACTIVE; | 319 | genpd_set_active(genpd); |
286 | wake_up_all(&genpd->status_wait_queue); | 320 | goto out; |
287 | |||
288 | return ret; | ||
289 | } | 321 | } |
290 | 322 | ||
291 | /** | 323 | /** |
@@ -327,11 +359,11 @@ static int pm_genpd_runtime_suspend(struct device *dev) | |||
327 | return ret; | 359 | return ret; |
328 | } | 360 | } |
329 | 361 | ||
330 | genpd_acquire_lock(genpd); | 362 | mutex_lock(&genpd->lock); |
331 | genpd->in_progress++; | 363 | genpd->in_progress++; |
332 | pm_genpd_poweroff(genpd); | 364 | pm_genpd_poweroff(genpd); |
333 | genpd->in_progress--; | 365 | genpd->in_progress--; |
334 | genpd_release_lock(genpd); | 366 | mutex_unlock(&genpd->lock); |
335 | 367 | ||
336 | return 0; | 368 | return 0; |
337 | } | 369 | } |
@@ -365,6 +397,7 @@ static void __pm_genpd_runtime_resume(struct device *dev, | |||
365 | static int pm_genpd_runtime_resume(struct device *dev) | 397 | static int pm_genpd_runtime_resume(struct device *dev) |
366 | { | 398 | { |
367 | struct generic_pm_domain *genpd; | 399 | struct generic_pm_domain *genpd; |
400 | DEFINE_WAIT(wait); | ||
368 | int ret; | 401 | int ret; |
369 | 402 | ||
370 | dev_dbg(dev, "%s()\n", __func__); | 403 | dev_dbg(dev, "%s()\n", __func__); |
@@ -377,12 +410,31 @@ static int pm_genpd_runtime_resume(struct device *dev) | |||
377 | if (ret) | 410 | if (ret) |
378 | return ret; | 411 | return ret; |
379 | 412 | ||
380 | genpd_acquire_lock(genpd); | 413 | mutex_lock(&genpd->lock); |
381 | genpd->status = GPD_STATE_BUSY; | 414 | genpd->status = GPD_STATE_BUSY; |
415 | genpd->resume_count++; | ||
416 | for (;;) { | ||
417 | prepare_to_wait(&genpd->status_wait_queue, &wait, | ||
418 | TASK_UNINTERRUPTIBLE); | ||
419 | /* | ||
420 | * If current is the powering off task, we have been called | ||
421 | * reentrantly from one of the device callbacks, so we should | ||
422 | * not wait. | ||
423 | */ | ||
424 | if (!genpd->poweroff_task || genpd->poweroff_task == current) | ||
425 | break; | ||
426 | mutex_unlock(&genpd->lock); | ||
427 | |||
428 | schedule(); | ||
429 | |||
430 | mutex_lock(&genpd->lock); | ||
431 | } | ||
432 | finish_wait(&genpd->status_wait_queue, &wait); | ||
382 | __pm_genpd_runtime_resume(dev, genpd); | 433 | __pm_genpd_runtime_resume(dev, genpd); |
383 | genpd->status = GPD_STATE_ACTIVE; | 434 | genpd->resume_count--; |
435 | genpd_set_active(genpd); | ||
384 | wake_up_all(&genpd->status_wait_queue); | 436 | wake_up_all(&genpd->status_wait_queue); |
385 | genpd_release_lock(genpd); | 437 | mutex_unlock(&genpd->lock); |
386 | 438 | ||
387 | if (genpd->start_device) | 439 | if (genpd->start_device) |
388 | genpd->start_device(dev); | 440 | genpd->start_device(dev); |
@@ -1130,6 +1182,8 @@ void pm_genpd_init(struct generic_pm_domain *genpd, | |||
1130 | genpd->sd_count = 0; | 1182 | genpd->sd_count = 0; |
1131 | genpd->status = is_off ? GPD_STATE_POWER_OFF : GPD_STATE_ACTIVE; | 1183 | genpd->status = is_off ? GPD_STATE_POWER_OFF : GPD_STATE_ACTIVE; |
1132 | init_waitqueue_head(&genpd->status_wait_queue); | 1184 | init_waitqueue_head(&genpd->status_wait_queue); |
1185 | genpd->poweroff_task = NULL; | ||
1186 | genpd->resume_count = 0; | ||
1133 | genpd->device_count = 0; | 1187 | genpd->device_count = 0; |
1134 | genpd->suspended_count = 0; | 1188 | genpd->suspended_count = 0; |
1135 | genpd->domain.ops.runtime_suspend = pm_genpd_runtime_suspend; | 1189 | genpd->domain.ops.runtime_suspend = pm_genpd_runtime_suspend; |