diff options
author | Rafael J. Wysocki <rjw@sisk.pl> | 2012-07-05 16:12:32 -0400 |
---|---|---|
committer | Rafael J. Wysocki <rjw@sisk.pl> | 2012-07-05 16:12:32 -0400 |
commit | 1d5fcfec22ce5f69db0d29284d2b65ff8ab1bfaa (patch) | |
tree | 907136239832a60a2f7af6e6dd442fdc3ac0f034 /drivers/base/power/domain.c | |
parent | cbc9ef0287ab764d3da0129efa673808df641fe3 (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>
Diffstat (limited to 'drivers/base/power/domain.c')
-rw-r--r-- | drivers/base/power/domain.c | 83 |
1 files changed, 57 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 | ||
1269 | static 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 | |||
1283 | static 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) | |||
1275 | int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev, | 1296 | int __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: |