Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

MyungJoo Ham and committed by
Rafael J. Wysocki
ce26c5bb 9005b650

+345
+8
Documentation/ABI/testing/sysfs-class-devfreq
··· 42 42 devfreq-provided central polling 43 43 (/sys/class/devfreq/.../central_polling is 0), this value 44 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.
+36
drivers/devfreq/Kconfig
··· 34 34 35 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 73 comment "DEVFREQ Drivers" 38 74 39 75 endif # PM_DEVFREQ
+4
drivers/devfreq/Makefile
··· 1 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
+29
drivers/devfreq/governor_performance.c
··· 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 + };
+29
drivers/devfreq/governor_powersave.c
··· 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 + };
+88
drivers/devfreq/governor_simpleondemand.c
··· 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 + };
+116
drivers/devfreq/governor_userspace.c
··· 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 + };
+35
include/linux/devfreq.h
··· 166 166 extern int devfreq_unregister_opp_notifier(struct device *dev, 167 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 199 #else /* !CONFIG_PM_DEVFREQ */ 170 200 static struct devfreq *devfreq_add_device(struct device *dev, 171 201 struct devfreq_dev_profile *profile, ··· 227 197 { 228 198 return -EINVAL; 229 199 } 200 + 201 + #define devfreq_powersave NULL 202 + #define devfreq_performance NULL 203 + #define devfreq_userspace NULL 204 + #define devfreq_simple_ondemand NULL 230 205 231 206 #endif /* CONFIG_PM_DEVFREQ */ 232 207