diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2012-07-22 16:36:52 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-07-22 16:36:52 -0400 |
commit | 7100e505b76b4e2efd88b2459d1a932214e29f8a (patch) | |
tree | a8eae8687dc1511c89463b1eb93c8349a7471ab3 /drivers/base | |
parent | cb47c1831fa406c964468b259f2082c16cc3f757 (diff) | |
parent | 75a4161a58dd157a2bd2dc8e9986e45b62ac46cf (diff) |
Merge tag 'pm-for-3.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm
Pull power management updates from Rafael Wysocki:
- ACPI conversion to PM handling based on struct dev_pm_ops.
- Conversion of a number of platform drivers to PM handling based on
struct dev_pm_ops and removal of empty legacy PM callbacks from a
couple of PCI drivers.
- Suspend-to-both for in-kernel hibernation from Bojan Smojver.
- cpuidle fixes and cleanups from ShuoX Liu, Daniel Lezcano and Preeti
Murthy.
- cpufreq bug fixes from Jonghwa Lee and Stephen Boyd.
- Suspend and hibernate fixes from Srivatsa Bhat and Colin Cross.
- Generic PM domains framework updates.
- RTC CMOS wakeup signaling update from Paul Fox.
- sparse warnings fixes from Sachin Kamat.
- Build warnings fixes for the generic PM domains framework and PM
sysfs code.
- sysfs switch for printing device suspend times from Sameer Nanda.
- Documentation fix from Oskar Schirmer.
* tag 'pm-for-3.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm: (70 commits)
cpufreq: Fix sysfs deadlock with concurrent hotplug/frequency switch
EXYNOS: bugfix on retrieving old_index from freqs.old
PM / Sleep: call early resume handlers when suspend_noirq fails
PM / QoS: Use NULL pointer instead of plain integer in qos.c
PM / QoS: Use NULL pointer instead of plain integer in pm_qos.h
PM / Sleep: Require CAP_BLOCK_SUSPEND to use wake_lock/wake_unlock
PM / Sleep: Add missing static storage class specifiers in main.c
cpuilde / ACPI: remove time from acpi_processor_cx structure
cpuidle / ACPI: remove usage from acpi_processor_cx structure
cpuidle / ACPI : remove latency_ticks from acpi_processor_cx structure
rtc-cmos: report wakeups from interrupt handler
PM / Sleep: Fix build warning in sysfs.c for CONFIG_PM_SLEEP unset
PM / Domains: Fix build warning for CONFIG_PM_RUNTIME unset
olpc-xo15-sci: Use struct dev_pm_ops for power management
PM / Domains: Replace plain integer with NULL pointer in domain.c file
PM / Domains: Add missing static storage class specifier in domain.c file
PM / crypto / ux500: Use struct dev_pm_ops for power management
PM / IPMI: Remove empty legacy PCI PM callbacks
tpm_nsc: Use struct dev_pm_ops for power management
tpm_tis: Use struct dev_pm_ops for power management
...
Diffstat (limited to 'drivers/base')
-rw-r--r-- | drivers/base/power/domain.c | 342 | ||||
-rw-r--r-- | drivers/base/power/main.c | 26 | ||||
-rw-r--r-- | drivers/base/power/qos.c | 2 | ||||
-rw-r--r-- | drivers/base/power/sysfs.c | 4 |
4 files changed, 291 insertions, 83 deletions
diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 83aa694a8efe..ba3487c9835b 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c | |||
@@ -75,19 +75,6 @@ static int genpd_start_dev(struct generic_pm_domain *genpd, struct device *dev) | |||
75 | start_latency_ns, "start"); | 75 | start_latency_ns, "start"); |
76 | } | 76 | } |
77 | 77 | ||
78 | static int genpd_save_dev(struct generic_pm_domain *genpd, struct device *dev) | ||
79 | { | ||
80 | return GENPD_DEV_TIMED_CALLBACK(genpd, int, save_state, dev, | ||
81 | save_state_latency_ns, "state save"); | ||
82 | } | ||
83 | |||
84 | static int genpd_restore_dev(struct generic_pm_domain *genpd, struct device *dev) | ||
85 | { | ||
86 | return GENPD_DEV_TIMED_CALLBACK(genpd, int, restore_state, dev, | ||
87 | restore_state_latency_ns, | ||
88 | "state restore"); | ||
89 | } | ||
90 | |||
91 | static bool genpd_sd_counter_dec(struct generic_pm_domain *genpd) | 78 | static bool genpd_sd_counter_dec(struct generic_pm_domain *genpd) |
92 | { | 79 | { |
93 | bool ret = false; | 80 | bool ret = false; |
@@ -139,6 +126,19 @@ static void genpd_set_active(struct generic_pm_domain *genpd) | |||
139 | genpd->status = GPD_STATE_ACTIVE; | 126 | genpd->status = GPD_STATE_ACTIVE; |
140 | } | 127 | } |
141 | 128 | ||
129 | static void genpd_recalc_cpu_exit_latency(struct generic_pm_domain *genpd) | ||
130 | { | ||
131 | s64 usecs64; | ||
132 | |||
133 | if (!genpd->cpu_data) | ||
134 | return; | ||
135 | |||
136 | usecs64 = genpd->power_on_latency_ns; | ||
137 | do_div(usecs64, NSEC_PER_USEC); | ||
138 | usecs64 += genpd->cpu_data->saved_exit_latency; | ||
139 | genpd->cpu_data->idle_state->exit_latency = usecs64; | ||
140 | } | ||
141 | |||
142 | /** | 142 | /** |
143 | * __pm_genpd_poweron - Restore power to a given PM domain and its masters. | 143 | * __pm_genpd_poweron - Restore power to a given PM domain and its masters. |
144 | * @genpd: PM domain to power up. | 144 | * @genpd: PM domain to power up. |
@@ -146,7 +146,7 @@ static void genpd_set_active(struct generic_pm_domain *genpd) | |||
146 | * Restore power to @genpd and all of its masters so that it is possible to | 146 | * Restore power to @genpd and all of its masters so that it is possible to |
147 | * resume a device belonging to it. | 147 | * resume a device belonging to it. |
148 | */ | 148 | */ |
149 | int __pm_genpd_poweron(struct generic_pm_domain *genpd) | 149 | static int __pm_genpd_poweron(struct generic_pm_domain *genpd) |
150 | __releases(&genpd->lock) __acquires(&genpd->lock) | 150 | __releases(&genpd->lock) __acquires(&genpd->lock) |
151 | { | 151 | { |
152 | struct gpd_link *link; | 152 | struct gpd_link *link; |
@@ -176,6 +176,13 @@ int __pm_genpd_poweron(struct generic_pm_domain *genpd) | |||
176 | return 0; | 176 | return 0; |
177 | } | 177 | } |
178 | 178 | ||
179 | if (genpd->cpu_data) { | ||
180 | cpuidle_pause_and_lock(); | ||
181 | genpd->cpu_data->idle_state->disabled = true; | ||
182 | cpuidle_resume_and_unlock(); | ||
183 | goto out; | ||
184 | } | ||
185 | |||
179 | /* | 186 | /* |
180 | * The list is guaranteed not to change while the loop below is being | 187 | * The list is guaranteed not to change while the loop below is being |
181 | * executed, unless one of the masters' .power_on() callbacks fiddles | 188 | * executed, unless one of the masters' .power_on() callbacks fiddles |
@@ -215,6 +222,7 @@ int __pm_genpd_poweron(struct generic_pm_domain *genpd) | |||
215 | if (elapsed_ns > genpd->power_on_latency_ns) { | 222 | if (elapsed_ns > genpd->power_on_latency_ns) { |
216 | genpd->power_on_latency_ns = elapsed_ns; | 223 | genpd->power_on_latency_ns = elapsed_ns; |
217 | genpd->max_off_time_changed = true; | 224 | genpd->max_off_time_changed = true; |
225 | genpd_recalc_cpu_exit_latency(genpd); | ||
218 | if (genpd->name) | 226 | if (genpd->name) |
219 | pr_warning("%s: Power-on latency exceeded, " | 227 | pr_warning("%s: Power-on latency exceeded, " |
220 | "new value %lld ns\n", genpd->name, | 228 | "new value %lld ns\n", genpd->name, |
@@ -222,6 +230,7 @@ int __pm_genpd_poweron(struct generic_pm_domain *genpd) | |||
222 | } | 230 | } |
223 | } | 231 | } |
224 | 232 | ||
233 | out: | ||
225 | genpd_set_active(genpd); | 234 | genpd_set_active(genpd); |
226 | 235 | ||
227 | return 0; | 236 | return 0; |
@@ -251,6 +260,19 @@ int pm_genpd_poweron(struct generic_pm_domain *genpd) | |||
251 | 260 | ||
252 | #ifdef CONFIG_PM_RUNTIME | 261 | #ifdef CONFIG_PM_RUNTIME |
253 | 262 | ||
263 | static int genpd_save_dev(struct generic_pm_domain *genpd, struct device *dev) | ||
264 | { | ||
265 | return GENPD_DEV_TIMED_CALLBACK(genpd, int, save_state, dev, | ||
266 | save_state_latency_ns, "state save"); | ||
267 | } | ||
268 | |||
269 | static int genpd_restore_dev(struct generic_pm_domain *genpd, struct device *dev) | ||
270 | { | ||
271 | return GENPD_DEV_TIMED_CALLBACK(genpd, int, restore_state, dev, | ||
272 | restore_state_latency_ns, | ||
273 | "state restore"); | ||
274 | } | ||
275 | |||
254 | static int genpd_dev_pm_qos_notifier(struct notifier_block *nb, | 276 | static int genpd_dev_pm_qos_notifier(struct notifier_block *nb, |
255 | unsigned long val, void *ptr) | 277 | unsigned long val, void *ptr) |
256 | { | 278 | { |
@@ -275,7 +297,7 @@ static int genpd_dev_pm_qos_notifier(struct notifier_block *nb, | |||
275 | 297 | ||
276 | pdd = dev->power.subsys_data ? | 298 | pdd = dev->power.subsys_data ? |
277 | dev->power.subsys_data->domain_data : NULL; | 299 | dev->power.subsys_data->domain_data : NULL; |
278 | if (pdd) { | 300 | if (pdd && pdd->dev) { |
279 | to_gpd_data(pdd)->td.constraint_changed = true; | 301 | to_gpd_data(pdd)->td.constraint_changed = true; |
280 | genpd = dev_to_genpd(dev); | 302 | genpd = dev_to_genpd(dev); |
281 | } else { | 303 | } else { |
@@ -339,19 +361,16 @@ static void __pm_genpd_restore_device(struct pm_domain_data *pdd, | |||
339 | { | 361 | { |
340 | struct generic_pm_domain_data *gpd_data = to_gpd_data(pdd); | 362 | struct generic_pm_domain_data *gpd_data = to_gpd_data(pdd); |
341 | struct device *dev = pdd->dev; | 363 | struct device *dev = pdd->dev; |
364 | bool need_restore = gpd_data->need_restore; | ||
342 | 365 | ||
343 | if (!gpd_data->need_restore) | 366 | gpd_data->need_restore = false; |
344 | return; | ||
345 | |||
346 | mutex_unlock(&genpd->lock); | 367 | mutex_unlock(&genpd->lock); |
347 | 368 | ||
348 | genpd_start_dev(genpd, dev); | 369 | genpd_start_dev(genpd, dev); |
349 | genpd_restore_dev(genpd, dev); | 370 | if (need_restore) |
350 | genpd_stop_dev(genpd, dev); | 371 | genpd_restore_dev(genpd, dev); |
351 | 372 | ||
352 | mutex_lock(&genpd->lock); | 373 | mutex_lock(&genpd->lock); |
353 | |||
354 | gpd_data->need_restore = false; | ||
355 | } | 374 | } |
356 | 375 | ||
357 | /** | 376 | /** |
@@ -458,6 +477,21 @@ static int pm_genpd_poweroff(struct generic_pm_domain *genpd) | |||
458 | } | 477 | } |
459 | } | 478 | } |
460 | 479 | ||
480 | if (genpd->cpu_data) { | ||
481 | /* | ||
482 | * If cpu_data is set, cpuidle should turn the domain off when | ||
483 | * the CPU in it is idle. In that case we don't decrement the | ||
484 | * subdomain counts of the master domains, so that power is not | ||
485 | * removed from the current domain prematurely as a result of | ||
486 | * cutting off the masters' power. | ||
487 | */ | ||
488 | genpd->status = GPD_STATE_POWER_OFF; | ||
489 | cpuidle_pause_and_lock(); | ||
490 | genpd->cpu_data->idle_state->disabled = false; | ||
491 | cpuidle_resume_and_unlock(); | ||
492 | goto out; | ||
493 | } | ||
494 | |||
461 | if (genpd->power_off) { | 495 | if (genpd->power_off) { |
462 | ktime_t time_start; | 496 | ktime_t time_start; |
463 | s64 elapsed_ns; | 497 | s64 elapsed_ns; |
@@ -595,7 +629,7 @@ static int pm_genpd_runtime_resume(struct device *dev) | |||
595 | 629 | ||
596 | /* If power.irq_safe, the PM domain is never powered off. */ | 630 | /* If power.irq_safe, the PM domain is never powered off. */ |
597 | if (dev->power.irq_safe) | 631 | if (dev->power.irq_safe) |
598 | goto out; | 632 | return genpd_start_dev(genpd, dev); |
599 | 633 | ||
600 | mutex_lock(&genpd->lock); | 634 | mutex_lock(&genpd->lock); |
601 | ret = __pm_genpd_poweron(genpd); | 635 | ret = __pm_genpd_poweron(genpd); |
@@ -628,9 +662,6 @@ static int pm_genpd_runtime_resume(struct device *dev) | |||
628 | wake_up_all(&genpd->status_wait_queue); | 662 | wake_up_all(&genpd->status_wait_queue); |
629 | mutex_unlock(&genpd->lock); | 663 | mutex_unlock(&genpd->lock); |
630 | 664 | ||
631 | out: | ||
632 | genpd_start_dev(genpd, dev); | ||
633 | |||
634 | return 0; | 665 | return 0; |
635 | } | 666 | } |
636 | 667 | ||
@@ -1235,6 +1266,27 @@ static void pm_genpd_complete(struct device *dev) | |||
1235 | 1266 | ||
1236 | #endif /* CONFIG_PM_SLEEP */ | 1267 | #endif /* CONFIG_PM_SLEEP */ |
1237 | 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 | |||
1238 | /** | 1290 | /** |
1239 | * __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. |
1240 | * @genpd: PM domain to add the device to. | 1292 | * @genpd: PM domain to add the device to. |
@@ -1244,7 +1296,7 @@ static void pm_genpd_complete(struct device *dev) | |||
1244 | 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, |
1245 | struct gpd_timing_data *td) | 1297 | struct gpd_timing_data *td) |
1246 | { | 1298 | { |
1247 | struct generic_pm_domain_data *gpd_data; | 1299 | struct generic_pm_domain_data *gpd_data_new, *gpd_data = NULL; |
1248 | struct pm_domain_data *pdd; | 1300 | struct pm_domain_data *pdd; |
1249 | int ret = 0; | 1301 | int ret = 0; |
1250 | 1302 | ||
@@ -1253,14 +1305,10 @@ int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev, | |||
1253 | if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev)) | 1305 | if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev)) |
1254 | return -EINVAL; | 1306 | return -EINVAL; |
1255 | 1307 | ||
1256 | gpd_data = kzalloc(sizeof(*gpd_data), GFP_KERNEL); | 1308 | gpd_data_new = __pm_genpd_alloc_dev_data(dev); |
1257 | if (!gpd_data) | 1309 | if (!gpd_data_new) |
1258 | return -ENOMEM; | 1310 | return -ENOMEM; |
1259 | 1311 | ||
1260 | mutex_init(&gpd_data->lock); | ||
1261 | gpd_data->nb.notifier_call = genpd_dev_pm_qos_notifier; | ||
1262 | dev_pm_qos_add_notifier(dev, &gpd_data->nb); | ||
1263 | |||
1264 | genpd_acquire_lock(genpd); | 1312 | genpd_acquire_lock(genpd); |
1265 | 1313 | ||
1266 | if (genpd->prepared_count > 0) { | 1314 | if (genpd->prepared_count > 0) { |
@@ -1274,35 +1322,42 @@ int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev, | |||
1274 | goto out; | 1322 | goto out; |
1275 | } | 1323 | } |
1276 | 1324 | ||
1325 | ret = dev_pm_get_subsys_data(dev); | ||
1326 | if (ret) | ||
1327 | goto out; | ||
1328 | |||
1277 | genpd->device_count++; | 1329 | genpd->device_count++; |
1278 | genpd->max_off_time_changed = true; | 1330 | genpd->max_off_time_changed = true; |
1279 | 1331 | ||
1280 | dev_pm_get_subsys_data(dev); | ||
1281 | |||
1282 | mutex_lock(&gpd_data->lock); | ||
1283 | spin_lock_irq(&dev->power.lock); | 1332 | spin_lock_irq(&dev->power.lock); |
1333 | |||
1284 | dev->pm_domain = &genpd->domain; | 1334 | dev->pm_domain = &genpd->domain; |
1285 | dev->power.subsys_data->domain_data = &gpd_data->base; | 1335 | if (dev->power.subsys_data->domain_data) { |
1286 | gpd_data->base.dev = dev; | 1336 | gpd_data = to_gpd_data(dev->power.subsys_data->domain_data); |
1287 | list_add_tail(&gpd_data->base.list_node, &genpd->dev_list); | 1337 | } else { |
1288 | 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++; | ||
1289 | if (td) | 1342 | if (td) |
1290 | gpd_data->td = *td; | 1343 | gpd_data->td = *td; |
1291 | 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; | ||
1292 | gpd_data->td.constraint_changed = true; | 1351 | gpd_data->td.constraint_changed = true; |
1293 | gpd_data->td.effective_constraint_ns = -1; | 1352 | gpd_data->td.effective_constraint_ns = -1; |
1294 | spin_unlock_irq(&dev->power.lock); | ||
1295 | mutex_unlock(&gpd_data->lock); | 1353 | mutex_unlock(&gpd_data->lock); |
1296 | 1354 | ||
1297 | genpd_release_lock(genpd); | ||
1298 | |||
1299 | return 0; | ||
1300 | |||
1301 | out: | 1355 | out: |
1302 | genpd_release_lock(genpd); | 1356 | genpd_release_lock(genpd); |
1303 | 1357 | ||
1304 | dev_pm_qos_remove_notifier(dev, &gpd_data->nb); | 1358 | if (gpd_data != gpd_data_new) |
1305 | kfree(gpd_data); | 1359 | __pm_genpd_free_dev_data(dev, gpd_data_new); |
1360 | |||
1306 | return ret; | 1361 | return ret; |
1307 | } | 1362 | } |
1308 | 1363 | ||
@@ -1348,6 +1403,7 @@ int pm_genpd_remove_device(struct generic_pm_domain *genpd, | |||
1348 | { | 1403 | { |
1349 | struct generic_pm_domain_data *gpd_data; | 1404 | struct generic_pm_domain_data *gpd_data; |
1350 | struct pm_domain_data *pdd; | 1405 | struct pm_domain_data *pdd; |
1406 | bool remove = false; | ||
1351 | int ret = 0; | 1407 | int ret = 0; |
1352 | 1408 | ||
1353 | dev_dbg(dev, "%s()\n", __func__); | 1409 | dev_dbg(dev, "%s()\n", __func__); |
@@ -1368,22 +1424,28 @@ int pm_genpd_remove_device(struct generic_pm_domain *genpd, | |||
1368 | genpd->max_off_time_changed = true; | 1424 | genpd->max_off_time_changed = true; |
1369 | 1425 | ||
1370 | spin_lock_irq(&dev->power.lock); | 1426 | spin_lock_irq(&dev->power.lock); |
1427 | |||
1371 | dev->pm_domain = NULL; | 1428 | dev->pm_domain = NULL; |
1372 | pdd = dev->power.subsys_data->domain_data; | 1429 | pdd = dev->power.subsys_data->domain_data; |
1373 | list_del_init(&pdd->list_node); | 1430 | list_del_init(&pdd->list_node); |
1374 | 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 | |||
1375 | spin_unlock_irq(&dev->power.lock); | 1437 | spin_unlock_irq(&dev->power.lock); |
1376 | 1438 | ||
1377 | gpd_data = to_gpd_data(pdd); | ||
1378 | mutex_lock(&gpd_data->lock); | 1439 | mutex_lock(&gpd_data->lock); |
1379 | pdd->dev = NULL; | 1440 | pdd->dev = NULL; |
1380 | mutex_unlock(&gpd_data->lock); | 1441 | mutex_unlock(&gpd_data->lock); |
1381 | 1442 | ||
1382 | genpd_release_lock(genpd); | 1443 | genpd_release_lock(genpd); |
1383 | 1444 | ||
1384 | dev_pm_qos_remove_notifier(dev, &gpd_data->nb); | ||
1385 | kfree(gpd_data); | ||
1386 | 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 | |||
1387 | return 0; | 1449 | return 0; |
1388 | 1450 | ||
1389 | out: | 1451 | out: |
@@ -1541,33 +1603,52 @@ int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd, | |||
1541 | * @dev: Device to add the callbacks to. | 1603 | * @dev: Device to add the callbacks to. |
1542 | * @ops: Set of callbacks to add. | 1604 | * @ops: Set of callbacks to add. |
1543 | * @td: Timing data to add to the device along with the callbacks (optional). | 1605 | * @td: Timing data to add to the device along with the callbacks (optional). |
1606 | * | ||
1607 | * Every call to this routine should be balanced with a call to | ||
1608 | * __pm_genpd_remove_callbacks() and they must not be nested. | ||
1544 | */ | 1609 | */ |
1545 | int pm_genpd_add_callbacks(struct device *dev, struct gpd_dev_ops *ops, | 1610 | int pm_genpd_add_callbacks(struct device *dev, struct gpd_dev_ops *ops, |
1546 | struct gpd_timing_data *td) | 1611 | struct gpd_timing_data *td) |
1547 | { | 1612 | { |
1548 | struct pm_domain_data *pdd; | 1613 | struct generic_pm_domain_data *gpd_data_new, *gpd_data = NULL; |
1549 | int ret = 0; | 1614 | int ret = 0; |
1550 | 1615 | ||
1551 | if (!(dev && dev->power.subsys_data && ops)) | 1616 | if (!(dev && ops)) |
1552 | return -EINVAL; | 1617 | return -EINVAL; |
1553 | 1618 | ||
1619 | gpd_data_new = __pm_genpd_alloc_dev_data(dev); | ||
1620 | if (!gpd_data_new) | ||
1621 | return -ENOMEM; | ||
1622 | |||
1554 | pm_runtime_disable(dev); | 1623 | pm_runtime_disable(dev); |
1555 | device_pm_lock(); | 1624 | device_pm_lock(); |
1556 | 1625 | ||
1557 | pdd = dev->power.subsys_data->domain_data; | 1626 | ret = dev_pm_get_subsys_data(dev); |
1558 | if (pdd) { | 1627 | if (ret) |
1559 | struct generic_pm_domain_data *gpd_data = to_gpd_data(pdd); | 1628 | goto out; |
1560 | 1629 | ||
1561 | gpd_data->ops = *ops; | 1630 | spin_lock_irq(&dev->power.lock); |
1562 | if (td) | 1631 | |
1563 | gpd_data->td = *td; | 1632 | if (dev->power.subsys_data->domain_data) { |
1633 | gpd_data = to_gpd_data(dev->power.subsys_data->domain_data); | ||
1564 | } else { | 1634 | } else { |
1565 | ret = -EINVAL; | 1635 | gpd_data = gpd_data_new; |
1636 | dev->power.subsys_data->domain_data = &gpd_data->base; | ||
1566 | } | 1637 | } |
1638 | gpd_data->refcount++; | ||
1639 | gpd_data->ops = *ops; | ||
1640 | if (td) | ||
1641 | gpd_data->td = *td; | ||
1642 | |||
1643 | spin_unlock_irq(&dev->power.lock); | ||
1567 | 1644 | ||
1645 | out: | ||
1568 | device_pm_unlock(); | 1646 | device_pm_unlock(); |
1569 | pm_runtime_enable(dev); | 1647 | pm_runtime_enable(dev); |
1570 | 1648 | ||
1649 | if (gpd_data != gpd_data_new) | ||
1650 | __pm_genpd_free_dev_data(dev, gpd_data_new); | ||
1651 | |||
1571 | return ret; | 1652 | return ret; |
1572 | } | 1653 | } |
1573 | EXPORT_SYMBOL_GPL(pm_genpd_add_callbacks); | 1654 | EXPORT_SYMBOL_GPL(pm_genpd_add_callbacks); |
@@ -1576,10 +1657,13 @@ EXPORT_SYMBOL_GPL(pm_genpd_add_callbacks); | |||
1576 | * __pm_genpd_remove_callbacks - Remove PM domain callbacks from a given device. | 1657 | * __pm_genpd_remove_callbacks - Remove PM domain callbacks from a given device. |
1577 | * @dev: Device to remove the callbacks from. | 1658 | * @dev: Device to remove the callbacks from. |
1578 | * @clear_td: If set, clear the device's timing data too. | 1659 | * @clear_td: If set, clear the device's timing data too. |
1660 | * | ||
1661 | * This routine can only be called after pm_genpd_add_callbacks(). | ||
1579 | */ | 1662 | */ |
1580 | int __pm_genpd_remove_callbacks(struct device *dev, bool clear_td) | 1663 | int __pm_genpd_remove_callbacks(struct device *dev, bool clear_td) |
1581 | { | 1664 | { |
1582 | struct pm_domain_data *pdd; | 1665 | struct generic_pm_domain_data *gpd_data = NULL; |
1666 | bool remove = false; | ||
1583 | int ret = 0; | 1667 | int ret = 0; |
1584 | 1668 | ||
1585 | if (!(dev && dev->power.subsys_data)) | 1669 | if (!(dev && dev->power.subsys_data)) |
@@ -1588,24 +1672,118 @@ int __pm_genpd_remove_callbacks(struct device *dev, bool clear_td) | |||
1588 | pm_runtime_disable(dev); | 1672 | pm_runtime_disable(dev); |
1589 | device_pm_lock(); | 1673 | device_pm_lock(); |
1590 | 1674 | ||
1591 | pdd = dev->power.subsys_data->domain_data; | 1675 | spin_lock_irq(&dev->power.lock); |
1592 | if (pdd) { | ||
1593 | struct generic_pm_domain_data *gpd_data = to_gpd_data(pdd); | ||
1594 | 1676 | ||
1595 | gpd_data->ops = (struct gpd_dev_ops){ 0 }; | 1677 | if (dev->power.subsys_data->domain_data) { |
1678 | gpd_data = to_gpd_data(dev->power.subsys_data->domain_data); | ||
1679 | gpd_data->ops = (struct gpd_dev_ops){ NULL }; | ||
1596 | if (clear_td) | 1680 | if (clear_td) |
1597 | gpd_data->td = (struct gpd_timing_data){ 0 }; | 1681 | gpd_data->td = (struct gpd_timing_data){ 0 }; |
1682 | |||
1683 | if (--gpd_data->refcount == 0) { | ||
1684 | dev->power.subsys_data->domain_data = NULL; | ||
1685 | remove = true; | ||
1686 | } | ||
1598 | } else { | 1687 | } else { |
1599 | ret = -EINVAL; | 1688 | ret = -EINVAL; |
1600 | } | 1689 | } |
1601 | 1690 | ||
1691 | spin_unlock_irq(&dev->power.lock); | ||
1692 | |||
1602 | device_pm_unlock(); | 1693 | device_pm_unlock(); |
1603 | pm_runtime_enable(dev); | 1694 | pm_runtime_enable(dev); |
1604 | 1695 | ||
1605 | return ret; | 1696 | if (ret) |
1697 | return ret; | ||
1698 | |||
1699 | dev_pm_put_subsys_data(dev); | ||
1700 | if (remove) | ||
1701 | __pm_genpd_free_dev_data(dev, gpd_data); | ||
1702 | |||
1703 | return 0; | ||
1606 | } | 1704 | } |
1607 | EXPORT_SYMBOL_GPL(__pm_genpd_remove_callbacks); | 1705 | EXPORT_SYMBOL_GPL(__pm_genpd_remove_callbacks); |
1608 | 1706 | ||
1707 | int genpd_attach_cpuidle(struct generic_pm_domain *genpd, int state) | ||
1708 | { | ||
1709 | struct cpuidle_driver *cpuidle_drv; | ||
1710 | struct gpd_cpu_data *cpu_data; | ||
1711 | struct cpuidle_state *idle_state; | ||
1712 | int ret = 0; | ||
1713 | |||
1714 | if (IS_ERR_OR_NULL(genpd) || state < 0) | ||
1715 | return -EINVAL; | ||
1716 | |||
1717 | genpd_acquire_lock(genpd); | ||
1718 | |||
1719 | if (genpd->cpu_data) { | ||
1720 | ret = -EEXIST; | ||
1721 | goto out; | ||
1722 | } | ||
1723 | cpu_data = kzalloc(sizeof(*cpu_data), GFP_KERNEL); | ||
1724 | if (!cpu_data) { | ||
1725 | ret = -ENOMEM; | ||
1726 | goto out; | ||
1727 | } | ||
1728 | cpuidle_drv = cpuidle_driver_ref(); | ||
1729 | if (!cpuidle_drv) { | ||
1730 | ret = -ENODEV; | ||
1731 | goto out; | ||
1732 | } | ||
1733 | if (cpuidle_drv->state_count <= state) { | ||
1734 | ret = -EINVAL; | ||
1735 | goto err; | ||
1736 | } | ||
1737 | idle_state = &cpuidle_drv->states[state]; | ||
1738 | if (!idle_state->disabled) { | ||
1739 | ret = -EAGAIN; | ||
1740 | goto err; | ||
1741 | } | ||
1742 | cpu_data->idle_state = idle_state; | ||
1743 | cpu_data->saved_exit_latency = idle_state->exit_latency; | ||
1744 | genpd->cpu_data = cpu_data; | ||
1745 | genpd_recalc_cpu_exit_latency(genpd); | ||
1746 | |||
1747 | out: | ||
1748 | genpd_release_lock(genpd); | ||
1749 | return ret; | ||
1750 | |||
1751 | err: | ||
1752 | cpuidle_driver_unref(); | ||
1753 | goto out; | ||
1754 | } | ||
1755 | |||
1756 | int genpd_detach_cpuidle(struct generic_pm_domain *genpd) | ||
1757 | { | ||
1758 | struct gpd_cpu_data *cpu_data; | ||
1759 | struct cpuidle_state *idle_state; | ||
1760 | int ret = 0; | ||
1761 | |||
1762 | if (IS_ERR_OR_NULL(genpd)) | ||
1763 | return -EINVAL; | ||
1764 | |||
1765 | genpd_acquire_lock(genpd); | ||
1766 | |||
1767 | cpu_data = genpd->cpu_data; | ||
1768 | if (!cpu_data) { | ||
1769 | ret = -ENODEV; | ||
1770 | goto out; | ||
1771 | } | ||
1772 | idle_state = cpu_data->idle_state; | ||
1773 | if (!idle_state->disabled) { | ||
1774 | ret = -EAGAIN; | ||
1775 | goto out; | ||
1776 | } | ||
1777 | idle_state->exit_latency = cpu_data->saved_exit_latency; | ||
1778 | cpuidle_driver_unref(); | ||
1779 | genpd->cpu_data = NULL; | ||
1780 | kfree(cpu_data); | ||
1781 | |||
1782 | out: | ||
1783 | genpd_release_lock(genpd); | ||
1784 | return ret; | ||
1785 | } | ||
1786 | |||
1609 | /* Default device callbacks for generic PM domains. */ | 1787 | /* Default device callbacks for generic PM domains. */ |
1610 | 1788 | ||
1611 | /** | 1789 | /** |
@@ -1615,16 +1793,24 @@ EXPORT_SYMBOL_GPL(__pm_genpd_remove_callbacks); | |||
1615 | static int pm_genpd_default_save_state(struct device *dev) | 1793 | static int pm_genpd_default_save_state(struct device *dev) |
1616 | { | 1794 | { |
1617 | int (*cb)(struct device *__dev); | 1795 | int (*cb)(struct device *__dev); |
1618 | struct device_driver *drv = dev->driver; | ||
1619 | 1796 | ||
1620 | cb = dev_gpd_data(dev)->ops.save_state; | 1797 | cb = dev_gpd_data(dev)->ops.save_state; |
1621 | if (cb) | 1798 | if (cb) |
1622 | return cb(dev); | 1799 | return cb(dev); |
1623 | 1800 | ||
1624 | if (drv && drv->pm && drv->pm->runtime_suspend) | 1801 | if (dev->type && dev->type->pm) |
1625 | return drv->pm->runtime_suspend(dev); | 1802 | cb = dev->type->pm->runtime_suspend; |
1803 | else if (dev->class && dev->class->pm) | ||
1804 | cb = dev->class->pm->runtime_suspend; | ||
1805 | else if (dev->bus && dev->bus->pm) | ||
1806 | cb = dev->bus->pm->runtime_suspend; | ||
1807 | else | ||
1808 | cb = NULL; | ||
1626 | 1809 | ||
1627 | return 0; | 1810 | if (!cb && dev->driver && dev->driver->pm) |
1811 | cb = dev->driver->pm->runtime_suspend; | ||
1812 | |||
1813 | return cb ? cb(dev) : 0; | ||
1628 | } | 1814 | } |
1629 | 1815 | ||
1630 | /** | 1816 | /** |
@@ -1634,16 +1820,24 @@ static int pm_genpd_default_save_state(struct device *dev) | |||
1634 | static int pm_genpd_default_restore_state(struct device *dev) | 1820 | static int pm_genpd_default_restore_state(struct device *dev) |
1635 | { | 1821 | { |
1636 | int (*cb)(struct device *__dev); | 1822 | int (*cb)(struct device *__dev); |
1637 | struct device_driver *drv = dev->driver; | ||
1638 | 1823 | ||
1639 | cb = dev_gpd_data(dev)->ops.restore_state; | 1824 | cb = dev_gpd_data(dev)->ops.restore_state; |
1640 | if (cb) | 1825 | if (cb) |
1641 | return cb(dev); | 1826 | return cb(dev); |
1642 | 1827 | ||
1643 | if (drv && drv->pm && drv->pm->runtime_resume) | 1828 | if (dev->type && dev->type->pm) |
1644 | return drv->pm->runtime_resume(dev); | 1829 | cb = dev->type->pm->runtime_resume; |
1830 | else if (dev->class && dev->class->pm) | ||
1831 | cb = dev->class->pm->runtime_resume; | ||
1832 | else if (dev->bus && dev->bus->pm) | ||
1833 | cb = dev->bus->pm->runtime_resume; | ||
1834 | else | ||
1835 | cb = NULL; | ||
1645 | 1836 | ||
1646 | return 0; | 1837 | if (!cb && dev->driver && dev->driver->pm) |
1838 | cb = dev->driver->pm->runtime_resume; | ||
1839 | |||
1840 | return cb ? cb(dev) : 0; | ||
1647 | } | 1841 | } |
1648 | 1842 | ||
1649 | #ifdef CONFIG_PM_SLEEP | 1843 | #ifdef CONFIG_PM_SLEEP |
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 9cb845e49334..0113adc310dc 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c | |||
@@ -28,7 +28,7 @@ | |||
28 | #include <linux/sched.h> | 28 | #include <linux/sched.h> |
29 | #include <linux/async.h> | 29 | #include <linux/async.h> |
30 | #include <linux/suspend.h> | 30 | #include <linux/suspend.h> |
31 | 31 | #include <linux/cpuidle.h> | |
32 | #include "../base.h" | 32 | #include "../base.h" |
33 | #include "power.h" | 33 | #include "power.h" |
34 | 34 | ||
@@ -45,10 +45,10 @@ typedef int (*pm_callback_t)(struct device *); | |||
45 | */ | 45 | */ |
46 | 46 | ||
47 | LIST_HEAD(dpm_list); | 47 | LIST_HEAD(dpm_list); |
48 | LIST_HEAD(dpm_prepared_list); | 48 | static LIST_HEAD(dpm_prepared_list); |
49 | LIST_HEAD(dpm_suspended_list); | 49 | static LIST_HEAD(dpm_suspended_list); |
50 | LIST_HEAD(dpm_late_early_list); | 50 | static LIST_HEAD(dpm_late_early_list); |
51 | LIST_HEAD(dpm_noirq_list); | 51 | static LIST_HEAD(dpm_noirq_list); |
52 | 52 | ||
53 | struct suspend_stats suspend_stats; | 53 | struct suspend_stats suspend_stats; |
54 | static DEFINE_MUTEX(dpm_list_mtx); | 54 | static DEFINE_MUTEX(dpm_list_mtx); |
@@ -166,7 +166,7 @@ static ktime_t initcall_debug_start(struct device *dev) | |||
166 | { | 166 | { |
167 | ktime_t calltime = ktime_set(0, 0); | 167 | ktime_t calltime = ktime_set(0, 0); |
168 | 168 | ||
169 | if (initcall_debug) { | 169 | if (pm_print_times_enabled) { |
170 | pr_info("calling %s+ @ %i, parent: %s\n", | 170 | pr_info("calling %s+ @ %i, parent: %s\n", |
171 | dev_name(dev), task_pid_nr(current), | 171 | dev_name(dev), task_pid_nr(current), |
172 | dev->parent ? dev_name(dev->parent) : "none"); | 172 | dev->parent ? dev_name(dev->parent) : "none"); |
@@ -181,7 +181,7 @@ static void initcall_debug_report(struct device *dev, ktime_t calltime, | |||
181 | { | 181 | { |
182 | ktime_t delta, rettime; | 182 | ktime_t delta, rettime; |
183 | 183 | ||
184 | if (initcall_debug) { | 184 | if (pm_print_times_enabled) { |
185 | rettime = ktime_get(); | 185 | rettime = ktime_get(); |
186 | delta = ktime_sub(rettime, calltime); | 186 | delta = ktime_sub(rettime, calltime); |
187 | pr_info("call %s+ returned %d after %Ld usecs\n", dev_name(dev), | 187 | pr_info("call %s+ returned %d after %Ld usecs\n", dev_name(dev), |
@@ -467,6 +467,7 @@ static void dpm_resume_noirq(pm_message_t state) | |||
467 | mutex_unlock(&dpm_list_mtx); | 467 | mutex_unlock(&dpm_list_mtx); |
468 | dpm_show_time(starttime, state, "noirq"); | 468 | dpm_show_time(starttime, state, "noirq"); |
469 | resume_device_irqs(); | 469 | resume_device_irqs(); |
470 | cpuidle_resume(); | ||
470 | } | 471 | } |
471 | 472 | ||
472 | /** | 473 | /** |
@@ -867,6 +868,7 @@ static int dpm_suspend_noirq(pm_message_t state) | |||
867 | ktime_t starttime = ktime_get(); | 868 | ktime_t starttime = ktime_get(); |
868 | int error = 0; | 869 | int error = 0; |
869 | 870 | ||
871 | cpuidle_pause(); | ||
870 | suspend_device_irqs(); | 872 | suspend_device_irqs(); |
871 | mutex_lock(&dpm_list_mtx); | 873 | mutex_lock(&dpm_list_mtx); |
872 | while (!list_empty(&dpm_late_early_list)) { | 874 | while (!list_empty(&dpm_late_early_list)) { |
@@ -989,8 +991,16 @@ static int dpm_suspend_late(pm_message_t state) | |||
989 | int dpm_suspend_end(pm_message_t state) | 991 | int dpm_suspend_end(pm_message_t state) |
990 | { | 992 | { |
991 | int error = dpm_suspend_late(state); | 993 | int error = dpm_suspend_late(state); |
994 | if (error) | ||
995 | return error; | ||
996 | |||
997 | error = dpm_suspend_noirq(state); | ||
998 | if (error) { | ||
999 | dpm_resume_early(state); | ||
1000 | return error; | ||
1001 | } | ||
992 | 1002 | ||
993 | return error ? : dpm_suspend_noirq(state); | 1003 | return 0; |
994 | } | 1004 | } |
995 | EXPORT_SYMBOL_GPL(dpm_suspend_end); | 1005 | EXPORT_SYMBOL_GPL(dpm_suspend_end); |
996 | 1006 | ||
diff --git a/drivers/base/power/qos.c b/drivers/base/power/qos.c index fd849a2c4fa8..74a67e0019a2 100644 --- a/drivers/base/power/qos.c +++ b/drivers/base/power/qos.c | |||
@@ -462,7 +462,7 @@ EXPORT_SYMBOL_GPL(dev_pm_qos_add_ancestor_request); | |||
462 | static void __dev_pm_qos_drop_user_request(struct device *dev) | 462 | static void __dev_pm_qos_drop_user_request(struct device *dev) |
463 | { | 463 | { |
464 | dev_pm_qos_remove_request(dev->power.pq_req); | 464 | dev_pm_qos_remove_request(dev->power.pq_req); |
465 | dev->power.pq_req = 0; | 465 | dev->power.pq_req = NULL; |
466 | } | 466 | } |
467 | 467 | ||
468 | /** | 468 | /** |
diff --git a/drivers/base/power/sysfs.c b/drivers/base/power/sysfs.c index 48be2ad4dd2c..b91dc6f1e914 100644 --- a/drivers/base/power/sysfs.c +++ b/drivers/base/power/sysfs.c | |||
@@ -474,6 +474,8 @@ static DEVICE_ATTR(runtime_enabled, 0444, rtpm_enabled_show, NULL); | |||
474 | 474 | ||
475 | #endif | 475 | #endif |
476 | 476 | ||
477 | #ifdef CONFIG_PM_SLEEP | ||
478 | |||
477 | static ssize_t async_show(struct device *dev, struct device_attribute *attr, | 479 | static ssize_t async_show(struct device *dev, struct device_attribute *attr, |
478 | char *buf) | 480 | char *buf) |
479 | { | 481 | { |
@@ -500,6 +502,8 @@ static ssize_t async_store(struct device *dev, struct device_attribute *attr, | |||
500 | } | 502 | } |
501 | 503 | ||
502 | static DEVICE_ATTR(async, 0644, async_show, async_store); | 504 | static DEVICE_ATTR(async, 0644, async_show, async_store); |
505 | |||
506 | #endif | ||
503 | #endif /* CONFIG_PM_ADVANCED_DEBUG */ | 507 | #endif /* CONFIG_PM_ADVANCED_DEBUG */ |
504 | 508 | ||
505 | static struct attribute *power_attrs[] = { | 509 | static struct attribute *power_attrs[] = { |