diff options
-rw-r--r-- | Documentation/ABI/testing/sysfs-class-devfreq | 52 | ||||
-rw-r--r-- | drivers/Kconfig | 2 | ||||
-rw-r--r-- | drivers/Makefile | 2 | ||||
-rw-r--r-- | drivers/base/power/opp.c | 30 | ||||
-rw-r--r-- | drivers/devfreq/Kconfig | 75 | ||||
-rw-r--r-- | drivers/devfreq/Makefile | 5 | ||||
-rw-r--r-- | drivers/devfreq/devfreq.c | 601 | ||||
-rw-r--r-- | drivers/devfreq/governor.h | 24 | ||||
-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 | 238 | ||||
-rw-r--r-- | include/linux/opp.h | 12 |
14 files changed, 1303 insertions, 0 deletions
diff --git a/Documentation/ABI/testing/sysfs-class-devfreq b/Documentation/ABI/testing/sysfs-class-devfreq new file mode 100644 index 000000000000..23d78b5aab11 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-class-devfreq | |||
@@ -0,0 +1,52 @@ | |||
1 | What: /sys/class/devfreq/.../ | ||
2 | Date: September 2011 | ||
3 | Contact: MyungJoo Ham <myungjoo.ham@samsung.com> | ||
4 | Description: | ||
5 | Provide a place in sysfs for the devfreq objects. | ||
6 | This allows accessing various devfreq specific variables. | ||
7 | The name of devfreq object denoted as ... is same as the | ||
8 | name of device using devfreq. | ||
9 | |||
10 | What: /sys/class/devfreq/.../governor | ||
11 | Date: September 2011 | ||
12 | Contact: MyungJoo Ham <myungjoo.ham@samsung.com> | ||
13 | Description: | ||
14 | The /sys/class/devfreq/.../governor shows the name of the | ||
15 | governor used by the corresponding devfreq object. | ||
16 | |||
17 | What: /sys/class/devfreq/.../cur_freq | ||
18 | Date: September 2011 | ||
19 | Contact: MyungJoo Ham <myungjoo.ham@samsung.com> | ||
20 | Description: | ||
21 | The /sys/class/devfreq/.../cur_freq shows the current | ||
22 | frequency of the corresponding devfreq object. | ||
23 | |||
24 | What: /sys/class/devfreq/.../central_polling | ||
25 | Date: September 2011 | ||
26 | Contact: MyungJoo Ham <myungjoo.ham@samsung.com> | ||
27 | Description: | ||
28 | The /sys/class/devfreq/.../central_polling shows whether | ||
29 | the devfreq ojbect is using devfreq-provided central | ||
30 | polling mechanism or not. | ||
31 | |||
32 | What: /sys/class/devfreq/.../polling_interval | ||
33 | Date: September 2011 | ||
34 | Contact: MyungJoo Ham <myungjoo.ham@samsung.com> | ||
35 | Description: | ||
36 | The /sys/class/devfreq/.../polling_interval shows and sets | ||
37 | the requested polling interval of the corresponding devfreq | ||
38 | object. The values are represented in ms. If the value is | ||
39 | less than 1 jiffy, it is considered to be 0, which means | ||
40 | no polling. This value is meaningless if the governor is | ||
41 | not polling; thus. If the governor is not using | ||
42 | devfreq-provided central polling | ||
43 | (/sys/class/devfreq/.../central_polling is 0), this value | ||
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/Kconfig b/drivers/Kconfig index 95b9e7eefadc..a1efd75070aa 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig | |||
@@ -130,4 +130,6 @@ source "drivers/iommu/Kconfig" | |||
130 | 130 | ||
131 | source "drivers/virt/Kconfig" | 131 | source "drivers/virt/Kconfig" |
132 | 132 | ||
133 | source "drivers/devfreq/Kconfig" | ||
134 | |||
133 | endmenu | 135 | endmenu |
diff --git a/drivers/Makefile b/drivers/Makefile index 7fa433a7030c..97c957b50819 100644 --- a/drivers/Makefile +++ b/drivers/Makefile | |||
@@ -127,3 +127,5 @@ obj-$(CONFIG_IOMMU_SUPPORT) += iommu/ | |||
127 | 127 | ||
128 | # Virtualization drivers | 128 | # Virtualization drivers |
129 | obj-$(CONFIG_VIRT_DRIVERS) += virt/ | 129 | obj-$(CONFIG_VIRT_DRIVERS) += virt/ |
130 | |||
131 | obj-$(CONFIG_PM_DEVFREQ) += devfreq/ | ||
diff --git a/drivers/base/power/opp.c b/drivers/base/power/opp.c index b23de185cb04..434a6c011675 100644 --- a/drivers/base/power/opp.c +++ b/drivers/base/power/opp.c | |||
@@ -73,6 +73,7 @@ struct opp { | |||
73 | * RCU usage: nodes are not modified in the list of device_opp, | 73 | * RCU usage: nodes are not modified in the list of device_opp, |
74 | * however addition is possible and is secured by dev_opp_list_lock | 74 | * however addition is possible and is secured by dev_opp_list_lock |
75 | * @dev: device pointer | 75 | * @dev: device pointer |
76 | * @head: notifier head to notify the OPP availability changes. | ||
76 | * @opp_list: list of opps | 77 | * @opp_list: list of opps |
77 | * | 78 | * |
78 | * This is an internal data structure maintaining the link to opps attached to | 79 | * This is an internal data structure maintaining the link to opps attached to |
@@ -83,6 +84,7 @@ struct device_opp { | |||
83 | struct list_head node; | 84 | struct list_head node; |
84 | 85 | ||
85 | struct device *dev; | 86 | struct device *dev; |
87 | struct srcu_notifier_head head; | ||
86 | struct list_head opp_list; | 88 | struct list_head opp_list; |
87 | }; | 89 | }; |
88 | 90 | ||
@@ -404,6 +406,7 @@ int opp_add(struct device *dev, unsigned long freq, unsigned long u_volt) | |||
404 | } | 406 | } |
405 | 407 | ||
406 | dev_opp->dev = dev; | 408 | dev_opp->dev = dev; |
409 | srcu_init_notifier_head(&dev_opp->head); | ||
407 | INIT_LIST_HEAD(&dev_opp->opp_list); | 410 | INIT_LIST_HEAD(&dev_opp->opp_list); |
408 | 411 | ||
409 | /* Secure the device list modification */ | 412 | /* Secure the device list modification */ |
@@ -428,6 +431,11 @@ int opp_add(struct device *dev, unsigned long freq, unsigned long u_volt) | |||
428 | list_add_rcu(&new_opp->node, head); | 431 | list_add_rcu(&new_opp->node, head); |
429 | mutex_unlock(&dev_opp_list_lock); | 432 | mutex_unlock(&dev_opp_list_lock); |
430 | 433 | ||
434 | /* | ||
435 | * Notify the changes in the availability of the operable | ||
436 | * frequency/voltage list. | ||
437 | */ | ||
438 | srcu_notifier_call_chain(&dev_opp->head, OPP_EVENT_ADD, new_opp); | ||
431 | return 0; | 439 | return 0; |
432 | } | 440 | } |
433 | 441 | ||
@@ -504,6 +512,14 @@ static int opp_set_availability(struct device *dev, unsigned long freq, | |||
504 | mutex_unlock(&dev_opp_list_lock); | 512 | mutex_unlock(&dev_opp_list_lock); |
505 | synchronize_rcu(); | 513 | synchronize_rcu(); |
506 | 514 | ||
515 | /* Notify the change of the OPP availability */ | ||
516 | if (availability_req) | ||
517 | srcu_notifier_call_chain(&dev_opp->head, OPP_EVENT_ENABLE, | ||
518 | new_opp); | ||
519 | else | ||
520 | srcu_notifier_call_chain(&dev_opp->head, OPP_EVENT_DISABLE, | ||
521 | new_opp); | ||
522 | |||
507 | /* clean up old opp */ | 523 | /* clean up old opp */ |
508 | new_opp = opp; | 524 | new_opp = opp; |
509 | goto out; | 525 | goto out; |
@@ -643,3 +659,17 @@ void opp_free_cpufreq_table(struct device *dev, | |||
643 | *table = NULL; | 659 | *table = NULL; |
644 | } | 660 | } |
645 | #endif /* CONFIG_CPU_FREQ */ | 661 | #endif /* CONFIG_CPU_FREQ */ |
662 | |||
663 | /** | ||
664 | * opp_get_notifier() - find notifier_head of the device with opp | ||
665 | * @dev: device pointer used to lookup device OPPs. | ||
666 | */ | ||
667 | struct srcu_notifier_head *opp_get_notifier(struct device *dev) | ||
668 | { | ||
669 | struct device_opp *dev_opp = find_device_opp(dev); | ||
670 | |||
671 | if (IS_ERR(dev_opp)) | ||
672 | return ERR_PTR(PTR_ERR(dev_opp)); /* matching type */ | ||
673 | |||
674 | return &dev_opp->head; | ||
675 | } | ||
diff --git a/drivers/devfreq/Kconfig b/drivers/devfreq/Kconfig new file mode 100644 index 000000000000..643b055ed3cd --- /dev/null +++ b/drivers/devfreq/Kconfig | |||
@@ -0,0 +1,75 @@ | |||
1 | config ARCH_HAS_DEVFREQ | ||
2 | bool | ||
3 | depends on ARCH_HAS_OPP | ||
4 | help | ||
5 | Denotes that the architecture supports DEVFREQ. If the architecture | ||
6 | supports multiple OPP entries per device and the frequency of the | ||
7 | devices with OPPs may be altered dynamically, the architecture | ||
8 | supports DEVFREQ. | ||
9 | |||
10 | menuconfig PM_DEVFREQ | ||
11 | bool "Generic Dynamic Voltage and Frequency Scaling (DVFS) support" | ||
12 | depends on PM_OPP && ARCH_HAS_DEVFREQ | ||
13 | help | ||
14 | With OPP support, a device may have a list of frequencies and | ||
15 | voltages available. DEVFREQ, a generic DVFS framework can be | ||
16 | registered for a device with OPP support in order to let the | ||
17 | governor provided to DEVFREQ choose an operating frequency | ||
18 | based on the OPP's list and the policy given with DEVFREQ. | ||
19 | |||
20 | Each device may have its own governor and policy. DEVFREQ can | ||
21 | reevaluate the device state periodically and/or based on the | ||
22 | OPP list changes (each frequency/voltage pair in OPP may be | ||
23 | disabled or enabled). | ||
24 | |||
25 | Like some CPUs with CPUFREQ, a device may have multiple clocks. | ||
26 | However, because the clock frequencies of a single device are | ||
27 | determined by the single device's state, an instance of DEVFREQ | ||
28 | is attached to a single device and returns a "representative" | ||
29 | clock frequency from the OPP of the device, which is also attached | ||
30 | to a device by 1-to-1. The device registering DEVFREQ takes the | ||
31 | responsiblity to "interpret" the frequency listed in OPP and | ||
32 | to set its every clock accordingly with the "target" callback | ||
33 | given to DEVFREQ. | ||
34 | |||
35 | if PM_DEVFREQ | ||
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 | |||
73 | comment "DEVFREQ Drivers" | ||
74 | |||
75 | endif # PM_DEVFREQ | ||
diff --git a/drivers/devfreq/Makefile b/drivers/devfreq/Makefile new file mode 100644 index 000000000000..4564a89e970a --- /dev/null +++ b/drivers/devfreq/Makefile | |||
@@ -0,0 +1,5 @@ | |||
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/devfreq.c b/drivers/devfreq/devfreq.c new file mode 100644 index 000000000000..5d15b812377b --- /dev/null +++ b/drivers/devfreq/devfreq.c | |||
@@ -0,0 +1,601 @@ | |||
1 | /* | ||
2 | * devfreq: Generic Dynamic Voltage and Frequency Scaling (DVFS) Framework | ||
3 | * for Non-CPU Devices. | ||
4 | * | ||
5 | * Copyright (C) 2011 Samsung Electronics | ||
6 | * MyungJoo Ham <myungjoo.ham@samsung.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | |||
13 | #include <linux/kernel.h> | ||
14 | #include <linux/sched.h> | ||
15 | #include <linux/errno.h> | ||
16 | #include <linux/err.h> | ||
17 | #include <linux/init.h> | ||
18 | #include <linux/slab.h> | ||
19 | #include <linux/opp.h> | ||
20 | #include <linux/devfreq.h> | ||
21 | #include <linux/workqueue.h> | ||
22 | #include <linux/platform_device.h> | ||
23 | #include <linux/list.h> | ||
24 | #include <linux/printk.h> | ||
25 | #include <linux/hrtimer.h> | ||
26 | #include "governor.h" | ||
27 | |||
28 | struct class *devfreq_class; | ||
29 | |||
30 | /* | ||
31 | * devfreq_work periodically monitors every registered device. | ||
32 | * The minimum polling interval is one jiffy. The polling interval is | ||
33 | * determined by the minimum polling period among all polling devfreq | ||
34 | * devices. The resolution of polling interval is one jiffy. | ||
35 | */ | ||
36 | static bool polling; | ||
37 | static struct workqueue_struct *devfreq_wq; | ||
38 | static struct delayed_work devfreq_work; | ||
39 | |||
40 | /* wait removing if this is to be removed */ | ||
41 | static struct devfreq *wait_remove_device; | ||
42 | |||
43 | /* The list of all device-devfreq */ | ||
44 | static LIST_HEAD(devfreq_list); | ||
45 | static DEFINE_MUTEX(devfreq_list_lock); | ||
46 | |||
47 | /** | ||
48 | * find_device_devfreq() - find devfreq struct using device pointer | ||
49 | * @dev: device pointer used to lookup device devfreq. | ||
50 | * | ||
51 | * Search the list of device devfreqs and return the matched device's | ||
52 | * devfreq info. devfreq_list_lock should be held by the caller. | ||
53 | */ | ||
54 | static struct devfreq *find_device_devfreq(struct device *dev) | ||
55 | { | ||
56 | struct devfreq *tmp_devfreq; | ||
57 | |||
58 | if (unlikely(IS_ERR_OR_NULL(dev))) { | ||
59 | pr_err("DEVFREQ: %s: Invalid parameters\n", __func__); | ||
60 | return ERR_PTR(-EINVAL); | ||
61 | } | ||
62 | WARN(!mutex_is_locked(&devfreq_list_lock), | ||
63 | "devfreq_list_lock must be locked."); | ||
64 | |||
65 | list_for_each_entry(tmp_devfreq, &devfreq_list, node) { | ||
66 | if (tmp_devfreq->dev.parent == dev) | ||
67 | return tmp_devfreq; | ||
68 | } | ||
69 | |||
70 | return ERR_PTR(-ENODEV); | ||
71 | } | ||
72 | |||
73 | /** | ||
74 | * update_devfreq() - Reevaluate the device and configure frequency. | ||
75 | * @devfreq: the devfreq instance. | ||
76 | * | ||
77 | * Note: Lock devfreq->lock before calling update_devfreq | ||
78 | * This function is exported for governors. | ||
79 | */ | ||
80 | int update_devfreq(struct devfreq *devfreq) | ||
81 | { | ||
82 | unsigned long freq; | ||
83 | int err = 0; | ||
84 | |||
85 | if (!mutex_is_locked(&devfreq->lock)) { | ||
86 | WARN(true, "devfreq->lock must be locked by the caller.\n"); | ||
87 | return -EINVAL; | ||
88 | } | ||
89 | |||
90 | /* Reevaluate the proper frequency */ | ||
91 | err = devfreq->governor->get_target_freq(devfreq, &freq); | ||
92 | if (err) | ||
93 | return err; | ||
94 | |||
95 | err = devfreq->profile->target(devfreq->dev.parent, &freq); | ||
96 | if (err) | ||
97 | return err; | ||
98 | |||
99 | devfreq->previous_freq = freq; | ||
100 | return err; | ||
101 | } | ||
102 | |||
103 | /** | ||
104 | * devfreq_notifier_call() - Notify that the device frequency requirements | ||
105 | * has been changed out of devfreq framework. | ||
106 | * @nb the notifier_block (supposed to be devfreq->nb) | ||
107 | * @type not used | ||
108 | * @devp not used | ||
109 | * | ||
110 | * Called by a notifier that uses devfreq->nb. | ||
111 | */ | ||
112 | static int devfreq_notifier_call(struct notifier_block *nb, unsigned long type, | ||
113 | void *devp) | ||
114 | { | ||
115 | struct devfreq *devfreq = container_of(nb, struct devfreq, nb); | ||
116 | int ret; | ||
117 | |||
118 | mutex_lock(&devfreq->lock); | ||
119 | ret = update_devfreq(devfreq); | ||
120 | mutex_unlock(&devfreq->lock); | ||
121 | |||
122 | return ret; | ||
123 | } | ||
124 | |||
125 | /** | ||
126 | * _remove_devfreq() - Remove devfreq from the device. | ||
127 | * @devfreq: the devfreq struct | ||
128 | * @skip: skip calling device_unregister(). | ||
129 | * | ||
130 | * Note that the caller should lock devfreq->lock before calling | ||
131 | * this. _remove_devfreq() will unlock it and free devfreq | ||
132 | * internally. devfreq_list_lock should be locked by the caller | ||
133 | * as well (not relased at return) | ||
134 | * | ||
135 | * Lock usage: | ||
136 | * devfreq->lock: locked before call. | ||
137 | * unlocked at return (and freed) | ||
138 | * devfreq_list_lock: locked before call. | ||
139 | * kept locked at return. | ||
140 | * if devfreq is centrally polled. | ||
141 | * | ||
142 | * Freed memory: | ||
143 | * devfreq | ||
144 | */ | ||
145 | static void _remove_devfreq(struct devfreq *devfreq, bool skip) | ||
146 | { | ||
147 | if (!mutex_is_locked(&devfreq->lock)) { | ||
148 | WARN(true, "devfreq->lock must be locked by the caller.\n"); | ||
149 | return; | ||
150 | } | ||
151 | if (!devfreq->governor->no_central_polling && | ||
152 | !mutex_is_locked(&devfreq_list_lock)) { | ||
153 | WARN(true, "devfreq_list_lock must be locked by the caller.\n"); | ||
154 | return; | ||
155 | } | ||
156 | |||
157 | if (devfreq->being_removed) | ||
158 | return; | ||
159 | |||
160 | devfreq->being_removed = true; | ||
161 | |||
162 | if (devfreq->profile->exit) | ||
163 | devfreq->profile->exit(devfreq->dev.parent); | ||
164 | |||
165 | if (devfreq->governor->exit) | ||
166 | devfreq->governor->exit(devfreq); | ||
167 | |||
168 | if (!skip && get_device(&devfreq->dev)) { | ||
169 | device_unregister(&devfreq->dev); | ||
170 | put_device(&devfreq->dev); | ||
171 | } | ||
172 | |||
173 | if (!devfreq->governor->no_central_polling) | ||
174 | list_del(&devfreq->node); | ||
175 | |||
176 | mutex_unlock(&devfreq->lock); | ||
177 | mutex_destroy(&devfreq->lock); | ||
178 | |||
179 | kfree(devfreq); | ||
180 | } | ||
181 | |||
182 | /** | ||
183 | * devfreq_dev_release() - Callback for struct device to release the device. | ||
184 | * @dev: the devfreq device | ||
185 | * | ||
186 | * This calls _remove_devfreq() if _remove_devfreq() is not called. | ||
187 | * Note that devfreq_dev_release() could be called by _remove_devfreq() as | ||
188 | * well as by others unregistering the device. | ||
189 | */ | ||
190 | static void devfreq_dev_release(struct device *dev) | ||
191 | { | ||
192 | struct devfreq *devfreq = to_devfreq(dev); | ||
193 | bool central_polling = !devfreq->governor->no_central_polling; | ||
194 | |||
195 | /* | ||
196 | * If devfreq_dev_release() was called by device_unregister() of | ||
197 | * _remove_devfreq(), we cannot mutex_lock(&devfreq->lock) and | ||
198 | * being_removed is already set. This also partially checks the case | ||
199 | * where devfreq_dev_release() is called from a thread other than | ||
200 | * the one called _remove_devfreq(); however, this case is | ||
201 | * dealt completely with another following being_removed check. | ||
202 | * | ||
203 | * Because being_removed is never being | ||
204 | * unset, we do not need to worry about race conditions on | ||
205 | * being_removed. | ||
206 | */ | ||
207 | if (devfreq->being_removed) | ||
208 | return; | ||
209 | |||
210 | if (central_polling) | ||
211 | mutex_lock(&devfreq_list_lock); | ||
212 | |||
213 | mutex_lock(&devfreq->lock); | ||
214 | |||
215 | /* | ||
216 | * Check being_removed flag again for the case where | ||
217 | * devfreq_dev_release() was called in a thread other than the one | ||
218 | * possibly called _remove_devfreq(). | ||
219 | */ | ||
220 | if (devfreq->being_removed) { | ||
221 | mutex_unlock(&devfreq->lock); | ||
222 | goto out; | ||
223 | } | ||
224 | |||
225 | /* devfreq->lock is unlocked and removed in _removed_devfreq() */ | ||
226 | _remove_devfreq(devfreq, true); | ||
227 | |||
228 | out: | ||
229 | if (central_polling) | ||
230 | mutex_unlock(&devfreq_list_lock); | ||
231 | } | ||
232 | |||
233 | /** | ||
234 | * devfreq_monitor() - Periodically poll devfreq objects. | ||
235 | * @work: the work struct used to run devfreq_monitor periodically. | ||
236 | * | ||
237 | */ | ||
238 | static void devfreq_monitor(struct work_struct *work) | ||
239 | { | ||
240 | static unsigned long last_polled_at; | ||
241 | struct devfreq *devfreq, *tmp; | ||
242 | int error; | ||
243 | unsigned long jiffies_passed; | ||
244 | unsigned long next_jiffies = ULONG_MAX, now = jiffies; | ||
245 | struct device *dev; | ||
246 | |||
247 | /* Initially last_polled_at = 0, polling every device at bootup */ | ||
248 | jiffies_passed = now - last_polled_at; | ||
249 | last_polled_at = now; | ||
250 | if (jiffies_passed == 0) | ||
251 | jiffies_passed = 1; | ||
252 | |||
253 | mutex_lock(&devfreq_list_lock); | ||
254 | list_for_each_entry_safe(devfreq, tmp, &devfreq_list, node) { | ||
255 | mutex_lock(&devfreq->lock); | ||
256 | dev = devfreq->dev.parent; | ||
257 | |||
258 | /* Do not remove tmp for a while */ | ||
259 | wait_remove_device = tmp; | ||
260 | |||
261 | if (devfreq->governor->no_central_polling || | ||
262 | devfreq->next_polling == 0) { | ||
263 | mutex_unlock(&devfreq->lock); | ||
264 | continue; | ||
265 | } | ||
266 | mutex_unlock(&devfreq_list_lock); | ||
267 | |||
268 | /* | ||
269 | * Reduce more next_polling if devfreq_wq took an extra | ||
270 | * delay. (i.e., CPU has been idled.) | ||
271 | */ | ||
272 | if (devfreq->next_polling <= jiffies_passed) { | ||
273 | error = update_devfreq(devfreq); | ||
274 | |||
275 | /* Remove a devfreq with an error. */ | ||
276 | if (error && error != -EAGAIN) { | ||
277 | |||
278 | dev_err(dev, "Due to update_devfreq error(%d), devfreq(%s) is removed from the device\n", | ||
279 | error, devfreq->governor->name); | ||
280 | |||
281 | /* | ||
282 | * Unlock devfreq before locking the list | ||
283 | * in order to avoid deadlock with | ||
284 | * find_device_devfreq or others | ||
285 | */ | ||
286 | mutex_unlock(&devfreq->lock); | ||
287 | mutex_lock(&devfreq_list_lock); | ||
288 | /* Check if devfreq is already removed */ | ||
289 | if (IS_ERR(find_device_devfreq(dev))) | ||
290 | continue; | ||
291 | mutex_lock(&devfreq->lock); | ||
292 | /* This unlocks devfreq->lock and free it */ | ||
293 | _remove_devfreq(devfreq, false); | ||
294 | continue; | ||
295 | } | ||
296 | devfreq->next_polling = devfreq->polling_jiffies; | ||
297 | } else { | ||
298 | devfreq->next_polling -= jiffies_passed; | ||
299 | } | ||
300 | |||
301 | if (devfreq->next_polling) | ||
302 | next_jiffies = (next_jiffies > devfreq->next_polling) ? | ||
303 | devfreq->next_polling : next_jiffies; | ||
304 | |||
305 | mutex_unlock(&devfreq->lock); | ||
306 | mutex_lock(&devfreq_list_lock); | ||
307 | } | ||
308 | wait_remove_device = NULL; | ||
309 | mutex_unlock(&devfreq_list_lock); | ||
310 | |||
311 | if (next_jiffies > 0 && next_jiffies < ULONG_MAX) { | ||
312 | polling = true; | ||
313 | queue_delayed_work(devfreq_wq, &devfreq_work, next_jiffies); | ||
314 | } else { | ||
315 | polling = false; | ||
316 | } | ||
317 | } | ||
318 | |||
319 | /** | ||
320 | * devfreq_add_device() - Add devfreq feature to the device | ||
321 | * @dev: the device to add devfreq feature. | ||
322 | * @profile: device-specific profile to run devfreq. | ||
323 | * @governor: the policy to choose frequency. | ||
324 | * @data: private data for the governor. The devfreq framework does not | ||
325 | * touch this value. | ||
326 | */ | ||
327 | struct devfreq *devfreq_add_device(struct device *dev, | ||
328 | struct devfreq_dev_profile *profile, | ||
329 | const struct devfreq_governor *governor, | ||
330 | void *data) | ||
331 | { | ||
332 | struct devfreq *devfreq; | ||
333 | int err = 0; | ||
334 | |||
335 | if (!dev || !profile || !governor) { | ||
336 | dev_err(dev, "%s: Invalid parameters.\n", __func__); | ||
337 | return ERR_PTR(-EINVAL); | ||
338 | } | ||
339 | |||
340 | |||
341 | if (!governor->no_central_polling) { | ||
342 | mutex_lock(&devfreq_list_lock); | ||
343 | devfreq = find_device_devfreq(dev); | ||
344 | mutex_unlock(&devfreq_list_lock); | ||
345 | if (!IS_ERR(devfreq)) { | ||
346 | dev_err(dev, "%s: Unable to create devfreq for the device. It already has one.\n", __func__); | ||
347 | err = -EINVAL; | ||
348 | goto out; | ||
349 | } | ||
350 | } | ||
351 | |||
352 | devfreq = kzalloc(sizeof(struct devfreq), GFP_KERNEL); | ||
353 | if (!devfreq) { | ||
354 | dev_err(dev, "%s: Unable to create devfreq for the device\n", | ||
355 | __func__); | ||
356 | err = -ENOMEM; | ||
357 | goto out; | ||
358 | } | ||
359 | |||
360 | mutex_init(&devfreq->lock); | ||
361 | mutex_lock(&devfreq->lock); | ||
362 | devfreq->dev.parent = dev; | ||
363 | devfreq->dev.class = devfreq_class; | ||
364 | devfreq->dev.release = devfreq_dev_release; | ||
365 | devfreq->profile = profile; | ||
366 | devfreq->governor = governor; | ||
367 | devfreq->previous_freq = profile->initial_freq; | ||
368 | devfreq->data = data; | ||
369 | devfreq->next_polling = devfreq->polling_jiffies | ||
370 | = msecs_to_jiffies(devfreq->profile->polling_ms); | ||
371 | devfreq->nb.notifier_call = devfreq_notifier_call; | ||
372 | |||
373 | dev_set_name(&devfreq->dev, dev_name(dev)); | ||
374 | err = device_register(&devfreq->dev); | ||
375 | if (err) { | ||
376 | put_device(&devfreq->dev); | ||
377 | goto err_dev; | ||
378 | } | ||
379 | |||
380 | if (governor->init) | ||
381 | err = governor->init(devfreq); | ||
382 | if (err) | ||
383 | goto err_init; | ||
384 | |||
385 | mutex_unlock(&devfreq->lock); | ||
386 | |||
387 | if (governor->no_central_polling) | ||
388 | goto out; | ||
389 | |||
390 | mutex_lock(&devfreq_list_lock); | ||
391 | |||
392 | list_add(&devfreq->node, &devfreq_list); | ||
393 | |||
394 | if (devfreq_wq && devfreq->next_polling && !polling) { | ||
395 | polling = true; | ||
396 | queue_delayed_work(devfreq_wq, &devfreq_work, | ||
397 | devfreq->next_polling); | ||
398 | } | ||
399 | mutex_unlock(&devfreq_list_lock); | ||
400 | goto out; | ||
401 | err_init: | ||
402 | device_unregister(&devfreq->dev); | ||
403 | err_dev: | ||
404 | mutex_unlock(&devfreq->lock); | ||
405 | kfree(devfreq); | ||
406 | out: | ||
407 | if (err) | ||
408 | return ERR_PTR(err); | ||
409 | else | ||
410 | return devfreq; | ||
411 | } | ||
412 | |||
413 | /** | ||
414 | * devfreq_remove_device() - Remove devfreq feature from a device. | ||
415 | * @devfreq the devfreq instance to be removed | ||
416 | */ | ||
417 | int devfreq_remove_device(struct devfreq *devfreq) | ||
418 | { | ||
419 | if (!devfreq) | ||
420 | return -EINVAL; | ||
421 | |||
422 | if (!devfreq->governor->no_central_polling) { | ||
423 | mutex_lock(&devfreq_list_lock); | ||
424 | while (wait_remove_device == devfreq) { | ||
425 | mutex_unlock(&devfreq_list_lock); | ||
426 | schedule(); | ||
427 | mutex_lock(&devfreq_list_lock); | ||
428 | } | ||
429 | } | ||
430 | |||
431 | mutex_lock(&devfreq->lock); | ||
432 | _remove_devfreq(devfreq, false); /* it unlocks devfreq->lock */ | ||
433 | |||
434 | if (!devfreq->governor->no_central_polling) | ||
435 | mutex_unlock(&devfreq_list_lock); | ||
436 | |||
437 | return 0; | ||
438 | } | ||
439 | |||
440 | static ssize_t show_governor(struct device *dev, | ||
441 | struct device_attribute *attr, char *buf) | ||
442 | { | ||
443 | return sprintf(buf, "%s\n", to_devfreq(dev)->governor->name); | ||
444 | } | ||
445 | |||
446 | static ssize_t show_freq(struct device *dev, | ||
447 | struct device_attribute *attr, char *buf) | ||
448 | { | ||
449 | return sprintf(buf, "%lu\n", to_devfreq(dev)->previous_freq); | ||
450 | } | ||
451 | |||
452 | static ssize_t show_polling_interval(struct device *dev, | ||
453 | struct device_attribute *attr, char *buf) | ||
454 | { | ||
455 | return sprintf(buf, "%d\n", to_devfreq(dev)->profile->polling_ms); | ||
456 | } | ||
457 | |||
458 | static ssize_t store_polling_interval(struct device *dev, | ||
459 | struct device_attribute *attr, | ||
460 | const char *buf, size_t count) | ||
461 | { | ||
462 | struct devfreq *df = to_devfreq(dev); | ||
463 | unsigned int value; | ||
464 | int ret; | ||
465 | |||
466 | ret = sscanf(buf, "%u", &value); | ||
467 | if (ret != 1) | ||
468 | goto out; | ||
469 | |||
470 | mutex_lock(&df->lock); | ||
471 | df->profile->polling_ms = value; | ||
472 | df->next_polling = df->polling_jiffies | ||
473 | = msecs_to_jiffies(value); | ||
474 | mutex_unlock(&df->lock); | ||
475 | |||
476 | ret = count; | ||
477 | |||
478 | if (df->governor->no_central_polling) | ||
479 | goto out; | ||
480 | |||
481 | mutex_lock(&devfreq_list_lock); | ||
482 | if (df->next_polling > 0 && !polling) { | ||
483 | polling = true; | ||
484 | queue_delayed_work(devfreq_wq, &devfreq_work, | ||
485 | df->next_polling); | ||
486 | } | ||
487 | mutex_unlock(&devfreq_list_lock); | ||
488 | out: | ||
489 | return ret; | ||
490 | } | ||
491 | |||
492 | static ssize_t show_central_polling(struct device *dev, | ||
493 | struct device_attribute *attr, char *buf) | ||
494 | { | ||
495 | return sprintf(buf, "%d\n", | ||
496 | !to_devfreq(dev)->governor->no_central_polling); | ||
497 | } | ||
498 | |||
499 | static struct device_attribute devfreq_attrs[] = { | ||
500 | __ATTR(governor, S_IRUGO, show_governor, NULL), | ||
501 | __ATTR(cur_freq, S_IRUGO, show_freq, NULL), | ||
502 | __ATTR(central_polling, S_IRUGO, show_central_polling, NULL), | ||
503 | __ATTR(polling_interval, S_IRUGO | S_IWUSR, show_polling_interval, | ||
504 | store_polling_interval), | ||
505 | { }, | ||
506 | }; | ||
507 | |||
508 | /** | ||
509 | * devfreq_start_polling() - Initialize data structure for devfreq framework and | ||
510 | * start polling registered devfreq devices. | ||
511 | */ | ||
512 | static int __init devfreq_start_polling(void) | ||
513 | { | ||
514 | mutex_lock(&devfreq_list_lock); | ||
515 | polling = false; | ||
516 | devfreq_wq = create_freezable_workqueue("devfreq_wq"); | ||
517 | INIT_DELAYED_WORK_DEFERRABLE(&devfreq_work, devfreq_monitor); | ||
518 | mutex_unlock(&devfreq_list_lock); | ||
519 | |||
520 | devfreq_monitor(&devfreq_work.work); | ||
521 | return 0; | ||
522 | } | ||
523 | late_initcall(devfreq_start_polling); | ||
524 | |||
525 | static int __init devfreq_init(void) | ||
526 | { | ||
527 | devfreq_class = class_create(THIS_MODULE, "devfreq"); | ||
528 | if (IS_ERR(devfreq_class)) { | ||
529 | pr_err("%s: couldn't create class\n", __FILE__); | ||
530 | return PTR_ERR(devfreq_class); | ||
531 | } | ||
532 | devfreq_class->dev_attrs = devfreq_attrs; | ||
533 | return 0; | ||
534 | } | ||
535 | subsys_initcall(devfreq_init); | ||
536 | |||
537 | static void __exit devfreq_exit(void) | ||
538 | { | ||
539 | class_destroy(devfreq_class); | ||
540 | } | ||
541 | module_exit(devfreq_exit); | ||
542 | |||
543 | /* | ||
544 | * The followings are helper functions for devfreq user device drivers with | ||
545 | * OPP framework. | ||
546 | */ | ||
547 | |||
548 | /** | ||
549 | * devfreq_recommended_opp() - Helper function to get proper OPP for the | ||
550 | * freq value given to target callback. | ||
551 | * @dev The devfreq user device. (parent of devfreq) | ||
552 | * @freq The frequency given to target function | ||
553 | * | ||
554 | */ | ||
555 | struct opp *devfreq_recommended_opp(struct device *dev, unsigned long *freq) | ||
556 | { | ||
557 | struct opp *opp = opp_find_freq_ceil(dev, freq); | ||
558 | |||
559 | if (opp == ERR_PTR(-ENODEV)) | ||
560 | opp = opp_find_freq_floor(dev, freq); | ||
561 | return opp; | ||
562 | } | ||
563 | |||
564 | /** | ||
565 | * devfreq_register_opp_notifier() - Helper function to get devfreq notified | ||
566 | * for any changes in the OPP availability | ||
567 | * changes | ||
568 | * @dev The devfreq user device. (parent of devfreq) | ||
569 | * @devfreq The devfreq object. | ||
570 | */ | ||
571 | int devfreq_register_opp_notifier(struct device *dev, struct devfreq *devfreq) | ||
572 | { | ||
573 | struct srcu_notifier_head *nh = opp_get_notifier(dev); | ||
574 | |||
575 | if (IS_ERR(nh)) | ||
576 | return PTR_ERR(nh); | ||
577 | return srcu_notifier_chain_register(nh, &devfreq->nb); | ||
578 | } | ||
579 | |||
580 | /** | ||
581 | * devfreq_unregister_opp_notifier() - Helper function to stop getting devfreq | ||
582 | * notified for any changes in the OPP | ||
583 | * availability changes anymore. | ||
584 | * @dev The devfreq user device. (parent of devfreq) | ||
585 | * @devfreq The devfreq object. | ||
586 | * | ||
587 | * At exit() callback of devfreq_dev_profile, this must be included if | ||
588 | * devfreq_recommended_opp is used. | ||
589 | */ | ||
590 | int devfreq_unregister_opp_notifier(struct device *dev, struct devfreq *devfreq) | ||
591 | { | ||
592 | struct srcu_notifier_head *nh = opp_get_notifier(dev); | ||
593 | |||
594 | if (IS_ERR(nh)) | ||
595 | return PTR_ERR(nh); | ||
596 | return srcu_notifier_chain_unregister(nh, &devfreq->nb); | ||
597 | } | ||
598 | |||
599 | MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>"); | ||
600 | MODULE_DESCRIPTION("devfreq class support"); | ||
601 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/devfreq/governor.h b/drivers/devfreq/governor.h new file mode 100644 index 000000000000..ea7f13c58ded --- /dev/null +++ b/drivers/devfreq/governor.h | |||
@@ -0,0 +1,24 @@ | |||
1 | /* | ||
2 | * governor.h - internal header for devfreq governors. | ||
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 | * This header is for devfreq governors in drivers/devfreq/ | ||
12 | */ | ||
13 | |||
14 | #ifndef _GOVERNOR_H | ||
15 | #define _GOVERNOR_H | ||
16 | |||
17 | #include <linux/devfreq.h> | ||
18 | |||
19 | #define to_devfreq(DEV) container_of((DEV), struct devfreq, dev) | ||
20 | |||
21 | /* Caution: devfreq->lock must be locked before calling update_devfreq */ | ||
22 | extern int update_devfreq(struct devfreq *devfreq); | ||
23 | |||
24 | #endif /* _GOVERNOR_H */ | ||
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 new file mode 100644 index 000000000000..afb94583960c --- /dev/null +++ b/include/linux/devfreq.h | |||
@@ -0,0 +1,238 @@ | |||
1 | /* | ||
2 | * devfreq: Generic Dynamic Voltage and Frequency Scaling (DVFS) Framework | ||
3 | * for Non-CPU Devices. | ||
4 | * | ||
5 | * Copyright (C) 2011 Samsung Electronics | ||
6 | * MyungJoo Ham <myungjoo.ham@samsung.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | |||
13 | #ifndef __LINUX_DEVFREQ_H__ | ||
14 | #define __LINUX_DEVFREQ_H__ | ||
15 | |||
16 | #include <linux/device.h> | ||
17 | #include <linux/notifier.h> | ||
18 | #include <linux/opp.h> | ||
19 | |||
20 | #define DEVFREQ_NAME_LEN 16 | ||
21 | |||
22 | struct devfreq; | ||
23 | |||
24 | /** | ||
25 | * struct devfreq_dev_status - Data given from devfreq user device to | ||
26 | * governors. Represents the performance | ||
27 | * statistics. | ||
28 | * @total_time The total time represented by this instance of | ||
29 | * devfreq_dev_status | ||
30 | * @busy_time The time that the device was working among the | ||
31 | * total_time. | ||
32 | * @current_frequency The operating frequency. | ||
33 | * @private_data An entry not specified by the devfreq framework. | ||
34 | * A device and a specific governor may have their | ||
35 | * own protocol with private_data. However, because | ||
36 | * this is governor-specific, a governor using this | ||
37 | * will be only compatible with devices aware of it. | ||
38 | */ | ||
39 | struct devfreq_dev_status { | ||
40 | /* both since the last measure */ | ||
41 | unsigned long total_time; | ||
42 | unsigned long busy_time; | ||
43 | unsigned long current_frequency; | ||
44 | void *private_date; | ||
45 | }; | ||
46 | |||
47 | /** | ||
48 | * struct devfreq_dev_profile - Devfreq's user device profile | ||
49 | * @initial_freq The operating frequency when devfreq_add_device() is | ||
50 | * called. | ||
51 | * @polling_ms The polling interval in ms. 0 disables polling. | ||
52 | * @target The device should set its operating frequency at | ||
53 | * freq or lowest-upper-than-freq value. If freq is | ||
54 | * higher than any operable frequency, set maximum. | ||
55 | * Before returning, target function should set | ||
56 | * freq at the current frequency. | ||
57 | * @get_dev_status The device should provide the current performance | ||
58 | * status to devfreq, which is used by governors. | ||
59 | * @exit An optional callback that is called when devfreq | ||
60 | * is removing the devfreq object due to error or | ||
61 | * from devfreq_remove_device() call. If the user | ||
62 | * has registered devfreq->nb at a notifier-head, | ||
63 | * this is the time to unregister it. | ||
64 | */ | ||
65 | struct devfreq_dev_profile { | ||
66 | unsigned long initial_freq; | ||
67 | unsigned int polling_ms; | ||
68 | |||
69 | int (*target)(struct device *dev, unsigned long *freq); | ||
70 | int (*get_dev_status)(struct device *dev, | ||
71 | struct devfreq_dev_status *stat); | ||
72 | void (*exit)(struct device *dev); | ||
73 | }; | ||
74 | |||
75 | /** | ||
76 | * struct devfreq_governor - Devfreq policy governor | ||
77 | * @name Governor's name | ||
78 | * @get_target_freq Returns desired operating frequency for the device. | ||
79 | * Basically, get_target_freq will run | ||
80 | * devfreq_dev_profile.get_dev_status() to get the | ||
81 | * status of the device (load = busy_time / total_time). | ||
82 | * If no_central_polling is set, this callback is called | ||
83 | * only with update_devfreq() notified by OPP. | ||
84 | * @init Called when the devfreq is being attached to a device | ||
85 | * @exit Called when the devfreq is being removed from a | ||
86 | * device. Governor should stop any internal routines | ||
87 | * before return because related data may be | ||
88 | * freed after exit(). | ||
89 | * @no_central_polling Do not use devfreq's central polling mechanism. | ||
90 | * When this is set, devfreq will not call | ||
91 | * get_target_freq with devfreq_monitor(). However, | ||
92 | * devfreq will call get_target_freq with | ||
93 | * devfreq_update() notified by OPP framework. | ||
94 | * | ||
95 | * Note that the callbacks are called with devfreq->lock locked by devfreq. | ||
96 | */ | ||
97 | struct devfreq_governor { | ||
98 | const char name[DEVFREQ_NAME_LEN]; | ||
99 | int (*get_target_freq)(struct devfreq *this, unsigned long *freq); | ||
100 | int (*init)(struct devfreq *this); | ||
101 | void (*exit)(struct devfreq *this); | ||
102 | const bool no_central_polling; | ||
103 | }; | ||
104 | |||
105 | /** | ||
106 | * struct devfreq - Device devfreq structure | ||
107 | * @node list node - contains the devices with devfreq that have been | ||
108 | * registered. | ||
109 | * @lock a mutex to protect accessing devfreq. | ||
110 | * @dev device registered by devfreq class. dev.parent is the device | ||
111 | * using devfreq. | ||
112 | * @profile device-specific devfreq profile | ||
113 | * @governor method how to choose frequency based on the usage. | ||
114 | * @nb notifier block used to notify devfreq object that it should | ||
115 | * reevaluate operable frequencies. Devfreq users may use | ||
116 | * devfreq.nb to the corresponding register notifier call chain. | ||
117 | * @polling_jiffies interval in jiffies. | ||
118 | * @previous_freq previously configured frequency value. | ||
119 | * @next_polling the number of remaining jiffies to poll with | ||
120 | * "devfreq_monitor" executions to reevaluate | ||
121 | * frequency/voltage of the device. Set by | ||
122 | * profile's polling_ms interval. | ||
123 | * @data Private data of the governor. The devfreq framework does not | ||
124 | * touch this. | ||
125 | * @being_removed a flag to mark that this object is being removed in | ||
126 | * order to prevent trying to remove the object multiple times. | ||
127 | * | ||
128 | * This structure stores the devfreq information for a give device. | ||
129 | * | ||
130 | * Note that when a governor accesses entries in struct devfreq in its | ||
131 | * functions except for the context of callbacks defined in struct | ||
132 | * devfreq_governor, the governor should protect its access with the | ||
133 | * struct mutex lock in struct devfreq. A governor may use this mutex | ||
134 | * to protect its own private data in void *data as well. | ||
135 | */ | ||
136 | struct devfreq { | ||
137 | struct list_head node; | ||
138 | |||
139 | struct mutex lock; | ||
140 | struct device dev; | ||
141 | struct devfreq_dev_profile *profile; | ||
142 | const struct devfreq_governor *governor; | ||
143 | struct notifier_block nb; | ||
144 | |||
145 | unsigned long polling_jiffies; | ||
146 | unsigned long previous_freq; | ||
147 | unsigned int next_polling; | ||
148 | |||
149 | void *data; /* private data for governors */ | ||
150 | |||
151 | bool being_removed; | ||
152 | }; | ||
153 | |||
154 | #if defined(CONFIG_PM_DEVFREQ) | ||
155 | extern struct devfreq *devfreq_add_device(struct device *dev, | ||
156 | struct devfreq_dev_profile *profile, | ||
157 | const struct devfreq_governor *governor, | ||
158 | void *data); | ||
159 | extern int devfreq_remove_device(struct devfreq *devfreq); | ||
160 | |||
161 | /* Helper functions for devfreq user device driver with OPP. */ | ||
162 | extern struct opp *devfreq_recommended_opp(struct device *dev, | ||
163 | unsigned long *freq); | ||
164 | extern int devfreq_register_opp_notifier(struct device *dev, | ||
165 | struct devfreq *devfreq); | ||
166 | extern int devfreq_unregister_opp_notifier(struct device *dev, | ||
167 | struct devfreq *devfreq); | ||
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 | |||
199 | #else /* !CONFIG_PM_DEVFREQ */ | ||
200 | static struct devfreq *devfreq_add_device(struct device *dev, | ||
201 | struct devfreq_dev_profile *profile, | ||
202 | struct devfreq_governor *governor, | ||
203 | void *data); | ||
204 | { | ||
205 | return NULL; | ||
206 | } | ||
207 | |||
208 | static int devfreq_remove_device(struct devfreq *devfreq); | ||
209 | { | ||
210 | return 0; | ||
211 | } | ||
212 | |||
213 | static struct opp *devfreq_recommended_opp(struct device *dev, | ||
214 | unsigned long *freq) | ||
215 | { | ||
216 | return -EINVAL; | ||
217 | } | ||
218 | |||
219 | static int devfreq_register_opp_notifier(struct device *dev, | ||
220 | struct devfreq *devfreq) | ||
221 | { | ||
222 | return -EINVAL; | ||
223 | } | ||
224 | |||
225 | static int devfreq_unregister_opp_notifier(struct device *dev, | ||
226 | struct devfreq *devfreq) | ||
227 | { | ||
228 | return -EINVAL; | ||
229 | } | ||
230 | |||
231 | #define devfreq_powersave NULL | ||
232 | #define devfreq_performance NULL | ||
233 | #define devfreq_userspace NULL | ||
234 | #define devfreq_simple_ondemand NULL | ||
235 | |||
236 | #endif /* CONFIG_PM_DEVFREQ */ | ||
237 | |||
238 | #endif /* __LINUX_DEVFREQ_H__ */ | ||
diff --git a/include/linux/opp.h b/include/linux/opp.h index 7020e9736fc5..87a9208f8aec 100644 --- a/include/linux/opp.h +++ b/include/linux/opp.h | |||
@@ -16,9 +16,14 @@ | |||
16 | 16 | ||
17 | #include <linux/err.h> | 17 | #include <linux/err.h> |
18 | #include <linux/cpufreq.h> | 18 | #include <linux/cpufreq.h> |
19 | #include <linux/notifier.h> | ||
19 | 20 | ||
20 | struct opp; | 21 | struct opp; |
21 | 22 | ||
23 | enum opp_event { | ||
24 | OPP_EVENT_ADD, OPP_EVENT_ENABLE, OPP_EVENT_DISABLE, | ||
25 | }; | ||
26 | |||
22 | #if defined(CONFIG_PM_OPP) | 27 | #if defined(CONFIG_PM_OPP) |
23 | 28 | ||
24 | unsigned long opp_get_voltage(struct opp *opp); | 29 | unsigned long opp_get_voltage(struct opp *opp); |
@@ -40,6 +45,8 @@ int opp_enable(struct device *dev, unsigned long freq); | |||
40 | 45 | ||
41 | int opp_disable(struct device *dev, unsigned long freq); | 46 | int opp_disable(struct device *dev, unsigned long freq); |
42 | 47 | ||
48 | struct srcu_notifier_head *opp_get_notifier(struct device *dev); | ||
49 | |||
43 | #else | 50 | #else |
44 | static inline unsigned long opp_get_voltage(struct opp *opp) | 51 | static inline unsigned long opp_get_voltage(struct opp *opp) |
45 | { | 52 | { |
@@ -89,6 +96,11 @@ static inline int opp_disable(struct device *dev, unsigned long freq) | |||
89 | { | 96 | { |
90 | return 0; | 97 | return 0; |
91 | } | 98 | } |
99 | |||
100 | struct srcu_notifier_head *opp_get_notifier(struct device *dev) | ||
101 | { | ||
102 | return ERR_PTR(-EINVAL); | ||
103 | } | ||
92 | #endif /* CONFIG_PM */ | 104 | #endif /* CONFIG_PM */ |
93 | 105 | ||
94 | #if defined(CONFIG_CPU_FREQ) && defined(CONFIG_PM_OPP) | 106 | #if defined(CONFIG_CPU_FREQ) && defined(CONFIG_PM_OPP) |