aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRafael J. Wysocki <rjw@sisk.pl>2012-07-05 16:12:32 -0400
committerRafael J. Wysocki <rjw@sisk.pl>2012-07-05 16:12:32 -0400
commit1d5fcfec22ce5f69db0d29284d2b65ff8ab1bfaa (patch)
tree907136239832a60a2f7af6e6dd442fdc3ac0f034
parentcbc9ef0287ab764d3da0129efa673808df641fe3 (diff)
PM / Domains: Add device domain data reference counter
Add a mechanism for counting references to the struct generic_pm_domain_data object pointed to by dev->power.subsys_data->domain_data if the device in question belongs to a generic PM domain. This change is necessary for a subsequent patch making it possible to allocate that object from within pm_genpd_add_callbacks(), so that drivers can attach their PM domain device callbacks to devices before those devices are added to PM domains. This patch has been tested on the SH7372 Mackerel board. Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
-rw-r--r--drivers/base/power/domain.c83
-rw-r--r--include/linux/pm_domain.h1
2 files changed, 58 insertions, 26 deletions
diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
index 4b5f090fccb6..45eb3b155b6d 100644
--- a/drivers/base/power/domain.c
+++ b/drivers/base/power/domain.c
@@ -297,7 +297,7 @@ static int genpd_dev_pm_qos_notifier(struct notifier_block *nb,
297 297
298 pdd = dev->power.subsys_data ? 298 pdd = dev->power.subsys_data ?
299 dev->power.subsys_data->domain_data : NULL; 299 dev->power.subsys_data->domain_data : NULL;
300 if (pdd) { 300 if (pdd && pdd->dev) {
301 to_gpd_data(pdd)->td.constraint_changed = true; 301 to_gpd_data(pdd)->td.constraint_changed = true;
302 genpd = dev_to_genpd(dev); 302 genpd = dev_to_genpd(dev);
303 } else { 303 } else {
@@ -1266,6 +1266,27 @@ static void pm_genpd_complete(struct device *dev)
1266 1266
1267#endif /* CONFIG_PM_SLEEP */ 1267#endif /* CONFIG_PM_SLEEP */
1268 1268
1269static struct generic_pm_domain_data *__pm_genpd_alloc_dev_data(struct device *dev)
1270{
1271 struct generic_pm_domain_data *gpd_data;
1272
1273 gpd_data = kzalloc(sizeof(*gpd_data), GFP_KERNEL);
1274 if (!gpd_data)
1275 return NULL;
1276
1277 mutex_init(&gpd_data->lock);
1278 gpd_data->nb.notifier_call = genpd_dev_pm_qos_notifier;
1279 dev_pm_qos_add_notifier(dev, &gpd_data->nb);
1280 return gpd_data;
1281}
1282
1283static void __pm_genpd_free_dev_data(struct device *dev,
1284 struct generic_pm_domain_data *gpd_data)
1285{
1286 dev_pm_qos_remove_notifier(dev, &gpd_data->nb);
1287 kfree(gpd_data);
1288}
1289
1269/** 1290/**
1270 * __pm_genpd_add_device - Add a device to an I/O PM domain. 1291 * __pm_genpd_add_device - Add a device to an I/O PM domain.
1271 * @genpd: PM domain to add the device to. 1292 * @genpd: PM domain to add the device to.
@@ -1275,7 +1296,7 @@ static void pm_genpd_complete(struct device *dev)
1275int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev, 1296int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev,
1276 struct gpd_timing_data *td) 1297 struct gpd_timing_data *td)
1277{ 1298{
1278 struct generic_pm_domain_data *gpd_data; 1299 struct generic_pm_domain_data *gpd_data_new, *gpd_data = NULL;
1279 struct pm_domain_data *pdd; 1300 struct pm_domain_data *pdd;
1280 int ret = 0; 1301 int ret = 0;
1281 1302
@@ -1284,14 +1305,10 @@ int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev,
1284 if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev)) 1305 if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev))
1285 return -EINVAL; 1306 return -EINVAL;
1286 1307
1287 gpd_data = kzalloc(sizeof(*gpd_data), GFP_KERNEL); 1308 gpd_data_new = __pm_genpd_alloc_dev_data(dev);
1288 if (!gpd_data) 1309 if (!gpd_data_new)
1289 return -ENOMEM; 1310 return -ENOMEM;
1290 1311
1291 mutex_init(&gpd_data->lock);
1292 gpd_data->nb.notifier_call = genpd_dev_pm_qos_notifier;
1293 dev_pm_qos_add_notifier(dev, &gpd_data->nb);
1294
1295 genpd_acquire_lock(genpd); 1312 genpd_acquire_lock(genpd);
1296 1313
1297 if (genpd->prepared_count > 0) { 1314 if (genpd->prepared_count > 0) {
@@ -1305,35 +1322,42 @@ int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev,
1305 goto out; 1322 goto out;
1306 } 1323 }
1307 1324
1325 ret = dev_pm_get_subsys_data(dev);
1326 if (ret)
1327 goto out;
1328
1308 genpd->device_count++; 1329 genpd->device_count++;
1309 genpd->max_off_time_changed = true; 1330 genpd->max_off_time_changed = true;
1310 1331
1311 dev_pm_get_subsys_data(dev);
1312
1313 mutex_lock(&gpd_data->lock);
1314 spin_lock_irq(&dev->power.lock); 1332 spin_lock_irq(&dev->power.lock);
1333
1315 dev->pm_domain = &genpd->domain; 1334 dev->pm_domain = &genpd->domain;
1316 dev->power.subsys_data->domain_data = &gpd_data->base; 1335 if (dev->power.subsys_data->domain_data) {
1317 gpd_data->base.dev = dev; 1336 gpd_data = to_gpd_data(dev->power.subsys_data->domain_data);
1318 list_add_tail(&gpd_data->base.list_node, &genpd->dev_list); 1337 } else {
1319 gpd_data->need_restore = genpd->status == GPD_STATE_POWER_OFF; 1338 gpd_data = gpd_data_new;
1339 dev->power.subsys_data->domain_data = &gpd_data->base;
1340 }
1341 gpd_data->refcount++;
1320 if (td) 1342 if (td)
1321 gpd_data->td = *td; 1343 gpd_data->td = *td;
1322 1344
1345 spin_unlock_irq(&dev->power.lock);
1346
1347 mutex_lock(&gpd_data->lock);
1348 gpd_data->base.dev = dev;
1349 list_add_tail(&gpd_data->base.list_node, &genpd->dev_list);
1350 gpd_data->need_restore = genpd->status == GPD_STATE_POWER_OFF;
1323 gpd_data->td.constraint_changed = true; 1351 gpd_data->td.constraint_changed = true;
1324 gpd_data->td.effective_constraint_ns = -1; 1352 gpd_data->td.effective_constraint_ns = -1;
1325 spin_unlock_irq(&dev->power.lock);
1326 mutex_unlock(&gpd_data->lock); 1353 mutex_unlock(&gpd_data->lock);
1327 1354
1328 genpd_release_lock(genpd);
1329
1330 return 0;
1331
1332 out: 1355 out:
1333 genpd_release_lock(genpd); 1356 genpd_release_lock(genpd);
1334 1357
1335 dev_pm_qos_remove_notifier(dev, &gpd_data->nb); 1358 if (gpd_data != gpd_data_new)
1336 kfree(gpd_data); 1359 __pm_genpd_free_dev_data(dev, gpd_data_new);
1360
1337 return ret; 1361 return ret;
1338} 1362}
1339 1363
@@ -1379,6 +1403,7 @@ int pm_genpd_remove_device(struct generic_pm_domain *genpd,
1379{ 1403{
1380 struct generic_pm_domain_data *gpd_data; 1404 struct generic_pm_domain_data *gpd_data;
1381 struct pm_domain_data *pdd; 1405 struct pm_domain_data *pdd;
1406 bool remove = false;
1382 int ret = 0; 1407 int ret = 0;
1383 1408
1384 dev_dbg(dev, "%s()\n", __func__); 1409 dev_dbg(dev, "%s()\n", __func__);
@@ -1399,22 +1424,28 @@ int pm_genpd_remove_device(struct generic_pm_domain *genpd,
1399 genpd->max_off_time_changed = true; 1424 genpd->max_off_time_changed = true;
1400 1425
1401 spin_lock_irq(&dev->power.lock); 1426 spin_lock_irq(&dev->power.lock);
1427
1402 dev->pm_domain = NULL; 1428 dev->pm_domain = NULL;
1403 pdd = dev->power.subsys_data->domain_data; 1429 pdd = dev->power.subsys_data->domain_data;
1404 list_del_init(&pdd->list_node); 1430 list_del_init(&pdd->list_node);
1405 dev->power.subsys_data->domain_data = NULL; 1431 gpd_data = to_gpd_data(pdd);
1432 if (--gpd_data->refcount == 0) {
1433 dev->power.subsys_data->domain_data = NULL;
1434 remove = true;
1435 }
1436
1406 spin_unlock_irq(&dev->power.lock); 1437 spin_unlock_irq(&dev->power.lock);
1407 1438
1408 gpd_data = to_gpd_data(pdd);
1409 mutex_lock(&gpd_data->lock); 1439 mutex_lock(&gpd_data->lock);
1410 pdd->dev = NULL; 1440 pdd->dev = NULL;
1411 mutex_unlock(&gpd_data->lock); 1441 mutex_unlock(&gpd_data->lock);
1412 1442
1413 genpd_release_lock(genpd); 1443 genpd_release_lock(genpd);
1414 1444
1415 dev_pm_qos_remove_notifier(dev, &gpd_data->nb);
1416 kfree(gpd_data);
1417 dev_pm_put_subsys_data(dev); 1445 dev_pm_put_subsys_data(dev);
1446 if (remove)
1447 __pm_genpd_free_dev_data(dev, gpd_data);
1448
1418 return 0; 1449 return 0;
1419 1450
1420 out: 1451 out:
diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h
index 2febe31d2675..a7d6172922d4 100644
--- a/include/linux/pm_domain.h
+++ b/include/linux/pm_domain.h
@@ -112,6 +112,7 @@ struct generic_pm_domain_data {
112 struct gpd_timing_data td; 112 struct gpd_timing_data td;
113 struct notifier_block nb; 113 struct notifier_block nb;
114 struct mutex lock; 114 struct mutex lock;
115 unsigned int refcount;
115 bool need_restore; 116 bool need_restore;
116 bool always_on; 117 bool always_on;
117}; 118};