1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37#include <linux/pm_qos.h>
38#include <linux/spinlock.h>
39#include <linux/slab.h>
40#include <linux/device.h>
41#include <linux/mutex.h>
42#include <linux/export.h>
43
44
45static DEFINE_MUTEX(dev_pm_qos_mtx);
46
47static BLOCKING_NOTIFIER_HEAD(dev_pm_notifiers);
48
49
50
51
52
53s32 dev_pm_qos_read_value(struct device *dev)
54{
55 struct pm_qos_constraints *c;
56 unsigned long flags;
57 s32 ret = 0;
58
59 spin_lock_irqsave(&dev->power.lock, flags);
60
61 c = dev->power.constraints;
62 if (c)
63 ret = pm_qos_read_value(c);
64
65 spin_unlock_irqrestore(&dev->power.lock, flags);
66
67 return ret;
68}
69
70
71
72
73
74
75
76
77
78
79
80static int apply_constraint(struct dev_pm_qos_request *req,
81 enum pm_qos_req_action action, int value)
82{
83 int ret, curr_value;
84
85 ret = pm_qos_update_target(req->dev->power.constraints,
86 &req->node, action, value);
87
88 if (ret) {
89
90 curr_value = pm_qos_read_value(req->dev->power.constraints);
91 blocking_notifier_call_chain(&dev_pm_notifiers,
92 (unsigned long)curr_value,
93 req);
94 }
95
96 return ret;
97}
98
99
100
101
102
103
104
105
106static int dev_pm_qos_constraints_allocate(struct device *dev)
107{
108 struct pm_qos_constraints *c;
109 struct blocking_notifier_head *n;
110
111 c = kzalloc(sizeof(*c), GFP_KERNEL);
112 if (!c)
113 return -ENOMEM;
114
115 n = kzalloc(sizeof(*n), GFP_KERNEL);
116 if (!n) {
117 kfree(c);
118 return -ENOMEM;
119 }
120 BLOCKING_INIT_NOTIFIER_HEAD(n);
121
122 plist_head_init(&c->list);
123 c->target_value = PM_QOS_DEV_LAT_DEFAULT_VALUE;
124 c->default_value = PM_QOS_DEV_LAT_DEFAULT_VALUE;
125 c->type = PM_QOS_MIN;
126 c->notifiers = n;
127
128 spin_lock_irq(&dev->power.lock);
129 dev->power.constraints = c;
130 spin_unlock_irq(&dev->power.lock);
131
132 return 0;
133}
134
135
136
137
138
139
140
141
142void dev_pm_qos_constraints_init(struct device *dev)
143{
144 mutex_lock(&dev_pm_qos_mtx);
145 dev->power.constraints = NULL;
146 dev->power.power_state = PMSG_ON;
147 mutex_unlock(&dev_pm_qos_mtx);
148}
149
150
151
152
153
154
155
156void dev_pm_qos_constraints_destroy(struct device *dev)
157{
158 struct dev_pm_qos_request *req, *tmp;
159 struct pm_qos_constraints *c;
160
161 mutex_lock(&dev_pm_qos_mtx);
162
163 dev->power.power_state = PMSG_INVALID;
164 c = dev->power.constraints;
165 if (!c)
166 goto out;
167
168
169 plist_for_each_entry_safe(req, tmp, &c->list, node) {
170
171
172
173
174 apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE);
175 memset(req, 0, sizeof(*req));
176 }
177
178 spin_lock_irq(&dev->power.lock);
179 dev->power.constraints = NULL;
180 spin_unlock_irq(&dev->power.lock);
181
182 kfree(c->notifiers);
183 kfree(c);
184
185 out:
186 mutex_unlock(&dev_pm_qos_mtx);
187}
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,
208 s32 value)
209{
210 int ret = 0;
211
212 if (!dev || !req)
213 return -EINVAL;
214
215 if (WARN(dev_pm_qos_request_active(req),
216 "%s() called for already added request\n", __func__))
217 return -EINVAL;
218
219 req->dev = dev;
220
221 mutex_lock(&dev_pm_qos_mtx);
222
223 if (!dev->power.constraints) {
224 if (dev->power.power_state.event == PM_EVENT_INVALID) {
225
226 req->dev = NULL;
227 ret = -ENODEV;
228 goto out;
229 } else {
230
231
232
233
234
235 ret = dev_pm_qos_constraints_allocate(dev);
236 }
237 }
238
239 if (!ret)
240 ret = apply_constraint(req, PM_QOS_ADD_REQ, value);
241
242 out:
243 mutex_unlock(&dev_pm_qos_mtx);
244
245 return ret;
246}
247EXPORT_SYMBOL_GPL(dev_pm_qos_add_request);
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264int dev_pm_qos_update_request(struct dev_pm_qos_request *req,
265 s32 new_value)
266{
267 int ret = 0;
268
269 if (!req)
270 return -EINVAL;
271
272 if (WARN(!dev_pm_qos_request_active(req),
273 "%s() called for unknown object\n", __func__))
274 return -EINVAL;
275
276 mutex_lock(&dev_pm_qos_mtx);
277
278 if (req->dev->power.constraints) {
279 if (new_value != req->node.prio)
280 ret = apply_constraint(req, PM_QOS_UPDATE_REQ,
281 new_value);
282 } else {
283
284 ret = -ENODEV;
285 }
286
287 mutex_unlock(&dev_pm_qos_mtx);
288 return ret;
289}
290EXPORT_SYMBOL_GPL(dev_pm_qos_update_request);
291
292
293
294
295
296
297
298
299
300
301
302
303
304int dev_pm_qos_remove_request(struct dev_pm_qos_request *req)
305{
306 int ret = 0;
307
308 if (!req)
309 return -EINVAL;
310
311 if (WARN(!dev_pm_qos_request_active(req),
312 "%s() called for unknown object\n", __func__))
313 return -EINVAL;
314
315 mutex_lock(&dev_pm_qos_mtx);
316
317 if (req->dev->power.constraints) {
318 ret = apply_constraint(req, PM_QOS_REMOVE_REQ,
319 PM_QOS_DEFAULT_VALUE);
320 memset(req, 0, sizeof(*req));
321 } else {
322
323 ret = -ENODEV;
324 }
325
326 mutex_unlock(&dev_pm_qos_mtx);
327 return ret;
328}
329EXPORT_SYMBOL_GPL(dev_pm_qos_remove_request);
330
331
332
333
334
335
336
337
338
339
340
341int dev_pm_qos_add_notifier(struct device *dev, struct notifier_block *notifier)
342{
343 int retval = 0;
344
345 mutex_lock(&dev_pm_qos_mtx);
346
347
348 if (dev->power.constraints)
349 retval = blocking_notifier_chain_register(
350 dev->power.constraints->notifiers,
351 notifier);
352
353 mutex_unlock(&dev_pm_qos_mtx);
354 return retval;
355}
356EXPORT_SYMBOL_GPL(dev_pm_qos_add_notifier);
357
358
359
360
361
362
363
364
365
366
367
368int dev_pm_qos_remove_notifier(struct device *dev,
369 struct notifier_block *notifier)
370{
371 int retval = 0;
372
373 mutex_lock(&dev_pm_qos_mtx);
374
375
376 if (dev->power.constraints)
377 retval = blocking_notifier_chain_unregister(
378 dev->power.constraints->notifiers,
379 notifier);
380
381 mutex_unlock(&dev_pm_qos_mtx);
382 return retval;
383}
384EXPORT_SYMBOL_GPL(dev_pm_qos_remove_notifier);
385
386
387
388
389
390
391
392
393
394
395int dev_pm_qos_add_global_notifier(struct notifier_block *notifier)
396{
397 return blocking_notifier_chain_register(&dev_pm_notifiers, notifier);
398}
399EXPORT_SYMBOL_GPL(dev_pm_qos_add_global_notifier);
400
401
402
403
404
405
406
407
408
409
410int dev_pm_qos_remove_global_notifier(struct notifier_block *notifier)
411{
412 return blocking_notifier_chain_unregister(&dev_pm_notifiers, notifier);
413}
414EXPORT_SYMBOL_GPL(dev_pm_qos_remove_global_notifier);
415