diff options
author | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2012-11-29 15:40:32 -0500 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2012-11-29 15:40:32 -0500 |
commit | c8b6817103b23ed42114516fea901c2f61271506 (patch) | |
tree | f3a54c55a37cfa02496de62c0f0b89f2a52dc219 /drivers/base/power | |
parent | 9489e9dcae718d5fde988e4a684a0f55b5f94d17 (diff) | |
parent | 35546bd477146b75ae2a9ff2cb9bfcdb0f701015 (diff) |
Merge branch 'pm-qos'
* pm-qos:
PM / QoS: Handle device PM QoS flags while removing constraints
PM / QoS: Resume device before exposing/hiding PM QoS flags
PM / QoS: Document request manipulation requirement for flags
PM / QoS: Fix a free error in the dev_pm_qos_constraints_destroy()
PM / QoS: Fix the return value of dev_pm_qos_update_request()
PM / ACPI: Take device PM QoS flags into account
PM / Domains: Check device PM QoS flags in pm_genpd_poweroff()
PM / QoS: Make it possible to expose PM QoS device flags to user space
PM / QoS: Introduce PM QoS device flags support
PM / QoS: Prepare struct dev_pm_qos_request for more request types
PM / QoS: Introduce request and constraint data types for PM QoS flags
PM / QoS: Prepare device structure for adding more constraint types
Diffstat (limited to 'drivers/base/power')
-rw-r--r-- | drivers/base/power/domain.c | 11 | ||||
-rw-r--r-- | drivers/base/power/power.h | 6 | ||||
-rw-r--r-- | drivers/base/power/qos.c | 321 | ||||
-rw-r--r-- | drivers/base/power/sysfs.c | 94 |
4 files changed, 355 insertions, 77 deletions
diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 96b71b6536d6..acc3a8ded29d 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c | |||
@@ -470,10 +470,19 @@ static int pm_genpd_poweroff(struct generic_pm_domain *genpd) | |||
470 | return -EBUSY; | 470 | return -EBUSY; |
471 | 471 | ||
472 | not_suspended = 0; | 472 | not_suspended = 0; |
473 | list_for_each_entry(pdd, &genpd->dev_list, list_node) | 473 | list_for_each_entry(pdd, &genpd->dev_list, list_node) { |
474 | enum pm_qos_flags_status stat; | ||
475 | |||
476 | stat = dev_pm_qos_flags(pdd->dev, | ||
477 | PM_QOS_FLAG_NO_POWER_OFF | ||
478 | | PM_QOS_FLAG_REMOTE_WAKEUP); | ||
479 | if (stat > PM_QOS_FLAGS_NONE) | ||
480 | return -EBUSY; | ||
481 | |||
474 | if (pdd->dev->driver && (!pm_runtime_suspended(pdd->dev) | 482 | if (pdd->dev->driver && (!pm_runtime_suspended(pdd->dev) |
475 | || pdd->dev->power.irq_safe)) | 483 | || pdd->dev->power.irq_safe)) |
476 | not_suspended++; | 484 | not_suspended++; |
485 | } | ||
477 | 486 | ||
478 | if (not_suspended > genpd->in_progress) | 487 | if (not_suspended > genpd->in_progress) |
479 | return -EBUSY; | 488 | return -EBUSY; |
diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h index 0dbfdf4419af..b16686a0a5a2 100644 --- a/drivers/base/power/power.h +++ b/drivers/base/power/power.h | |||
@@ -93,8 +93,10 @@ extern void dpm_sysfs_remove(struct device *dev); | |||
93 | extern void rpm_sysfs_remove(struct device *dev); | 93 | extern void rpm_sysfs_remove(struct device *dev); |
94 | extern int wakeup_sysfs_add(struct device *dev); | 94 | extern int wakeup_sysfs_add(struct device *dev); |
95 | extern void wakeup_sysfs_remove(struct device *dev); | 95 | extern void wakeup_sysfs_remove(struct device *dev); |
96 | extern int pm_qos_sysfs_add(struct device *dev); | 96 | extern int pm_qos_sysfs_add_latency(struct device *dev); |
97 | extern void pm_qos_sysfs_remove(struct device *dev); | 97 | extern void pm_qos_sysfs_remove_latency(struct device *dev); |
98 | extern int pm_qos_sysfs_add_flags(struct device *dev); | ||
99 | extern void pm_qos_sysfs_remove_flags(struct device *dev); | ||
98 | 100 | ||
99 | #else /* CONFIG_PM */ | 101 | #else /* CONFIG_PM */ |
100 | 102 | ||
diff --git a/drivers/base/power/qos.c b/drivers/base/power/qos.c index fbbd4ed2edf2..ff46387f5308 100644 --- a/drivers/base/power/qos.c +++ b/drivers/base/power/qos.c | |||
@@ -40,6 +40,7 @@ | |||
40 | #include <linux/device.h> | 40 | #include <linux/device.h> |
41 | #include <linux/mutex.h> | 41 | #include <linux/mutex.h> |
42 | #include <linux/export.h> | 42 | #include <linux/export.h> |
43 | #include <linux/pm_runtime.h> | ||
43 | 44 | ||
44 | #include "power.h" | 45 | #include "power.h" |
45 | 46 | ||
@@ -48,6 +49,50 @@ static DEFINE_MUTEX(dev_pm_qos_mtx); | |||
48 | static BLOCKING_NOTIFIER_HEAD(dev_pm_notifiers); | 49 | static BLOCKING_NOTIFIER_HEAD(dev_pm_notifiers); |
49 | 50 | ||
50 | /** | 51 | /** |
52 | * __dev_pm_qos_flags - Check PM QoS flags for a given device. | ||
53 | * @dev: Device to check the PM QoS flags for. | ||
54 | * @mask: Flags to check against. | ||
55 | * | ||
56 | * This routine must be called with dev->power.lock held. | ||
57 | */ | ||
58 | enum pm_qos_flags_status __dev_pm_qos_flags(struct device *dev, s32 mask) | ||
59 | { | ||
60 | struct dev_pm_qos *qos = dev->power.qos; | ||
61 | struct pm_qos_flags *pqf; | ||
62 | s32 val; | ||
63 | |||
64 | if (!qos) | ||
65 | return PM_QOS_FLAGS_UNDEFINED; | ||
66 | |||
67 | pqf = &qos->flags; | ||
68 | if (list_empty(&pqf->list)) | ||
69 | return PM_QOS_FLAGS_UNDEFINED; | ||
70 | |||
71 | val = pqf->effective_flags & mask; | ||
72 | if (val) | ||
73 | return (val == mask) ? PM_QOS_FLAGS_ALL : PM_QOS_FLAGS_SOME; | ||
74 | |||
75 | return PM_QOS_FLAGS_NONE; | ||
76 | } | ||
77 | |||
78 | /** | ||
79 | * dev_pm_qos_flags - Check PM QoS flags for a given device (locked). | ||
80 | * @dev: Device to check the PM QoS flags for. | ||
81 | * @mask: Flags to check against. | ||
82 | */ | ||
83 | enum pm_qos_flags_status dev_pm_qos_flags(struct device *dev, s32 mask) | ||
84 | { | ||
85 | unsigned long irqflags; | ||
86 | enum pm_qos_flags_status ret; | ||
87 | |||
88 | spin_lock_irqsave(&dev->power.lock, irqflags); | ||
89 | ret = __dev_pm_qos_flags(dev, mask); | ||
90 | spin_unlock_irqrestore(&dev->power.lock, irqflags); | ||
91 | |||
92 | return ret; | ||
93 | } | ||
94 | |||
95 | /** | ||
51 | * __dev_pm_qos_read_value - Get PM QoS constraint for a given device. | 96 | * __dev_pm_qos_read_value - Get PM QoS constraint for a given device. |
52 | * @dev: Device to get the PM QoS constraint value for. | 97 | * @dev: Device to get the PM QoS constraint value for. |
53 | * | 98 | * |
@@ -55,9 +100,7 @@ static BLOCKING_NOTIFIER_HEAD(dev_pm_notifiers); | |||
55 | */ | 100 | */ |
56 | s32 __dev_pm_qos_read_value(struct device *dev) | 101 | s32 __dev_pm_qos_read_value(struct device *dev) |
57 | { | 102 | { |
58 | struct pm_qos_constraints *c = dev->power.constraints; | 103 | return dev->power.qos ? pm_qos_read_value(&dev->power.qos->latency) : 0; |
59 | |||
60 | return c ? pm_qos_read_value(c) : 0; | ||
61 | } | 104 | } |
62 | 105 | ||
63 | /** | 106 | /** |
@@ -76,30 +119,39 @@ s32 dev_pm_qos_read_value(struct device *dev) | |||
76 | return ret; | 119 | return ret; |
77 | } | 120 | } |
78 | 121 | ||
79 | /* | 122 | /** |
80 | * apply_constraint | 123 | * apply_constraint - Add/modify/remove device PM QoS request. |
81 | * @req: constraint request to apply | 124 | * @req: Constraint request to apply |
82 | * @action: action to perform add/update/remove, of type enum pm_qos_req_action | 125 | * @action: Action to perform (add/update/remove). |
83 | * @value: defines the qos request | 126 | * @value: Value to assign to the QoS request. |
84 | * | 127 | * |
85 | * Internal function to update the constraints list using the PM QoS core | 128 | * Internal function to update the constraints list using the PM QoS core |
86 | * code and if needed call the per-device and the global notification | 129 | * code and if needed call the per-device and the global notification |
87 | * callbacks | 130 | * callbacks |
88 | */ | 131 | */ |
89 | static int apply_constraint(struct dev_pm_qos_request *req, | 132 | static int apply_constraint(struct dev_pm_qos_request *req, |
90 | enum pm_qos_req_action action, int value) | 133 | enum pm_qos_req_action action, s32 value) |
91 | { | 134 | { |
92 | int ret, curr_value; | 135 | struct dev_pm_qos *qos = req->dev->power.qos; |
93 | 136 | int ret; | |
94 | ret = pm_qos_update_target(req->dev->power.constraints, | ||
95 | &req->node, action, value); | ||
96 | 137 | ||
97 | if (ret) { | 138 | switch(req->type) { |
98 | /* Call the global callbacks if needed */ | 139 | case DEV_PM_QOS_LATENCY: |
99 | curr_value = pm_qos_read_value(req->dev->power.constraints); | 140 | ret = pm_qos_update_target(&qos->latency, &req->data.pnode, |
100 | blocking_notifier_call_chain(&dev_pm_notifiers, | 141 | action, value); |
101 | (unsigned long)curr_value, | 142 | if (ret) { |
102 | req); | 143 | value = pm_qos_read_value(&qos->latency); |
144 | blocking_notifier_call_chain(&dev_pm_notifiers, | ||
145 | (unsigned long)value, | ||
146 | req); | ||
147 | } | ||
148 | break; | ||
149 | case DEV_PM_QOS_FLAGS: | ||
150 | ret = pm_qos_update_flags(&qos->flags, &req->data.flr, | ||
151 | action, value); | ||
152 | break; | ||
153 | default: | ||
154 | ret = -EINVAL; | ||
103 | } | 155 | } |
104 | 156 | ||
105 | return ret; | 157 | return ret; |
@@ -114,28 +166,32 @@ static int apply_constraint(struct dev_pm_qos_request *req, | |||
114 | */ | 166 | */ |
115 | static int dev_pm_qos_constraints_allocate(struct device *dev) | 167 | static int dev_pm_qos_constraints_allocate(struct device *dev) |
116 | { | 168 | { |
169 | struct dev_pm_qos *qos; | ||
117 | struct pm_qos_constraints *c; | 170 | struct pm_qos_constraints *c; |
118 | struct blocking_notifier_head *n; | 171 | struct blocking_notifier_head *n; |
119 | 172 | ||
120 | c = kzalloc(sizeof(*c), GFP_KERNEL); | 173 | qos = kzalloc(sizeof(*qos), GFP_KERNEL); |
121 | if (!c) | 174 | if (!qos) |
122 | return -ENOMEM; | 175 | return -ENOMEM; |
123 | 176 | ||
124 | n = kzalloc(sizeof(*n), GFP_KERNEL); | 177 | n = kzalloc(sizeof(*n), GFP_KERNEL); |
125 | if (!n) { | 178 | if (!n) { |
126 | kfree(c); | 179 | kfree(qos); |
127 | return -ENOMEM; | 180 | return -ENOMEM; |
128 | } | 181 | } |
129 | BLOCKING_INIT_NOTIFIER_HEAD(n); | 182 | BLOCKING_INIT_NOTIFIER_HEAD(n); |
130 | 183 | ||
184 | c = &qos->latency; | ||
131 | plist_head_init(&c->list); | 185 | plist_head_init(&c->list); |
132 | c->target_value = PM_QOS_DEV_LAT_DEFAULT_VALUE; | 186 | c->target_value = PM_QOS_DEV_LAT_DEFAULT_VALUE; |
133 | c->default_value = PM_QOS_DEV_LAT_DEFAULT_VALUE; | 187 | c->default_value = PM_QOS_DEV_LAT_DEFAULT_VALUE; |
134 | c->type = PM_QOS_MIN; | 188 | c->type = PM_QOS_MIN; |
135 | c->notifiers = n; | 189 | c->notifiers = n; |
136 | 190 | ||
191 | INIT_LIST_HEAD(&qos->flags.list); | ||
192 | |||
137 | spin_lock_irq(&dev->power.lock); | 193 | spin_lock_irq(&dev->power.lock); |
138 | dev->power.constraints = c; | 194 | dev->power.qos = qos; |
139 | spin_unlock_irq(&dev->power.lock); | 195 | spin_unlock_irq(&dev->power.lock); |
140 | 196 | ||
141 | return 0; | 197 | return 0; |
@@ -151,7 +207,7 @@ static int dev_pm_qos_constraints_allocate(struct device *dev) | |||
151 | void dev_pm_qos_constraints_init(struct device *dev) | 207 | void dev_pm_qos_constraints_init(struct device *dev) |
152 | { | 208 | { |
153 | mutex_lock(&dev_pm_qos_mtx); | 209 | mutex_lock(&dev_pm_qos_mtx); |
154 | dev->power.constraints = NULL; | 210 | dev->power.qos = NULL; |
155 | dev->power.power_state = PMSG_ON; | 211 | dev->power.power_state = PMSG_ON; |
156 | mutex_unlock(&dev_pm_qos_mtx); | 212 | mutex_unlock(&dev_pm_qos_mtx); |
157 | } | 213 | } |
@@ -164,24 +220,28 @@ void dev_pm_qos_constraints_init(struct device *dev) | |||
164 | */ | 220 | */ |
165 | void dev_pm_qos_constraints_destroy(struct device *dev) | 221 | void dev_pm_qos_constraints_destroy(struct device *dev) |
166 | { | 222 | { |
223 | struct dev_pm_qos *qos; | ||
167 | struct dev_pm_qos_request *req, *tmp; | 224 | struct dev_pm_qos_request *req, *tmp; |
168 | struct pm_qos_constraints *c; | 225 | struct pm_qos_constraints *c; |
226 | struct pm_qos_flags *f; | ||
169 | 227 | ||
170 | /* | 228 | /* |
171 | * If the device's PM QoS resume latency limit has been exposed to user | 229 | * If the device's PM QoS resume latency limit or PM QoS flags have been |
172 | * space, it has to be hidden at this point. | 230 | * exposed to user space, they have to be hidden at this point. |
173 | */ | 231 | */ |
174 | dev_pm_qos_hide_latency_limit(dev); | 232 | dev_pm_qos_hide_latency_limit(dev); |
233 | dev_pm_qos_hide_flags(dev); | ||
175 | 234 | ||
176 | mutex_lock(&dev_pm_qos_mtx); | 235 | mutex_lock(&dev_pm_qos_mtx); |
177 | 236 | ||
178 | dev->power.power_state = PMSG_INVALID; | 237 | dev->power.power_state = PMSG_INVALID; |
179 | c = dev->power.constraints; | 238 | qos = dev->power.qos; |
180 | if (!c) | 239 | if (!qos) |
181 | goto out; | 240 | goto out; |
182 | 241 | ||
183 | /* Flush the constraints list for the device */ | 242 | /* Flush the constraints lists for the device. */ |
184 | plist_for_each_entry_safe(req, tmp, &c->list, node) { | 243 | c = &qos->latency; |
244 | plist_for_each_entry_safe(req, tmp, &c->list, data.pnode) { | ||
185 | /* | 245 | /* |
186 | * Update constraints list and call the notification | 246 | * Update constraints list and call the notification |
187 | * callbacks if needed | 247 | * callbacks if needed |
@@ -189,13 +249,18 @@ void dev_pm_qos_constraints_destroy(struct device *dev) | |||
189 | apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE); | 249 | apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE); |
190 | memset(req, 0, sizeof(*req)); | 250 | memset(req, 0, sizeof(*req)); |
191 | } | 251 | } |
252 | f = &qos->flags; | ||
253 | list_for_each_entry_safe(req, tmp, &f->list, data.flr.node) { | ||
254 | apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE); | ||
255 | memset(req, 0, sizeof(*req)); | ||
256 | } | ||
192 | 257 | ||
193 | spin_lock_irq(&dev->power.lock); | 258 | spin_lock_irq(&dev->power.lock); |
194 | dev->power.constraints = NULL; | 259 | dev->power.qos = NULL; |
195 | spin_unlock_irq(&dev->power.lock); | 260 | spin_unlock_irq(&dev->power.lock); |
196 | 261 | ||
197 | kfree(c->notifiers); | 262 | kfree(c->notifiers); |
198 | kfree(c); | 263 | kfree(qos); |
199 | 264 | ||
200 | out: | 265 | out: |
201 | mutex_unlock(&dev_pm_qos_mtx); | 266 | mutex_unlock(&dev_pm_qos_mtx); |
@@ -205,6 +270,7 @@ void dev_pm_qos_constraints_destroy(struct device *dev) | |||
205 | * dev_pm_qos_add_request - inserts new qos request into the list | 270 | * dev_pm_qos_add_request - inserts new qos request into the list |
206 | * @dev: target device for the constraint | 271 | * @dev: target device for the constraint |
207 | * @req: pointer to a preallocated handle | 272 | * @req: pointer to a preallocated handle |
273 | * @type: type of the request | ||
208 | * @value: defines the qos request | 274 | * @value: defines the qos request |
209 | * | 275 | * |
210 | * This function inserts a new entry in the device constraints list of | 276 | * This function inserts a new entry in the device constraints list of |
@@ -218,9 +284,12 @@ void dev_pm_qos_constraints_destroy(struct device *dev) | |||
218 | * -EINVAL in case of wrong parameters, -ENOMEM if there's not enough memory | 284 | * -EINVAL in case of wrong parameters, -ENOMEM if there's not enough memory |
219 | * to allocate for data structures, -ENODEV if the device has just been removed | 285 | * to allocate for data structures, -ENODEV if the device has just been removed |
220 | * from the system. | 286 | * from the system. |
287 | * | ||
288 | * Callers should ensure that the target device is not RPM_SUSPENDED before | ||
289 | * using this function for requests of type DEV_PM_QOS_FLAGS. | ||
221 | */ | 290 | */ |
222 | int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req, | 291 | int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req, |
223 | s32 value) | 292 | enum dev_pm_qos_req_type type, s32 value) |
224 | { | 293 | { |
225 | int ret = 0; | 294 | int ret = 0; |
226 | 295 | ||
@@ -235,7 +304,7 @@ int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req, | |||
235 | 304 | ||
236 | mutex_lock(&dev_pm_qos_mtx); | 305 | mutex_lock(&dev_pm_qos_mtx); |
237 | 306 | ||
238 | if (!dev->power.constraints) { | 307 | if (!dev->power.qos) { |
239 | if (dev->power.power_state.event == PM_EVENT_INVALID) { | 308 | if (dev->power.power_state.event == PM_EVENT_INVALID) { |
240 | /* The device has been removed from the system. */ | 309 | /* The device has been removed from the system. */ |
241 | req->dev = NULL; | 310 | req->dev = NULL; |
@@ -251,8 +320,10 @@ int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req, | |||
251 | } | 320 | } |
252 | } | 321 | } |
253 | 322 | ||
254 | if (!ret) | 323 | if (!ret) { |
324 | req->type = type; | ||
255 | ret = apply_constraint(req, PM_QOS_ADD_REQ, value); | 325 | ret = apply_constraint(req, PM_QOS_ADD_REQ, value); |
326 | } | ||
256 | 327 | ||
257 | out: | 328 | out: |
258 | mutex_unlock(&dev_pm_qos_mtx); | 329 | mutex_unlock(&dev_pm_qos_mtx); |
@@ -262,6 +333,37 @@ int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req, | |||
262 | EXPORT_SYMBOL_GPL(dev_pm_qos_add_request); | 333 | EXPORT_SYMBOL_GPL(dev_pm_qos_add_request); |
263 | 334 | ||
264 | /** | 335 | /** |
336 | * __dev_pm_qos_update_request - Modify an existing device PM QoS request. | ||
337 | * @req : PM QoS request to modify. | ||
338 | * @new_value: New value to request. | ||
339 | */ | ||
340 | static int __dev_pm_qos_update_request(struct dev_pm_qos_request *req, | ||
341 | s32 new_value) | ||
342 | { | ||
343 | s32 curr_value; | ||
344 | int ret = 0; | ||
345 | |||
346 | if (!req->dev->power.qos) | ||
347 | return -ENODEV; | ||
348 | |||
349 | switch(req->type) { | ||
350 | case DEV_PM_QOS_LATENCY: | ||
351 | curr_value = req->data.pnode.prio; | ||
352 | break; | ||
353 | case DEV_PM_QOS_FLAGS: | ||
354 | curr_value = req->data.flr.flags; | ||
355 | break; | ||
356 | default: | ||
357 | return -EINVAL; | ||
358 | } | ||
359 | |||
360 | if (curr_value != new_value) | ||
361 | ret = apply_constraint(req, PM_QOS_UPDATE_REQ, new_value); | ||
362 | |||
363 | return ret; | ||
364 | } | ||
365 | |||
366 | /** | ||
265 | * dev_pm_qos_update_request - modifies an existing qos request | 367 | * dev_pm_qos_update_request - modifies an existing qos request |
266 | * @req : handle to list element holding a dev_pm_qos request to use | 368 | * @req : handle to list element holding a dev_pm_qos request to use |
267 | * @new_value: defines the qos request | 369 | * @new_value: defines the qos request |
@@ -275,11 +377,13 @@ EXPORT_SYMBOL_GPL(dev_pm_qos_add_request); | |||
275 | * 0 if the aggregated constraint value has not changed, | 377 | * 0 if the aggregated constraint value has not changed, |
276 | * -EINVAL in case of wrong parameters, -ENODEV if the device has been | 378 | * -EINVAL in case of wrong parameters, -ENODEV if the device has been |
277 | * removed from the system | 379 | * removed from the system |
380 | * | ||
381 | * Callers should ensure that the target device is not RPM_SUSPENDED before | ||
382 | * using this function for requests of type DEV_PM_QOS_FLAGS. | ||
278 | */ | 383 | */ |
279 | int dev_pm_qos_update_request(struct dev_pm_qos_request *req, | 384 | int dev_pm_qos_update_request(struct dev_pm_qos_request *req, s32 new_value) |
280 | s32 new_value) | ||
281 | { | 385 | { |
282 | int ret = 0; | 386 | int ret; |
283 | 387 | ||
284 | if (!req) /*guard against callers passing in null */ | 388 | if (!req) /*guard against callers passing in null */ |
285 | return -EINVAL; | 389 | return -EINVAL; |
@@ -289,17 +393,9 @@ int dev_pm_qos_update_request(struct dev_pm_qos_request *req, | |||
289 | return -EINVAL; | 393 | return -EINVAL; |
290 | 394 | ||
291 | mutex_lock(&dev_pm_qos_mtx); | 395 | mutex_lock(&dev_pm_qos_mtx); |
292 | 396 | ret = __dev_pm_qos_update_request(req, new_value); | |
293 | if (req->dev->power.constraints) { | ||
294 | if (new_value != req->node.prio) | ||
295 | ret = apply_constraint(req, PM_QOS_UPDATE_REQ, | ||
296 | new_value); | ||
297 | } else { | ||
298 | /* Return if the device has been removed */ | ||
299 | ret = -ENODEV; | ||
300 | } | ||
301 | |||
302 | mutex_unlock(&dev_pm_qos_mtx); | 397 | mutex_unlock(&dev_pm_qos_mtx); |
398 | |||
303 | return ret; | 399 | return ret; |
304 | } | 400 | } |
305 | EXPORT_SYMBOL_GPL(dev_pm_qos_update_request); | 401 | EXPORT_SYMBOL_GPL(dev_pm_qos_update_request); |
@@ -315,6 +411,9 @@ EXPORT_SYMBOL_GPL(dev_pm_qos_update_request); | |||
315 | * 0 if the aggregated constraint value has not changed, | 411 | * 0 if the aggregated constraint value has not changed, |
316 | * -EINVAL in case of wrong parameters, -ENODEV if the device has been | 412 | * -EINVAL in case of wrong parameters, -ENODEV if the device has been |
317 | * removed from the system | 413 | * removed from the system |
414 | * | ||
415 | * Callers should ensure that the target device is not RPM_SUSPENDED before | ||
416 | * using this function for requests of type DEV_PM_QOS_FLAGS. | ||
318 | */ | 417 | */ |
319 | int dev_pm_qos_remove_request(struct dev_pm_qos_request *req) | 418 | int dev_pm_qos_remove_request(struct dev_pm_qos_request *req) |
320 | { | 419 | { |
@@ -329,7 +428,7 @@ int dev_pm_qos_remove_request(struct dev_pm_qos_request *req) | |||
329 | 428 | ||
330 | mutex_lock(&dev_pm_qos_mtx); | 429 | mutex_lock(&dev_pm_qos_mtx); |
331 | 430 | ||
332 | if (req->dev->power.constraints) { | 431 | if (req->dev->power.qos) { |
333 | ret = apply_constraint(req, PM_QOS_REMOVE_REQ, | 432 | ret = apply_constraint(req, PM_QOS_REMOVE_REQ, |
334 | PM_QOS_DEFAULT_VALUE); | 433 | PM_QOS_DEFAULT_VALUE); |
335 | memset(req, 0, sizeof(*req)); | 434 | memset(req, 0, sizeof(*req)); |
@@ -362,13 +461,13 @@ int dev_pm_qos_add_notifier(struct device *dev, struct notifier_block *notifier) | |||
362 | 461 | ||
363 | mutex_lock(&dev_pm_qos_mtx); | 462 | mutex_lock(&dev_pm_qos_mtx); |
364 | 463 | ||
365 | if (!dev->power.constraints) | 464 | if (!dev->power.qos) |
366 | ret = dev->power.power_state.event != PM_EVENT_INVALID ? | 465 | ret = dev->power.power_state.event != PM_EVENT_INVALID ? |
367 | dev_pm_qos_constraints_allocate(dev) : -ENODEV; | 466 | dev_pm_qos_constraints_allocate(dev) : -ENODEV; |
368 | 467 | ||
369 | if (!ret) | 468 | if (!ret) |
370 | ret = blocking_notifier_chain_register( | 469 | ret = blocking_notifier_chain_register( |
371 | dev->power.constraints->notifiers, notifier); | 470 | dev->power.qos->latency.notifiers, notifier); |
372 | 471 | ||
373 | mutex_unlock(&dev_pm_qos_mtx); | 472 | mutex_unlock(&dev_pm_qos_mtx); |
374 | return ret; | 473 | return ret; |
@@ -393,9 +492,9 @@ int dev_pm_qos_remove_notifier(struct device *dev, | |||
393 | mutex_lock(&dev_pm_qos_mtx); | 492 | mutex_lock(&dev_pm_qos_mtx); |
394 | 493 | ||
395 | /* Silently return if the constraints object is not present. */ | 494 | /* Silently return if the constraints object is not present. */ |
396 | if (dev->power.constraints) | 495 | if (dev->power.qos) |
397 | retval = blocking_notifier_chain_unregister( | 496 | retval = blocking_notifier_chain_unregister( |
398 | dev->power.constraints->notifiers, | 497 | dev->power.qos->latency.notifiers, |
399 | notifier); | 498 | notifier); |
400 | 499 | ||
401 | mutex_unlock(&dev_pm_qos_mtx); | 500 | mutex_unlock(&dev_pm_qos_mtx); |
@@ -449,7 +548,8 @@ int dev_pm_qos_add_ancestor_request(struct device *dev, | |||
449 | ancestor = ancestor->parent; | 548 | ancestor = ancestor->parent; |
450 | 549 | ||
451 | if (ancestor) | 550 | if (ancestor) |
452 | error = dev_pm_qos_add_request(ancestor, req, value); | 551 | error = dev_pm_qos_add_request(ancestor, req, |
552 | DEV_PM_QOS_LATENCY, value); | ||
453 | 553 | ||
454 | if (error < 0) | 554 | if (error < 0) |
455 | req->dev = NULL; | 555 | req->dev = NULL; |
@@ -459,10 +559,19 @@ int dev_pm_qos_add_ancestor_request(struct device *dev, | |||
459 | EXPORT_SYMBOL_GPL(dev_pm_qos_add_ancestor_request); | 559 | EXPORT_SYMBOL_GPL(dev_pm_qos_add_ancestor_request); |
460 | 560 | ||
461 | #ifdef CONFIG_PM_RUNTIME | 561 | #ifdef CONFIG_PM_RUNTIME |
462 | static void __dev_pm_qos_drop_user_request(struct device *dev) | 562 | static void __dev_pm_qos_drop_user_request(struct device *dev, |
563 | enum dev_pm_qos_req_type type) | ||
463 | { | 564 | { |
464 | dev_pm_qos_remove_request(dev->power.pq_req); | 565 | switch(type) { |
465 | dev->power.pq_req = NULL; | 566 | case DEV_PM_QOS_LATENCY: |
567 | dev_pm_qos_remove_request(dev->power.qos->latency_req); | ||
568 | dev->power.qos->latency_req = NULL; | ||
569 | break; | ||
570 | case DEV_PM_QOS_FLAGS: | ||
571 | dev_pm_qos_remove_request(dev->power.qos->flags_req); | ||
572 | dev->power.qos->flags_req = NULL; | ||
573 | break; | ||
574 | } | ||
466 | } | 575 | } |
467 | 576 | ||
468 | /** | 577 | /** |
@@ -478,21 +587,21 @@ int dev_pm_qos_expose_latency_limit(struct device *dev, s32 value) | |||
478 | if (!device_is_registered(dev) || value < 0) | 587 | if (!device_is_registered(dev) || value < 0) |
479 | return -EINVAL; | 588 | return -EINVAL; |
480 | 589 | ||
481 | if (dev->power.pq_req) | 590 | if (dev->power.qos && dev->power.qos->latency_req) |
482 | return -EEXIST; | 591 | return -EEXIST; |
483 | 592 | ||
484 | req = kzalloc(sizeof(*req), GFP_KERNEL); | 593 | req = kzalloc(sizeof(*req), GFP_KERNEL); |
485 | if (!req) | 594 | if (!req) |
486 | return -ENOMEM; | 595 | return -ENOMEM; |
487 | 596 | ||
488 | ret = dev_pm_qos_add_request(dev, req, value); | 597 | ret = dev_pm_qos_add_request(dev, req, DEV_PM_QOS_LATENCY, value); |
489 | if (ret < 0) | 598 | if (ret < 0) |
490 | return ret; | 599 | return ret; |
491 | 600 | ||
492 | dev->power.pq_req = req; | 601 | dev->power.qos->latency_req = req; |
493 | ret = pm_qos_sysfs_add(dev); | 602 | ret = pm_qos_sysfs_add_latency(dev); |
494 | if (ret) | 603 | if (ret) |
495 | __dev_pm_qos_drop_user_request(dev); | 604 | __dev_pm_qos_drop_user_request(dev, DEV_PM_QOS_LATENCY); |
496 | 605 | ||
497 | return ret; | 606 | return ret; |
498 | } | 607 | } |
@@ -504,10 +613,92 @@ EXPORT_SYMBOL_GPL(dev_pm_qos_expose_latency_limit); | |||
504 | */ | 613 | */ |
505 | void dev_pm_qos_hide_latency_limit(struct device *dev) | 614 | void dev_pm_qos_hide_latency_limit(struct device *dev) |
506 | { | 615 | { |
507 | if (dev->power.pq_req) { | 616 | if (dev->power.qos && dev->power.qos->latency_req) { |
508 | pm_qos_sysfs_remove(dev); | 617 | pm_qos_sysfs_remove_latency(dev); |
509 | __dev_pm_qos_drop_user_request(dev); | 618 | __dev_pm_qos_drop_user_request(dev, DEV_PM_QOS_LATENCY); |
510 | } | 619 | } |
511 | } | 620 | } |
512 | EXPORT_SYMBOL_GPL(dev_pm_qos_hide_latency_limit); | 621 | EXPORT_SYMBOL_GPL(dev_pm_qos_hide_latency_limit); |
622 | |||
623 | /** | ||
624 | * dev_pm_qos_expose_flags - Expose PM QoS flags of a device to user space. | ||
625 | * @dev: Device whose PM QoS flags are to be exposed to user space. | ||
626 | * @val: Initial values of the flags. | ||
627 | */ | ||
628 | int dev_pm_qos_expose_flags(struct device *dev, s32 val) | ||
629 | { | ||
630 | struct dev_pm_qos_request *req; | ||
631 | int ret; | ||
632 | |||
633 | if (!device_is_registered(dev)) | ||
634 | return -EINVAL; | ||
635 | |||
636 | if (dev->power.qos && dev->power.qos->flags_req) | ||
637 | return -EEXIST; | ||
638 | |||
639 | req = kzalloc(sizeof(*req), GFP_KERNEL); | ||
640 | if (!req) | ||
641 | return -ENOMEM; | ||
642 | |||
643 | pm_runtime_get_sync(dev); | ||
644 | ret = dev_pm_qos_add_request(dev, req, DEV_PM_QOS_FLAGS, val); | ||
645 | if (ret < 0) | ||
646 | goto fail; | ||
647 | |||
648 | dev->power.qos->flags_req = req; | ||
649 | ret = pm_qos_sysfs_add_flags(dev); | ||
650 | if (ret) | ||
651 | __dev_pm_qos_drop_user_request(dev, DEV_PM_QOS_FLAGS); | ||
652 | |||
653 | fail: | ||
654 | pm_runtime_put(dev); | ||
655 | return ret; | ||
656 | } | ||
657 | EXPORT_SYMBOL_GPL(dev_pm_qos_expose_flags); | ||
658 | |||
659 | /** | ||
660 | * dev_pm_qos_hide_flags - Hide PM QoS flags of a device from user space. | ||
661 | * @dev: Device whose PM QoS flags are to be hidden from user space. | ||
662 | */ | ||
663 | void dev_pm_qos_hide_flags(struct device *dev) | ||
664 | { | ||
665 | if (dev->power.qos && dev->power.qos->flags_req) { | ||
666 | pm_qos_sysfs_remove_flags(dev); | ||
667 | pm_runtime_get_sync(dev); | ||
668 | __dev_pm_qos_drop_user_request(dev, DEV_PM_QOS_FLAGS); | ||
669 | pm_runtime_put(dev); | ||
670 | } | ||
671 | } | ||
672 | EXPORT_SYMBOL_GPL(dev_pm_qos_hide_flags); | ||
673 | |||
674 | /** | ||
675 | * dev_pm_qos_update_flags - Update PM QoS flags request owned by user space. | ||
676 | * @dev: Device to update the PM QoS flags request for. | ||
677 | * @mask: Flags to set/clear. | ||
678 | * @set: Whether to set or clear the flags (true means set). | ||
679 | */ | ||
680 | int dev_pm_qos_update_flags(struct device *dev, s32 mask, bool set) | ||
681 | { | ||
682 | s32 value; | ||
683 | int ret; | ||
684 | |||
685 | if (!dev->power.qos || !dev->power.qos->flags_req) | ||
686 | return -EINVAL; | ||
687 | |||
688 | pm_runtime_get_sync(dev); | ||
689 | mutex_lock(&dev_pm_qos_mtx); | ||
690 | |||
691 | value = dev_pm_qos_requested_flags(dev); | ||
692 | if (set) | ||
693 | value |= mask; | ||
694 | else | ||
695 | value &= ~mask; | ||
696 | |||
697 | ret = __dev_pm_qos_update_request(dev->power.qos->flags_req, value); | ||
698 | |||
699 | mutex_unlock(&dev_pm_qos_mtx); | ||
700 | pm_runtime_put(dev); | ||
701 | |||
702 | return ret; | ||
703 | } | ||
513 | #endif /* CONFIG_PM_RUNTIME */ | 704 | #endif /* CONFIG_PM_RUNTIME */ |
diff --git a/drivers/base/power/sysfs.c b/drivers/base/power/sysfs.c index b91dc6f1e914..50d16e3cb0a9 100644 --- a/drivers/base/power/sysfs.c +++ b/drivers/base/power/sysfs.c | |||
@@ -221,7 +221,7 @@ static DEVICE_ATTR(autosuspend_delay_ms, 0644, autosuspend_delay_ms_show, | |||
221 | static ssize_t pm_qos_latency_show(struct device *dev, | 221 | static ssize_t pm_qos_latency_show(struct device *dev, |
222 | struct device_attribute *attr, char *buf) | 222 | struct device_attribute *attr, char *buf) |
223 | { | 223 | { |
224 | return sprintf(buf, "%d\n", dev->power.pq_req->node.prio); | 224 | return sprintf(buf, "%d\n", dev_pm_qos_requested_latency(dev)); |
225 | } | 225 | } |
226 | 226 | ||
227 | static ssize_t pm_qos_latency_store(struct device *dev, | 227 | static ssize_t pm_qos_latency_store(struct device *dev, |
@@ -237,12 +237,66 @@ static ssize_t pm_qos_latency_store(struct device *dev, | |||
237 | if (value < 0) | 237 | if (value < 0) |
238 | return -EINVAL; | 238 | return -EINVAL; |
239 | 239 | ||
240 | ret = dev_pm_qos_update_request(dev->power.pq_req, value); | 240 | ret = dev_pm_qos_update_request(dev->power.qos->latency_req, value); |
241 | return ret < 0 ? ret : n; | 241 | return ret < 0 ? ret : n; |
242 | } | 242 | } |
243 | 243 | ||
244 | static DEVICE_ATTR(pm_qos_resume_latency_us, 0644, | 244 | static DEVICE_ATTR(pm_qos_resume_latency_us, 0644, |
245 | pm_qos_latency_show, pm_qos_latency_store); | 245 | pm_qos_latency_show, pm_qos_latency_store); |
246 | |||
247 | static ssize_t pm_qos_no_power_off_show(struct device *dev, | ||
248 | struct device_attribute *attr, | ||
249 | char *buf) | ||
250 | { | ||
251 | return sprintf(buf, "%d\n", !!(dev_pm_qos_requested_flags(dev) | ||
252 | & PM_QOS_FLAG_NO_POWER_OFF)); | ||
253 | } | ||
254 | |||
255 | static ssize_t pm_qos_no_power_off_store(struct device *dev, | ||
256 | struct device_attribute *attr, | ||
257 | const char *buf, size_t n) | ||
258 | { | ||
259 | int ret; | ||
260 | |||
261 | if (kstrtoint(buf, 0, &ret)) | ||
262 | return -EINVAL; | ||
263 | |||
264 | if (ret != 0 && ret != 1) | ||
265 | return -EINVAL; | ||
266 | |||
267 | ret = dev_pm_qos_update_flags(dev, PM_QOS_FLAG_NO_POWER_OFF, ret); | ||
268 | return ret < 0 ? ret : n; | ||
269 | } | ||
270 | |||
271 | static DEVICE_ATTR(pm_qos_no_power_off, 0644, | ||
272 | pm_qos_no_power_off_show, pm_qos_no_power_off_store); | ||
273 | |||
274 | static ssize_t pm_qos_remote_wakeup_show(struct device *dev, | ||
275 | struct device_attribute *attr, | ||
276 | char *buf) | ||
277 | { | ||
278 | return sprintf(buf, "%d\n", !!(dev_pm_qos_requested_flags(dev) | ||
279 | & PM_QOS_FLAG_REMOTE_WAKEUP)); | ||
280 | } | ||
281 | |||
282 | static ssize_t pm_qos_remote_wakeup_store(struct device *dev, | ||
283 | struct device_attribute *attr, | ||
284 | const char *buf, size_t n) | ||
285 | { | ||
286 | int ret; | ||
287 | |||
288 | if (kstrtoint(buf, 0, &ret)) | ||
289 | return -EINVAL; | ||
290 | |||
291 | if (ret != 0 && ret != 1) | ||
292 | return -EINVAL; | ||
293 | |||
294 | ret = dev_pm_qos_update_flags(dev, PM_QOS_FLAG_REMOTE_WAKEUP, ret); | ||
295 | return ret < 0 ? ret : n; | ||
296 | } | ||
297 | |||
298 | static DEVICE_ATTR(pm_qos_remote_wakeup, 0644, | ||
299 | pm_qos_remote_wakeup_show, pm_qos_remote_wakeup_store); | ||
246 | #endif /* CONFIG_PM_RUNTIME */ | 300 | #endif /* CONFIG_PM_RUNTIME */ |
247 | 301 | ||
248 | #ifdef CONFIG_PM_SLEEP | 302 | #ifdef CONFIG_PM_SLEEP |
@@ -564,15 +618,27 @@ static struct attribute_group pm_runtime_attr_group = { | |||
564 | .attrs = runtime_attrs, | 618 | .attrs = runtime_attrs, |
565 | }; | 619 | }; |
566 | 620 | ||
567 | static struct attribute *pm_qos_attrs[] = { | 621 | static struct attribute *pm_qos_latency_attrs[] = { |
568 | #ifdef CONFIG_PM_RUNTIME | 622 | #ifdef CONFIG_PM_RUNTIME |
569 | &dev_attr_pm_qos_resume_latency_us.attr, | 623 | &dev_attr_pm_qos_resume_latency_us.attr, |
570 | #endif /* CONFIG_PM_RUNTIME */ | 624 | #endif /* CONFIG_PM_RUNTIME */ |
571 | NULL, | 625 | NULL, |
572 | }; | 626 | }; |
573 | static struct attribute_group pm_qos_attr_group = { | 627 | static struct attribute_group pm_qos_latency_attr_group = { |
574 | .name = power_group_name, | 628 | .name = power_group_name, |
575 | .attrs = pm_qos_attrs, | 629 | .attrs = pm_qos_latency_attrs, |
630 | }; | ||
631 | |||
632 | static struct attribute *pm_qos_flags_attrs[] = { | ||
633 | #ifdef CONFIG_PM_RUNTIME | ||
634 | &dev_attr_pm_qos_no_power_off.attr, | ||
635 | &dev_attr_pm_qos_remote_wakeup.attr, | ||
636 | #endif /* CONFIG_PM_RUNTIME */ | ||
637 | NULL, | ||
638 | }; | ||
639 | static struct attribute_group pm_qos_flags_attr_group = { | ||
640 | .name = power_group_name, | ||
641 | .attrs = pm_qos_flags_attrs, | ||
576 | }; | 642 | }; |
577 | 643 | ||
578 | int dpm_sysfs_add(struct device *dev) | 644 | int dpm_sysfs_add(struct device *dev) |
@@ -615,14 +681,24 @@ void wakeup_sysfs_remove(struct device *dev) | |||
615 | sysfs_unmerge_group(&dev->kobj, &pm_wakeup_attr_group); | 681 | sysfs_unmerge_group(&dev->kobj, &pm_wakeup_attr_group); |
616 | } | 682 | } |
617 | 683 | ||
618 | int pm_qos_sysfs_add(struct device *dev) | 684 | int pm_qos_sysfs_add_latency(struct device *dev) |
685 | { | ||
686 | return sysfs_merge_group(&dev->kobj, &pm_qos_latency_attr_group); | ||
687 | } | ||
688 | |||
689 | void pm_qos_sysfs_remove_latency(struct device *dev) | ||
690 | { | ||
691 | sysfs_unmerge_group(&dev->kobj, &pm_qos_latency_attr_group); | ||
692 | } | ||
693 | |||
694 | int pm_qos_sysfs_add_flags(struct device *dev) | ||
619 | { | 695 | { |
620 | return sysfs_merge_group(&dev->kobj, &pm_qos_attr_group); | 696 | return sysfs_merge_group(&dev->kobj, &pm_qos_flags_attr_group); |
621 | } | 697 | } |
622 | 698 | ||
623 | void pm_qos_sysfs_remove(struct device *dev) | 699 | void pm_qos_sysfs_remove_flags(struct device *dev) |
624 | { | 700 | { |
625 | sysfs_unmerge_group(&dev->kobj, &pm_qos_attr_group); | 701 | sysfs_unmerge_group(&dev->kobj, &pm_qos_flags_attr_group); |
626 | } | 702 | } |
627 | 703 | ||
628 | void rpm_sysfs_remove(struct device *dev) | 704 | void rpm_sysfs_remove(struct device *dev) |