aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/base/power/Makefile4
-rw-r--r--drivers/base/power/main.c3
-rw-r--r--drivers/base/power/qos.c338
-rw-r--r--include/linux/pm.h9
-rw-r--r--include/linux/pm_qos.h42
5 files changed, 394 insertions, 2 deletions
diff --git a/drivers/base/power/Makefile b/drivers/base/power/Makefile
index 6488ce12f586..81676dd17900 100644
--- a/drivers/base/power/Makefile
+++ b/drivers/base/power/Makefile
@@ -1,4 +1,4 @@
1obj-$(CONFIG_PM) += sysfs.o generic_ops.o common.o 1obj-$(CONFIG_PM) += sysfs.o generic_ops.o common.o qos.o
2obj-$(CONFIG_PM_SLEEP) += main.o wakeup.o 2obj-$(CONFIG_PM_SLEEP) += main.o wakeup.o
3obj-$(CONFIG_PM_RUNTIME) += runtime.o 3obj-$(CONFIG_PM_RUNTIME) += runtime.o
4obj-$(CONFIG_PM_TRACE_RTC) += trace.o 4obj-$(CONFIG_PM_TRACE_RTC) += trace.o
@@ -6,4 +6,4 @@ obj-$(CONFIG_PM_OPP) += opp.o
6obj-$(CONFIG_PM_GENERIC_DOMAINS) += domain.o 6obj-$(CONFIG_PM_GENERIC_DOMAINS) += domain.o
7obj-$(CONFIG_HAVE_CLK) += clock_ops.o 7obj-$(CONFIG_HAVE_CLK) += clock_ops.o
8 8
9ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG \ No newline at end of file 9ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c
index a85459126bc6..956443f86254 100644
--- a/drivers/base/power/main.c
+++ b/drivers/base/power/main.c
@@ -22,6 +22,7 @@
22#include <linux/mutex.h> 22#include <linux/mutex.h>
23#include <linux/pm.h> 23#include <linux/pm.h>
24#include <linux/pm_runtime.h> 24#include <linux/pm_runtime.h>
25#include <linux/pm_qos.h>
25#include <linux/resume-trace.h> 26#include <linux/resume-trace.h>
26#include <linux/interrupt.h> 27#include <linux/interrupt.h>
27#include <linux/sched.h> 28#include <linux/sched.h>
@@ -97,6 +98,7 @@ void device_pm_add(struct device *dev)
97 dev_name(dev->parent)); 98 dev_name(dev->parent));
98 list_add_tail(&dev->power.entry, &dpm_list); 99 list_add_tail(&dev->power.entry, &dpm_list);
99 mutex_unlock(&dpm_list_mtx); 100 mutex_unlock(&dpm_list_mtx);
101 dev_pm_qos_constraints_init(dev);
100} 102}
101 103
102/** 104/**
@@ -107,6 +109,7 @@ void device_pm_remove(struct device *dev)
107{ 109{
108 pr_debug("PM: Removing info for %s:%s\n", 110 pr_debug("PM: Removing info for %s:%s\n",
109 dev->bus ? dev->bus->name : "No Bus", dev_name(dev)); 111 dev->bus ? dev->bus->name : "No Bus", dev_name(dev));
112 dev_pm_qos_constraints_destroy(dev);
110 complete_all(&dev->power.completion); 113 complete_all(&dev->power.completion);
111 mutex_lock(&dpm_list_mtx); 114 mutex_lock(&dpm_list_mtx);
112 list_del_init(&dev->power.entry); 115 list_del_init(&dev->power.entry);
diff --git a/drivers/base/power/qos.c b/drivers/base/power/qos.c
new file mode 100644
index 000000000000..cc4c541398aa
--- /dev/null
+++ b/drivers/base/power/qos.c
@@ -0,0 +1,338 @@
1/*
2 * Devices PM QoS constraints management
3 *
4 * Copyright (C) 2011 Texas Instruments, Inc.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 *
10 *
11 * This module exposes the interface to kernel space for specifying
12 * per-device PM QoS dependencies. It provides infrastructure for registration
13 * of:
14 *
15 * Dependents on a QoS value : register requests
16 * Watchers of QoS value : get notified when target QoS value changes
17 *
18 * This QoS design is best effort based. Dependents register their QoS needs.
19 * Watchers register to keep track of the current QoS needs of the system.
20 *
21 * Note about the per-device constraint data struct allocation:
22 * . The per-device constraints data struct ptr is tored into the device
23 * dev_pm_info.
24 * . To minimize the data usage by the per-device constraints, the data struct
25 * is only allocated at the first call to dev_pm_qos_add_request.
26 * . The data is later free'd when the device is removed from the system.
27 * . The constraints_state variable from dev_pm_info tracks the data struct
28 * allocation state:
29 * DEV_PM_QOS_NO_DEVICE: No device present or device removed, no data
30 * allocated,
31 * DEV_PM_QOS_DEVICE_PRESENT: Device present, data not allocated and will be
32 * allocated at the first call to dev_pm_qos_add_request,
33 * DEV_PM_QOS_ALLOCATED: Device present, data allocated. The per-device
34 * PM QoS constraints framework is operational and constraints can be
35 * added, updated or removed using the dev_pm_qos_* API.
36 * . A global mutex protects the constraints users from the data being
37 * allocated and free'd.
38 */
39
40#include <linux/pm_qos.h>
41#include <linux/spinlock.h>
42#include <linux/slab.h>
43#include <linux/device.h>
44#include <linux/mutex.h>
45
46
47static DEFINE_MUTEX(dev_pm_qos_mtx);
48
49/*
50 * dev_pm_qos_constraints_allocate
51 * @dev: device to allocate data for
52 *
53 * Called at the first call to add_request, for constraint data allocation
54 * Must be called with the dev_pm_qos_mtx mutex held
55 */
56static int dev_pm_qos_constraints_allocate(struct device *dev)
57{
58 struct pm_qos_constraints *c;
59 struct blocking_notifier_head *n;
60
61 c = kzalloc(sizeof(*c), GFP_KERNEL);
62 if (!c)
63 return -ENOMEM;
64
65 n = kzalloc(sizeof(*n), GFP_KERNEL);
66 if (!n) {
67 kfree(c);
68 return -ENOMEM;
69 }
70 BLOCKING_INIT_NOTIFIER_HEAD(n);
71
72 dev->power.constraints = c;
73 plist_head_init(&dev->power.constraints->list);
74 dev->power.constraints->target_value = PM_QOS_DEV_LAT_DEFAULT_VALUE;
75 dev->power.constraints->default_value = PM_QOS_DEV_LAT_DEFAULT_VALUE;
76 dev->power.constraints->type = PM_QOS_MIN;
77 dev->power.constraints->notifiers = n;
78 dev->power.constraints_state = DEV_PM_QOS_ALLOCATED;
79
80 return 0;
81}
82
83/**
84 * dev_pm_qos_constraints_init
85 * @dev: target device
86 *
87 * Called from the device PM subsystem at device insertion
88 */
89void dev_pm_qos_constraints_init(struct device *dev)
90{
91 mutex_lock(&dev_pm_qos_mtx);
92 dev->power.constraints_state = DEV_PM_QOS_DEVICE_PRESENT;
93 mutex_unlock(&dev_pm_qos_mtx);
94}
95
96/**
97 * dev_pm_qos_constraints_destroy
98 * @dev: target device
99 *
100 * Called from the device PM subsystem at device removal
101 */
102void dev_pm_qos_constraints_destroy(struct device *dev)
103{
104 struct dev_pm_qos_request *req, *tmp;
105
106 mutex_lock(&dev_pm_qos_mtx);
107
108 if (dev->power.constraints_state == DEV_PM_QOS_ALLOCATED) {
109 /* Flush the constraints list for the device */
110 plist_for_each_entry_safe(req, tmp,
111 &dev->power.constraints->list,
112 node) {
113 /*
114 * Update constraints list and call the per-device
115 * callbacks if needed
116 */
117 pm_qos_update_target(req->dev->power.constraints,
118 &req->node, PM_QOS_REMOVE_REQ,
119 PM_QOS_DEFAULT_VALUE);
120 memset(req, 0, sizeof(*req));
121 }
122
123 kfree(dev->power.constraints->notifiers);
124 kfree(dev->power.constraints);
125 dev->power.constraints = NULL;
126 }
127 dev->power.constraints_state = DEV_PM_QOS_NO_DEVICE;
128
129 mutex_unlock(&dev_pm_qos_mtx);
130}
131
132/**
133 * dev_pm_qos_add_request - inserts new qos request into the list
134 * @dev: target device for the constraint
135 * @req: pointer to a preallocated handle
136 * @value: defines the qos request
137 *
138 * This function inserts a new entry in the device constraints list of
139 * requested qos performance characteristics. It recomputes the aggregate
140 * QoS expectations of parameters and initializes the dev_pm_qos_request
141 * handle. Caller needs to save this handle for later use in updates and
142 * removal.
143 *
144 * Returns 1 if the aggregated constraint value has changed,
145 * 0 if the aggregated constraint value has not changed,
146 * -EINVAL in case of wrong parameters, -ENODEV if the device has been
147 * removed from the system
148 */
149int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,
150 s32 value)
151{
152 int ret = 0;
153
154 if (!dev || !req) /*guard against callers passing in null */
155 return -EINVAL;
156
157 if (dev_pm_qos_request_active(req)) {
158 WARN(1, KERN_ERR "dev_pm_qos_add_request() called for already "
159 "added request\n");
160 return -EINVAL;
161 }
162
163 mutex_lock(&dev_pm_qos_mtx);
164 req->dev = dev;
165
166 /* Return if the device has been removed */
167 if (req->dev->power.constraints_state == DEV_PM_QOS_NO_DEVICE) {
168 ret = -ENODEV;
169 goto out;
170 }
171
172 /*
173 * Allocate the constraints data on the first call to add_request,
174 * i.e. only if the data is not already allocated and if the device has
175 * not been removed
176 */
177 if (dev->power.constraints_state == DEV_PM_QOS_DEVICE_PRESENT)
178 ret = dev_pm_qos_constraints_allocate(dev);
179
180 if (!ret)
181 ret = pm_qos_update_target(dev->power.constraints, &req->node,
182 PM_QOS_ADD_REQ, value);
183
184out:
185 mutex_unlock(&dev_pm_qos_mtx);
186 return ret;
187}
188EXPORT_SYMBOL_GPL(dev_pm_qos_add_request);
189
190/**
191 * dev_pm_qos_update_request - modifies an existing qos request
192 * @req : handle to list element holding a dev_pm_qos request to use
193 * @new_value: defines the qos request
194 *
195 * Updates an existing dev PM qos request along with updating the
196 * target value.
197 *
198 * Attempts are made to make this code callable on hot code paths.
199 *
200 * Returns 1 if the aggregated constraint value has changed,
201 * 0 if the aggregated constraint value has not changed,
202 * -EINVAL in case of wrong parameters, -ENODEV if the device has been
203 * removed from the system
204 */
205int dev_pm_qos_update_request(struct dev_pm_qos_request *req,
206 s32 new_value)
207{
208 int ret = 0;
209
210 if (!req) /*guard against callers passing in null */
211 return -EINVAL;
212
213 if (!dev_pm_qos_request_active(req)) {
214 WARN(1, KERN_ERR "dev_pm_qos_update_request() called for "
215 "unknown object\n");
216 return -EINVAL;
217 }
218
219 mutex_lock(&dev_pm_qos_mtx);
220
221 if (req->dev->power.constraints_state == DEV_PM_QOS_ALLOCATED) {
222 if (new_value != req->node.prio)
223 ret = pm_qos_update_target(req->dev->power.constraints,
224 &req->node,
225 PM_QOS_UPDATE_REQ,
226 new_value);
227 } else {
228 /* Return if the device has been removed */
229 ret = -ENODEV;
230 }
231
232 mutex_unlock(&dev_pm_qos_mtx);
233 return ret;
234}
235EXPORT_SYMBOL_GPL(dev_pm_qos_update_request);
236
237/**
238 * dev_pm_qos_remove_request - modifies an existing qos request
239 * @req: handle to request list element
240 *
241 * Will remove pm qos request from the list of constraints and
242 * recompute the current target value. Call this on slow code paths.
243 *
244 * Returns 1 if the aggregated constraint value has changed,
245 * 0 if the aggregated constraint value has not changed,
246 * -EINVAL in case of wrong parameters, -ENODEV if the device has been
247 * removed from the system
248 */
249int dev_pm_qos_remove_request(struct dev_pm_qos_request *req)
250{
251 int ret = 0;
252
253 if (!req) /*guard against callers passing in null */
254 return -EINVAL;
255
256 if (!dev_pm_qos_request_active(req)) {
257 WARN(1, KERN_ERR "dev_pm_qos_remove_request() called for "
258 "unknown object\n");
259 return -EINVAL;
260 }
261
262 mutex_lock(&dev_pm_qos_mtx);
263
264 if (req->dev->power.constraints_state == DEV_PM_QOS_ALLOCATED) {
265 ret = pm_qos_update_target(req->dev->power.constraints,
266 &req->node, PM_QOS_REMOVE_REQ,
267 PM_QOS_DEFAULT_VALUE);
268 memset(req, 0, sizeof(*req));
269 } else {
270 /* Return if the device has been removed */
271 ret = -ENODEV;
272 }
273
274 mutex_unlock(&dev_pm_qos_mtx);
275 return ret;
276}
277EXPORT_SYMBOL_GPL(dev_pm_qos_remove_request);
278
279/**
280 * dev_pm_qos_add_notifier - sets notification entry for changes to target value
281 * of per-device PM QoS constraints
282 *
283 * @dev: target device for the constraint
284 * @notifier: notifier block managed by caller.
285 *
286 * Will register the notifier into a notification chain that gets called
287 * upon changes to the target value for the device.
288 */
289int dev_pm_qos_add_notifier(struct device *dev, struct notifier_block *notifier)
290{
291 int retval = 0;
292
293 mutex_lock(&dev_pm_qos_mtx);
294
295 /* Silently return if the device has been removed */
296 if (dev->power.constraints_state != DEV_PM_QOS_ALLOCATED)
297 goto out;
298
299 retval = blocking_notifier_chain_register(
300 dev->power.constraints->notifiers,
301 notifier);
302
303out:
304 mutex_unlock(&dev_pm_qos_mtx);
305 return retval;
306}
307EXPORT_SYMBOL_GPL(dev_pm_qos_add_notifier);
308
309/**
310 * dev_pm_qos_remove_notifier - deletes notification for changes to target value
311 * of per-device PM QoS constraints
312 *
313 * @dev: target device for the constraint
314 * @notifier: notifier block to be removed.
315 *
316 * Will remove the notifier from the notification chain that gets called
317 * upon changes to the target value.
318 */
319int dev_pm_qos_remove_notifier(struct device *dev,
320 struct notifier_block *notifier)
321{
322 int retval = 0;
323
324 mutex_lock(&dev_pm_qos_mtx);
325
326 /* Silently return if the device has been removed */
327 if (dev->power.constraints_state != DEV_PM_QOS_ALLOCATED)
328 goto out;
329
330 retval = blocking_notifier_chain_unregister(
331 dev->power.constraints->notifiers,
332 notifier);
333
334out:
335 mutex_unlock(&dev_pm_qos_mtx);
336 return retval;
337}
338EXPORT_SYMBOL_GPL(dev_pm_qos_remove_notifier);
diff --git a/include/linux/pm.h b/include/linux/pm.h
index ed10f24d5259..d78187e9ca99 100644
--- a/include/linux/pm.h
+++ b/include/linux/pm.h
@@ -419,6 +419,13 @@ enum rpm_request {
419 RPM_REQ_RESUME, 419 RPM_REQ_RESUME,
420}; 420};
421 421
422/* Per-device PM QoS constraints data struct state */
423enum dev_pm_qos_state {
424 DEV_PM_QOS_NO_DEVICE, /* No device present */
425 DEV_PM_QOS_DEVICE_PRESENT, /* Device present, data not allocated */
426 DEV_PM_QOS_ALLOCATED, /* Device present, data allocated */
427};
428
422struct wakeup_source; 429struct wakeup_source;
423 430
424struct pm_domain_data { 431struct pm_domain_data {
@@ -480,6 +487,8 @@ struct dev_pm_info {
480 unsigned long accounting_timestamp; 487 unsigned long accounting_timestamp;
481#endif 488#endif
482 struct pm_subsys_data *subsys_data; /* Owned by the subsystem. */ 489 struct pm_subsys_data *subsys_data; /* Owned by the subsystem. */
490 struct pm_qos_constraints *constraints;
491 enum dev_pm_qos_state constraints_state;
483}; 492};
484 493
485extern void update_pm_runtime_accounting(struct device *dev); 494extern void update_pm_runtime_accounting(struct device *dev);
diff --git a/include/linux/pm_qos.h b/include/linux/pm_qos.h
index 84aa15089896..f75f74dd9b90 100644
--- a/include/linux/pm_qos.h
+++ b/include/linux/pm_qos.h
@@ -19,12 +19,18 @@
19#define PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE (2000 * USEC_PER_SEC) 19#define PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE (2000 * USEC_PER_SEC)
20#define PM_QOS_NETWORK_LAT_DEFAULT_VALUE (2000 * USEC_PER_SEC) 20#define PM_QOS_NETWORK_LAT_DEFAULT_VALUE (2000 * USEC_PER_SEC)
21#define PM_QOS_NETWORK_THROUGHPUT_DEFAULT_VALUE 0 21#define PM_QOS_NETWORK_THROUGHPUT_DEFAULT_VALUE 0
22#define PM_QOS_DEV_LAT_DEFAULT_VALUE 0
22 23
23struct pm_qos_request { 24struct pm_qos_request {
24 struct plist_node node; 25 struct plist_node node;
25 int pm_qos_class; 26 int pm_qos_class;
26}; 27};
27 28
29struct dev_pm_qos_request {
30 struct plist_node node;
31 struct device *dev;
32};
33
28enum pm_qos_type { 34enum pm_qos_type {
29 PM_QOS_UNITIALIZED, 35 PM_QOS_UNITIALIZED,
30 PM_QOS_MAX, /* return the largest value */ 36 PM_QOS_MAX, /* return the largest value */
@@ -51,6 +57,11 @@ enum pm_qos_req_action {
51 PM_QOS_REMOVE_REQ /* Remove an existing request */ 57 PM_QOS_REMOVE_REQ /* Remove an existing request */
52}; 58};
53 59
60static inline int dev_pm_qos_request_active(struct dev_pm_qos_request *req)
61{
62 return req->dev != 0;
63}
64
54#ifdef CONFIG_PM 65#ifdef CONFIG_PM
55int pm_qos_update_target(struct pm_qos_constraints *c, struct plist_node *node, 66int pm_qos_update_target(struct pm_qos_constraints *c, struct plist_node *node,
56 enum pm_qos_req_action action, int value); 67 enum pm_qos_req_action action, int value);
@@ -64,6 +75,17 @@ int pm_qos_request(int pm_qos_class);
64int pm_qos_add_notifier(int pm_qos_class, struct notifier_block *notifier); 75int pm_qos_add_notifier(int pm_qos_class, struct notifier_block *notifier);
65int pm_qos_remove_notifier(int pm_qos_class, struct notifier_block *notifier); 76int pm_qos_remove_notifier(int pm_qos_class, struct notifier_block *notifier);
66int pm_qos_request_active(struct pm_qos_request *req); 77int pm_qos_request_active(struct pm_qos_request *req);
78
79int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,
80 s32 value);
81int dev_pm_qos_update_request(struct dev_pm_qos_request *req, s32 new_value);
82int dev_pm_qos_remove_request(struct dev_pm_qos_request *req);
83int dev_pm_qos_add_notifier(struct device *dev,
84 struct notifier_block *notifier);
85int dev_pm_qos_remove_notifier(struct device *dev,
86 struct notifier_block *notifier);
87void dev_pm_qos_constraints_init(struct device *dev);
88void dev_pm_qos_constraints_destroy(struct device *dev);
67#else 89#else
68static inline int pm_qos_update_target(struct pm_qos_constraints *c, 90static inline int pm_qos_update_target(struct pm_qos_constraints *c,
69 struct plist_node *node, 91 struct plist_node *node,
@@ -89,6 +111,26 @@ static inline int pm_qos_remove_notifier(int pm_qos_class,
89 { return 0; } 111 { return 0; }
90static inline int pm_qos_request_active(struct pm_qos_request *req) 112static inline int pm_qos_request_active(struct pm_qos_request *req)
91 { return 0; } 113 { return 0; }
114
115static inline int dev_pm_qos_add_request(struct device *dev,
116 struct dev_pm_qos_request *req,
117 s32 value)
118 { return 0; }
119static inline int dev_pm_qos_update_request(struct dev_pm_qos_request *req,
120 s32 new_value)
121 { return 0; }
122static inline int dev_pm_qos_remove_request(struct dev_pm_qos_request *req)
123 { return 0; }
124static inline int dev_pm_qos_add_notifier(struct device *dev,
125 struct notifier_block *notifier)
126 { return 0; }
127static inline int dev_pm_qos_remove_notifier(struct device *dev,
128 struct notifier_block *notifier)
129 { return 0; }
130static inline void dev_pm_qos_constraints_init(struct device *dev)
131 { return; }
132static inline void dev_pm_qos_constraints_destroy(struct device *dev)
133 { return; }
92#endif 134#endif
93 135
94#endif 136#endif