aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMyungJoo Ham <myungjoo.ham@samsung.com>2011-10-01 18:19:34 -0400
committerRafael J. Wysocki <rjw@sisk.pl>2011-10-01 18:19:34 -0400
commitce26c5bb9569d8b826f01b8620fc16d8da6821e9 (patch)
tree3960fee73ec26f939295c92a231a09abcb4c816e
parent9005b65099ee4f14b6be691c4574612fe947531a (diff)
PM / devfreq: Add basic governors
Four cpufreq-like governors are provided as examples. powersave: use the lowest frequency possible. The user (device) should set the polling_ms as 0 because polling is useless for this governor. performance: use the highest freqeuncy possible. The user (device) should set the polling_ms as 0 because polling is useless for this governor. userspace: use the user specified frequency stored at devfreq.user_set_freq. With sysfs support in the following patch, a user may set the value with the sysfs interface. simple_ondemand: simplified version of cpufreq's ondemand governor. When a user updates OPP entries (enable/disable/add), OPP framework automatically notifies devfreq to update operating frequency accordingly. Thus, devfreq users (device drivers) do not need to update devfreq manually with OPP entry updates or set polling_ms for powersave , performance, userspace, or any other "static" governors. Note that these are given only as basic examples for governors and any devices with devfreq may implement their own governors with the drivers and use them. Signed-off-by: MyungJoo Ham <myungjoo.ham@samsung.com> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com> Reviewed-by: Mike Turquette <mturquette@ti.com> Acked-by: Kevin Hilman <khilman@ti.com> Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
-rw-r--r--Documentation/ABI/testing/sysfs-class-devfreq8
-rw-r--r--drivers/devfreq/Kconfig36
-rw-r--r--drivers/devfreq/Makefile4
-rw-r--r--drivers/devfreq/governor_performance.c29
-rw-r--r--drivers/devfreq/governor_powersave.c29
-rw-r--r--drivers/devfreq/governor_simpleondemand.c88
-rw-r--r--drivers/devfreq/governor_userspace.c116
-rw-r--r--include/linux/devfreq.h35
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
46What: /sys/class/devfreq/.../userspace/set_freq
47Date: September 2011
48Contact: MyungJoo Ham <myungjoo.ham@samsung.com>
49Description:
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
35if PM_DEVFREQ 35if PM_DEVFREQ
36 36
37comment "DEVFREQ Governors"
38
39config 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
48config 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
56config 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
64config 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
37comment "DEVFREQ Drivers" 73comment "DEVFREQ Drivers"
38 74
39endif # PM_DEVFREQ 75endif # 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 @@
1obj-$(CONFIG_PM_DEVFREQ) += devfreq.o 1obj-$(CONFIG_PM_DEVFREQ) += devfreq.o
2obj-$(CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND) += governor_simpleondemand.o
3obj-$(CONFIG_DEVFREQ_GOV_PERFORMANCE) += governor_performance.o
4obj-$(CONFIG_DEVFREQ_GOV_POWERSAVE) += governor_powersave.o
5obj-$(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
14static 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
25const 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
14static 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
25const 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)
19static 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
85const 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
19struct userspace_data {
20 unsigned long user_frequency;
21 bool valid;
22};
23
24static 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
35static 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
57static 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
75static DEVICE_ATTR(set_freq, 0644, show_freq, store_freq);
76static struct attribute *dev_entries[] = {
77 &dev_attr_set_freq.attr,
78 NULL,
79};
80static struct attribute_group dev_attr_group = {
81 .name = "userspace",
82 .attrs = dev_entries,
83};
84
85static 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);
99out:
100 return err;
101}
102
103static 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
110const 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,
166extern int devfreq_unregister_opp_notifier(struct device *dev, 166extern int devfreq_unregister_opp_notifier(struct device *dev,
167 struct devfreq *devfreq); 167 struct devfreq *devfreq);
168 168
169#ifdef CONFIG_DEVFREQ_GOV_POWERSAVE
170extern const struct devfreq_governor devfreq_powersave;
171#endif
172#ifdef CONFIG_DEVFREQ_GOV_PERFORMANCE
173extern const struct devfreq_governor devfreq_performance;
174#endif
175#ifdef CONFIG_DEVFREQ_GOV_USERSPACE
176extern const struct devfreq_governor devfreq_userspace;
177#endif
178#ifdef CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND
179extern 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 */
193struct 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 */
170static struct devfreq *devfreq_add_device(struct device *dev, 200static 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__ */