aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/base/power/domain.c
diff options
context:
space:
mode:
authorRafael J. Wysocki <rjw@sisk.pl>2011-08-08 17:43:29 -0400
committerRafael J. Wysocki <rjw@sisk.pl>2011-08-25 09:33:44 -0400
commit3f241775c30365c33a0d2f6d40f4cf12470f48c6 (patch)
tree45b3fa3b5da4d9cbb006762118dcd0f8837434ff /drivers/base/power/domain.c
parent9e08cf429697090d0fac57d493dc7b6de17a5eee (diff)
PM / Domains: Add "wait for parent" status for generic PM domains
The next patch will make it possible for a generic PM domain to have multiple parents (i.e. multiple PM domains it depends on). To prepare for that change it is necessary to change pm_genpd_poweron() so that it doesn't jump to the start label after running itself recursively for the parent domain. For this purpose, introduce a new PM domain status value GPD_STATE_WAIT_PARENT that will be set by pm_genpd_poweron() before calling itself recursively for the parent domain and modify the code in drivers/base/power/domain.c so that the GPD_STATE_WAIT_PARENT status is guaranteed to be preserved during the execution of pm_genpd_poweron() for the parent. This change also causes pm_genpd_add_subdomain() and pm_genpd_remove_subdomain() to wait for started pm_genpd_poweron() to complete and allows pm_genpd_runtime_resume() to avoid dropping the lock after powering on the PM domain. Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Diffstat (limited to 'drivers/base/power/domain.c')
-rw-r--r--drivers/base/power/domain.c90
1 files changed, 60 insertions, 30 deletions
diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
index dc423a99c67c..1f4b1326c6a9 100644
--- a/drivers/base/power/domain.c
+++ b/drivers/base/power/domain.c
@@ -81,45 +81,59 @@ static void genpd_set_active(struct generic_pm_domain *genpd)
81} 81}
82 82
83/** 83/**
84 * pm_genpd_poweron - Restore power to a given PM domain and its parents. 84 * __pm_genpd_poweron - Restore power to a given PM domain and its parents.
85 * @genpd: PM domain to power up. 85 * @genpd: PM domain to power up.
86 * 86 *
87 * Restore power to @genpd and all of its parents so that it is possible to 87 * Restore power to @genpd and all of its parents so that it is possible to
88 * resume a device belonging to it. 88 * resume a device belonging to it.
89 */ 89 */
90int pm_genpd_poweron(struct generic_pm_domain *genpd) 90int __pm_genpd_poweron(struct generic_pm_domain *genpd)
91 __releases(&genpd->lock) __acquires(&genpd->lock)
91{ 92{
92 struct generic_pm_domain *parent; 93 DEFINE_WAIT(wait);
93 int ret = 0; 94 int ret = 0;
94 95
95 mutex_lock(&genpd->lock); 96 /* If the domain's parent is being waited for, we have to wait too. */
97 for (;;) {
98 prepare_to_wait(&genpd->status_wait_queue, &wait,
99 TASK_UNINTERRUPTIBLE);
100 if (genpd->status != GPD_STATE_WAIT_PARENT)
101 break;
102 mutex_unlock(&genpd->lock);
96 103
97 parent = genpd->parent; 104 schedule();
105
106 mutex_lock(&genpd->lock);
107 }
108 finish_wait(&genpd->status_wait_queue, &wait);
98 109
99 start:
100 if (genpd->status == GPD_STATE_ACTIVE 110 if (genpd->status == GPD_STATE_ACTIVE
101 || (genpd->prepared_count > 0 && genpd->suspend_power_off)) 111 || (genpd->prepared_count > 0 && genpd->suspend_power_off))
102 goto out; 112 return 0;
103 113
104 if (genpd->status != GPD_STATE_POWER_OFF) { 114 if (genpd->status != GPD_STATE_POWER_OFF) {
105 genpd_set_active(genpd); 115 genpd_set_active(genpd);
106 goto out; 116 return 0;
107 } 117 }
108 118
109 if (parent) { 119 if (genpd->parent) {
110 genpd_sd_counter_inc(parent); 120 genpd_sd_counter_inc(genpd->parent);
121 genpd->status = GPD_STATE_WAIT_PARENT;
111 122
112 mutex_unlock(&genpd->lock); 123 mutex_unlock(&genpd->lock);
113 124
114 ret = pm_genpd_poweron(parent); 125 ret = pm_genpd_poweron(genpd->parent);
115 126
116 mutex_lock(&genpd->lock); 127 mutex_lock(&genpd->lock);
117 128
129 /*
130 * The "wait for parent" status is guaranteed not to change
131 * while the parent is powering on.
132 */
133 genpd->status = GPD_STATE_POWER_OFF;
134 wake_up_all(&genpd->status_wait_queue);
118 if (ret) 135 if (ret)
119 goto err; 136 goto err;
120
121 parent = NULL;
122 goto start;
123 } 137 }
124 138
125 if (genpd->power_on) { 139 if (genpd->power_on) {
@@ -130,16 +144,27 @@ int pm_genpd_poweron(struct generic_pm_domain *genpd)
130 144
131 genpd_set_active(genpd); 145 genpd_set_active(genpd);
132 146
133 out: 147 return 0;
134 mutex_unlock(&genpd->lock);
135
136 return ret;
137 148
138 err: 149 err:
139 if (genpd->parent) 150 if (genpd->parent)
140 genpd_sd_counter_dec(genpd->parent); 151 genpd_sd_counter_dec(genpd->parent);
141 152
142 goto out; 153 return ret;
154}
155
156/**
157 * pm_genpd_poweron - Restore power to a given PM domain and its parents.
158 * @genpd: PM domain to power up.
159 */
160int pm_genpd_poweron(struct generic_pm_domain *genpd)
161{
162 int ret;
163
164 mutex_lock(&genpd->lock);
165 ret = __pm_genpd_poweron(genpd);
166 mutex_unlock(&genpd->lock);
167 return ret;
143} 168}
144 169
145#endif /* CONFIG_PM */ 170#endif /* CONFIG_PM */
@@ -225,7 +250,8 @@ static void __pm_genpd_restore_device(struct dev_list_entry *dle,
225 */ 250 */
226static bool genpd_abort_poweroff(struct generic_pm_domain *genpd) 251static bool genpd_abort_poweroff(struct generic_pm_domain *genpd)
227{ 252{
228 return genpd->status == GPD_STATE_ACTIVE || genpd->resume_count > 0; 253 return genpd->status == GPD_STATE_WAIT_PARENT
254 || genpd->status == GPD_STATE_ACTIVE || genpd->resume_count > 0;
229} 255}
230 256
231/** 257/**
@@ -261,11 +287,13 @@ static int pm_genpd_poweroff(struct generic_pm_domain *genpd)
261 /* 287 /*
262 * Do not try to power off the domain in the following situations: 288 * Do not try to power off the domain in the following situations:
263 * (1) The domain is already in the "power off" state. 289 * (1) The domain is already in the "power off" state.
264 * (2) System suspend is in progress. 290 * (2) The domain is waiting for its parent to power up.
265 * (3) One of the domain's devices is being resumed right now. 291 * (3) One of the domain's devices is being resumed right now.
292 * (4) System suspend is in progress.
266 */ 293 */
267 if (genpd->status == GPD_STATE_POWER_OFF || genpd->prepared_count > 0 294 if (genpd->status == GPD_STATE_POWER_OFF
268 || genpd->resume_count > 0) 295 || genpd->status == GPD_STATE_WAIT_PARENT
296 || genpd->resume_count > 0 || genpd->prepared_count > 0)
269 return 0; 297 return 0;
270 298
271 if (atomic_read(&genpd->sd_count) > 0) 299 if (atomic_read(&genpd->sd_count) > 0)
@@ -299,14 +327,15 @@ static int pm_genpd_poweroff(struct generic_pm_domain *genpd)
299 list_for_each_entry_reverse(dle, &genpd->dev_list, node) { 327 list_for_each_entry_reverse(dle, &genpd->dev_list, node) {
300 ret = atomic_read(&genpd->sd_count) == 0 ? 328 ret = atomic_read(&genpd->sd_count) == 0 ?
301 __pm_genpd_save_device(dle, genpd) : -EBUSY; 329 __pm_genpd_save_device(dle, genpd) : -EBUSY;
330
331 if (genpd_abort_poweroff(genpd))
332 goto out;
333
302 if (ret) { 334 if (ret) {
303 genpd_set_active(genpd); 335 genpd_set_active(genpd);
304 goto out; 336 goto out;
305 } 337 }
306 338
307 if (genpd_abort_poweroff(genpd))
308 goto out;
309
310 if (genpd->status == GPD_STATE_REPEAT) { 339 if (genpd->status == GPD_STATE_REPEAT) {
311 genpd->poweroff_task = NULL; 340 genpd->poweroff_task = NULL;
312 goto start; 341 goto start;
@@ -432,11 +461,12 @@ static int pm_genpd_runtime_resume(struct device *dev)
432 if (IS_ERR(genpd)) 461 if (IS_ERR(genpd))
433 return -EINVAL; 462 return -EINVAL;
434 463
435 ret = pm_genpd_poweron(genpd);
436 if (ret)
437 return ret;
438
439 mutex_lock(&genpd->lock); 464 mutex_lock(&genpd->lock);
465 ret = __pm_genpd_poweron(genpd);
466 if (ret) {
467 mutex_unlock(&genpd->lock);
468 return ret;
469 }
440 genpd->status = GPD_STATE_BUSY; 470 genpd->status = GPD_STATE_BUSY;
441 genpd->resume_count++; 471 genpd->resume_count++;
442 for (;;) { 472 for (;;) {