diff options
author | Rafael J. Wysocki <rjw@sisk.pl> | 2011-08-08 17:43:29 -0400 |
---|---|---|
committer | Rafael J. Wysocki <rjw@sisk.pl> | 2011-08-25 09:33:44 -0400 |
commit | 3f241775c30365c33a0d2f6d40f4cf12470f48c6 (patch) | |
tree | 45b3fa3b5da4d9cbb006762118dcd0f8837434ff /drivers/base/power | |
parent | 9e08cf429697090d0fac57d493dc7b6de17a5eee (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')
-rw-r--r-- | drivers/base/power/domain.c | 90 |
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 | */ |
90 | int pm_genpd_poweron(struct generic_pm_domain *genpd) | 90 | int __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 | */ | ||
160 | int 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 | */ |
226 | static bool genpd_abort_poweroff(struct generic_pm_domain *genpd) | 251 | static 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 (;;) { |