diff options
-rw-r--r-- | Documentation/ABI/testing/sysfs-devices-power | 18 | ||||
-rw-r--r-- | drivers/base/power/power.h | 4 | ||||
-rw-r--r-- | drivers/base/power/qos.c | 61 | ||||
-rw-r--r-- | drivers/base/power/sysfs.c | 47 | ||||
-rw-r--r-- | drivers/mmc/host/sh_mmcif.c | 5 | ||||
-rw-r--r-- | drivers/mmc/host/tmio_mmc_pio.c | 5 | ||||
-rw-r--r-- | include/linux/pm.h | 1 | ||||
-rw-r--r-- | include/linux/pm_qos.h | 9 |
8 files changed, 150 insertions, 0 deletions
diff --git a/Documentation/ABI/testing/sysfs-devices-power b/Documentation/ABI/testing/sysfs-devices-power index 8ffbc25376a0..840f7d64d483 100644 --- a/Documentation/ABI/testing/sysfs-devices-power +++ b/Documentation/ABI/testing/sysfs-devices-power | |||
@@ -165,3 +165,21 @@ Description: | |||
165 | 165 | ||
166 | Not all drivers support this attribute. If it isn't supported, | 166 | Not all drivers support this attribute. If it isn't supported, |
167 | attempts to read or write it will yield I/O errors. | 167 | attempts to read or write it will yield I/O errors. |
168 | |||
169 | What: /sys/devices/.../power/pm_qos_latency_us | ||
170 | Date: March 2012 | ||
171 | Contact: Rafael J. Wysocki <rjw@sisk.pl> | ||
172 | Description: | ||
173 | The /sys/devices/.../power/pm_qos_resume_latency_us attribute | ||
174 | contains the PM QoS resume latency limit for the given device, | ||
175 | which is the maximum allowed time it can take to resume the | ||
176 | device, after it has been suspended at run time, from a resume | ||
177 | request to the moment the device will be ready to process I/O, | ||
178 | in microseconds. If it is equal to 0, however, this means that | ||
179 | the PM QoS resume latency may be arbitrary. | ||
180 | |||
181 | Not all drivers support this attribute. If it isn't supported, | ||
182 | it is not present. | ||
183 | |||
184 | This attribute has no effect on system-wide suspend/resume and | ||
185 | hibernation. | ||
diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h index 9bf62323aaf3..eeb4bff9505c 100644 --- a/drivers/base/power/power.h +++ b/drivers/base/power/power.h | |||
@@ -71,6 +71,8 @@ extern void dpm_sysfs_remove(struct device *dev); | |||
71 | extern void rpm_sysfs_remove(struct device *dev); | 71 | extern void rpm_sysfs_remove(struct device *dev); |
72 | extern int wakeup_sysfs_add(struct device *dev); | 72 | extern int wakeup_sysfs_add(struct device *dev); |
73 | extern void wakeup_sysfs_remove(struct device *dev); | 73 | extern void wakeup_sysfs_remove(struct device *dev); |
74 | extern int pm_qos_sysfs_add(struct device *dev); | ||
75 | extern void pm_qos_sysfs_remove(struct device *dev); | ||
74 | 76 | ||
75 | #else /* CONFIG_PM */ | 77 | #else /* CONFIG_PM */ |
76 | 78 | ||
@@ -79,5 +81,7 @@ static inline void dpm_sysfs_remove(struct device *dev) {} | |||
79 | static inline void rpm_sysfs_remove(struct device *dev) {} | 81 | static inline void rpm_sysfs_remove(struct device *dev) {} |
80 | static inline int wakeup_sysfs_add(struct device *dev) { return 0; } | 82 | static inline int wakeup_sysfs_add(struct device *dev) { return 0; } |
81 | static inline void wakeup_sysfs_remove(struct device *dev) {} | 83 | static inline void wakeup_sysfs_remove(struct device *dev) {} |
84 | static inline int pm_qos_sysfs_add(struct device *dev) { return 0; } | ||
85 | static inline void pm_qos_sysfs_remove(struct device *dev) {} | ||
82 | 86 | ||
83 | #endif | 87 | #endif |
diff --git a/drivers/base/power/qos.c b/drivers/base/power/qos.c index c5d358837461..71855570922d 100644 --- a/drivers/base/power/qos.c +++ b/drivers/base/power/qos.c | |||
@@ -41,6 +41,7 @@ | |||
41 | #include <linux/mutex.h> | 41 | #include <linux/mutex.h> |
42 | #include <linux/export.h> | 42 | #include <linux/export.h> |
43 | 43 | ||
44 | #include "power.h" | ||
44 | 45 | ||
45 | static DEFINE_MUTEX(dev_pm_qos_mtx); | 46 | static DEFINE_MUTEX(dev_pm_qos_mtx); |
46 | 47 | ||
@@ -166,6 +167,12 @@ void dev_pm_qos_constraints_destroy(struct device *dev) | |||
166 | struct dev_pm_qos_request *req, *tmp; | 167 | struct dev_pm_qos_request *req, *tmp; |
167 | struct pm_qos_constraints *c; | 168 | struct pm_qos_constraints *c; |
168 | 169 | ||
170 | /* | ||
171 | * If the device's PM QoS resume latency limit has been exposed to user | ||
172 | * space, it has to be hidden at this point. | ||
173 | */ | ||
174 | dev_pm_qos_hide_latency_limit(dev); | ||
175 | |||
169 | mutex_lock(&dev_pm_qos_mtx); | 176 | mutex_lock(&dev_pm_qos_mtx); |
170 | 177 | ||
171 | dev->power.power_state = PMSG_INVALID; | 178 | dev->power.power_state = PMSG_INVALID; |
@@ -445,3 +452,57 @@ int dev_pm_qos_add_ancestor_request(struct device *dev, | |||
445 | return error; | 452 | return error; |
446 | } | 453 | } |
447 | EXPORT_SYMBOL_GPL(dev_pm_qos_add_ancestor_request); | 454 | EXPORT_SYMBOL_GPL(dev_pm_qos_add_ancestor_request); |
455 | |||
456 | #ifdef CONFIG_PM_RUNTIME | ||
457 | static void __dev_pm_qos_drop_user_request(struct device *dev) | ||
458 | { | ||
459 | dev_pm_qos_remove_request(dev->power.pq_req); | ||
460 | dev->power.pq_req = 0; | ||
461 | } | ||
462 | |||
463 | /** | ||
464 | * dev_pm_qos_expose_latency_limit - Expose PM QoS latency limit to user space. | ||
465 | * @dev: Device whose PM QoS latency limit is to be exposed to user space. | ||
466 | * @value: Initial value of the latency limit. | ||
467 | */ | ||
468 | int dev_pm_qos_expose_latency_limit(struct device *dev, s32 value) | ||
469 | { | ||
470 | struct dev_pm_qos_request *req; | ||
471 | int ret; | ||
472 | |||
473 | if (!device_is_registered(dev) || value < 0) | ||
474 | return -EINVAL; | ||
475 | |||
476 | if (dev->power.pq_req) | ||
477 | return -EEXIST; | ||
478 | |||
479 | req = kzalloc(sizeof(*req), GFP_KERNEL); | ||
480 | if (!req) | ||
481 | return -ENOMEM; | ||
482 | |||
483 | ret = dev_pm_qos_add_request(dev, req, value); | ||
484 | if (ret < 0) | ||
485 | return ret; | ||
486 | |||
487 | dev->power.pq_req = req; | ||
488 | ret = pm_qos_sysfs_add(dev); | ||
489 | if (ret) | ||
490 | __dev_pm_qos_drop_user_request(dev); | ||
491 | |||
492 | return ret; | ||
493 | } | ||
494 | EXPORT_SYMBOL_GPL(dev_pm_qos_expose_latency_limit); | ||
495 | |||
496 | /** | ||
497 | * dev_pm_qos_hide_latency_limit - Hide PM QoS latency limit from user space. | ||
498 | * @dev: Device whose PM QoS latency limit is to be hidden from user space. | ||
499 | */ | ||
500 | void dev_pm_qos_hide_latency_limit(struct device *dev) | ||
501 | { | ||
502 | if (dev->power.pq_req) { | ||
503 | pm_qos_sysfs_remove(dev); | ||
504 | __dev_pm_qos_drop_user_request(dev); | ||
505 | } | ||
506 | } | ||
507 | EXPORT_SYMBOL_GPL(dev_pm_qos_hide_latency_limit); | ||
508 | #endif /* CONFIG_PM_RUNTIME */ | ||
diff --git a/drivers/base/power/sysfs.c b/drivers/base/power/sysfs.c index adf41be0ea66..95c12f6cb5b9 100644 --- a/drivers/base/power/sysfs.c +++ b/drivers/base/power/sysfs.c | |||
@@ -5,6 +5,7 @@ | |||
5 | #include <linux/device.h> | 5 | #include <linux/device.h> |
6 | #include <linux/string.h> | 6 | #include <linux/string.h> |
7 | #include <linux/export.h> | 7 | #include <linux/export.h> |
8 | #include <linux/pm_qos.h> | ||
8 | #include <linux/pm_runtime.h> | 9 | #include <linux/pm_runtime.h> |
9 | #include <linux/atomic.h> | 10 | #include <linux/atomic.h> |
10 | #include <linux/jiffies.h> | 11 | #include <linux/jiffies.h> |
@@ -217,6 +218,31 @@ static ssize_t autosuspend_delay_ms_store(struct device *dev, | |||
217 | static DEVICE_ATTR(autosuspend_delay_ms, 0644, autosuspend_delay_ms_show, | 218 | static DEVICE_ATTR(autosuspend_delay_ms, 0644, autosuspend_delay_ms_show, |
218 | autosuspend_delay_ms_store); | 219 | autosuspend_delay_ms_store); |
219 | 220 | ||
221 | static ssize_t pm_qos_latency_show(struct device *dev, | ||
222 | struct device_attribute *attr, char *buf) | ||
223 | { | ||
224 | return sprintf(buf, "%d\n", dev->power.pq_req->node.prio); | ||
225 | } | ||
226 | |||
227 | static ssize_t pm_qos_latency_store(struct device *dev, | ||
228 | struct device_attribute *attr, | ||
229 | const char *buf, size_t n) | ||
230 | { | ||
231 | s32 value; | ||
232 | int ret; | ||
233 | |||
234 | if (kstrtos32(buf, 0, &value)) | ||
235 | return -EINVAL; | ||
236 | |||
237 | if (value < 0) | ||
238 | return -EINVAL; | ||
239 | |||
240 | ret = dev_pm_qos_update_request(dev->power.pq_req, value); | ||
241 | return ret < 0 ? ret : n; | ||
242 | } | ||
243 | |||
244 | static DEVICE_ATTR(pm_qos_resume_latency_us, 0644, | ||
245 | pm_qos_latency_show, pm_qos_latency_store); | ||
220 | #endif /* CONFIG_PM_RUNTIME */ | 246 | #endif /* CONFIG_PM_RUNTIME */ |
221 | 247 | ||
222 | #ifdef CONFIG_PM_SLEEP | 248 | #ifdef CONFIG_PM_SLEEP |
@@ -490,6 +516,17 @@ static struct attribute_group pm_runtime_attr_group = { | |||
490 | .attrs = runtime_attrs, | 516 | .attrs = runtime_attrs, |
491 | }; | 517 | }; |
492 | 518 | ||
519 | static struct attribute *pm_qos_attrs[] = { | ||
520 | #ifdef CONFIG_PM_RUNTIME | ||
521 | &dev_attr_pm_qos_resume_latency_us.attr, | ||
522 | #endif /* CONFIG_PM_RUNTIME */ | ||
523 | NULL, | ||
524 | }; | ||
525 | static struct attribute_group pm_qos_attr_group = { | ||
526 | .name = power_group_name, | ||
527 | .attrs = pm_qos_attrs, | ||
528 | }; | ||
529 | |||
493 | int dpm_sysfs_add(struct device *dev) | 530 | int dpm_sysfs_add(struct device *dev) |
494 | { | 531 | { |
495 | int rc; | 532 | int rc; |
@@ -530,6 +567,16 @@ void wakeup_sysfs_remove(struct device *dev) | |||
530 | sysfs_unmerge_group(&dev->kobj, &pm_wakeup_attr_group); | 567 | sysfs_unmerge_group(&dev->kobj, &pm_wakeup_attr_group); |
531 | } | 568 | } |
532 | 569 | ||
570 | int pm_qos_sysfs_add(struct device *dev) | ||
571 | { | ||
572 | return sysfs_merge_group(&dev->kobj, &pm_qos_attr_group); | ||
573 | } | ||
574 | |||
575 | void pm_qos_sysfs_remove(struct device *dev) | ||
576 | { | ||
577 | sysfs_unmerge_group(&dev->kobj, &pm_qos_attr_group); | ||
578 | } | ||
579 | |||
533 | void rpm_sysfs_remove(struct device *dev) | 580 | void rpm_sysfs_remove(struct device *dev) |
534 | { | 581 | { |
535 | sysfs_unmerge_group(&dev->kobj, &pm_runtime_attr_group); | 582 | sysfs_unmerge_group(&dev->kobj, &pm_runtime_attr_group); |
diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index 352d4797865b..75a485448796 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c | |||
@@ -56,6 +56,7 @@ | |||
56 | #include <linux/mmc/sh_mmcif.h> | 56 | #include <linux/mmc/sh_mmcif.h> |
57 | #include <linux/pagemap.h> | 57 | #include <linux/pagemap.h> |
58 | #include <linux/platform_device.h> | 58 | #include <linux/platform_device.h> |
59 | #include <linux/pm_qos.h> | ||
59 | #include <linux/pm_runtime.h> | 60 | #include <linux/pm_runtime.h> |
60 | #include <linux/spinlock.h> | 61 | #include <linux/spinlock.h> |
61 | #include <linux/module.h> | 62 | #include <linux/module.h> |
@@ -1346,6 +1347,8 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev) | |||
1346 | if (ret < 0) | 1347 | if (ret < 0) |
1347 | goto clean_up5; | 1348 | goto clean_up5; |
1348 | 1349 | ||
1350 | dev_pm_qos_expose_latency_limit(&pdev->dev, 100); | ||
1351 | |||
1349 | dev_info(&pdev->dev, "driver version %s\n", DRIVER_VERSION); | 1352 | dev_info(&pdev->dev, "driver version %s\n", DRIVER_VERSION); |
1350 | dev_dbg(&pdev->dev, "chip ver H'%04x\n", | 1353 | dev_dbg(&pdev->dev, "chip ver H'%04x\n", |
1351 | sh_mmcif_readl(host->addr, MMCIF_CE_VERSION) & 0x0000ffff); | 1354 | sh_mmcif_readl(host->addr, MMCIF_CE_VERSION) & 0x0000ffff); |
@@ -1376,6 +1379,8 @@ static int __devexit sh_mmcif_remove(struct platform_device *pdev) | |||
1376 | host->dying = true; | 1379 | host->dying = true; |
1377 | pm_runtime_get_sync(&pdev->dev); | 1380 | pm_runtime_get_sync(&pdev->dev); |
1378 | 1381 | ||
1382 | dev_pm_qos_hide_latency_limit(&pdev->dev); | ||
1383 | |||
1379 | mmc_remove_host(host->mmc); | 1384 | mmc_remove_host(host->mmc); |
1380 | sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, MASK_ALL); | 1385 | sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, MASK_ALL); |
1381 | 1386 | ||
diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c index 5f9ad74fbf80..e21988901c36 100644 --- a/drivers/mmc/host/tmio_mmc_pio.c +++ b/drivers/mmc/host/tmio_mmc_pio.c | |||
@@ -39,6 +39,7 @@ | |||
39 | #include <linux/module.h> | 39 | #include <linux/module.h> |
40 | #include <linux/pagemap.h> | 40 | #include <linux/pagemap.h> |
41 | #include <linux/platform_device.h> | 41 | #include <linux/platform_device.h> |
42 | #include <linux/pm_qos.h> | ||
42 | #include <linux/pm_runtime.h> | 43 | #include <linux/pm_runtime.h> |
43 | #include <linux/scatterlist.h> | 44 | #include <linux/scatterlist.h> |
44 | #include <linux/spinlock.h> | 45 | #include <linux/spinlock.h> |
@@ -955,6 +956,8 @@ int __devinit tmio_mmc_host_probe(struct tmio_mmc_host **host, | |||
955 | 956 | ||
956 | mmc_add_host(mmc); | 957 | mmc_add_host(mmc); |
957 | 958 | ||
959 | dev_pm_qos_expose_latency_limit(&pdev->dev, 100); | ||
960 | |||
958 | /* Unmask the IRQs we want to know about */ | 961 | /* Unmask the IRQs we want to know about */ |
959 | if (!_host->chan_rx) | 962 | if (!_host->chan_rx) |
960 | irq_mask |= TMIO_MASK_READOP; | 963 | irq_mask |= TMIO_MASK_READOP; |
@@ -993,6 +996,8 @@ void tmio_mmc_host_remove(struct tmio_mmc_host *host) | |||
993 | || host->mmc->caps & MMC_CAP_NONREMOVABLE) | 996 | || host->mmc->caps & MMC_CAP_NONREMOVABLE) |
994 | pm_runtime_get_sync(&pdev->dev); | 997 | pm_runtime_get_sync(&pdev->dev); |
995 | 998 | ||
999 | dev_pm_qos_hide_latency_limit(&pdev->dev); | ||
1000 | |||
996 | mmc_remove_host(host->mmc); | 1001 | mmc_remove_host(host->mmc); |
997 | cancel_work_sync(&host->done); | 1002 | cancel_work_sync(&host->done); |
998 | cancel_delayed_work_sync(&host->delayed_reset_work); | 1003 | cancel_delayed_work_sync(&host->delayed_reset_work); |
diff --git a/include/linux/pm.h b/include/linux/pm.h index d6dd6f612b8d..715305e05123 100644 --- a/include/linux/pm.h +++ b/include/linux/pm.h | |||
@@ -546,6 +546,7 @@ struct dev_pm_info { | |||
546 | unsigned long accounting_timestamp; | 546 | unsigned long accounting_timestamp; |
547 | ktime_t suspend_time; | 547 | ktime_t suspend_time; |
548 | s64 max_time_suspended_ns; | 548 | s64 max_time_suspended_ns; |
549 | struct dev_pm_qos_request *pq_req; | ||
549 | #endif | 550 | #endif |
550 | struct pm_subsys_data *subsys_data; /* Owned by the subsystem. */ | 551 | struct pm_subsys_data *subsys_data; /* Owned by the subsystem. */ |
551 | struct pm_qos_constraints *constraints; | 552 | struct pm_qos_constraints *constraints; |
diff --git a/include/linux/pm_qos.h b/include/linux/pm_qos.h index c8a541e13380..2e9191a712f3 100644 --- a/include/linux/pm_qos.h +++ b/include/linux/pm_qos.h | |||
@@ -137,4 +137,13 @@ static inline int dev_pm_qos_add_ancestor_request(struct device *dev, | |||
137 | { return 0; } | 137 | { return 0; } |
138 | #endif | 138 | #endif |
139 | 139 | ||
140 | #ifdef CONFIG_PM_RUNTIME | ||
141 | int dev_pm_qos_expose_latency_limit(struct device *dev, s32 value); | ||
142 | void dev_pm_qos_hide_latency_limit(struct device *dev); | ||
143 | #else | ||
144 | static inline int dev_pm_qos_expose_latency_limit(struct device *dev, s32 value) | ||
145 | { return 0; } | ||
146 | static inline void dev_pm_qos_hide_latency_limit(struct device *dev) {} | ||
147 | #endif | ||
148 | |||
140 | #endif | 149 | #endif |