aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/pm_qos_params.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/pm_qos_params.c')
-rw-r--r--kernel/pm_qos_params.c101
1 files changed, 76 insertions, 25 deletions
diff --git a/kernel/pm_qos_params.c b/kernel/pm_qos_params.c
index 645e541a45f6..6824ca7d4d0c 100644
--- a/kernel/pm_qos_params.c
+++ b/kernel/pm_qos_params.c
@@ -40,6 +40,7 @@
40#include <linux/string.h> 40#include <linux/string.h>
41#include <linux/platform_device.h> 41#include <linux/platform_device.h>
42#include <linux/init.h> 42#include <linux/init.h>
43#include <linux/kernel.h>
43 44
44#include <linux/uaccess.h> 45#include <linux/uaccess.h>
45 46
@@ -53,11 +54,17 @@ enum pm_qos_type {
53 PM_QOS_MIN /* return the smallest value */ 54 PM_QOS_MIN /* return the smallest value */
54}; 55};
55 56
57/*
58 * Note: The lockless read path depends on the CPU accessing
59 * target_value atomically. Atomic access is only guaranteed on all CPU
60 * types linux supports for 32 bit quantites
61 */
56struct pm_qos_object { 62struct pm_qos_object {
57 struct plist_head requests; 63 struct plist_head requests;
58 struct blocking_notifier_head *notifiers; 64 struct blocking_notifier_head *notifiers;
59 struct miscdevice pm_qos_power_miscdev; 65 struct miscdevice pm_qos_power_miscdev;
60 char *name; 66 char *name;
67 s32 target_value; /* Do not change to 64 bit */
61 s32 default_value; 68 s32 default_value;
62 enum pm_qos_type type; 69 enum pm_qos_type type;
63}; 70};
@@ -70,7 +77,8 @@ static struct pm_qos_object cpu_dma_pm_qos = {
70 .requests = PLIST_HEAD_INIT(cpu_dma_pm_qos.requests, pm_qos_lock), 77 .requests = PLIST_HEAD_INIT(cpu_dma_pm_qos.requests, pm_qos_lock),
71 .notifiers = &cpu_dma_lat_notifier, 78 .notifiers = &cpu_dma_lat_notifier,
72 .name = "cpu_dma_latency", 79 .name = "cpu_dma_latency",
73 .default_value = 2000 * USEC_PER_SEC, 80 .target_value = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE,
81 .default_value = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE,
74 .type = PM_QOS_MIN, 82 .type = PM_QOS_MIN,
75}; 83};
76 84
@@ -79,7 +87,8 @@ static struct pm_qos_object network_lat_pm_qos = {
79 .requests = PLIST_HEAD_INIT(network_lat_pm_qos.requests, pm_qos_lock), 87 .requests = PLIST_HEAD_INIT(network_lat_pm_qos.requests, pm_qos_lock),
80 .notifiers = &network_lat_notifier, 88 .notifiers = &network_lat_notifier,
81 .name = "network_latency", 89 .name = "network_latency",
82 .default_value = 2000 * USEC_PER_SEC, 90 .target_value = PM_QOS_NETWORK_LAT_DEFAULT_VALUE,
91 .default_value = PM_QOS_NETWORK_LAT_DEFAULT_VALUE,
83 .type = PM_QOS_MIN 92 .type = PM_QOS_MIN
84}; 93};
85 94
@@ -89,7 +98,8 @@ static struct pm_qos_object network_throughput_pm_qos = {
89 .requests = PLIST_HEAD_INIT(network_throughput_pm_qos.requests, pm_qos_lock), 98 .requests = PLIST_HEAD_INIT(network_throughput_pm_qos.requests, pm_qos_lock),
90 .notifiers = &network_throughput_notifier, 99 .notifiers = &network_throughput_notifier,
91 .name = "network_throughput", 100 .name = "network_throughput",
92 .default_value = 0, 101 .target_value = PM_QOS_NETWORK_THROUGHPUT_DEFAULT_VALUE,
102 .default_value = PM_QOS_NETWORK_THROUGHPUT_DEFAULT_VALUE,
93 .type = PM_QOS_MAX, 103 .type = PM_QOS_MAX,
94}; 104};
95 105
@@ -103,13 +113,17 @@ static struct pm_qos_object *pm_qos_array[] = {
103 113
104static ssize_t pm_qos_power_write(struct file *filp, const char __user *buf, 114static ssize_t pm_qos_power_write(struct file *filp, const char __user *buf,
105 size_t count, loff_t *f_pos); 115 size_t count, loff_t *f_pos);
116static ssize_t pm_qos_power_read(struct file *filp, char __user *buf,
117 size_t count, loff_t *f_pos);
106static int pm_qos_power_open(struct inode *inode, struct file *filp); 118static int pm_qos_power_open(struct inode *inode, struct file *filp);
107static int pm_qos_power_release(struct inode *inode, struct file *filp); 119static int pm_qos_power_release(struct inode *inode, struct file *filp);
108 120
109static const struct file_operations pm_qos_power_fops = { 121static const struct file_operations pm_qos_power_fops = {
110 .write = pm_qos_power_write, 122 .write = pm_qos_power_write,
123 .read = pm_qos_power_read,
111 .open = pm_qos_power_open, 124 .open = pm_qos_power_open,
112 .release = pm_qos_power_release, 125 .release = pm_qos_power_release,
126 .llseek = noop_llseek,
113}; 127};
114 128
115/* unlocked internal variant */ 129/* unlocked internal variant */
@@ -120,10 +134,10 @@ static inline int pm_qos_get_value(struct pm_qos_object *o)
120 134
121 switch (o->type) { 135 switch (o->type) {
122 case PM_QOS_MIN: 136 case PM_QOS_MIN:
123 return plist_last(&o->requests)->prio; 137 return plist_first(&o->requests)->prio;
124 138
125 case PM_QOS_MAX: 139 case PM_QOS_MAX:
126 return plist_first(&o->requests)->prio; 140 return plist_last(&o->requests)->prio;
127 141
128 default: 142 default:
129 /* runtime check for not using enum */ 143 /* runtime check for not using enum */
@@ -131,6 +145,16 @@ static inline int pm_qos_get_value(struct pm_qos_object *o)
131 } 145 }
132} 146}
133 147
148static inline s32 pm_qos_read_value(struct pm_qos_object *o)
149{
150 return o->target_value;
151}
152
153static inline void pm_qos_set_value(struct pm_qos_object *o, s32 value)
154{
155 o->target_value = value;
156}
157
134static void update_target(struct pm_qos_object *o, struct plist_node *node, 158static void update_target(struct pm_qos_object *o, struct plist_node *node,
135 int del, int value) 159 int del, int value)
136{ 160{
@@ -155,6 +179,7 @@ static void update_target(struct pm_qos_object *o, struct plist_node *node,
155 plist_add(node, &o->requests); 179 plist_add(node, &o->requests);
156 } 180 }
157 curr_value = pm_qos_get_value(o); 181 curr_value = pm_qos_get_value(o);
182 pm_qos_set_value(o, curr_value);
158 spin_unlock_irqrestore(&pm_qos_lock, flags); 183 spin_unlock_irqrestore(&pm_qos_lock, flags);
159 184
160 if (prev_value != curr_value) 185 if (prev_value != curr_value)
@@ -189,18 +214,11 @@ static int find_pm_qos_object_by_minor(int minor)
189 * pm_qos_request - returns current system wide qos expectation 214 * pm_qos_request - returns current system wide qos expectation
190 * @pm_qos_class: identification of which qos value is requested 215 * @pm_qos_class: identification of which qos value is requested
191 * 216 *
192 * This function returns the current target value in an atomic manner. 217 * This function returns the current target value.
193 */ 218 */
194int pm_qos_request(int pm_qos_class) 219int pm_qos_request(int pm_qos_class)
195{ 220{
196 unsigned long flags; 221 return pm_qos_read_value(pm_qos_array[pm_qos_class]);
197 int value;
198
199 spin_lock_irqsave(&pm_qos_lock, flags);
200 value = pm_qos_get_value(pm_qos_array[pm_qos_class]);
201 spin_unlock_irqrestore(&pm_qos_lock, flags);
202
203 return value;
204} 222}
205EXPORT_SYMBOL_GPL(pm_qos_request); 223EXPORT_SYMBOL_GPL(pm_qos_request);
206 224
@@ -375,30 +393,63 @@ static int pm_qos_power_release(struct inode *inode, struct file *filp)
375} 393}
376 394
377 395
396static ssize_t pm_qos_power_read(struct file *filp, char __user *buf,
397 size_t count, loff_t *f_pos)
398{
399 s32 value;
400 unsigned long flags;
401 struct pm_qos_object *o;
402 struct pm_qos_request_list *pm_qos_req = filp->private_data;
403
404 if (!pm_qos_req)
405 return -EINVAL;
406 if (!pm_qos_request_active(pm_qos_req))
407 return -EINVAL;
408
409 o = pm_qos_array[pm_qos_req->pm_qos_class];
410 spin_lock_irqsave(&pm_qos_lock, flags);
411 value = pm_qos_get_value(o);
412 spin_unlock_irqrestore(&pm_qos_lock, flags);
413
414 return simple_read_from_buffer(buf, count, f_pos, &value, sizeof(s32));
415}
416
378static ssize_t pm_qos_power_write(struct file *filp, const char __user *buf, 417static ssize_t pm_qos_power_write(struct file *filp, const char __user *buf,
379 size_t count, loff_t *f_pos) 418 size_t count, loff_t *f_pos)
380{ 419{
381 s32 value; 420 s32 value;
382 int x;
383 char ascii_value[11];
384 struct pm_qos_request_list *pm_qos_req; 421 struct pm_qos_request_list *pm_qos_req;
385 422
386 if (count == sizeof(s32)) { 423 if (count == sizeof(s32)) {
387 if (copy_from_user(&value, buf, sizeof(s32))) 424 if (copy_from_user(&value, buf, sizeof(s32)))
388 return -EFAULT; 425 return -EFAULT;
389 } else if (count == 11) { /* len('0x12345678/0') */ 426 } else if (count <= 11) { /* ASCII perhaps? */
390 if (copy_from_user(ascii_value, buf, 11)) 427 char ascii_value[11];
428 unsigned long int ulval;
429 int ret;
430
431 if (copy_from_user(ascii_value, buf, count))
391 return -EFAULT; 432 return -EFAULT;
392 if (strlen(ascii_value) != 10) 433
393 return -EINVAL; 434 if (count > 10) {
394 x = sscanf(ascii_value, "%x", &value); 435 if (ascii_value[10] == '\n')
395 if (x != 1) 436 ascii_value[10] = '\0';
437 else
438 return -EINVAL;
439 } else {
440 ascii_value[count] = '\0';
441 }
442 ret = strict_strtoul(ascii_value, 16, &ulval);
443 if (ret) {
444 pr_debug("%s, 0x%lx, 0x%x\n", ascii_value, ulval, ret);
396 return -EINVAL; 445 return -EINVAL;
397 pr_debug("%s, %d, 0x%x\n", ascii_value, x, value); 446 }
398 } else 447 value = (s32)lower_32_bits(ulval);
448 } else {
399 return -EINVAL; 449 return -EINVAL;
450 }
400 451
401 pm_qos_req = (struct pm_qos_request_list *)filp->private_data; 452 pm_qos_req = filp->private_data;
402 pm_qos_update_request(pm_qos_req, value); 453 pm_qos_update_request(pm_qos_req, value);
403 454
404 return count; 455 return count;