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__ */ |
