diff options
-rw-r--r-- | Documentation/ABI/testing/sysfs-class-devfreq | 8 | ||||
-rw-r--r-- | drivers/devfreq/Kconfig | 36 | ||||
-rw-r--r-- | drivers/devfreq/Makefile | 4 | ||||
-rw-r--r-- | drivers/devfreq/governor_performance.c | 29 | ||||
-rw-r--r-- | drivers/devfreq/governor_powersave.c | 29 | ||||
-rw-r--r-- | drivers/devfreq/governor_simpleondemand.c | 88 | ||||
-rw-r--r-- | drivers/devfreq/governor_userspace.c | 116 | ||||
-rw-r--r-- | include/linux/devfreq.h | 35 |
8 files changed, 345 insertions, 0 deletions
diff --git a/Documentation/ABI/testing/sysfs-class-devfreq b/Documentation/ABI/testing/sysfs-class-devfreq index 0ec855f479ce..23d78b5aab11 100644 --- a/Documentation/ABI/testing/sysfs-class-devfreq +++ b/Documentation/ABI/testing/sysfs-class-devfreq | |||
@@ -42,3 +42,11 @@ Description: | |||
42 | devfreq-provided central polling | 42 | devfreq-provided central polling |
43 | (/sys/class/devfreq/.../central_polling is 0), this value | 43 | (/sys/class/devfreq/.../central_polling is 0), this value |
44 | may be useless. | 44 | may be useless. |
45 | |||
46 | What: /sys/class/devfreq/.../userspace/set_freq | ||
47 | Date: September 2011 | ||
48 | Contact: MyungJoo Ham <myungjoo.ham@samsung.com> | ||
49 | Description: | ||
50 | The /sys/class/devfreq/.../userspace/set_freq shows and | ||
51 | sets the requested frequency for the devfreq object if | ||
52 | userspace governor is in effect. | ||
diff --git a/drivers/devfreq/Kconfig b/drivers/devfreq/Kconfig index 1fb42de4f420..643b055ed3cd 100644 --- a/drivers/devfreq/Kconfig +++ b/drivers/devfreq/Kconfig | |||
@@ -34,6 +34,42 @@ menuconfig PM_DEVFREQ | |||
34 | 34 | ||
35 | if PM_DEVFREQ | 35 | if PM_DEVFREQ |
36 | 36 | ||
37 | comment "DEVFREQ Governors" | ||
38 | |||
39 | config DEVFREQ_GOV_SIMPLE_ONDEMAND | ||
40 | bool "Simple Ondemand" | ||
41 | help | ||
42 | Chooses frequency based on the recent load on the device. Works | ||
43 | similar as ONDEMAND governor of CPUFREQ does. A device with | ||
44 | Simple-Ondemand should be able to provide busy/total counter | ||
45 | values that imply the usage rate. A device may provide tuned | ||
46 | values to the governor with data field at devfreq_add_device(). | ||
47 | |||
48 | config DEVFREQ_GOV_PERFORMANCE | ||
49 | bool "Performance" | ||
50 | help | ||
51 | Sets the frequency at the maximum available frequency. | ||
52 | This governor always returns UINT_MAX as frequency so that | ||
53 | the DEVFREQ framework returns the highest frequency available | ||
54 | at any time. | ||
55 | |||
56 | config DEVFREQ_GOV_POWERSAVE | ||
57 | bool "Powersave" | ||
58 | help | ||
59 | Sets the frequency at the minimum available frequency. | ||
60 | This governor always returns 0 as frequency so that | ||
61 | the DEVFREQ framework returns the lowest frequency available | ||
62 | at any time. | ||
63 | |||
64 | config DEVFREQ_GOV_USERSPACE | ||
65 | bool "Userspace" | ||
66 | help | ||
67 | Sets the frequency at the user specified one. | ||
68 | This governor returns the user configured frequency if there | ||
69 | has been an input to /sys/devices/.../power/devfreq_set_freq. | ||
70 | Otherwise, the governor does not change the frequnecy | ||
71 | given at the initialization. | ||
72 | |||
37 | comment "DEVFREQ Drivers" | 73 | comment "DEVFREQ Drivers" |
38 | 74 | ||
39 | endif # PM_DEVFREQ | 75 | endif # PM_DEVFREQ |
diff --git a/drivers/devfreq/Makefile b/drivers/devfreq/Makefile index 168934a12b38..4564a89e970a 100644 --- a/drivers/devfreq/Makefile +++ b/drivers/devfreq/Makefile | |||
@@ -1 +1,5 @@ | |||
1 | obj-$(CONFIG_PM_DEVFREQ) += devfreq.o | 1 | obj-$(CONFIG_PM_DEVFREQ) += devfreq.o |
2 | obj-$(CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND) += governor_simpleondemand.o | ||
3 | obj-$(CONFIG_DEVFREQ_GOV_PERFORMANCE) += governor_performance.o | ||
4 | obj-$(CONFIG_DEVFREQ_GOV_POWERSAVE) += governor_powersave.o | ||
5 | obj-$(CONFIG_DEVFREQ_GOV_USERSPACE) += governor_userspace.o | ||
diff --git a/drivers/devfreq/governor_performance.c b/drivers/devfreq/governor_performance.c new file mode 100644 index 000000000000..c0596b291761 --- /dev/null +++ b/drivers/devfreq/governor_performance.c | |||
@@ -0,0 +1,29 @@ | |||
1 | /* | ||
2 | * linux/drivers/devfreq/governor_performance.c | ||
3 | * | ||
4 | * Copyright (C) 2011 Samsung Electronics | ||
5 | * MyungJoo Ham <myungjoo.ham@samsung.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | |||
12 | #include <linux/devfreq.h> | ||
13 | |||
14 | static int devfreq_performance_func(struct devfreq *df, | ||
15 | unsigned long *freq) | ||
16 | { | ||
17 | /* | ||
18 | * target callback should be able to get floor value as | ||
19 | * said in devfreq.h | ||
20 | */ | ||
21 | *freq = UINT_MAX; | ||
22 | return 0; | ||
23 | } | ||
24 | |||
25 | const struct devfreq_governor devfreq_performance = { | ||
26 | .name = "performance", | ||
27 | .get_target_freq = devfreq_performance_func, | ||
28 | .no_central_polling = true, | ||
29 | }; | ||
diff --git a/drivers/devfreq/governor_powersave.c b/drivers/devfreq/governor_powersave.c new file mode 100644 index 000000000000..2483a85a266f --- /dev/null +++ b/drivers/devfreq/governor_powersave.c | |||
@@ -0,0 +1,29 @@ | |||
1 | /* | ||
2 | * linux/drivers/devfreq/governor_powersave.c | ||
3 | * | ||
4 | * Copyright (C) 2011 Samsung Electronics | ||
5 | * MyungJoo Ham <myungjoo.ham@samsung.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | |||
12 | #include <linux/devfreq.h> | ||
13 | |||
14 | static int devfreq_powersave_func(struct devfreq *df, | ||
15 | unsigned long *freq) | ||
16 | { | ||
17 | /* | ||
18 | * target callback should be able to get ceiling value as | ||
19 | * said in devfreq.h | ||
20 | */ | ||
21 | *freq = 0; | ||
22 | return 0; | ||
23 | } | ||
24 | |||
25 | const struct devfreq_governor devfreq_powersave = { | ||
26 | .name = "powersave", | ||
27 | .get_target_freq = devfreq_powersave_func, | ||
28 | .no_central_polling = true, | ||
29 | }; | ||
diff --git a/drivers/devfreq/governor_simpleondemand.c b/drivers/devfreq/governor_simpleondemand.c new file mode 100644 index 000000000000..efad8dcf9028 --- /dev/null +++ b/drivers/devfreq/governor_simpleondemand.c | |||
@@ -0,0 +1,88 @@ | |||
1 | /* | ||
2 | * linux/drivers/devfreq/governor_simpleondemand.c | ||
3 | * | ||
4 | * Copyright (C) 2011 Samsung Electronics | ||
5 | * MyungJoo Ham <myungjoo.ham@samsung.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | |||
12 | #include <linux/errno.h> | ||
13 | #include <linux/devfreq.h> | ||
14 | #include <linux/math64.h> | ||
15 | |||
16 | /* Default constants for DevFreq-Simple-Ondemand (DFSO) */ | ||
17 | #define DFSO_UPTHRESHOLD (90) | ||
18 | #define DFSO_DOWNDIFFERENCTIAL (5) | ||
19 | static int devfreq_simple_ondemand_func(struct devfreq *df, | ||
20 | unsigned long *freq) | ||
21 | { | ||
22 | struct devfreq_dev_status stat; | ||
23 | int err = df->profile->get_dev_status(df->dev.parent, &stat); | ||
24 | unsigned long long a, b; | ||
25 | unsigned int dfso_upthreshold = DFSO_UPTHRESHOLD; | ||
26 | unsigned int dfso_downdifferential = DFSO_DOWNDIFFERENCTIAL; | ||
27 | struct devfreq_simple_ondemand_data *data = df->data; | ||
28 | |||
29 | if (err) | ||
30 | return err; | ||
31 | |||
32 | if (data) { | ||
33 | if (data->upthreshold) | ||
34 | dfso_upthreshold = data->upthreshold; | ||
35 | if (data->downdifferential) | ||
36 | dfso_downdifferential = data->downdifferential; | ||
37 | } | ||
38 | if (dfso_upthreshold > 100 || | ||
39 | dfso_upthreshold < dfso_downdifferential) | ||
40 | return -EINVAL; | ||
41 | |||
42 | /* Assume MAX if it is going to be divided by zero */ | ||
43 | if (stat.total_time == 0) { | ||
44 | *freq = UINT_MAX; | ||
45 | return 0; | ||
46 | } | ||
47 | |||
48 | /* Prevent overflow */ | ||
49 | if (stat.busy_time >= (1 << 24) || stat.total_time >= (1 << 24)) { | ||
50 | stat.busy_time >>= 7; | ||
51 | stat.total_time >>= 7; | ||
52 | } | ||
53 | |||
54 | /* Set MAX if it's busy enough */ | ||
55 | if (stat.busy_time * 100 > | ||
56 | stat.total_time * dfso_upthreshold) { | ||
57 | *freq = UINT_MAX; | ||
58 | return 0; | ||
59 | } | ||
60 | |||
61 | /* Set MAX if we do not know the initial frequency */ | ||
62 | if (stat.current_frequency == 0) { | ||
63 | *freq = UINT_MAX; | ||
64 | return 0; | ||
65 | } | ||
66 | |||
67 | /* Keep the current frequency */ | ||
68 | if (stat.busy_time * 100 > | ||
69 | stat.total_time * (dfso_upthreshold - dfso_downdifferential)) { | ||
70 | *freq = stat.current_frequency; | ||
71 | return 0; | ||
72 | } | ||
73 | |||
74 | /* Set the desired frequency based on the load */ | ||
75 | a = stat.busy_time; | ||
76 | a *= stat.current_frequency; | ||
77 | b = div_u64(a, stat.total_time); | ||
78 | b *= 100; | ||
79 | b = div_u64(b, (dfso_upthreshold - dfso_downdifferential / 2)); | ||
80 | *freq = (unsigned long) b; | ||
81 | |||
82 | return 0; | ||
83 | } | ||
84 | |||
85 | const struct devfreq_governor devfreq_simple_ondemand = { | ||
86 | .name = "simple_ondemand", | ||
87 | .get_target_freq = devfreq_simple_ondemand_func, | ||
88 | }; | ||
diff --git a/drivers/devfreq/governor_userspace.c b/drivers/devfreq/governor_userspace.c new file mode 100644 index 000000000000..4f8b563da782 --- /dev/null +++ b/drivers/devfreq/governor_userspace.c | |||
@@ -0,0 +1,116 @@ | |||
1 | /* | ||
2 | * linux/drivers/devfreq/governor_simpleondemand.c | ||
3 | * | ||
4 | * Copyright (C) 2011 Samsung Electronics | ||
5 | * MyungJoo Ham <myungjoo.ham@samsung.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | |||
12 | #include <linux/slab.h> | ||
13 | #include <linux/device.h> | ||
14 | #include <linux/devfreq.h> | ||
15 | #include <linux/pm.h> | ||
16 | #include <linux/mutex.h> | ||
17 | #include "governor.h" | ||
18 | |||
19 | struct userspace_data { | ||
20 | unsigned long user_frequency; | ||
21 | bool valid; | ||
22 | }; | ||
23 | |||
24 | static int devfreq_userspace_func(struct devfreq *df, unsigned long *freq) | ||
25 | { | ||
26 | struct userspace_data *data = df->data; | ||
27 | |||
28 | if (!data->valid) | ||
29 | *freq = df->previous_freq; /* No user freq specified yet */ | ||
30 | else | ||
31 | *freq = data->user_frequency; | ||
32 | return 0; | ||
33 | } | ||
34 | |||
35 | static ssize_t store_freq(struct device *dev, struct device_attribute *attr, | ||
36 | const char *buf, size_t count) | ||
37 | { | ||
38 | struct devfreq *devfreq = to_devfreq(dev); | ||
39 | struct userspace_data *data; | ||
40 | unsigned long wanted; | ||
41 | int err = 0; | ||
42 | |||
43 | |||
44 | mutex_lock(&devfreq->lock); | ||
45 | data = devfreq->data; | ||
46 | |||
47 | sscanf(buf, "%lu", &wanted); | ||
48 | data->user_frequency = wanted; | ||
49 | data->valid = true; | ||
50 | err = update_devfreq(devfreq); | ||
51 | if (err == 0) | ||
52 | err = count; | ||
53 | mutex_unlock(&devfreq->lock); | ||
54 | return err; | ||
55 | } | ||
56 | |||
57 | static ssize_t show_freq(struct device *dev, struct device_attribute *attr, | ||
58 | char *buf) | ||
59 | { | ||
60 | struct devfreq *devfreq = to_devfreq(dev); | ||
61 | struct userspace_data *data; | ||
62 | int err = 0; | ||
63 | |||
64 | mutex_lock(&devfreq->lock); | ||
65 | data = devfreq->data; | ||
66 | |||
67 | if (data->valid) | ||
68 | err = sprintf(buf, "%lu\n", data->user_frequency); | ||
69 | else | ||
70 | err = sprintf(buf, "undefined\n"); | ||
71 | mutex_unlock(&devfreq->lock); | ||
72 | return err; | ||
73 | } | ||
74 | |||
75 | static DEVICE_ATTR(set_freq, 0644, show_freq, store_freq); | ||
76 | static struct attribute *dev_entries[] = { | ||
77 | &dev_attr_set_freq.attr, | ||
78 | NULL, | ||
79 | }; | ||
80 | static struct attribute_group dev_attr_group = { | ||
81 | .name = "userspace", | ||
82 | .attrs = dev_entries, | ||
83 | }; | ||
84 | |||
85 | static int userspace_init(struct devfreq *devfreq) | ||
86 | { | ||
87 | int err = 0; | ||
88 | struct userspace_data *data = kzalloc(sizeof(struct userspace_data), | ||
89 | GFP_KERNEL); | ||
90 | |||
91 | if (!data) { | ||
92 | err = -ENOMEM; | ||
93 | goto out; | ||
94 | } | ||
95 | data->valid = false; | ||
96 | devfreq->data = data; | ||
97 | |||
98 | err = sysfs_create_group(&devfreq->dev.kobj, &dev_attr_group); | ||
99 | out: | ||
100 | return err; | ||
101 | } | ||
102 | |||
103 | static void userspace_exit(struct devfreq *devfreq) | ||
104 | { | ||
105 | sysfs_remove_group(&devfreq->dev.kobj, &dev_attr_group); | ||
106 | kfree(devfreq->data); | ||
107 | devfreq->data = NULL; | ||
108 | } | ||
109 | |||
110 | const struct devfreq_governor devfreq_userspace = { | ||
111 | .name = "userspace", | ||
112 | .get_target_freq = devfreq_userspace_func, | ||
113 | .init = userspace_init, | ||
114 | .exit = userspace_exit, | ||
115 | .no_central_polling = true, | ||
116 | }; | ||
diff --git a/include/linux/devfreq.h b/include/linux/devfreq.h index b3be3d3cbaa7..afb94583960c 100644 --- a/include/linux/devfreq.h +++ b/include/linux/devfreq.h | |||
@@ -166,6 +166,36 @@ extern int devfreq_register_opp_notifier(struct device *dev, | |||
166 | extern int devfreq_unregister_opp_notifier(struct device *dev, | 166 | extern int devfreq_unregister_opp_notifier(struct device *dev, |
167 | struct devfreq *devfreq); | 167 | struct devfreq *devfreq); |
168 | 168 | ||
169 | #ifdef CONFIG_DEVFREQ_GOV_POWERSAVE | ||
170 | extern const struct devfreq_governor devfreq_powersave; | ||
171 | #endif | ||
172 | #ifdef CONFIG_DEVFREQ_GOV_PERFORMANCE | ||
173 | extern const struct devfreq_governor devfreq_performance; | ||
174 | #endif | ||
175 | #ifdef CONFIG_DEVFREQ_GOV_USERSPACE | ||
176 | extern const struct devfreq_governor devfreq_userspace; | ||
177 | #endif | ||
178 | #ifdef CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND | ||
179 | extern const struct devfreq_governor devfreq_simple_ondemand; | ||
180 | /** | ||
181 | * struct devfreq_simple_ondemand_data - void *data fed to struct devfreq | ||
182 | * and devfreq_add_device | ||
183 | * @ upthreshold If the load is over this value, the frequency jumps. | ||
184 | * Specify 0 to use the default. Valid value = 0 to 100. | ||
185 | * @ downdifferential If the load is under upthreshold - downdifferential, | ||
186 | * the governor may consider slowing the frequency down. | ||
187 | * Specify 0 to use the default. Valid value = 0 to 100. | ||
188 | * downdifferential < upthreshold must hold. | ||
189 | * | ||
190 | * If the fed devfreq_simple_ondemand_data pointer is NULL to the governor, | ||
191 | * the governor uses the default values. | ||
192 | */ | ||
193 | struct devfreq_simple_ondemand_data { | ||
194 | unsigned int upthreshold; | ||
195 | unsigned int downdifferential; | ||
196 | }; | ||
197 | #endif | ||
198 | |||
169 | #else /* !CONFIG_PM_DEVFREQ */ | 199 | #else /* !CONFIG_PM_DEVFREQ */ |
170 | static struct devfreq *devfreq_add_device(struct device *dev, | 200 | static struct devfreq *devfreq_add_device(struct device *dev, |
171 | struct devfreq_dev_profile *profile, | 201 | struct devfreq_dev_profile *profile, |
@@ -198,6 +228,11 @@ static int devfreq_unregister_opp_notifier(struct device *dev, | |||
198 | return -EINVAL; | 228 | return -EINVAL; |
199 | } | 229 | } |
200 | 230 | ||
231 | #define devfreq_powersave NULL | ||
232 | #define devfreq_performance NULL | ||
233 | #define devfreq_userspace NULL | ||
234 | #define devfreq_simple_ondemand NULL | ||
235 | |||
201 | #endif /* CONFIG_PM_DEVFREQ */ | 236 | #endif /* CONFIG_PM_DEVFREQ */ |
202 | 237 | ||
203 | #endif /* __LINUX_DEVFREQ_H__ */ | 238 | #endif /* __LINUX_DEVFREQ_H__ */ |