aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/devfreq
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 /drivers/devfreq
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>
Diffstat (limited to 'drivers/devfreq')
-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
6 files changed, 302 insertions, 0 deletions
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};