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#include <linux/pm_qos.h>
33#include <linux/sched.h>
34#include <linux/spinlock.h>
35#include <linux/slab.h>
36#include <linux/time.h>
37#include <linux/fs.h>
38#include <linux/device.h>
39#include <linux/miscdevice.h>
40#include <linux/string.h>
41#include <linux/platform_device.h>
42#include <linux/init.h>
43#include <linux/kernel.h>
44
45#include <linux/uaccess.h>
46#include <linux/export.h>
47
48
49
50
51
52
53struct pm_qos_object {
54 struct pm_qos_constraints *constraints;
55 struct miscdevice pm_qos_power_miscdev;
56 char *name;
57};
58
59static DEFINE_SPINLOCK(pm_qos_lock);
60
61static struct pm_qos_object null_pm_qos;
62
63static BLOCKING_NOTIFIER_HEAD(cpu_dma_lat_notifier);
64static struct pm_qos_constraints cpu_dma_constraints = {
65 .list = PLIST_HEAD_INIT(cpu_dma_constraints.list),
66 .target_value = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE,
67 .default_value = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE,
68 .type = PM_QOS_MIN,
69 .notifiers = &cpu_dma_lat_notifier,
70};
71static struct pm_qos_object cpu_dma_pm_qos = {
72 .constraints = &cpu_dma_constraints,
73 .name = "cpu_dma_latency",
74};
75
76static BLOCKING_NOTIFIER_HEAD(network_lat_notifier);
77static struct pm_qos_constraints network_lat_constraints = {
78 .list = PLIST_HEAD_INIT(network_lat_constraints.list),
79 .target_value = PM_QOS_NETWORK_LAT_DEFAULT_VALUE,
80 .default_value = PM_QOS_NETWORK_LAT_DEFAULT_VALUE,
81 .type = PM_QOS_MIN,
82 .notifiers = &network_lat_notifier,
83};
84static struct pm_qos_object network_lat_pm_qos = {
85 .constraints = &network_lat_constraints,
86 .name = "network_latency",
87};
88
89
90static BLOCKING_NOTIFIER_HEAD(network_throughput_notifier);
91static struct pm_qos_constraints network_tput_constraints = {
92 .list = PLIST_HEAD_INIT(network_tput_constraints.list),
93 .target_value = PM_QOS_NETWORK_THROUGHPUT_DEFAULT_VALUE,
94 .default_value = PM_QOS_NETWORK_THROUGHPUT_DEFAULT_VALUE,
95 .type = PM_QOS_MAX,
96 .notifiers = &network_throughput_notifier,
97};
98static struct pm_qos_object network_throughput_pm_qos = {
99 .constraints = &network_tput_constraints,
100 .name = "network_throughput",
101};
102
103
104static struct pm_qos_object *pm_qos_array[] = {
105 &null_pm_qos,
106 &cpu_dma_pm_qos,
107 &network_lat_pm_qos,
108 &network_throughput_pm_qos
109};
110
111static ssize_t pm_qos_power_write(struct file *filp, const char __user *buf,
112 size_t count, loff_t *f_pos);
113static ssize_t pm_qos_power_read(struct file *filp, char __user *buf,
114 size_t count, loff_t *f_pos);
115static int pm_qos_power_open(struct inode *inode, struct file *filp);
116static int pm_qos_power_release(struct inode *inode, struct file *filp);
117
118static const struct file_operations pm_qos_power_fops = {
119 .write = pm_qos_power_write,
120 .read = pm_qos_power_read,
121 .open = pm_qos_power_open,
122 .release = pm_qos_power_release,
123 .llseek = noop_llseek,
124};
125
126
127static inline int pm_qos_get_value(struct pm_qos_constraints *c)
128{
129 if (plist_head_empty(&c->list))
130 return c->default_value;
131
132 switch (c->type) {
133 case PM_QOS_MIN:
134 return plist_first(&c->list)->prio;
135
136 case PM_QOS_MAX:
137 return plist_last(&c->list)->prio;
138
139 default:
140
141 BUG();
142 }
143}
144
145s32 pm_qos_read_value(struct pm_qos_constraints *c)
146{
147 return c->target_value;
148}
149
150static inline void pm_qos_set_value(struct pm_qos_constraints *c, s32 value)
151{
152 c->target_value = value;
153}
154
155
156
157
158
159
160
161
162
163
164
165
166int pm_qos_update_target(struct pm_qos_constraints *c, struct plist_node *node,
167 enum pm_qos_req_action action, int value)
168{
169 unsigned long flags;
170 int prev_value, curr_value, new_value;
171
172 spin_lock_irqsave(&pm_qos_lock, flags);
173 prev_value = pm_qos_get_value(c);
174 if (value == PM_QOS_DEFAULT_VALUE)
175 new_value = c->default_value;
176 else
177 new_value = value;
178
179 switch (action) {
180 case PM_QOS_REMOVE_REQ:
181 plist_del(node, &c->list);
182 break;
183 case PM_QOS_UPDATE_REQ:
184
185
186
187
188
189 plist_del(node, &c->list);
190 case PM_QOS_ADD_REQ:
191 plist_node_init(node, new_value);
192 plist_add(node, &c->list);
193 break;
194 default:
195
196 ;
197 }
198
199 curr_value = pm_qos_get_value(c);
200 pm_qos_set_value(c, curr_value);
201
202 spin_unlock_irqrestore(&pm_qos_lock, flags);
203
204 if (prev_value != curr_value) {
205 blocking_notifier_call_chain(c->notifiers,
206 (unsigned long)curr_value,
207 NULL);
208 return 1;
209 } else {
210 return 0;
211 }
212}
213
214
215
216
217
218
219
220int pm_qos_request(int pm_qos_class)
221{
222 return pm_qos_read_value(pm_qos_array[pm_qos_class]->constraints);
223}
224EXPORT_SYMBOL_GPL(pm_qos_request);
225
226int pm_qos_request_active(struct pm_qos_request *req)
227{
228 return req->pm_qos_class != 0;
229}
230EXPORT_SYMBOL_GPL(pm_qos_request_active);
231
232
233
234
235
236
237
238static void pm_qos_work_fn(struct work_struct *work)
239{
240 struct pm_qos_request *req = container_of(to_delayed_work(work),
241 struct pm_qos_request,
242 work);
243
244 pm_qos_update_request(req, PM_QOS_DEFAULT_VALUE);
245}
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260void pm_qos_add_request(struct pm_qos_request *req,
261 int pm_qos_class, s32 value)
262{
263 if (!req)
264 return;
265
266 if (pm_qos_request_active(req)) {
267 WARN(1, KERN_ERR "pm_qos_add_request() called for already added request\n");
268 return;
269 }
270 req->pm_qos_class = pm_qos_class;
271 INIT_DELAYED_WORK(&req->work, pm_qos_work_fn);
272 pm_qos_update_target(pm_qos_array[pm_qos_class]->constraints,
273 &req->node, PM_QOS_ADD_REQ, value);
274}
275EXPORT_SYMBOL_GPL(pm_qos_add_request);
276
277
278
279
280
281
282
283
284
285
286
287void pm_qos_update_request(struct pm_qos_request *req,
288 s32 new_value)
289{
290 if (!req)
291 return;
292
293 if (!pm_qos_request_active(req)) {
294 WARN(1, KERN_ERR "pm_qos_update_request() called for unknown object\n");
295 return;
296 }
297
298 if (delayed_work_pending(&req->work))
299 cancel_delayed_work_sync(&req->work);
300
301 if (new_value != req->node.prio)
302 pm_qos_update_target(
303 pm_qos_array[req->pm_qos_class]->constraints,
304 &req->node, PM_QOS_UPDATE_REQ, new_value);
305}
306EXPORT_SYMBOL_GPL(pm_qos_update_request);
307
308
309
310
311
312
313
314
315
316void pm_qos_update_request_timeout(struct pm_qos_request *req, s32 new_value,
317 unsigned long timeout_us)
318{
319 if (!req)
320 return;
321 if (WARN(!pm_qos_request_active(req),
322 "%s called for unknown object.", __func__))
323 return;
324
325 if (delayed_work_pending(&req->work))
326 cancel_delayed_work_sync(&req->work);
327
328 if (new_value != req->node.prio)
329 pm_qos_update_target(
330 pm_qos_array[req->pm_qos_class]->constraints,
331 &req->node, PM_QOS_UPDATE_REQ, new_value);
332
333 schedule_delayed_work(&req->work, usecs_to_jiffies(timeout_us));
334}
335
336
337
338
339
340
341
342
343
344void pm_qos_remove_request(struct pm_qos_request *req)
345{
346 if (!req)
347 return;
348
349
350 if (!pm_qos_request_active(req)) {
351 WARN(1, KERN_ERR "pm_qos_remove_request() called for unknown object\n");
352 return;
353 }
354
355 if (delayed_work_pending(&req->work))
356 cancel_delayed_work_sync(&req->work);
357
358 pm_qos_update_target(pm_qos_array[req->pm_qos_class]->constraints,
359 &req->node, PM_QOS_REMOVE_REQ,
360 PM_QOS_DEFAULT_VALUE);
361 memset(req, 0, sizeof(*req));
362}
363EXPORT_SYMBOL_GPL(pm_qos_remove_request);
364
365
366
367
368
369
370
371
372
373int pm_qos_add_notifier(int pm_qos_class, struct notifier_block *notifier)
374{
375 int retval;
376
377 retval = blocking_notifier_chain_register(
378 pm_qos_array[pm_qos_class]->constraints->notifiers,
379 notifier);
380
381 return retval;
382}
383EXPORT_SYMBOL_GPL(pm_qos_add_notifier);
384
385
386
387
388
389
390
391
392
393int pm_qos_remove_notifier(int pm_qos_class, struct notifier_block *notifier)
394{
395 int retval;
396
397 retval = blocking_notifier_chain_unregister(
398 pm_qos_array[pm_qos_class]->constraints->notifiers,
399 notifier);
400
401 return retval;
402}
403EXPORT_SYMBOL_GPL(pm_qos_remove_notifier);
404
405
406static int register_pm_qos_misc(struct pm_qos_object *qos)
407{
408 qos->pm_qos_power_miscdev.minor = MISC_DYNAMIC_MINOR;
409 qos->pm_qos_power_miscdev.name = qos->name;
410 qos->pm_qos_power_miscdev.fops = &pm_qos_power_fops;
411
412 return misc_register(&qos->pm_qos_power_miscdev);
413}
414
415static int find_pm_qos_object_by_minor(int minor)
416{
417 int pm_qos_class;
418
419 for (pm_qos_class = 0;
420 pm_qos_class < PM_QOS_NUM_CLASSES; pm_qos_class++) {
421 if (minor ==
422 pm_qos_array[pm_qos_class]->pm_qos_power_miscdev.minor)
423 return pm_qos_class;
424 }
425 return -1;
426}
427
428static int pm_qos_power_open(struct inode *inode, struct file *filp)
429{
430 long pm_qos_class;
431
432 pm_qos_class = find_pm_qos_object_by_minor(iminor(inode));
433 if (pm_qos_class >= 0) {
434 struct pm_qos_request *req = kzalloc(sizeof(*req), GFP_KERNEL);
435 if (!req)
436 return -ENOMEM;
437
438 pm_qos_add_request(req, pm_qos_class, PM_QOS_DEFAULT_VALUE);
439 filp->private_data = req;
440
441 return 0;
442 }
443 return -EPERM;
444}
445
446static int pm_qos_power_release(struct inode *inode, struct file *filp)
447{
448 struct pm_qos_request *req;
449
450 req = filp->private_data;
451 pm_qos_remove_request(req);
452 kfree(req);
453
454 return 0;
455}
456
457
458static ssize_t pm_qos_power_read(struct file *filp, char __user *buf,
459 size_t count, loff_t *f_pos)
460{
461 s32 value;
462 unsigned long flags;
463 struct pm_qos_request *req = filp->private_data;
464
465 if (!req)
466 return -EINVAL;
467 if (!pm_qos_request_active(req))
468 return -EINVAL;
469
470 spin_lock_irqsave(&pm_qos_lock, flags);
471 value = pm_qos_get_value(pm_qos_array[req->pm_qos_class]->constraints);
472 spin_unlock_irqrestore(&pm_qos_lock, flags);
473
474 return simple_read_from_buffer(buf, count, f_pos, &value, sizeof(s32));
475}
476
477static ssize_t pm_qos_power_write(struct file *filp, const char __user *buf,
478 size_t count, loff_t *f_pos)
479{
480 s32 value;
481 struct pm_qos_request *req;
482
483 if (count == sizeof(s32)) {
484 if (copy_from_user(&value, buf, sizeof(s32)))
485 return -EFAULT;
486 } else if (count <= 11) {
487 char ascii_value[11];
488 unsigned long int ulval;
489 int ret;
490
491 if (copy_from_user(ascii_value, buf, count))
492 return -EFAULT;
493
494 if (count > 10) {
495 if (ascii_value[10] == '\n')
496 ascii_value[10] = '\0';
497 else
498 return -EINVAL;
499 } else {
500 ascii_value[count] = '\0';
501 }
502 ret = strict_strtoul(ascii_value, 16, &ulval);
503 if (ret) {
504 pr_debug("%s, 0x%lx, 0x%x\n", ascii_value, ulval, ret);
505 return -EINVAL;
506 }
507 value = (s32)lower_32_bits(ulval);
508 } else {
509 return -EINVAL;
510 }
511
512 req = filp->private_data;
513 pm_qos_update_request(req, value);
514
515 return count;
516}
517
518
519static int __init pm_qos_power_init(void)
520{
521 int ret = 0;
522 int i;
523
524 BUILD_BUG_ON(ARRAY_SIZE(pm_qos_array) != PM_QOS_NUM_CLASSES);
525
526 for (i = 1; i < PM_QOS_NUM_CLASSES; i++) {
527 ret = register_pm_qos_misc(pm_qos_array[i]);
528 if (ret < 0) {
529 printk(KERN_ERR "pm_qos_param: %s setup failed\n",
530 pm_qos_array[i]->name);
531 return ret;
532 }
533 }
534
535 return ret;
536}
537
538late_initcall(pm_qos_power_init);
539