diff options
-rw-r--r-- | MAINTAINERS | 2 | ||||
-rw-r--r-- | drivers/devfreq/devfreq.c | 139 | ||||
-rw-r--r-- | drivers/devfreq/exynos-bus.c | 5 | ||||
-rw-r--r-- | drivers/devfreq/governor_passive.c | 2 | ||||
-rw-r--r-- | drivers/devfreq/governor_performance.c | 2 | ||||
-rw-r--r-- | drivers/devfreq/governor_powersave.c | 2 | ||||
-rw-r--r-- | drivers/devfreq/governor_simpleondemand.c | 2 | ||||
-rw-r--r-- | drivers/devfreq/governor_userspace.c | 2 | ||||
-rw-r--r-- | drivers/devfreq/rk3399_dmc.c | 2 | ||||
-rw-r--r-- | include/linux/devfreq.h | 16 | ||||
-rw-r--r-- | tools/power/cpupower/.gitignore | 3 | ||||
-rw-r--r-- | tools/power/cpupower/Makefile | 6 | ||||
-rw-r--r-- | tools/power/cpupower/utils/cpufreq-info.c | 2 |
13 files changed, 132 insertions, 53 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index 4df1aba58b43..7c7cfce77aba 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3636,6 +3636,8 @@ F: drivers/cpufreq/arm_big_little_dt.c CPU POWER MONITORING SUBSYSTEM M: Thomas Renninger <trenn@suse.com> +M: Shuah Khan <shuahkh@osg.samsung.com> +M: Shuah Khan <shuah@kernel.org> L: linux-pm@vger.kernel.org S: Maintained F: tools/power/cpupower/ diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index a1c4ee818614..78fb496ecb4e 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -28,6 +28,9 @@ #include <linux/of.h> #include "governor.h" +#define MAX(a,b) ((a > b) ? a : b) +#define MIN(a,b) ((a < b) ? a : b) + static struct class *devfreq_class; /* @@ -69,6 +72,34 @@ static struct devfreq *find_device_devfreq(struct device *dev) return ERR_PTR(-ENODEV); } +static unsigned long find_available_min_freq(struct devfreq *devfreq) +{ + struct dev_pm_opp *opp; + unsigned long min_freq = 0; + + opp = dev_pm_opp_find_freq_ceil(devfreq->dev.parent, &min_freq); + if (IS_ERR(opp)) + min_freq = 0; + else + dev_pm_opp_put(opp); + + return min_freq; +} + +static unsigned long find_available_max_freq(struct devfreq *devfreq) +{ + struct dev_pm_opp *opp; + unsigned long max_freq = ULONG_MAX; + + opp = dev_pm_opp_find_freq_floor(devfreq->dev.parent, &max_freq); + if (IS_ERR(opp)) + max_freq = 0; + else + dev_pm_opp_put(opp); + + return max_freq; +} + /** * devfreq_get_freq_level() - Lookup freq_table for the frequency * @devfreq: the devfreq instance @@ -85,11 +116,7 @@ static int devfreq_get_freq_level(struct devfreq *devfreq, unsigned long freq) return -EINVAL; } -/** - * devfreq_set_freq_table() - Initialize freq_table for the frequency - * @devfreq: the devfreq instance - */ -static void devfreq_set_freq_table(struct devfreq *devfreq) +static int set_freq_table(struct devfreq *devfreq) { struct devfreq_dev_profile *profile = devfreq->profile; struct dev_pm_opp *opp; @@ -99,7 +126,7 @@ static void devfreq_set_freq_table(struct devfreq *devfreq) /* Initialize the freq_table from OPP table */ count = dev_pm_opp_get_opp_count(devfreq->dev.parent); if (count <= 0) - return; + return -EINVAL; profile->max_state = count; profile->freq_table = devm_kcalloc(devfreq->dev.parent, @@ -108,7 +135,7 @@ static void devfreq_set_freq_table(struct devfreq *devfreq) GFP_KERNEL); if (!profile->freq_table) { profile->max_state = 0; - return; + return -ENOMEM; } for (i = 0, freq = 0; i < profile->max_state; i++, freq++) { @@ -116,11 +143,13 @@ static void devfreq_set_freq_table(struct devfreq *devfreq) if (IS_ERR(opp)) { devm_kfree(devfreq->dev.parent, profile->freq_table); profile->max_state = 0; - return; + return PTR_ERR(opp); } dev_pm_opp_put(opp); profile->freq_table[i] = freq; } + + return 0; } /** @@ -227,7 +256,7 @@ static int devfreq_notify_transition(struct devfreq *devfreq, int update_devfreq(struct devfreq *devfreq) { struct devfreq_freqs freqs; - unsigned long freq, cur_freq; + unsigned long freq, cur_freq, min_freq, max_freq; int err = 0; u32 flags = 0; @@ -245,19 +274,21 @@ int update_devfreq(struct devfreq *devfreq) return err; /* - * Adjust the frequency with user freq and QoS. + * Adjust the frequency with user freq, QoS and available freq. * * List from the highest priority * max_freq * min_freq */ + max_freq = MIN(devfreq->scaling_max_freq, devfreq->max_freq); + min_freq = MAX(devfreq->scaling_min_freq, devfreq->min_freq); - if (devfreq->min_freq && freq < devfreq->min_freq) { - freq = devfreq->min_freq; + if (min_freq && freq < min_freq) { + freq = min_freq; flags &= ~DEVFREQ_FLAG_LEAST_UPPER_BOUND; /* Use GLB */ } - if (devfreq->max_freq && freq > devfreq->max_freq) { - freq = devfreq->max_freq; + if (max_freq && freq > max_freq) { + freq = max_freq; flags |= DEVFREQ_FLAG_LEAST_UPPER_BOUND; /* Use LUB */ } @@ -280,10 +311,9 @@ int update_devfreq(struct devfreq *devfreq) freqs.new = freq; devfreq_notify_transition(devfreq, &freqs, DEVFREQ_POSTCHANGE); - if (devfreq->profile->freq_table) - if (devfreq_update_status(devfreq, freq)) - dev_err(&devfreq->dev, - "Couldn't update frequency transition information.\n"); + if (devfreq_update_status(devfreq, freq)) + dev_err(&devfreq->dev, + "Couldn't update frequency transition information.\n"); devfreq->previous_freq = freq; return err; @@ -466,6 +496,19 @@ static int devfreq_notifier_call(struct notifier_block *nb, unsigned long type, int ret; mutex_lock(&devfreq->lock); + + devfreq->scaling_min_freq = find_available_min_freq(devfreq); + if (!devfreq->scaling_min_freq) { + mutex_unlock(&devfreq->lock); + return -EINVAL; + } + + devfreq->scaling_max_freq = find_available_max_freq(devfreq); + if (!devfreq->scaling_max_freq) { + mutex_unlock(&devfreq->lock); + return -EINVAL; + } + ret = update_devfreq(devfreq); mutex_unlock(&devfreq->lock); @@ -555,10 +598,28 @@ struct devfreq *devfreq_add_device(struct device *dev, if (!devfreq->profile->max_state && !devfreq->profile->freq_table) { mutex_unlock(&devfreq->lock); - devfreq_set_freq_table(devfreq); + err = set_freq_table(devfreq); + if (err < 0) + goto err_out; mutex_lock(&devfreq->lock); } + devfreq->min_freq = find_available_min_freq(devfreq); + if (!devfreq->min_freq) { + mutex_unlock(&devfreq->lock); + err = -EINVAL; + goto err_dev; + } + devfreq->scaling_min_freq = devfreq->min_freq; + + devfreq->max_freq = find_available_max_freq(devfreq); + if (!devfreq->max_freq) { + mutex_unlock(&devfreq->lock); + err = -EINVAL; + goto err_dev; + } + devfreq->scaling_max_freq = devfreq->max_freq; + dev_set_name(&devfreq->dev, "devfreq%d", atomic_inc_return(&devfreq_no)); err = device_register(&devfreq->dev); @@ -1082,6 +1143,14 @@ unlock: return ret; } +static ssize_t min_freq_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct devfreq *df = to_devfreq(dev); + + return sprintf(buf, "%lu\n", MAX(df->scaling_min_freq, df->min_freq)); +} + static ssize_t max_freq_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { @@ -1108,17 +1177,15 @@ unlock: mutex_unlock(&df->lock); return ret; } +static DEVICE_ATTR_RW(min_freq); -#define show_one(name) \ -static ssize_t name##_show \ -(struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - return sprintf(buf, "%lu\n", to_devfreq(dev)->name); \ -} -show_one(min_freq); -show_one(max_freq); +static ssize_t max_freq_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct devfreq *df = to_devfreq(dev); -static DEVICE_ATTR_RW(min_freq); + return sprintf(buf, "%lu\n", MIN(df->scaling_max_freq, df->max_freq)); +} static DEVICE_ATTR_RW(max_freq); static ssize_t available_frequencies_show(struct device *d, @@ -1126,22 +1193,16 @@ static ssize_t available_frequencies_show(struct device *d, char *buf) { struct devfreq *df = to_devfreq(d); - struct device *dev = df->dev.parent; - struct dev_pm_opp *opp; ssize_t count = 0; - unsigned long freq = 0; + int i; - do { - opp = dev_pm_opp_find_freq_ceil(dev, &freq); - if (IS_ERR(opp)) - break; + mutex_lock(&df->lock); - dev_pm_opp_put(opp); + for (i = 0; i < df->profile->max_state; i++) count += scnprintf(&buf[count], (PAGE_SIZE - count - 2), - "%lu ", freq); - freq++; - } while (1); + "%lu ", df->profile->freq_table[i]); + mutex_unlock(&df->lock); /* Truncate the trailing space */ if (count) count--; diff --git a/drivers/devfreq/exynos-bus.c b/drivers/devfreq/exynos-bus.c index 49f68929e024..c25658b26598 100644 --- a/drivers/devfreq/exynos-bus.c +++ b/drivers/devfreq/exynos-bus.c @@ -436,7 +436,8 @@ static int exynos_bus_probe(struct platform_device *pdev) ondemand_data->downdifferential = 5; /* Add devfreq device to monitor and handle the exynos bus */ - bus->devfreq = devm_devfreq_add_device(dev, profile, "simple_ondemand", + bus->devfreq = devm_devfreq_add_device(dev, profile, + DEVFREQ_GOV_SIMPLE_ONDEMAND, ondemand_data); if (IS_ERR(bus->devfreq)) { dev_err(dev, "failed to add devfreq device\n"); @@ -488,7 +489,7 @@ passive: passive_data->parent = parent_devfreq; /* Add devfreq device for exynos bus with passive governor */ - bus->devfreq = devm_devfreq_add_device(dev, profile, "passive", + bus->devfreq = devm_devfreq_add_device(dev, profile, DEVFREQ_GOV_PASSIVE, passive_data); if (IS_ERR(bus->devfreq)) { dev_err(dev, diff --git a/drivers/devfreq/governor_passive.c b/drivers/devfreq/governor_passive.c index 673ad8cc9a1d..3bc29acbd54e 100644 --- a/drivers/devfreq/governor_passive.c +++ b/drivers/devfreq/governor_passive.c @@ -183,7 +183,7 @@ static int devfreq_passive_event_handler(struct devfreq *devfreq, } static struct devfreq_governor devfreq_passive = { - .name = "passive", + .name = DEVFREQ_GOV_PASSIVE, .immutable = 1, .get_target_freq = devfreq_passive_get_target_freq, .event_handler = devfreq_passive_event_handler, diff --git a/drivers/devfreq/governor_performance.c b/drivers/devfreq/governor_performance.c index c72f942f30a8..4d23ecfbd948 100644 --- a/drivers/devfreq/governor_performance.c +++ b/drivers/devfreq/governor_performance.c @@ -42,7 +42,7 @@ static int devfreq_performance_handler(struct devfreq *devfreq, } static struct devfreq_governor devfreq_performance = { - .name = "performance", + .name = DEVFREQ_GOV_PERFORMANCE, .get_target_freq = devfreq_performance_func, .event_handler = devfreq_performance_handler, }; diff --git a/drivers/devfreq/governor_powersave.c b/drivers/devfreq/governor_powersave.c index 0c6bed567e6d..0c42f23249ef 100644 --- a/drivers/devfreq/governor_powersave.c +++ b/drivers/devfreq/governor_powersave.c @@ -39,7 +39,7 @@ static int devfreq_powersave_handler(struct devfreq *devfreq, } static struct devfreq_governor devfreq_powersave = { - .name = "powersave", + .name = DEVFREQ_GOV_POWERSAVE, .get_target_freq = devfreq_powersave_func, .event_handler = devfreq_powersave_handler, }; diff --git a/drivers/devfreq/governor_simpleondemand.c b/drivers/devfreq/governor_simpleondemand.c index ae72ba5e78df..28e0f2de7100 100644 --- a/drivers/devfreq/governor_simpleondemand.c +++ b/drivers/devfreq/governor_simpleondemand.c @@ -125,7 +125,7 @@ static int devfreq_simple_ondemand_handler(struct devfreq *devfreq, } static struct devfreq_governor devfreq_simple_ondemand = { - .name = "simple_ondemand", + .name = DEVFREQ_GOV_SIMPLE_ONDEMAND, .get_target_freq = devfreq_simple_ondemand_func, .event_handler = devfreq_simple_ondemand_handler, }; diff --git a/drivers/devfreq/governor_userspace.c b/drivers/devfreq/governor_userspace.c index 77028c27593c..080607c3f34d 100644 --- a/drivers/devfreq/governor_userspace.c +++ b/drivers/devfreq/governor_userspace.c @@ -87,7 +87,7 @@ static struct attribute *dev_entries[] = { NULL, }; static const struct attribute_group dev_attr_group = { - .name = "userspace", + .name = DEVFREQ_GOV_USERSPACE, .attrs = dev_entries, }; diff --git a/drivers/devfreq/rk3399_dmc.c b/drivers/devfreq/rk3399_dmc.c index 1b89ebbad02c..5dfbfa3cc878 100644 --- a/drivers/devfreq/rk3399_dmc.c +++ b/drivers/devfreq/rk3399_dmc.c @@ -431,7 +431,7 @@ static int rk3399_dmcfreq_probe(struct platform_device *pdev) data->devfreq = devm_devfreq_add_device(dev, &rk3399_devfreq_dmc_profile, - "simple_ondemand", + DEVFREQ_GOV_SIMPLE_ONDEMAND, &data->ondemand_data); if (IS_ERR(data->devfreq)) return PTR_ERR(data->devfreq); diff --git a/include/linux/devfreq.h b/include/linux/devfreq.h index 597294e0cc40..3aae5b3af87c 100644 --- a/include/linux/devfreq.h +++ b/include/linux/devfreq.h @@ -19,6 +19,13 @@ #define DEVFREQ_NAME_LEN 16 +/* DEVFREQ governor name */ +#define DEVFREQ_GOV_SIMPLE_ONDEMAND "simple_ondemand" +#define DEVFREQ_GOV_PERFORMANCE "performance" +#define DEVFREQ_GOV_POWERSAVE "powersave" +#define DEVFREQ_GOV_USERSPACE "userspace" +#define DEVFREQ_GOV_PASSIVE "passive" + /* DEVFREQ notifier interface */ #define DEVFREQ_TRANSITION_NOTIFIER (0) @@ -84,8 +91,9 @@ struct devfreq_dev_status { * from devfreq_remove_device() call. If the user * has registered devfreq->nb at a notifier-head, * this is the time to unregister it. - * @freq_table: Optional list of frequencies to support statistics. - * @max_state: The size of freq_table. + * @freq_table: Optional list of frequencies to support statistics + * and freq_table must be generated in ascending order. + * @max_state: The size of freq_table. */ struct devfreq_dev_profile { unsigned long initial_freq; @@ -120,6 +128,8 @@ struct devfreq_dev_profile { * touch this. * @min_freq: Limit minimum frequency requested by user (0: none) * @max_freq: Limit maximum frequency requested by user (0: none) + * @scaling_min_freq: Limit minimum frequency requested by OPP interface + * @scaling_max_freq: Limit maximum frequency requested by OPP interface * @stop_polling: devfreq polling status of a device. * @total_trans: Number of devfreq transitions * @trans_table: Statistics of devfreq transitions @@ -153,6 +163,8 @@ struct devfreq { unsigned long min_freq; unsigned long max_freq; + unsigned long scaling_min_freq; + unsigned long scaling_max_freq; bool stop_polling; /* information for device frequency transition */ diff --git a/tools/power/cpupower/.gitignore b/tools/power/cpupower/.gitignore index d42073f12609..1f9977cc609c 100644 --- a/tools/power/cpupower/.gitignore +++ b/tools/power/cpupower/.gitignore @@ -1,7 +1,6 @@ .libs libcpupower.so -libcpupower.so.0 -libcpupower.so.0.0.0 +libcpupower.so.* build/ccdv cpufreq-info cpufreq-set diff --git a/tools/power/cpupower/Makefile b/tools/power/cpupower/Makefile index d6e1c02ddcfe..da205d1fa03c 100644 --- a/tools/power/cpupower/Makefile +++ b/tools/power/cpupower/Makefile @@ -30,6 +30,8 @@ OUTDIR := $(shell cd $(OUTPUT) && /bin/pwd) $(if $(OUTDIR),, $(error output directory "$(OUTPUT)" does not exist)) endif +include ../../scripts/Makefile.arch + # --- CONFIGURATION BEGIN --- # Set the following to `true' to make a unstripped, unoptimized @@ -79,7 +81,11 @@ bindir ?= /usr/bin sbindir ?= /usr/sbin mandir ?= /usr/man includedir ?= /usr/include +ifeq ($(IS_64_BIT), 1) +libdir ?= /usr/lib64 +else libdir ?= /usr/lib +endif localedir ?= /usr/share/locale docdir ?= /usr/share/doc/packages/cpupower confdir ?= /etc/ diff --git a/tools/power/cpupower/utils/cpufreq-info.c b/tools/power/cpupower/utils/cpufreq-info.c index 3e701f0e9c14..df43cd45d810 100644 --- a/tools/power/cpupower/utils/cpufreq-info.c +++ b/tools/power/cpupower/utils/cpufreq-info.c @@ -93,8 +93,6 @@ static void print_speed(unsigned long speed) if (speed > 1000000) printf("%u.%06u GHz", ((unsigned int) speed/1000000), ((unsigned int) speed%1000000)); - else if (speed > 100000) - printf("%u MHz", (unsigned int) speed); else if (speed > 1000) printf("%u.%03u MHz", ((unsigned int) speed/1000), (unsigned int) (speed%1000)); |