From 285881b51eb58dacae78073763e782e56e2fb253 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 31 Jan 2019 13:53:25 +0530 Subject: PM / OPP: Remove unused parameter of _generic_set_opp_clk_only() The previous frequency value isn't getting used in the routine _generic_set_opp_clk_only(), drop it. Reviewed-by: Stephen Boyd Signed-off-by: Viresh Kumar --- drivers/opp/core.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'drivers/opp') diff --git a/drivers/opp/core.c b/drivers/opp/core.c index e5507add8f04..57d88a275de0 100644 --- a/drivers/opp/core.c +++ b/drivers/opp/core.c @@ -533,9 +533,8 @@ static int _set_opp_voltage(struct device *dev, struct regulator *reg, return ret; } -static inline int -_generic_set_opp_clk_only(struct device *dev, struct clk *clk, - unsigned long old_freq, unsigned long freq) +static inline int _generic_set_opp_clk_only(struct device *dev, struct clk *clk, + unsigned long freq) { int ret; @@ -572,7 +571,7 @@ static int _generic_set_opp_regulator(const struct opp_table *opp_table, } /* Change frequency */ - ret = _generic_set_opp_clk_only(dev, opp_table->clk, old_freq, freq); + ret = _generic_set_opp_clk_only(dev, opp_table->clk, freq); if (ret) goto restore_voltage; @@ -586,7 +585,7 @@ static int _generic_set_opp_regulator(const struct opp_table *opp_table, return 0; restore_freq: - if (_generic_set_opp_clk_only(dev, opp_table->clk, freq, old_freq)) + if (_generic_set_opp_clk_only(dev, opp_table->clk, old_freq)) dev_err(dev, "%s: failed to restore old-freq (%lu Hz)\n", __func__, old_freq); restore_voltage: @@ -759,7 +758,7 @@ int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq) opp->supplies); } else { /* Only frequency scaling */ - ret = _generic_set_opp_clk_only(dev, clk, old_freq, freq); + ret = _generic_set_opp_clk_only(dev, clk, freq); } /* Scaling down? Configure required OPPs after frequency */ -- cgit v1.2.3 From a4f342b9607d8c2034d3135cbbb11b4028be3678 Mon Sep 17 00:00:00 2001 From: Quentin Perret Date: Mon, 4 Feb 2019 11:09:48 +0000 Subject: PM / OPP: Introduce a power estimation helper The Energy Model (EM) framework provides an API to let drivers register the active power of CPUs. The drivers are expected to provide a callback method which estimates the power consumed by a CPU at each available performance levels. How exactly this should be implemented, however, depends on the platform. On some systems, PM_OPP knows the voltage and frequency at which CPUs can run. When coupled with the CPU 'capacitance' (as provided by the 'dynamic-power-coefficient' devicetree binding), it is possible to estimate the dynamic power consumption of a CPU as P = C * V^2 * f, with C its capacitance and V and f respectively the voltage and frequency of the OPP. The Intelligent Power Allocator (IPA) thermal governor already implements that estimation method, in the thermal framework. However, this power estimation method can be applied to any platform where all the parameters are known (C, V and f), and not only those suffering thermal issues. As such, the code implementing this feature can be re-used to also populate the EM framework now used by EAS. As a first step, introduce in PM_OPP a helper function which CPUFreq drivers can use to register into the EM framework. This duplicates the power estimation done in IPA until it can be migrated to using the EM framework. This will be done later, once the EM framework has support for at least all platforms currently supported by IPA. Signed-off-by: Quentin Perret Tested-by: Matthias Kaehlcke Reviewed-by: Matthias Kaehlcke Signed-off-by: Viresh Kumar --- drivers/opp/of.c | 99 ++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/pm_opp.h | 6 +++ 2 files changed, 105 insertions(+) (limited to 'drivers/opp') diff --git a/drivers/opp/of.c b/drivers/opp/of.c index 06f0f632ec47..cd58959e5158 100644 --- a/drivers/opp/of.c +++ b/drivers/opp/of.c @@ -20,6 +20,7 @@ #include #include #include +#include #include "opp.h" @@ -1047,3 +1048,101 @@ struct device_node *dev_pm_opp_get_of_node(struct dev_pm_opp *opp) return of_node_get(opp->np); } EXPORT_SYMBOL_GPL(dev_pm_opp_get_of_node); + +/* + * Callback function provided to the Energy Model framework upon registration. + * This computes the power estimated by @CPU at @kHz if it is the frequency + * of an existing OPP, or at the frequency of the first OPP above @kHz otherwise + * (see dev_pm_opp_find_freq_ceil()). This function updates @kHz to the ceiled + * frequency and @mW to the associated power. The power is estimated as + * P = C * V^2 * f with C being the CPU's capacitance and V and f respectively + * the voltage and frequency of the OPP. + * + * Returns -ENODEV if the CPU device cannot be found, -EINVAL if the power + * calculation failed because of missing parameters, 0 otherwise. + */ +static int __maybe_unused _get_cpu_power(unsigned long *mW, unsigned long *kHz, + int cpu) +{ + struct device *cpu_dev; + struct dev_pm_opp *opp; + struct device_node *np; + unsigned long mV, Hz; + u32 cap; + u64 tmp; + int ret; + + cpu_dev = get_cpu_device(cpu); + if (!cpu_dev) + return -ENODEV; + + np = of_node_get(cpu_dev->of_node); + if (!np) + return -EINVAL; + + ret = of_property_read_u32(np, "dynamic-power-coefficient", &cap); + of_node_put(np); + if (ret) + return -EINVAL; + + Hz = *kHz * 1000; + opp = dev_pm_opp_find_freq_ceil(cpu_dev, &Hz); + if (IS_ERR(opp)) + return -EINVAL; + + mV = dev_pm_opp_get_voltage(opp) / 1000; + dev_pm_opp_put(opp); + if (!mV) + return -EINVAL; + + tmp = (u64)cap * mV * mV * (Hz / 1000000); + do_div(tmp, 1000000000); + + *mW = (unsigned long)tmp; + *kHz = Hz / 1000; + + return 0; +} + +/** + * dev_pm_opp_of_register_em() - Attempt to register an Energy Model + * @cpus : CPUs for which an Energy Model has to be registered + * + * This checks whether the "dynamic-power-coefficient" devicetree property has + * been specified, and tries to register an Energy Model with it if it has. + */ +void dev_pm_opp_of_register_em(struct cpumask *cpus) +{ + struct em_data_callback em_cb = EM_DATA_CB(_get_cpu_power); + int ret, nr_opp, cpu = cpumask_first(cpus); + struct device *cpu_dev; + struct device_node *np; + u32 cap; + + cpu_dev = get_cpu_device(cpu); + if (!cpu_dev) + return; + + nr_opp = dev_pm_opp_get_opp_count(cpu_dev); + if (nr_opp <= 0) + return; + + np = of_node_get(cpu_dev->of_node); + if (!np) + return; + + /* + * Register an EM only if the 'dynamic-power-coefficient' property is + * set in devicetree. It is assumed the voltage values are known if that + * property is set since it is useless otherwise. If voltages are not + * known, just let the EM registration fail with an error to alert the + * user about the inconsistent configuration. + */ + ret = of_property_read_u32(np, "dynamic-power-coefficient", &cap); + of_node_put(np); + if (ret || !cap) + return; + + em_register_perf_domain(cpus, nr_opp, &em_cb); +} +EXPORT_SYMBOL_GPL(dev_pm_opp_of_register_em); diff --git a/include/linux/pm_opp.h b/include/linux/pm_opp.h index 0a2a88e5a383..1470c57933cf 100644 --- a/include/linux/pm_opp.h +++ b/include/linux/pm_opp.h @@ -322,6 +322,7 @@ int dev_pm_opp_of_get_sharing_cpus(struct device *cpu_dev, struct cpumask *cpuma struct device_node *dev_pm_opp_of_get_opp_desc_node(struct device *dev); struct device_node *dev_pm_opp_get_of_node(struct dev_pm_opp *opp); int of_get_required_opp_performance_state(struct device_node *np, int index); +void dev_pm_opp_of_register_em(struct cpumask *cpus); #else static inline int dev_pm_opp_of_add_table(struct device *dev) { @@ -360,6 +361,11 @@ static inline struct device_node *dev_pm_opp_get_of_node(struct dev_pm_opp *opp) { return NULL; } + +static inline void dev_pm_opp_of_register_em(struct cpumask *cpus) +{ +} + static inline int of_get_required_opp_performance_state(struct device_node *np, int index) { return -ENOTSUPP; -- cgit v1.2.3 From a2dea4cb907022447298d46b15a5c41f3f9903e6 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 22 Jan 2019 16:21:17 +0100 Subject: opp: no need to check return value of debugfs_create functions When calling debugfs functions, there is no need to ever check the return value. The function can work or not, but the code logic should never do something different based on this. Cc: Viresh Kumar Cc: Nishanth Menon Cc: Stephen Boyd Cc: linux-pm@vger.kernel.org Reviewed-by: Stephen Boyd Signed-off-by: Greg Kroah-Hartman Signed-off-by: Viresh Kumar --- drivers/opp/core.c | 11 +---- drivers/opp/debugfs.c | 110 +++++++++++++------------------------------------- drivers/opp/opp.h | 15 ++++--- 3 files changed, 38 insertions(+), 98 deletions(-) (limited to 'drivers/opp') diff --git a/drivers/opp/core.c b/drivers/opp/core.c index 57d88a275de0..fa15aa8e00b0 100644 --- a/drivers/opp/core.c +++ b/drivers/opp/core.c @@ -792,7 +792,6 @@ static struct opp_device *_add_opp_dev_unlocked(const struct device *dev, struct opp_table *opp_table) { struct opp_device *opp_dev; - int ret; opp_dev = kzalloc(sizeof(*opp_dev), GFP_KERNEL); if (!opp_dev) @@ -804,10 +803,7 @@ static struct opp_device *_add_opp_dev_unlocked(const struct device *dev, list_add(&opp_dev->node, &opp_table->dev_list); /* Create debugfs entries for the opp_table */ - ret = opp_debug_register(opp_dev, opp_table); - if (ret) - dev_err(dev, "%s: Failed to register opp debugfs (%d)\n", - __func__, ret); + opp_debug_register(opp_dev, opp_table); return opp_dev; } @@ -1175,10 +1171,7 @@ int _opp_add(struct device *dev, struct dev_pm_opp *new_opp, new_opp->opp_table = opp_table; kref_init(&new_opp->kref); - ret = opp_debug_create_one(new_opp, opp_table); - if (ret) - dev_err(dev, "%s: Failed to register opp to debugfs (%d)\n", - __func__, ret); + opp_debug_create_one(new_opp, opp_table); if (!_opp_supported_by_regulators(new_opp, opp_table)) { new_opp->available = false; diff --git a/drivers/opp/debugfs.c b/drivers/opp/debugfs.c index e6828e5f81b0..a1c57fe14de4 100644 --- a/drivers/opp/debugfs.c +++ b/drivers/opp/debugfs.c @@ -35,7 +35,7 @@ void opp_debug_remove_one(struct dev_pm_opp *opp) debugfs_remove_recursive(opp->dentry); } -static bool opp_debug_create_supplies(struct dev_pm_opp *opp, +static void opp_debug_create_supplies(struct dev_pm_opp *opp, struct opp_table *opp_table, struct dentry *pdentry) { @@ -50,30 +50,21 @@ static bool opp_debug_create_supplies(struct dev_pm_opp *opp, /* Create per-opp directory */ d = debugfs_create_dir(name, pdentry); - if (!d) - return false; + debugfs_create_ulong("u_volt_target", S_IRUGO, d, + &opp->supplies[i].u_volt); - if (!debugfs_create_ulong("u_volt_target", S_IRUGO, d, - &opp->supplies[i].u_volt)) - return false; + debugfs_create_ulong("u_volt_min", S_IRUGO, d, + &opp->supplies[i].u_volt_min); - if (!debugfs_create_ulong("u_volt_min", S_IRUGO, d, - &opp->supplies[i].u_volt_min)) - return false; + debugfs_create_ulong("u_volt_max", S_IRUGO, d, + &opp->supplies[i].u_volt_max); - if (!debugfs_create_ulong("u_volt_max", S_IRUGO, d, - &opp->supplies[i].u_volt_max)) - return false; - - if (!debugfs_create_ulong("u_amp", S_IRUGO, d, - &opp->supplies[i].u_amp)) - return false; + debugfs_create_ulong("u_amp", S_IRUGO, d, + &opp->supplies[i].u_amp); } - - return true; } -int opp_debug_create_one(struct dev_pm_opp *opp, struct opp_table *opp_table) +void opp_debug_create_one(struct dev_pm_opp *opp, struct opp_table *opp_table) { struct dentry *pdentry = opp_table->dentry; struct dentry *d; @@ -95,40 +86,23 @@ int opp_debug_create_one(struct dev_pm_opp *opp, struct opp_table *opp_table) /* Create per-opp directory */ d = debugfs_create_dir(name, pdentry); - if (!d) - return -ENOMEM; - - if (!debugfs_create_bool("available", S_IRUGO, d, &opp->available)) - return -ENOMEM; - - if (!debugfs_create_bool("dynamic", S_IRUGO, d, &opp->dynamic)) - return -ENOMEM; - - if (!debugfs_create_bool("turbo", S_IRUGO, d, &opp->turbo)) - return -ENOMEM; - - if (!debugfs_create_bool("suspend", S_IRUGO, d, &opp->suspend)) - return -ENOMEM; - - if (!debugfs_create_u32("performance_state", S_IRUGO, d, &opp->pstate)) - return -ENOMEM; - if (!debugfs_create_ulong("rate_hz", S_IRUGO, d, &opp->rate)) - return -ENOMEM; + debugfs_create_bool("available", S_IRUGO, d, &opp->available); + debugfs_create_bool("dynamic", S_IRUGO, d, &opp->dynamic); + debugfs_create_bool("turbo", S_IRUGO, d, &opp->turbo); + debugfs_create_bool("suspend", S_IRUGO, d, &opp->suspend); + debugfs_create_u32("performance_state", S_IRUGO, d, &opp->pstate); + debugfs_create_ulong("rate_hz", S_IRUGO, d, &opp->rate); + debugfs_create_ulong("clock_latency_ns", S_IRUGO, d, + &opp->clock_latency_ns); - if (!opp_debug_create_supplies(opp, opp_table, d)) - return -ENOMEM; - - if (!debugfs_create_ulong("clock_latency_ns", S_IRUGO, d, - &opp->clock_latency_ns)) - return -ENOMEM; + opp_debug_create_supplies(opp, opp_table, d); opp->dentry = d; - return 0; } -static int opp_list_debug_create_dir(struct opp_device *opp_dev, - struct opp_table *opp_table) +static void opp_list_debug_create_dir(struct opp_device *opp_dev, + struct opp_table *opp_table) { const struct device *dev = opp_dev->dev; struct dentry *d; @@ -137,36 +111,21 @@ static int opp_list_debug_create_dir(struct opp_device *opp_dev, /* Create device specific directory */ d = debugfs_create_dir(opp_table->dentry_name, rootdir); - if (!d) { - dev_err(dev, "%s: Failed to create debugfs dir\n", __func__); - return -ENOMEM; - } opp_dev->dentry = d; opp_table->dentry = d; - - return 0; } -static int opp_list_debug_create_link(struct opp_device *opp_dev, - struct opp_table *opp_table) +static void opp_list_debug_create_link(struct opp_device *opp_dev, + struct opp_table *opp_table) { - const struct device *dev = opp_dev->dev; char name[NAME_MAX]; - struct dentry *d; opp_set_dev_name(opp_dev->dev, name); /* Create device specific directory link */ - d = debugfs_create_symlink(name, rootdir, opp_table->dentry_name); - if (!d) { - dev_err(dev, "%s: Failed to create link\n", __func__); - return -ENOMEM; - } - - opp_dev->dentry = d; - - return 0; + opp_dev->dentry = debugfs_create_symlink(name, rootdir, + opp_table->dentry_name); } /** @@ -177,20 +136,13 @@ static int opp_list_debug_create_link(struct opp_device *opp_dev, * Dynamically adds device specific directory in debugfs 'opp' directory. If the * device-opp is shared with other devices, then links will be created for all * devices except the first. - * - * Return: 0 on success, otherwise negative error. */ -int opp_debug_register(struct opp_device *opp_dev, struct opp_table *opp_table) +void opp_debug_register(struct opp_device *opp_dev, struct opp_table *opp_table) { - if (!rootdir) { - pr_debug("%s: Uninitialized rootdir\n", __func__); - return -EINVAL; - } - if (opp_table->dentry) - return opp_list_debug_create_link(opp_dev, opp_table); - - return opp_list_debug_create_dir(opp_dev, opp_table); + opp_list_debug_create_link(opp_dev, opp_table); + else + opp_list_debug_create_dir(opp_dev, opp_table); } static void opp_migrate_dentry(struct opp_device *opp_dev, @@ -252,10 +204,6 @@ static int __init opp_debug_init(void) { /* Create /sys/kernel/debug/opp directory */ rootdir = debugfs_create_dir("opp", NULL); - if (!rootdir) { - pr_err("%s: Failed to create root directory\n", __func__); - return -ENOMEM; - } return 0; } diff --git a/drivers/opp/opp.h b/drivers/opp/opp.h index e24d81497375..810a85b9a66d 100644 --- a/drivers/opp/opp.h +++ b/drivers/opp/opp.h @@ -236,18 +236,17 @@ static inline void _of_opp_free_required_opps(struct opp_table *opp_table, #ifdef CONFIG_DEBUG_FS void opp_debug_remove_one(struct dev_pm_opp *opp); -int opp_debug_create_one(struct dev_pm_opp *opp, struct opp_table *opp_table); -int opp_debug_register(struct opp_device *opp_dev, struct opp_table *opp_table); +void opp_debug_create_one(struct dev_pm_opp *opp, struct opp_table *opp_table); +void opp_debug_register(struct opp_device *opp_dev, struct opp_table *opp_table); void opp_debug_unregister(struct opp_device *opp_dev, struct opp_table *opp_table); #else static inline void opp_debug_remove_one(struct dev_pm_opp *opp) {} -static inline int opp_debug_create_one(struct dev_pm_opp *opp, - struct opp_table *opp_table) -{ return 0; } -static inline int opp_debug_register(struct opp_device *opp_dev, - struct opp_table *opp_table) -{ return 0; } +static inline void opp_debug_create_one(struct dev_pm_opp *opp, + struct opp_table *opp_table) { } + +static inline void opp_debug_register(struct opp_device *opp_dev, + struct opp_table *opp_table) { } static inline void opp_debug_unregister(struct opp_device *opp_dev, struct opp_table *opp_table) -- cgit v1.2.3