aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/base
diff options
context:
space:
mode:
authorRafael J. Wysocki <rjw@sisk.pl>2011-08-08 17:43:40 -0400
committerRafael J. Wysocki <rjw@sisk.pl>2011-08-25 09:33:44 -0400
commit5063ce1571b73865cbdcd92db002e85809750c97 (patch)
tree0c844d3e9c61768492c1e87d4df78c44d7f4ed1e /drivers/base
parent3f241775c30365c33a0d2f6d40f4cf12470f48c6 (diff)
PM / Domains: Allow generic PM domains to have multiple masters
Currently, for a given generic PM domain there may be only one parent domain (i.e. a PM domain it depends on). However, there is at least one real-life case in which there should be two parents (masters) for one PM domain (the A3RV domain on SH7372 turns out to depend on the A4LC domain and it depends on the A4R domain and the same time). For this reason, allow a PM domain to have multiple parents (masters) by introducing objects representing links between PM domains. The (logical) links between PM domains represent relationships in which one domain is a master (i.e. it is depended on) and another domain is a slave (i.e. it depends on the master) with the rule that the slave cannot be powered on if the master is not powered on and the master cannot be powered off if the slave is not powered off. Each struct generic_pm_domain object representing a PM domain has two lists of links, a list of links in which it is a master and a list of links in which it is a slave. The first of these lists replaces the list of subdomains and the second one is used in place of the parent pointer. Each link is represented by struct gpd_link object containing pointers to the master and the slave and two struct list_head members allowing it to hook into two lists (the master's list of "master" links and the slave's list of "slave" links). This allows the code to get to the link from each side (either from the master or from the slave) and follow it in each direction. Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Diffstat (limited to 'drivers/base')
-rw-r--r--drivers/base/power/domain.c99
1 files changed, 58 insertions, 41 deletions
diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
index 1f4b1326c6a9..8fc538da1de0 100644
--- a/drivers/base/power/domain.c
+++ b/drivers/base/power/domain.c
@@ -81,19 +81,20 @@ 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 masters.
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 masters 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 __releases(&genpd->lock) __acquires(&genpd->lock)
92{ 92{
93 struct gpd_link *link;
93 DEFINE_WAIT(wait); 94 DEFINE_WAIT(wait);
94 int ret = 0; 95 int ret = 0;
95 96
96 /* If the domain's parent is being waited for, we have to wait too. */ 97 /* If the domain's master is being waited for, we have to wait too. */
97 for (;;) { 98 for (;;) {
98 prepare_to_wait(&genpd->status_wait_queue, &wait, 99 prepare_to_wait(&genpd->status_wait_queue, &wait,
99 TASK_UNINTERRUPTIBLE); 100 TASK_UNINTERRUPTIBLE);
@@ -116,24 +117,31 @@ int __pm_genpd_poweron(struct generic_pm_domain *genpd)
116 return 0; 117 return 0;
117 } 118 }
118 119
119 if (genpd->parent) { 120 /*
120 genpd_sd_counter_inc(genpd->parent); 121 * The list is guaranteed not to change while the loop below is being
122 * executed, unless one of the masters' .power_on() callbacks fiddles
123 * with it.
124 */
125 list_for_each_entry(link, &genpd->slave_links, slave_node) {
126 genpd_sd_counter_inc(link->master);
121 genpd->status = GPD_STATE_WAIT_PARENT; 127 genpd->status = GPD_STATE_WAIT_PARENT;
122 128
123 mutex_unlock(&genpd->lock); 129 mutex_unlock(&genpd->lock);
124 130
125 ret = pm_genpd_poweron(genpd->parent); 131 ret = pm_genpd_poweron(link->master);
126 132
127 mutex_lock(&genpd->lock); 133 mutex_lock(&genpd->lock);
128 134
129 /* 135 /*
130 * The "wait for parent" status is guaranteed not to change 136 * The "wait for parent" status is guaranteed not to change
131 * while the parent is powering on. 137 * while the master is powering on.
132 */ 138 */
133 genpd->status = GPD_STATE_POWER_OFF; 139 genpd->status = GPD_STATE_POWER_OFF;
134 wake_up_all(&genpd->status_wait_queue); 140 wake_up_all(&genpd->status_wait_queue);
135 if (ret) 141 if (ret) {
142 genpd_sd_counter_dec(link->master);
136 goto err; 143 goto err;
144 }
137 } 145 }
138 146
139 if (genpd->power_on) { 147 if (genpd->power_on) {
@@ -147,14 +155,14 @@ int __pm_genpd_poweron(struct generic_pm_domain *genpd)
147 return 0; 155 return 0;
148 156
149 err: 157 err:
150 if (genpd->parent) 158 list_for_each_entry_continue_reverse(link, &genpd->slave_links, slave_node)
151 genpd_sd_counter_dec(genpd->parent); 159 genpd_sd_counter_dec(link->master);
152 160
153 return ret; 161 return ret;
154} 162}
155 163
156/** 164/**
157 * pm_genpd_poweron - Restore power to a given PM domain and its parents. 165 * pm_genpd_poweron - Restore power to a given PM domain and its masters.
158 * @genpd: PM domain to power up. 166 * @genpd: PM domain to power up.
159 */ 167 */
160int pm_genpd_poweron(struct generic_pm_domain *genpd) 168int pm_genpd_poweron(struct generic_pm_domain *genpd)
@@ -278,8 +286,8 @@ void genpd_queue_power_off_work(struct generic_pm_domain *genpd)
278static int pm_genpd_poweroff(struct generic_pm_domain *genpd) 286static int pm_genpd_poweroff(struct generic_pm_domain *genpd)
279 __releases(&genpd->lock) __acquires(&genpd->lock) 287 __releases(&genpd->lock) __acquires(&genpd->lock)
280{ 288{
281 struct generic_pm_domain *parent;
282 struct dev_list_entry *dle; 289 struct dev_list_entry *dle;
290 struct gpd_link *link;
283 unsigned int not_suspended; 291 unsigned int not_suspended;
284 int ret = 0; 292 int ret = 0;
285 293
@@ -287,7 +295,7 @@ static int pm_genpd_poweroff(struct generic_pm_domain *genpd)
287 /* 295 /*
288 * Do not try to power off the domain in the following situations: 296 * Do not try to power off the domain in the following situations:
289 * (1) The domain is already in the "power off" state. 297 * (1) The domain is already in the "power off" state.
290 * (2) The domain is waiting for its parent to power up. 298 * (2) The domain is waiting for its master to power up.
291 * (3) One of the domain's devices is being resumed right now. 299 * (3) One of the domain's devices is being resumed right now.
292 * (4) System suspend is in progress. 300 * (4) System suspend is in progress.
293 */ 301 */
@@ -349,8 +357,8 @@ static int pm_genpd_poweroff(struct generic_pm_domain *genpd)
349 } 357 }
350 358
351 /* 359 /*
352 * If sd_count > 0 at this point, one of the children hasn't 360 * If sd_count > 0 at this point, one of the subdomains hasn't
353 * managed to call pm_genpd_poweron() for the parent yet after 361 * managed to call pm_genpd_poweron() for the master yet after
354 * incrementing it. In that case pm_genpd_poweron() will wait 362 * incrementing it. In that case pm_genpd_poweron() will wait
355 * for us to drop the lock, so we can call .power_off() and let 363 * for us to drop the lock, so we can call .power_off() and let
356 * the pm_genpd_poweron() restore power for us (this shouldn't 364 * the pm_genpd_poweron() restore power for us (this shouldn't
@@ -365,9 +373,10 @@ static int pm_genpd_poweroff(struct generic_pm_domain *genpd)
365 373
366 genpd->status = GPD_STATE_POWER_OFF; 374 genpd->status = GPD_STATE_POWER_OFF;
367 375
368 parent = genpd->parent; 376 list_for_each_entry(link, &genpd->slave_links, slave_node) {
369 if (parent && genpd_sd_counter_dec(parent)) 377 genpd_sd_counter_dec(link->master);
370 genpd_queue_power_off_work(parent); 378 genpd_queue_power_off_work(link->master);
379 }
371 380
372 out: 381 out:
373 genpd->poweroff_task = NULL; 382 genpd->poweroff_task = NULL;
@@ -527,11 +536,11 @@ static inline void __pm_genpd_runtime_resume(struct device *dev,
527#ifdef CONFIG_PM_SLEEP 536#ifdef CONFIG_PM_SLEEP
528 537
529/** 538/**
530 * pm_genpd_sync_poweroff - Synchronously power off a PM domain and its parents. 539 * pm_genpd_sync_poweroff - Synchronously power off a PM domain and its masters.
531 * @genpd: PM domain to power off, if possible. 540 * @genpd: PM domain to power off, if possible.
532 * 541 *
533 * Check if the given PM domain can be powered off (during system suspend or 542 * Check if the given PM domain can be powered off (during system suspend or
534 * hibernation) and do that if so. Also, in that case propagate to its parent. 543 * hibernation) and do that if so. Also, in that case propagate to its masters.
535 * 544 *
536 * This function is only called in "noirq" stages of system power transitions, 545 * This function is only called in "noirq" stages of system power transitions,
537 * so it need not acquire locks (all of the "noirq" callbacks are executed 546 * so it need not acquire locks (all of the "noirq" callbacks are executed
@@ -539,7 +548,7 @@ static inline void __pm_genpd_runtime_resume(struct device *dev,
539 */ 548 */
540static void pm_genpd_sync_poweroff(struct generic_pm_domain *genpd) 549static void pm_genpd_sync_poweroff(struct generic_pm_domain *genpd)
541{ 550{
542 struct generic_pm_domain *parent = genpd->parent; 551 struct gpd_link *link;
543 552
544 if (genpd->status == GPD_STATE_POWER_OFF) 553 if (genpd->status == GPD_STATE_POWER_OFF)
545 return; 554 return;
@@ -552,9 +561,10 @@ static void pm_genpd_sync_poweroff(struct generic_pm_domain *genpd)
552 genpd->power_off(genpd); 561 genpd->power_off(genpd);
553 562
554 genpd->status = GPD_STATE_POWER_OFF; 563 genpd->status = GPD_STATE_POWER_OFF;
555 if (parent) { 564
556 genpd_sd_counter_dec(parent); 565 list_for_each_entry(link, &genpd->slave_links, slave_node) {
557 pm_genpd_sync_poweroff(parent); 566 genpd_sd_counter_dec(link->master);
567 pm_genpd_sync_poweroff(link->master);
558 } 568 }
559} 569}
560 570
@@ -1173,7 +1183,7 @@ int pm_genpd_remove_device(struct generic_pm_domain *genpd,
1173int pm_genpd_add_subdomain(struct generic_pm_domain *genpd, 1183int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
1174 struct generic_pm_domain *new_subdomain) 1184 struct generic_pm_domain *new_subdomain)
1175{ 1185{
1176 struct generic_pm_domain *subdomain; 1186 struct gpd_link *link;
1177 int ret = 0; 1187 int ret = 0;
1178 1188
1179 if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(new_subdomain)) 1189 if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(new_subdomain))
@@ -1196,16 +1206,23 @@ int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
1196 goto out; 1206 goto out;
1197 } 1207 }
1198 1208
1199 list_for_each_entry(subdomain, &genpd->sd_list, sd_node) { 1209 list_for_each_entry(link, &genpd->slave_links, slave_node) {
1200 if (subdomain == new_subdomain) { 1210 if (link->slave == new_subdomain && link->master == genpd) {
1201 ret = -EINVAL; 1211 ret = -EINVAL;
1202 goto out; 1212 goto out;
1203 } 1213 }
1204 } 1214 }
1205 1215
1206 list_add_tail(&new_subdomain->sd_node, &genpd->sd_list); 1216 link = kzalloc(sizeof(*link), GFP_KERNEL);
1207 new_subdomain->parent = genpd; 1217 if (!link) {
1208 if (subdomain->status != GPD_STATE_POWER_OFF) 1218 ret = -ENOMEM;
1219 goto out;
1220 }
1221 link->master = genpd;
1222 list_add_tail(&link->master_node, &genpd->master_links);
1223 link->slave = new_subdomain;
1224 list_add_tail(&link->slave_node, &new_subdomain->slave_links);
1225 if (new_subdomain->status != GPD_STATE_POWER_OFF)
1209 genpd_sd_counter_inc(genpd); 1226 genpd_sd_counter_inc(genpd);
1210 1227
1211 out: 1228 out:
@@ -1218,22 +1235,22 @@ int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
1218/** 1235/**
1219 * pm_genpd_remove_subdomain - Remove a subdomain from an I/O PM domain. 1236 * pm_genpd_remove_subdomain - Remove a subdomain from an I/O PM domain.
1220 * @genpd: Master PM domain to remove the subdomain from. 1237 * @genpd: Master PM domain to remove the subdomain from.
1221 * @target: Subdomain to be removed. 1238 * @subdomain: Subdomain to be removed.
1222 */ 1239 */
1223int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd, 1240int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
1224 struct generic_pm_domain *target) 1241 struct generic_pm_domain *subdomain)
1225{ 1242{
1226 struct generic_pm_domain *subdomain; 1243 struct gpd_link *link;
1227 int ret = -EINVAL; 1244 int ret = -EINVAL;
1228 1245
1229 if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(target)) 1246 if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(subdomain))
1230 return -EINVAL; 1247 return -EINVAL;
1231 1248
1232 start: 1249 start:
1233 genpd_acquire_lock(genpd); 1250 genpd_acquire_lock(genpd);
1234 1251
1235 list_for_each_entry(subdomain, &genpd->sd_list, sd_node) { 1252 list_for_each_entry(link, &genpd->master_links, master_node) {
1236 if (subdomain != target) 1253 if (link->slave != subdomain)
1237 continue; 1254 continue;
1238 1255
1239 mutex_lock_nested(&subdomain->lock, SINGLE_DEPTH_NESTING); 1256 mutex_lock_nested(&subdomain->lock, SINGLE_DEPTH_NESTING);
@@ -1245,8 +1262,9 @@ int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
1245 goto start; 1262 goto start;
1246 } 1263 }
1247 1264
1248 list_del(&subdomain->sd_node); 1265 list_del(&link->master_node);
1249 subdomain->parent = NULL; 1266 list_del(&link->slave_node);
1267 kfree(link);
1250 if (subdomain->status != GPD_STATE_POWER_OFF) 1268 if (subdomain->status != GPD_STATE_POWER_OFF)
1251 genpd_sd_counter_dec(genpd); 1269 genpd_sd_counter_dec(genpd);
1252 1270
@@ -1273,10 +1291,9 @@ void pm_genpd_init(struct generic_pm_domain *genpd,
1273 if (IS_ERR_OR_NULL(genpd)) 1291 if (IS_ERR_OR_NULL(genpd))
1274 return; 1292 return;
1275 1293
1276 INIT_LIST_HEAD(&genpd->sd_node); 1294 INIT_LIST_HEAD(&genpd->master_links);
1277 genpd->parent = NULL; 1295 INIT_LIST_HEAD(&genpd->slave_links);
1278 INIT_LIST_HEAD(&genpd->dev_list); 1296 INIT_LIST_HEAD(&genpd->dev_list);
1279 INIT_LIST_HEAD(&genpd->sd_list);
1280 mutex_init(&genpd->lock); 1297 mutex_init(&genpd->lock);
1281 genpd->gov = gov; 1298 genpd->gov = gov;
1282 INIT_WORK(&genpd->power_off_work, genpd_power_off_work_fn); 1299 INIT_WORK(&genpd->power_off_work, genpd_power_off_work_fn);