From 1224451bb6f938023dd7fa4e7ba43bfb185bc9e3 Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Mon, 8 Mar 2021 14:30:37 +0100 Subject: PM / devfreq: Register devfreq as a cooling device on demand Currently the default behavior is to manually having the devfreq backend to register themselves as a devfreq cooling device. Instead of adding the code in the drivers for the thermal cooling device registering, let's provide a flag in the devfreq's profile to tell the common devfreq code to register the newly created devfreq as a cooling device. Suggested-by: Chanwoo Choi Signed-off-by: Daniel Lezcano Reviewed-by: Steven Price Reviewed-by: Lukasz Luba Signed-off-by: Chanwoo Choi --- drivers/devfreq/devfreq.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers') diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index bf3047896e41..b6d3e7db0b09 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -935,6 +936,12 @@ struct devfreq *devfreq_add_device(struct device *dev, mutex_unlock(&devfreq_list_lock); + if (devfreq->profile->is_cooling_device) { + devfreq->cdev = devfreq_cooling_em_register(devfreq, NULL); + if (IS_ERR(devfreq->cdev)) + devfreq->cdev = NULL; + } + return devfreq; err_init: @@ -960,6 +967,8 @@ int devfreq_remove_device(struct devfreq *devfreq) if (!devfreq) return -EINVAL; + devfreq_cooling_unregister(devfreq->cdev); + if (devfreq->governor) { devfreq->governor->event_handler(devfreq, DEVFREQ_GOV_STOP, NULL); -- cgit v1.2.3-70-g09d2 From eec73529a9321616ed13cf732cd21a17eb1a2836 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 10 Mar 2021 08:16:40 +0530 Subject: arch_topology: Rename freq_scale as arch_freq_scale Rename freq_scale to a less generic name, as it will get exported soon for modules. Since x86 already names its own implementation of this as arch_freq_scale, lets stick to that. Suggested-by: Will Deacon Signed-off-by: Viresh Kumar --- arch/arm64/kernel/topology.c | 6 +++--- drivers/base/arch_topology.c | 4 ++-- include/linux/arch_topology.h | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/arch/arm64/kernel/topology.c b/arch/arm64/kernel/topology.c index e08a4126453a..aa8d893619ed 100644 --- a/arch/arm64/kernel/topology.c +++ b/arch/arm64/kernel/topology.c @@ -256,8 +256,8 @@ static int init_amu_fie_callback(struct notifier_block *nb, unsigned long val, * initialized AMU support and enabled invariance. The AMU counters will * keep on working just fine in the absence of the cpufreq driver, and * for the CPUs for which there are no counters available, the last set - * value of freq_scale will remain valid as that is the frequency those - * CPUs are running at. + * value of arch_freq_scale will remain valid as that is the frequency + * those CPUs are running at. */ return 0; @@ -327,7 +327,7 @@ void topology_scale_freq_tick(void) const_cnt - prev_const_cnt); scale = min_t(unsigned long, scale, SCHED_CAPACITY_SCALE); - this_cpu_write(freq_scale, (unsigned long)scale); + this_cpu_write(arch_freq_scale, (unsigned long)scale); } #ifdef CONFIG_ACPI_CPPC_LIB diff --git a/drivers/base/arch_topology.c b/drivers/base/arch_topology.c index de8587cc119e..2a1cecbde0a4 100644 --- a/drivers/base/arch_topology.c +++ b/drivers/base/arch_topology.c @@ -31,7 +31,7 @@ __weak bool arch_freq_counters_available(const struct cpumask *cpus) { return false; } -DEFINE_PER_CPU(unsigned long, freq_scale) = SCHED_CAPACITY_SCALE; +DEFINE_PER_CPU(unsigned long, arch_freq_scale) = SCHED_CAPACITY_SCALE; void topology_set_freq_scale(const struct cpumask *cpus, unsigned long cur_freq, unsigned long max_freq) @@ -53,7 +53,7 @@ void topology_set_freq_scale(const struct cpumask *cpus, unsigned long cur_freq, scale = (cur_freq << SCHED_CAPACITY_SHIFT) / max_freq; for_each_cpu(i, cpus) - per_cpu(freq_scale, i) = scale; + per_cpu(arch_freq_scale, i) = scale; } DEFINE_PER_CPU(unsigned long, cpu_scale) = SCHED_CAPACITY_SCALE; diff --git a/include/linux/arch_topology.h b/include/linux/arch_topology.h index 0f6cd6b73a61..583af517f123 100644 --- a/include/linux/arch_topology.h +++ b/include/linux/arch_topology.h @@ -23,11 +23,11 @@ static inline unsigned long topology_get_cpu_scale(int cpu) void topology_set_cpu_scale(unsigned int cpu, unsigned long capacity); -DECLARE_PER_CPU(unsigned long, freq_scale); +DECLARE_PER_CPU(unsigned long, arch_freq_scale); static inline unsigned long topology_get_freq_scale(int cpu) { - return per_cpu(freq_scale, cpu); + return per_cpu(arch_freq_scale, cpu); } void topology_set_freq_scale(const struct cpumask *cpus, unsigned long cur_freq, -- cgit v1.2.3-70-g09d2 From 01e055c120a46e78650b5f903088badbbdaae9ad Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 10 Mar 2021 08:21:04 +0530 Subject: arch_topology: Allow multiple entities to provide sched_freq_tick() callback This patch attempts to make it generic enough so other parts of the kernel can also provide their own implementation of scale_freq_tick() callback, which is called by the scheduler periodically to update the per-cpu arch_freq_scale variable. The implementations now need to provide 'struct scale_freq_data' for the CPUs for which they have hardware counters available, and a callback gets registered for each possible CPU in a per-cpu variable. The arch specific (or ARM AMU) counters are updated to adapt to this and they take the highest priority if they are available, i.e. they will be used instead of CPPC based counters for example. The special code to rebuild the sched domains, in case invariance status change for the system, is moved out of arm64 specific code and is added to arch_topology.c. Note that this also defines SCALE_FREQ_SOURCE_CPUFREQ but doesn't use it and it is added to show that cpufreq is also acts as source of information for FIE and will be used by default if no other counters are supported for a platform. Reviewed-by: Ionela Voinescu Tested-by: Ionela Voinescu Acked-by: Will Deacon # for arm64 Tested-by: Vincent Guittot Signed-off-by: Viresh Kumar --- arch/arm64/include/asm/topology.h | 10 +--- arch/arm64/kernel/topology.c | 105 ++++++++++++++------------------------ drivers/base/arch_topology.c | 82 +++++++++++++++++++++++++++-- include/linux/arch_topology.h | 14 ++++- 4 files changed, 131 insertions(+), 80 deletions(-) (limited to 'drivers') diff --git a/arch/arm64/include/asm/topology.h b/arch/arm64/include/asm/topology.h index 3b8dca4eb08d..ec2db3419c41 100644 --- a/arch/arm64/include/asm/topology.h +++ b/arch/arm64/include/asm/topology.h @@ -17,17 +17,9 @@ int pcibus_to_node(struct pci_bus *bus); #include void update_freq_counters_refs(void); -void topology_scale_freq_tick(void); - -#ifdef CONFIG_ARM64_AMU_EXTN -/* - * Replace task scheduler's default counter-based - * frequency-invariance scale factor setting. - */ -#define arch_scale_freq_tick topology_scale_freq_tick -#endif /* CONFIG_ARM64_AMU_EXTN */ /* Replace task scheduler's default frequency-invariant accounting */ +#define arch_scale_freq_tick topology_scale_freq_tick #define arch_set_freq_scale topology_set_freq_scale #define arch_scale_freq_capacity topology_get_freq_scale #define arch_scale_freq_invariant topology_scale_freq_invariant diff --git a/arch/arm64/kernel/topology.c b/arch/arm64/kernel/topology.c index aa8d893619ed..4dd14a6620c1 100644 --- a/arch/arm64/kernel/topology.c +++ b/arch/arm64/kernel/topology.c @@ -199,12 +199,47 @@ static int freq_inv_set_max_ratio(int cpu, u64 max_rate, u64 ref_rate) return 0; } -static DEFINE_STATIC_KEY_FALSE(amu_fie_key); -#define amu_freq_invariant() static_branch_unlikely(&amu_fie_key) +static void amu_scale_freq_tick(void) +{ + u64 prev_core_cnt, prev_const_cnt; + u64 core_cnt, const_cnt, scale; + + prev_const_cnt = this_cpu_read(arch_const_cycles_prev); + prev_core_cnt = this_cpu_read(arch_core_cycles_prev); + + update_freq_counters_refs(); + + const_cnt = this_cpu_read(arch_const_cycles_prev); + core_cnt = this_cpu_read(arch_core_cycles_prev); + + if (unlikely(core_cnt <= prev_core_cnt || + const_cnt <= prev_const_cnt)) + return; + + /* + * /\core arch_max_freq_scale + * scale = ------- * -------------------- + * /\const SCHED_CAPACITY_SCALE + * + * See validate_cpu_freq_invariance_counters() for details on + * arch_max_freq_scale and the use of SCHED_CAPACITY_SHIFT. + */ + scale = core_cnt - prev_core_cnt; + scale *= this_cpu_read(arch_max_freq_scale); + scale = div64_u64(scale >> SCHED_CAPACITY_SHIFT, + const_cnt - prev_const_cnt); + + scale = min_t(unsigned long, scale, SCHED_CAPACITY_SCALE); + this_cpu_write(arch_freq_scale, (unsigned long)scale); +} + +static struct scale_freq_data amu_sfd = { + .source = SCALE_FREQ_SOURCE_ARCH, + .set_freq_scale = amu_scale_freq_tick, +}; static void amu_fie_setup(const struct cpumask *cpus) { - bool invariant; int cpu; /* We are already set since the last insmod of cpufreq driver */ @@ -221,25 +256,10 @@ static void amu_fie_setup(const struct cpumask *cpus) cpumask_or(amu_fie_cpus, amu_fie_cpus, cpus); - invariant = topology_scale_freq_invariant(); - - /* We aren't fully invariant yet */ - if (!invariant && !cpumask_equal(amu_fie_cpus, cpu_present_mask)) - return; - - static_branch_enable(&amu_fie_key); + topology_set_scale_freq_source(&amu_sfd, amu_fie_cpus); pr_debug("CPUs[%*pbl]: counters will be used for FIE.", cpumask_pr_args(cpus)); - - /* - * Task scheduler behavior depends on frequency invariance support, - * either cpufreq or counter driven. If the support status changes as - * a result of counter initialisation and use, retrigger the build of - * scheduling domains to ensure the information is propagated properly. - */ - if (!invariant) - rebuild_sched_domains_energy(); } static int init_amu_fie_callback(struct notifier_block *nb, unsigned long val, @@ -283,53 +303,6 @@ static int __init init_amu_fie(void) } core_initcall(init_amu_fie); -bool arch_freq_counters_available(const struct cpumask *cpus) -{ - return amu_freq_invariant() && - cpumask_subset(cpus, amu_fie_cpus); -} - -void topology_scale_freq_tick(void) -{ - u64 prev_core_cnt, prev_const_cnt; - u64 core_cnt, const_cnt, scale; - int cpu = smp_processor_id(); - - if (!amu_freq_invariant()) - return; - - if (!cpumask_test_cpu(cpu, amu_fie_cpus)) - return; - - prev_const_cnt = this_cpu_read(arch_const_cycles_prev); - prev_core_cnt = this_cpu_read(arch_core_cycles_prev); - - update_freq_counters_refs(); - - const_cnt = this_cpu_read(arch_const_cycles_prev); - core_cnt = this_cpu_read(arch_core_cycles_prev); - - if (unlikely(core_cnt <= prev_core_cnt || - const_cnt <= prev_const_cnt)) - return; - - /* - * /\core arch_max_freq_scale - * scale = ------- * -------------------- - * /\const SCHED_CAPACITY_SCALE - * - * See validate_cpu_freq_invariance_counters() for details on - * arch_max_freq_scale and the use of SCHED_CAPACITY_SHIFT. - */ - scale = core_cnt - prev_core_cnt; - scale *= this_cpu_read(arch_max_freq_scale); - scale = div64_u64(scale >> SCHED_CAPACITY_SHIFT, - const_cnt - prev_const_cnt); - - scale = min_t(unsigned long, scale, SCHED_CAPACITY_SCALE); - this_cpu_write(arch_freq_scale, (unsigned long)scale); -} - #ifdef CONFIG_ACPI_CPPC_LIB #include diff --git a/drivers/base/arch_topology.c b/drivers/base/arch_topology.c index 2a1cecbde0a4..ebcd2ea3091f 100644 --- a/drivers/base/arch_topology.c +++ b/drivers/base/arch_topology.c @@ -21,16 +21,90 @@ #include #include +static DEFINE_PER_CPU(struct scale_freq_data *, sft_data); +static struct cpumask scale_freq_counters_mask; +static bool scale_freq_invariant; + +static bool supports_scale_freq_counters(const struct cpumask *cpus) +{ + return cpumask_subset(cpus, &scale_freq_counters_mask); +} + bool topology_scale_freq_invariant(void) { return cpufreq_supports_freq_invariance() || - arch_freq_counters_available(cpu_online_mask); + supports_scale_freq_counters(cpu_online_mask); } -__weak bool arch_freq_counters_available(const struct cpumask *cpus) +static void update_scale_freq_invariant(bool status) { - return false; + if (scale_freq_invariant == status) + return; + + /* + * Task scheduler behavior depends on frequency invariance support, + * either cpufreq or counter driven. If the support status changes as + * a result of counter initialisation and use, retrigger the build of + * scheduling domains to ensure the information is propagated properly. + */ + if (topology_scale_freq_invariant() == status) { + scale_freq_invariant = status; + rebuild_sched_domains_energy(); + } } + +void topology_set_scale_freq_source(struct scale_freq_data *data, + const struct cpumask *cpus) +{ + struct scale_freq_data *sfd; + int cpu; + + /* + * Avoid calling rebuild_sched_domains() unnecessarily if FIE is + * supported by cpufreq. + */ + if (cpumask_empty(&scale_freq_counters_mask)) + scale_freq_invariant = topology_scale_freq_invariant(); + + for_each_cpu(cpu, cpus) { + sfd = per_cpu(sft_data, cpu); + + /* Use ARCH provided counters whenever possible */ + if (!sfd || sfd->source != SCALE_FREQ_SOURCE_ARCH) { + per_cpu(sft_data, cpu) = data; + cpumask_set_cpu(cpu, &scale_freq_counters_mask); + } + } + + update_scale_freq_invariant(true); +} + +void topology_clear_scale_freq_source(enum scale_freq_source source, + const struct cpumask *cpus) +{ + struct scale_freq_data *sfd; + int cpu; + + for_each_cpu(cpu, cpus) { + sfd = per_cpu(sft_data, cpu); + + if (sfd && sfd->source == source) { + per_cpu(sft_data, cpu) = NULL; + cpumask_clear_cpu(cpu, &scale_freq_counters_mask); + } + } + + update_scale_freq_invariant(false); +} + +void topology_scale_freq_tick(void) +{ + struct scale_freq_data *sfd = *this_cpu_ptr(&sft_data); + + if (sfd) + sfd->set_freq_scale(); +} + DEFINE_PER_CPU(unsigned long, arch_freq_scale) = SCHED_CAPACITY_SCALE; void topology_set_freq_scale(const struct cpumask *cpus, unsigned long cur_freq, @@ -47,7 +121,7 @@ void topology_set_freq_scale(const struct cpumask *cpus, unsigned long cur_freq, * want to update the scale factor with information from CPUFREQ. * Instead the scale factor will be updated from arch_scale_freq_tick. */ - if (arch_freq_counters_available(cpus)) + if (supports_scale_freq_counters(cpus)) return; scale = (cur_freq << SCHED_CAPACITY_SHIFT) / max_freq; diff --git a/include/linux/arch_topology.h b/include/linux/arch_topology.h index 583af517f123..11e555cfaecb 100644 --- a/include/linux/arch_topology.h +++ b/include/linux/arch_topology.h @@ -34,7 +34,19 @@ void topology_set_freq_scale(const struct cpumask *cpus, unsigned long cur_freq, unsigned long max_freq); bool topology_scale_freq_invariant(void); -bool arch_freq_counters_available(const struct cpumask *cpus); +enum scale_freq_source { + SCALE_FREQ_SOURCE_CPUFREQ = 0, + SCALE_FREQ_SOURCE_ARCH, +}; + +struct scale_freq_data { + enum scale_freq_source source; + void (*set_freq_scale)(void); +}; + +void topology_scale_freq_tick(void); +void topology_set_scale_freq_source(struct scale_freq_data *data, const struct cpumask *cpus); +void topology_clear_scale_freq_source(enum scale_freq_source source, const struct cpumask *cpus); DECLARE_PER_CPU(unsigned long, thermal_pressure); -- cgit v1.2.3-70-g09d2 From 2f5339582e7b540851bfb37e184d4dee0ab9e387 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 10 Mar 2021 08:25:27 +0530 Subject: arch_topology: Export arch_freq_scale and helpers It is possible now for other parts of the kernel to provide their own implementation of sched_freq_tick() and they can very well be modules themselves (like CPPC cpufreq driver, which is going to use these in a later commit). Export arch_freq_scale and topology_{set|clear}_scale_freq_source(). Reviewed-by: Ionela Voinescu Tested-by: Ionela Voinescu Tested-by: Vincent Guittot Signed-off-by: Viresh Kumar --- drivers/base/arch_topology.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/base/arch_topology.c b/drivers/base/arch_topology.c index ebcd2ea3091f..c1179edc0f3b 100644 --- a/drivers/base/arch_topology.c +++ b/drivers/base/arch_topology.c @@ -78,6 +78,7 @@ void topology_set_scale_freq_source(struct scale_freq_data *data, update_scale_freq_invariant(true); } +EXPORT_SYMBOL_GPL(topology_set_scale_freq_source); void topology_clear_scale_freq_source(enum scale_freq_source source, const struct cpumask *cpus) @@ -96,6 +97,7 @@ void topology_clear_scale_freq_source(enum scale_freq_source source, update_scale_freq_invariant(false); } +EXPORT_SYMBOL_GPL(topology_clear_scale_freq_source); void topology_scale_freq_tick(void) { @@ -106,6 +108,7 @@ void topology_scale_freq_tick(void) } DEFINE_PER_CPU(unsigned long, arch_freq_scale) = SCHED_CAPACITY_SCALE; +EXPORT_PER_CPU_SYMBOL_GPL(arch_freq_scale); void topology_set_freq_scale(const struct cpumask *cpus, unsigned long cur_freq, unsigned long max_freq) -- cgit v1.2.3-70-g09d2 From a74f681c3710b47a093d910ca7c6666b3d1e3a2c Mon Sep 17 00:00:00 2001 From: Yangtao Li Date: Sun, 14 Mar 2021 19:33:54 +0300 Subject: opp: Add devres wrapper for dev_pm_opp_set_clkname Add devres wrapper for dev_pm_opp_set_clkname() to simplify drivers code. Signed-off-by: Yangtao Li Signed-off-by: Dmitry Osipenko Signed-off-by: Viresh Kumar --- drivers/opp/core.c | 27 +++++++++++++++++++++++++++ include/linux/pm_opp.h | 6 ++++++ 2 files changed, 33 insertions(+) (limited to 'drivers') diff --git a/drivers/opp/core.c b/drivers/opp/core.c index 1556998425d5..61c48b2d4679 100644 --- a/drivers/opp/core.c +++ b/drivers/opp/core.c @@ -2119,6 +2119,33 @@ void dev_pm_opp_put_clkname(struct opp_table *opp_table) } EXPORT_SYMBOL_GPL(dev_pm_opp_put_clkname); +static void devm_pm_opp_clkname_release(void *data) +{ + dev_pm_opp_put_clkname(data); +} + +/** + * devm_pm_opp_set_clkname() - Set clk name for the device + * @dev: Device for which clk name is being set. + * @name: Clk name. + * + * This is a resource-managed variant of dev_pm_opp_set_clkname(). + * + * Return: 0 on success and errorno otherwise. + */ +int devm_pm_opp_set_clkname(struct device *dev, const char *name) +{ + struct opp_table *opp_table; + + opp_table = dev_pm_opp_set_clkname(dev, name); + if (IS_ERR(opp_table)) + return PTR_ERR(opp_table); + + return devm_add_action_or_reset(dev, devm_pm_opp_clkname_release, + opp_table); +} +EXPORT_SYMBOL_GPL(devm_pm_opp_set_clkname); + /** * dev_pm_opp_register_set_opp_helper() - Register custom set OPP helper * @dev: Device for which the helper is getting registered. diff --git a/include/linux/pm_opp.h b/include/linux/pm_opp.h index c0371efa4a0f..0583d775aa5a 100644 --- a/include/linux/pm_opp.h +++ b/include/linux/pm_opp.h @@ -150,6 +150,7 @@ struct opp_table *dev_pm_opp_set_regulators(struct device *dev, const char * con void dev_pm_opp_put_regulators(struct opp_table *opp_table); struct opp_table *dev_pm_opp_set_clkname(struct device *dev, const char *name); void dev_pm_opp_put_clkname(struct opp_table *opp_table); +int devm_pm_opp_set_clkname(struct device *dev, const char *name); struct opp_table *dev_pm_opp_register_set_opp_helper(struct device *dev, int (*set_opp)(struct dev_pm_set_opp_data *data)); void dev_pm_opp_unregister_set_opp_helper(struct opp_table *opp_table); struct opp_table *devm_pm_opp_register_set_opp_helper(struct device *dev, int (*set_opp)(struct dev_pm_set_opp_data *data)); @@ -355,6 +356,11 @@ static inline struct opp_table *dev_pm_opp_set_clkname(struct device *dev, const static inline void dev_pm_opp_put_clkname(struct opp_table *opp_table) {} +static inline int devm_pm_opp_set_clkname(struct device *dev, const char *name) +{ + return -EOPNOTSUPP; +} + static inline struct opp_table *dev_pm_opp_attach_genpd(struct device *dev, const char **names, struct device ***virt_devs) { return ERR_PTR(-EOPNOTSUPP); -- cgit v1.2.3-70-g09d2 From 32aee78bc5184c7a51a081939721e97cfad4a44e Mon Sep 17 00:00:00 2001 From: Yangtao Li Date: Sun, 14 Mar 2021 19:33:55 +0300 Subject: opp: Add devres wrapper for dev_pm_opp_set_regulators Add devres wrapper for dev_pm_opp_set_regulators() to simplify drivers code. Signed-off-by: Yangtao Li Signed-off-by: Dmitry Osipenko Signed-off-by: Viresh Kumar --- drivers/opp/core.c | 30 ++++++++++++++++++++++++++++++ include/linux/pm_opp.h | 8 ++++++++ 2 files changed, 38 insertions(+) (limited to 'drivers') diff --git a/drivers/opp/core.c b/drivers/opp/core.c index 61c48b2d4679..d77ec2c55783 100644 --- a/drivers/opp/core.c +++ b/drivers/opp/core.c @@ -2047,6 +2047,36 @@ put_opp_table: } EXPORT_SYMBOL_GPL(dev_pm_opp_put_regulators); +static void devm_pm_opp_regulators_release(void *data) +{ + dev_pm_opp_put_regulators(data); +} + +/** + * devm_pm_opp_set_regulators() - Set regulator names for the device + * @dev: Device for which regulator name is being set. + * @names: Array of pointers to the names of the regulator. + * @count: Number of regulators. + * + * This is a resource-managed variant of dev_pm_opp_set_regulators(). + * + * Return: 0 on success and errorno otherwise. + */ +int devm_pm_opp_set_regulators(struct device *dev, + const char * const names[], + unsigned int count) +{ + struct opp_table *opp_table; + + opp_table = dev_pm_opp_set_regulators(dev, names, count); + if (IS_ERR(opp_table)) + return PTR_ERR(opp_table); + + return devm_add_action_or_reset(dev, devm_pm_opp_regulators_release, + opp_table); +} +EXPORT_SYMBOL_GPL(devm_pm_opp_set_regulators); + /** * dev_pm_opp_set_clkname() - Set clk name for the device * @dev: Device for which clk name is being set. diff --git a/include/linux/pm_opp.h b/include/linux/pm_opp.h index 0583d775aa5a..3e667af57211 100644 --- a/include/linux/pm_opp.h +++ b/include/linux/pm_opp.h @@ -148,6 +148,7 @@ struct opp_table *dev_pm_opp_set_prop_name(struct device *dev, const char *name) void dev_pm_opp_put_prop_name(struct opp_table *opp_table); struct opp_table *dev_pm_opp_set_regulators(struct device *dev, const char * const names[], unsigned int count); void dev_pm_opp_put_regulators(struct opp_table *opp_table); +int devm_pm_opp_set_regulators(struct device *dev, const char * const names[], unsigned int count); struct opp_table *dev_pm_opp_set_clkname(struct device *dev, const char *name); void dev_pm_opp_put_clkname(struct opp_table *opp_table); int devm_pm_opp_set_clkname(struct device *dev, const char *name); @@ -349,6 +350,13 @@ static inline struct opp_table *dev_pm_opp_set_regulators(struct device *dev, co static inline void dev_pm_opp_put_regulators(struct opp_table *opp_table) {} +static inline int devm_pm_opp_set_regulators(struct device *dev, + const char * const names[], + unsigned int count) +{ + return -EOPNOTSUPP; +} + static inline struct opp_table *dev_pm_opp_set_clkname(struct device *dev, const char *name) { return ERR_PTR(-EOPNOTSUPP); -- cgit v1.2.3-70-g09d2 From 9c4f220f3dc260e325c92e8588ade2affcb6528c Mon Sep 17 00:00:00 2001 From: Yangtao Li Date: Sun, 14 Mar 2021 19:33:56 +0300 Subject: opp: Add devres wrapper for dev_pm_opp_set_supported_hw Add devres wrapper for dev_pm_opp_set_supported_hw() to simplify drivers code. Signed-off-by: Yangtao Li Signed-off-by: Dmitry Osipenko Signed-off-by: Viresh Kumar --- drivers/opp/core.c | 29 +++++++++++++++++++++++++++++ include/linux/pm_opp.h | 8 ++++++++ 2 files changed, 37 insertions(+) (limited to 'drivers') diff --git a/drivers/opp/core.c b/drivers/opp/core.c index d77ec2c55783..956ea22a21d6 100644 --- a/drivers/opp/core.c +++ b/drivers/opp/core.c @@ -1857,6 +1857,35 @@ void dev_pm_opp_put_supported_hw(struct opp_table *opp_table) } EXPORT_SYMBOL_GPL(dev_pm_opp_put_supported_hw); +static void devm_pm_opp_supported_hw_release(void *data) +{ + dev_pm_opp_put_supported_hw(data); +} + +/** + * devm_pm_opp_set_supported_hw() - Set supported platforms + * @dev: Device for which supported-hw has to be set. + * @versions: Array of hierarchy of versions to match. + * @count: Number of elements in the array. + * + * This is a resource-managed variant of dev_pm_opp_set_supported_hw(). + * + * Return: 0 on success and errorno otherwise. + */ +int devm_pm_opp_set_supported_hw(struct device *dev, const u32 *versions, + unsigned int count) +{ + struct opp_table *opp_table; + + opp_table = dev_pm_opp_set_supported_hw(dev, versions, count); + if (IS_ERR(opp_table)) + return PTR_ERR(opp_table); + + return devm_add_action_or_reset(dev, devm_pm_opp_supported_hw_release, + opp_table); +} +EXPORT_SYMBOL_GPL(devm_pm_opp_set_supported_hw); + /** * dev_pm_opp_set_prop_name() - Set prop-extn name * @dev: Device for which the prop-name has to be set. diff --git a/include/linux/pm_opp.h b/include/linux/pm_opp.h index 3e667af57211..e455b187e405 100644 --- a/include/linux/pm_opp.h +++ b/include/linux/pm_opp.h @@ -144,6 +144,7 @@ int dev_pm_opp_unregister_notifier(struct device *dev, struct notifier_block *nb struct opp_table *dev_pm_opp_set_supported_hw(struct device *dev, const u32 *versions, unsigned int count); void dev_pm_opp_put_supported_hw(struct opp_table *opp_table); +int devm_pm_opp_set_supported_hw(struct device *dev, const u32 *versions, unsigned int count); struct opp_table *dev_pm_opp_set_prop_name(struct device *dev, const char *name); void dev_pm_opp_put_prop_name(struct opp_table *opp_table); struct opp_table *dev_pm_opp_set_regulators(struct device *dev, const char * const names[], unsigned int count); @@ -321,6 +322,13 @@ static inline struct opp_table *dev_pm_opp_set_supported_hw(struct device *dev, static inline void dev_pm_opp_put_supported_hw(struct opp_table *opp_table) {} +static inline int devm_pm_opp_set_supported_hw(struct device *dev, + const u32 *versions, + unsigned int count) +{ + return -EOPNOTSUPP; +} + static inline struct opp_table *dev_pm_opp_register_set_opp_helper(struct device *dev, int (*set_opp)(struct dev_pm_set_opp_data *data)) { -- cgit v1.2.3-70-g09d2 From 3d5cfbb69508db7d092475be01c66edc86066717 Mon Sep 17 00:00:00 2001 From: Yangtao Li Date: Sun, 14 Mar 2021 19:33:57 +0300 Subject: opp: Add devres wrapper for dev_pm_opp_of_add_table Add devres wrapper for dev_pm_opp_of_add_table() to simplify drivers code. Signed-off-by: Yangtao Li Signed-off-by: Dmitry Osipenko Signed-off-by: Viresh Kumar --- drivers/opp/of.c | 36 ++++++++++++++++++++++++++++++++++++ include/linux/pm_opp.h | 6 ++++++ 2 files changed, 42 insertions(+) (limited to 'drivers') diff --git a/drivers/opp/of.c b/drivers/opp/of.c index f480c10e6314..c582a9ca397b 100644 --- a/drivers/opp/of.c +++ b/drivers/opp/of.c @@ -1104,6 +1104,42 @@ static int _of_add_table_indexed(struct device *dev, int index, bool getclk) return ret; } +static void devm_pm_opp_of_table_release(void *data) +{ + dev_pm_opp_of_remove_table(data); +} + +/** + * devm_pm_opp_of_add_table() - Initialize opp table from device tree + * @dev: device pointer used to lookup OPP table. + * + * Register the initial OPP table with the OPP library for given device. + * + * The opp_table structure will be freed after the device is destroyed. + * + * Return: + * 0 On success OR + * Duplicate OPPs (both freq and volt are same) and opp->available + * -EEXIST Freq are same and volt are different OR + * Duplicate OPPs (both freq and volt are same) and !opp->available + * -ENOMEM Memory allocation failure + * -ENODEV when 'operating-points' property is not found or is invalid data + * in device node. + * -ENODATA when empty 'operating-points' property is found + * -EINVAL when invalid entries are found in opp-v2 table + */ +int devm_pm_opp_of_add_table(struct device *dev) +{ + int ret; + + ret = dev_pm_opp_of_add_table(dev); + if (ret) + return ret; + + return devm_add_action_or_reset(dev, devm_pm_opp_of_table_release, dev); +} +EXPORT_SYMBOL_GPL(devm_pm_opp_of_add_table); + /** * dev_pm_opp_of_add_table() - Initialize opp table from device tree * @dev: device pointer used to lookup OPP table. diff --git a/include/linux/pm_opp.h b/include/linux/pm_opp.h index e455b187e405..3f8894012429 100644 --- a/include/linux/pm_opp.h +++ b/include/linux/pm_opp.h @@ -441,6 +441,7 @@ int dev_pm_opp_of_add_table(struct device *dev); int dev_pm_opp_of_add_table_indexed(struct device *dev, int index); int dev_pm_opp_of_add_table_noclk(struct device *dev, int index); void dev_pm_opp_of_remove_table(struct device *dev); +int devm_pm_opp_of_add_table(struct device *dev); int dev_pm_opp_of_cpumask_add_table(const struct cpumask *cpumask); void dev_pm_opp_of_cpumask_remove_table(const struct cpumask *cpumask); int dev_pm_opp_of_get_sharing_cpus(struct device *cpu_dev, struct cpumask *cpumask); @@ -473,6 +474,11 @@ static inline void dev_pm_opp_of_remove_table(struct device *dev) { } +static inline int devm_pm_opp_of_add_table(struct device *dev) +{ + return -EOPNOTSUPP; +} + static inline int dev_pm_opp_of_cpumask_add_table(const struct cpumask *cpumask) { return -EOPNOTSUPP; -- cgit v1.2.3-70-g09d2 From c41c8a3485b0b36b7e308eeff8716eb77093596a Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Sun, 14 Mar 2021 19:33:58 +0300 Subject: opp: Change return type of devm_pm_opp_register_set_opp_helper() Make devm_pm_opp_register_set_opp_helper() to return error code instead of opp_table pointer in order to have return type consistent with the other resource-managed OPP helpers. Signed-off-by: Dmitry Osipenko Signed-off-by: Viresh Kumar --- drivers/opp/core.c | 18 ++++++------------ include/linux/pm_opp.h | 7 +++---- 2 files changed, 9 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/opp/core.c b/drivers/opp/core.c index 956ea22a21d6..89c3b0b219ce 100644 --- a/drivers/opp/core.c +++ b/drivers/opp/core.c @@ -2295,25 +2295,19 @@ static void devm_pm_opp_unregister_set_opp_helper(void *data) * * This is a resource-managed version of dev_pm_opp_register_set_opp_helper(). * - * Return: pointer to 'struct opp_table' on success and errorno otherwise. + * Return: 0 on success and errorno otherwise. */ -struct opp_table * -devm_pm_opp_register_set_opp_helper(struct device *dev, - int (*set_opp)(struct dev_pm_set_opp_data *data)) +int devm_pm_opp_register_set_opp_helper(struct device *dev, + int (*set_opp)(struct dev_pm_set_opp_data *data)) { struct opp_table *opp_table; - int err; opp_table = dev_pm_opp_register_set_opp_helper(dev, set_opp); if (IS_ERR(opp_table)) - return opp_table; - - err = devm_add_action_or_reset(dev, devm_pm_opp_unregister_set_opp_helper, - opp_table); - if (err) - return ERR_PTR(err); + return PTR_ERR(opp_table); - return opp_table; + return devm_add_action_or_reset(dev, devm_pm_opp_unregister_set_opp_helper, + opp_table); } EXPORT_SYMBOL_GPL(devm_pm_opp_register_set_opp_helper); diff --git a/include/linux/pm_opp.h b/include/linux/pm_opp.h index 3f8894012429..2cf9694908a2 100644 --- a/include/linux/pm_opp.h +++ b/include/linux/pm_opp.h @@ -155,7 +155,7 @@ void dev_pm_opp_put_clkname(struct opp_table *opp_table); int devm_pm_opp_set_clkname(struct device *dev, const char *name); struct opp_table *dev_pm_opp_register_set_opp_helper(struct device *dev, int (*set_opp)(struct dev_pm_set_opp_data *data)); void dev_pm_opp_unregister_set_opp_helper(struct opp_table *opp_table); -struct opp_table *devm_pm_opp_register_set_opp_helper(struct device *dev, int (*set_opp)(struct dev_pm_set_opp_data *data)); +int devm_pm_opp_register_set_opp_helper(struct device *dev, int (*set_opp)(struct dev_pm_set_opp_data *data)); struct opp_table *dev_pm_opp_attach_genpd(struct device *dev, const char **names, struct device ***virt_devs); void dev_pm_opp_detach_genpd(struct opp_table *opp_table); struct opp_table *devm_pm_opp_attach_genpd(struct device *dev, const char **names, struct device ***virt_devs); @@ -337,11 +337,10 @@ static inline struct opp_table *dev_pm_opp_register_set_opp_helper(struct device static inline void dev_pm_opp_unregister_set_opp_helper(struct opp_table *opp_table) {} -static inline struct opp_table * -devm_pm_opp_register_set_opp_helper(struct device *dev, +static inline int devm_pm_opp_register_set_opp_helper(struct device *dev, int (*set_opp)(struct dev_pm_set_opp_data *data)) { - return ERR_PTR(-EOPNOTSUPP); + return -EOPNOTSUPP; } static inline struct opp_table *dev_pm_opp_set_prop_name(struct device *dev, const char *name) -- cgit v1.2.3-70-g09d2 From 9edf48a4bfb75456f7612972a4750a12d8a83702 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Sun, 14 Mar 2021 19:33:59 +0300 Subject: opp: Change return type of devm_pm_opp_attach_genpd() Make devm_pm_opp_attach_genpd() to return error code instead of opp_table pointer in order to have return type consistent with the other resource-managed OPP helpers. Signed-off-by: Dmitry Osipenko Signed-off-by: Viresh Kumar --- drivers/opp/core.c | 18 ++++++------------ include/linux/pm_opp.h | 9 +++++---- 2 files changed, 11 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/opp/core.c b/drivers/opp/core.c index 89c3b0b219ce..e366218d6736 100644 --- a/drivers/opp/core.c +++ b/drivers/opp/core.c @@ -2460,25 +2460,19 @@ static void devm_pm_opp_detach_genpd(void *data) * * This is a resource-managed version of dev_pm_opp_attach_genpd(). * - * Return: pointer to 'struct opp_table' on success and errorno otherwise. + * Return: 0 on success and errorno otherwise. */ -struct opp_table * -devm_pm_opp_attach_genpd(struct device *dev, const char **names, - struct device ***virt_devs) +int devm_pm_opp_attach_genpd(struct device *dev, const char **names, + struct device ***virt_devs) { struct opp_table *opp_table; - int err; opp_table = dev_pm_opp_attach_genpd(dev, names, virt_devs); if (IS_ERR(opp_table)) - return opp_table; - - err = devm_add_action_or_reset(dev, devm_pm_opp_detach_genpd, - opp_table); - if (err) - return ERR_PTR(err); + return PTR_ERR(opp_table); - return opp_table; + return devm_add_action_or_reset(dev, devm_pm_opp_detach_genpd, + opp_table); } EXPORT_SYMBOL_GPL(devm_pm_opp_attach_genpd); diff --git a/include/linux/pm_opp.h b/include/linux/pm_opp.h index 2cf9694908a2..84150a22fd7c 100644 --- a/include/linux/pm_opp.h +++ b/include/linux/pm_opp.h @@ -158,7 +158,7 @@ void dev_pm_opp_unregister_set_opp_helper(struct opp_table *opp_table); int devm_pm_opp_register_set_opp_helper(struct device *dev, int (*set_opp)(struct dev_pm_set_opp_data *data)); struct opp_table *dev_pm_opp_attach_genpd(struct device *dev, const char **names, struct device ***virt_devs); void dev_pm_opp_detach_genpd(struct opp_table *opp_table); -struct opp_table *devm_pm_opp_attach_genpd(struct device *dev, const char **names, struct device ***virt_devs); +int devm_pm_opp_attach_genpd(struct device *dev, const char **names, struct device ***virt_devs); struct dev_pm_opp *dev_pm_opp_xlate_required_opp(struct opp_table *src_table, struct opp_table *dst_table, struct dev_pm_opp *src_opp); int dev_pm_opp_xlate_performance_state(struct opp_table *src_table, struct opp_table *dst_table, unsigned int pstate); int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq); @@ -383,10 +383,11 @@ static inline struct opp_table *dev_pm_opp_attach_genpd(struct device *dev, cons static inline void dev_pm_opp_detach_genpd(struct opp_table *opp_table) {} -static inline struct opp_table *devm_pm_opp_attach_genpd(struct device *dev, - const char **names, struct device ***virt_devs) +static inline int devm_pm_opp_attach_genpd(struct device *dev, + const char **names, + struct device ***virt_devs) { - return ERR_PTR(-EOPNOTSUPP); + return -EOPNOTSUPP; } static inline struct dev_pm_opp *dev_pm_opp_xlate_required_opp(struct opp_table *src_table, -- cgit v1.2.3-70-g09d2 From 300894a6fef7f78432372c8096b7c9b455832560 Mon Sep 17 00:00:00 2001 From: Yangtao Li Date: Sun, 14 Mar 2021 19:34:00 +0300 Subject: serial: qcom_geni_serial: Convert to use resource-managed OPP API Use resource-managed OPP API to simplify code. Signed-off-by: Yangtao Li Signed-off-by: Dmitry Osipenko Signed-off-by: Viresh Kumar --- drivers/tty/serial/qcom_geni_serial.c | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c index 291649f02821..3872bbfac24b 100644 --- a/drivers/tty/serial/qcom_geni_serial.c +++ b/drivers/tty/serial/qcom_geni_serial.c @@ -1433,14 +1433,14 @@ static int qcom_geni_serial_probe(struct platform_device *pdev) if (of_property_read_bool(pdev->dev.of_node, "cts-rts-swap")) port->cts_rts_swap = true; - port->se.opp_table = dev_pm_opp_set_clkname(&pdev->dev, "se"); - if (IS_ERR(port->se.opp_table)) - return PTR_ERR(port->se.opp_table); + ret = devm_pm_opp_set_clkname(&pdev->dev, "se"); + if (ret) + return ret; /* OPP table is optional */ - ret = dev_pm_opp_of_add_table(&pdev->dev); + ret = devm_pm_opp_of_add_table(&pdev->dev); if (ret && ret != -ENODEV) { dev_err(&pdev->dev, "invalid OPP table in device tree\n"); - goto put_clkname; + return ret; } port->private_data.drv = drv; @@ -1450,7 +1450,7 @@ static int qcom_geni_serial_probe(struct platform_device *pdev) ret = uart_add_one_port(drv, uport); if (ret) - goto err; + return ret; irq_set_status_flags(uport->irq, IRQ_NOAUTOEN); ret = devm_request_irq(uport->dev, uport->irq, qcom_geni_serial_isr, @@ -1458,7 +1458,7 @@ static int qcom_geni_serial_probe(struct platform_device *pdev) if (ret) { dev_err(uport->dev, "Failed to get IRQ ret %d\n", ret); uart_remove_one_port(drv, uport); - goto err; + return ret; } /* @@ -1475,16 +1475,11 @@ static int qcom_geni_serial_probe(struct platform_device *pdev) if (ret) { device_init_wakeup(&pdev->dev, false); uart_remove_one_port(drv, uport); - goto err; + return ret; } } return 0; -err: - dev_pm_opp_of_remove_table(&pdev->dev); -put_clkname: - dev_pm_opp_put_clkname(port->se.opp_table); - return ret; } static int qcom_geni_serial_remove(struct platform_device *pdev) @@ -1492,8 +1487,6 @@ static int qcom_geni_serial_remove(struct platform_device *pdev) struct qcom_geni_serial_port *port = platform_get_drvdata(pdev); struct uart_driver *drv = port->private_data.drv; - dev_pm_opp_of_remove_table(&pdev->dev); - dev_pm_opp_put_clkname(port->se.opp_table); dev_pm_clear_wake_irq(&pdev->dev); device_init_wakeup(&pdev->dev, false); uart_remove_one_port(drv, &port->uport); -- cgit v1.2.3-70-g09d2 From cfb12911ffb0ed38c7c420efb9d5dc8494e28109 Mon Sep 17 00:00:00 2001 From: Yangtao Li Date: Sun, 14 Mar 2021 19:34:01 +0300 Subject: spi: spi-geni-qcom: Convert to use resource-managed OPP API Use resource-managed OPP API to simplify code. Signed-off-by: Yangtao Li Acked-by: Mark Brown Signed-off-by: Dmitry Osipenko Signed-off-by: Viresh Kumar --- drivers/spi/spi-geni-qcom.c | 16 ++++++---------- include/linux/qcom-geni-se.h | 2 -- 2 files changed, 6 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/spi-geni-qcom.c b/drivers/spi/spi-geni-qcom.c index 881f645661cc..3d0d8ddd5772 100644 --- a/drivers/spi/spi-geni-qcom.c +++ b/drivers/spi/spi-geni-qcom.c @@ -691,14 +691,15 @@ static int spi_geni_probe(struct platform_device *pdev) mas->se.wrapper = dev_get_drvdata(dev->parent); mas->se.base = base; mas->se.clk = clk; - mas->se.opp_table = dev_pm_opp_set_clkname(&pdev->dev, "se"); - if (IS_ERR(mas->se.opp_table)) - return PTR_ERR(mas->se.opp_table); + + ret = devm_pm_opp_set_clkname(&pdev->dev, "se"); + if (ret) + return ret; /* OPP table is optional */ - ret = dev_pm_opp_of_add_table(&pdev->dev); + ret = devm_pm_opp_of_add_table(&pdev->dev); if (ret && ret != -ENODEV) { dev_err(&pdev->dev, "invalid OPP table in device tree\n"); - goto put_clkname; + return ret; } spi->bus_num = -1; @@ -750,9 +751,6 @@ spi_geni_probe_free_irq: free_irq(mas->irq, spi); spi_geni_probe_runtime_disable: pm_runtime_disable(dev); - dev_pm_opp_of_remove_table(&pdev->dev); -put_clkname: - dev_pm_opp_put_clkname(mas->se.opp_table); return ret; } @@ -766,8 +764,6 @@ static int spi_geni_remove(struct platform_device *pdev) free_irq(mas->irq, spi); pm_runtime_disable(&pdev->dev); - dev_pm_opp_of_remove_table(&pdev->dev); - dev_pm_opp_put_clkname(mas->se.opp_table); return 0; } diff --git a/include/linux/qcom-geni-se.h b/include/linux/qcom-geni-se.h index ec2ad4b0fe14..cddef864a760 100644 --- a/include/linux/qcom-geni-se.h +++ b/include/linux/qcom-geni-se.h @@ -47,7 +47,6 @@ struct geni_icc_path { * @num_clk_levels: Number of valid clock levels in clk_perf_tbl * @clk_perf_tbl: Table of clock frequency input to serial engine clock * @icc_paths: Array of ICC paths for SE - * @opp_table: Pointer to the OPP table */ struct geni_se { void __iomem *base; @@ -57,7 +56,6 @@ struct geni_se { unsigned int num_clk_levels; unsigned long *clk_perf_tbl; struct geni_icc_path icc_paths[3]; - struct opp_table *opp_table; }; /* Common SE registers */ -- cgit v1.2.3-70-g09d2 From 6504dcae67ad5fc241217c23825e5649f53c09fc Mon Sep 17 00:00:00 2001 From: Yangtao Li Date: Sun, 14 Mar 2021 19:34:02 +0300 Subject: spi: spi-qcom-qspi: Convert to use resource-managed OPP API Use resource-managed OPP API to simplify code. Signed-off-by: Yangtao Li Acked-by: Mark Brown Signed-off-by: Dmitry Osipenko Signed-off-by: Viresh Kumar --- drivers/spi/spi-qcom-qspi.c | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/spi-qcom-qspi.c b/drivers/spi/spi-qcom-qspi.c index 1dbcc410cd35..c334dfec4117 100644 --- a/drivers/spi/spi-qcom-qspi.c +++ b/drivers/spi/spi-qcom-qspi.c @@ -142,7 +142,6 @@ struct qcom_qspi { struct clk_bulk_data *clks; struct qspi_xfer xfer; struct icc_path *icc_path_cpu_to_qspi; - struct opp_table *opp_table; unsigned long last_speed; /* Lock to protect data accessed by IRQs */ spinlock_t lock; @@ -530,14 +529,14 @@ static int qcom_qspi_probe(struct platform_device *pdev) master->handle_err = qcom_qspi_handle_err; master->auto_runtime_pm = true; - ctrl->opp_table = dev_pm_opp_set_clkname(&pdev->dev, "core"); - if (IS_ERR(ctrl->opp_table)) - return PTR_ERR(ctrl->opp_table); + ret = devm_pm_opp_set_clkname(&pdev->dev, "core"); + if (ret) + return ret; /* OPP table is optional */ - ret = dev_pm_opp_of_add_table(&pdev->dev); + ret = devm_pm_opp_of_add_table(&pdev->dev); if (ret && ret != -ENODEV) { dev_err(&pdev->dev, "invalid OPP table in device tree\n"); - goto exit_probe_put_clkname; + return ret; } pm_runtime_use_autosuspend(dev); @@ -549,10 +548,6 @@ static int qcom_qspi_probe(struct platform_device *pdev) return 0; pm_runtime_disable(dev); - dev_pm_opp_of_remove_table(&pdev->dev); - -exit_probe_put_clkname: - dev_pm_opp_put_clkname(ctrl->opp_table); return ret; } @@ -560,14 +555,11 @@ exit_probe_put_clkname: static int qcom_qspi_remove(struct platform_device *pdev) { struct spi_master *master = platform_get_drvdata(pdev); - struct qcom_qspi *ctrl = spi_master_get_devdata(master); /* Unregister _before_ disabling pm_runtime() so we stop transfers */ spi_unregister_master(master); pm_runtime_disable(&pdev->dev); - dev_pm_opp_of_remove_table(&pdev->dev); - dev_pm_opp_put_clkname(ctrl->opp_table); return 0; } -- cgit v1.2.3-70-g09d2 From 411281d24bb84889f31ddd9d733f495b29e10a8b Mon Sep 17 00:00:00 2001 From: Yangtao Li Date: Sun, 14 Mar 2021 19:34:03 +0300 Subject: mmc: sdhci-msm: Convert to use resource-managed OPP API Use resource-managed OPP API to simplify code. Signed-off-by: Yangtao Li Signed-off-by: Dmitry Osipenko Signed-off-by: Viresh Kumar --- drivers/mmc/host/sdhci-msm.c | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index 5e1da4df096f..d170c919e6e4 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -264,7 +264,6 @@ struct sdhci_msm_host { struct clk_bulk_data bulk_clks[5]; unsigned long clk_rate; struct mmc_host *mmc; - struct opp_table *opp_table; bool use_14lpp_dll_reset; bool tuning_done; bool calibration_done; @@ -2551,17 +2550,15 @@ static int sdhci_msm_probe(struct platform_device *pdev) if (ret) goto bus_clk_disable; - msm_host->opp_table = dev_pm_opp_set_clkname(&pdev->dev, "core"); - if (IS_ERR(msm_host->opp_table)) { - ret = PTR_ERR(msm_host->opp_table); + ret = devm_pm_opp_set_clkname(&pdev->dev, "core"); + if (ret) goto bus_clk_disable; - } /* OPP table is optional */ - ret = dev_pm_opp_of_add_table(&pdev->dev); + ret = devm_pm_opp_of_add_table(&pdev->dev); if (ret && ret != -ENODEV) { dev_err(&pdev->dev, "Invalid OPP table in Device tree\n"); - goto opp_put_clkname; + goto bus_clk_disable; } /* Vote for maximum clock rate for maximum performance */ @@ -2587,7 +2584,7 @@ static int sdhci_msm_probe(struct platform_device *pdev) ret = clk_bulk_prepare_enable(ARRAY_SIZE(msm_host->bulk_clks), msm_host->bulk_clks); if (ret) - goto opp_cleanup; + goto bus_clk_disable; /* * xo clock is needed for FLL feature of cm_dll. @@ -2732,10 +2729,6 @@ pm_runtime_disable: clk_disable: clk_bulk_disable_unprepare(ARRAY_SIZE(msm_host->bulk_clks), msm_host->bulk_clks); -opp_cleanup: - dev_pm_opp_of_remove_table(&pdev->dev); -opp_put_clkname: - dev_pm_opp_put_clkname(msm_host->opp_table); bus_clk_disable: if (!IS_ERR(msm_host->bus_clk)) clk_disable_unprepare(msm_host->bus_clk); @@ -2754,8 +2747,6 @@ static int sdhci_msm_remove(struct platform_device *pdev) sdhci_remove_host(host, dead); - dev_pm_opp_of_remove_table(&pdev->dev); - dev_pm_opp_put_clkname(msm_host->opp_table); pm_runtime_get_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); pm_runtime_put_noidle(&pdev->dev); -- cgit v1.2.3-70-g09d2 From 864a270189cfc34c3158412459fc81836289c2a5 Mon Sep 17 00:00:00 2001 From: Yangtao Li Date: Sun, 14 Mar 2021 19:34:05 +0300 Subject: drm/lima: Convert to use resource-managed OPP API Use resource-managed OPP API to simplify code. Signed-off-by: Yangtao Li Signed-off-by: Dmitry Osipenko Signed-off-by: Viresh Kumar --- drivers/gpu/drm/lima/lima_devfreq.c | 47 +++++++++---------------------------- drivers/gpu/drm/lima/lima_devfreq.h | 3 --- 2 files changed, 11 insertions(+), 39 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/lima/lima_devfreq.c b/drivers/gpu/drm/lima/lima_devfreq.c index 5686ad4aaf7c..dbc1d1eb9543 100644 --- a/drivers/gpu/drm/lima/lima_devfreq.c +++ b/drivers/gpu/drm/lima/lima_devfreq.c @@ -99,20 +99,12 @@ void lima_devfreq_fini(struct lima_device *ldev) devm_devfreq_remove_device(ldev->dev, devfreq->devfreq); devfreq->devfreq = NULL; } - - dev_pm_opp_of_remove_table(ldev->dev); - - dev_pm_opp_put_regulators(devfreq->regulators_opp_table); - dev_pm_opp_put_clkname(devfreq->clkname_opp_table); - devfreq->regulators_opp_table = NULL; - devfreq->clkname_opp_table = NULL; } int lima_devfreq_init(struct lima_device *ldev) { struct thermal_cooling_device *cooling; struct device *dev = ldev->dev; - struct opp_table *opp_table; struct devfreq *devfreq; struct lima_devfreq *ldevfreq = &ldev->devfreq; struct dev_pm_opp *opp; @@ -125,40 +117,28 @@ int lima_devfreq_init(struct lima_device *ldev) spin_lock_init(&ldevfreq->lock); - opp_table = dev_pm_opp_set_clkname(dev, "core"); - if (IS_ERR(opp_table)) { - ret = PTR_ERR(opp_table); - goto err_fini; - } - - ldevfreq->clkname_opp_table = opp_table; - - opp_table = dev_pm_opp_set_regulators(dev, - (const char *[]){ "mali" }, - 1); - if (IS_ERR(opp_table)) { - ret = PTR_ERR(opp_table); + ret = devm_pm_opp_set_clkname(dev, "core"); + if (ret) + return ret; + ret = devm_pm_opp_set_regulators(dev, (const char *[]){ "mali" }, 1); + if (ret) { /* Continue if the optional regulator is missing */ if (ret != -ENODEV) - goto err_fini; - } else { - ldevfreq->regulators_opp_table = opp_table; + return ret; } - ret = dev_pm_opp_of_add_table(dev); + ret = devm_pm_opp_of_add_table(dev); if (ret) - goto err_fini; + return ret; lima_devfreq_reset(ldevfreq); cur_freq = clk_get_rate(ldev->clk_gpu); opp = devfreq_recommended_opp(dev, &cur_freq, 0); - if (IS_ERR(opp)) { - ret = PTR_ERR(opp); - goto err_fini; - } + if (IS_ERR(opp)) + return PTR_ERR(opp); lima_devfreq_profile.initial_freq = cur_freq; dev_pm_opp_put(opp); @@ -167,8 +147,7 @@ int lima_devfreq_init(struct lima_device *ldev) DEVFREQ_GOV_SIMPLE_ONDEMAND, NULL); if (IS_ERR(devfreq)) { dev_err(dev, "Couldn't initialize GPU devfreq\n"); - ret = PTR_ERR(devfreq); - goto err_fini; + return PTR_ERR(devfreq); } ldevfreq->devfreq = devfreq; @@ -180,10 +159,6 @@ int lima_devfreq_init(struct lima_device *ldev) ldevfreq->cooling = cooling; return 0; - -err_fini: - lima_devfreq_fini(ldev); - return ret; } void lima_devfreq_record_busy(struct lima_devfreq *devfreq) diff --git a/drivers/gpu/drm/lima/lima_devfreq.h b/drivers/gpu/drm/lima/lima_devfreq.h index 2d9b3008ce77..688ee71e263a 100644 --- a/drivers/gpu/drm/lima/lima_devfreq.h +++ b/drivers/gpu/drm/lima/lima_devfreq.h @@ -8,15 +8,12 @@ #include struct devfreq; -struct opp_table; struct thermal_cooling_device; struct lima_device; struct lima_devfreq { struct devfreq *devfreq; - struct opp_table *clkname_opp_table; - struct opp_table *regulators_opp_table; struct thermal_cooling_device *cooling; ktime_t busy_time; -- cgit v1.2.3-70-g09d2 From 6d8a154f1dd7f6b0a1c291d94012b39e085e016a Mon Sep 17 00:00:00 2001 From: Yangtao Li Date: Sun, 14 Mar 2021 19:34:06 +0300 Subject: drm/panfrost: Convert to use resource-managed OPP API Use resource-managed OPP API to simplify code. Signed-off-by: Yangtao Li Reviewed-by: Steven Price Signed-off-by: Dmitry Osipenko Signed-off-by: Viresh Kumar --- drivers/gpu/drm/panfrost/panfrost_devfreq.c | 37 +++++++---------------------- drivers/gpu/drm/panfrost/panfrost_devfreq.h | 2 -- 2 files changed, 9 insertions(+), 30 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/panfrost/panfrost_devfreq.c b/drivers/gpu/drm/panfrost/panfrost_devfreq.c index 56b3f5935703..c878391f3e8c 100644 --- a/drivers/gpu/drm/panfrost/panfrost_devfreq.c +++ b/drivers/gpu/drm/panfrost/panfrost_devfreq.c @@ -89,29 +89,25 @@ int panfrost_devfreq_init(struct panfrost_device *pfdev) unsigned long cur_freq; struct device *dev = &pfdev->pdev->dev; struct devfreq *devfreq; - struct opp_table *opp_table; struct thermal_cooling_device *cooling; struct panfrost_devfreq *pfdevfreq = &pfdev->pfdevfreq; - opp_table = dev_pm_opp_set_regulators(dev, pfdev->comp->supply_names, - pfdev->comp->num_supplies); - if (IS_ERR(opp_table)) { - ret = PTR_ERR(opp_table); + ret = devm_pm_opp_set_regulators(dev, pfdev->comp->supply_names, + pfdev->comp->num_supplies); + if (ret) { /* Continue if the optional regulator is missing */ if (ret != -ENODEV) { DRM_DEV_ERROR(dev, "Couldn't set OPP regulators\n"); - goto err_fini; + return ret; } - } else { - pfdevfreq->regulators_opp_table = opp_table; } - ret = dev_pm_opp_of_add_table(dev); + ret = devm_pm_opp_of_add_table(dev); if (ret) { /* Optional, continue without devfreq */ if (ret == -ENODEV) ret = 0; - goto err_fini; + return ret; } pfdevfreq->opp_of_table_added = true; @@ -122,10 +118,8 @@ int panfrost_devfreq_init(struct panfrost_device *pfdev) cur_freq = clk_get_rate(pfdev->clock); opp = devfreq_recommended_opp(dev, &cur_freq, 0); - if (IS_ERR(opp)) { - ret = PTR_ERR(opp); - goto err_fini; - } + if (IS_ERR(opp)) + return PTR_ERR(opp); panfrost_devfreq_profile.initial_freq = cur_freq; dev_pm_opp_put(opp); @@ -134,8 +128,7 @@ int panfrost_devfreq_init(struct panfrost_device *pfdev) DEVFREQ_GOV_SIMPLE_ONDEMAND, NULL); if (IS_ERR(devfreq)) { DRM_DEV_ERROR(dev, "Couldn't initialize GPU devfreq\n"); - ret = PTR_ERR(devfreq); - goto err_fini; + return PTR_ERR(devfreq); } pfdevfreq->devfreq = devfreq; @@ -146,10 +139,6 @@ int panfrost_devfreq_init(struct panfrost_device *pfdev) pfdevfreq->cooling = cooling; return 0; - -err_fini: - panfrost_devfreq_fini(pfdev); - return ret; } void panfrost_devfreq_fini(struct panfrost_device *pfdev) @@ -160,14 +149,6 @@ void panfrost_devfreq_fini(struct panfrost_device *pfdev) devfreq_cooling_unregister(pfdevfreq->cooling); pfdevfreq->cooling = NULL; } - - if (pfdevfreq->opp_of_table_added) { - dev_pm_opp_of_remove_table(&pfdev->pdev->dev); - pfdevfreq->opp_of_table_added = false; - } - - dev_pm_opp_put_regulators(pfdevfreq->regulators_opp_table); - pfdevfreq->regulators_opp_table = NULL; } void panfrost_devfreq_resume(struct panfrost_device *pfdev) diff --git a/drivers/gpu/drm/panfrost/panfrost_devfreq.h b/drivers/gpu/drm/panfrost/panfrost_devfreq.h index db6ea48e21f9..210269944687 100644 --- a/drivers/gpu/drm/panfrost/panfrost_devfreq.h +++ b/drivers/gpu/drm/panfrost/panfrost_devfreq.h @@ -8,14 +8,12 @@ #include struct devfreq; -struct opp_table; struct thermal_cooling_device; struct panfrost_device; struct panfrost_devfreq { struct devfreq *devfreq; - struct opp_table *regulators_opp_table; struct thermal_cooling_device *cooling; bool opp_of_table_added; -- cgit v1.2.3-70-g09d2 From d484b8bfc6fa71a088e4ac85d9ce11aa0385867e Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Mon, 8 Mar 2021 16:31:46 +0200 Subject: intel_idle: update ICX C6 data Change IceLake Xeon C6 latency from 128 us to 170 us. The latency was measured with the "wult" tool and corresponds to the 99.99th percentile when measuring with the "nic" method. Note, the 128 us figure correspond to the median latency, but in intel_idle we use the "worst case" latency figure instead. C6 target residency was increased from 384 us to 600 us, which may result in less C6 residency in some workloads. This value was tested and compared to values 384, and 1000. Value 600 is a reasonable tradeoff between power and performance. Signed-off-by: Artem Bityutskiy Acked-by: Zhang Rui Signed-off-by: Rafael J. Wysocki --- drivers/idle/intel_idle.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c index 3273360f30f7..6cac0b748efa 100644 --- a/drivers/idle/intel_idle.c +++ b/drivers/idle/intel_idle.c @@ -744,8 +744,8 @@ static struct cpuidle_state icx_cstates[] __initdata = { .name = "C6", .desc = "MWAIT 0x20", .flags = MWAIT2flg(0x20) | CPUIDLE_FLAG_TLB_FLUSHED, - .exit_latency = 128, - .target_residency = 384, + .exit_latency = 170, + .target_residency = 600, .enter = &intel_idle, .enter_s2idle = intel_idle_s2idle, }, { -- cgit v1.2.3-70-g09d2 From c1df456d0f06eb9275c1cd4c66548fc5738ea428 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Thu, 4 Mar 2021 20:28:43 +0100 Subject: PM: domains: Don't runtime resume devices at genpd_prepare() Runtime resuming a device upfront in the genpd_prepare() callback, to check if there is a wakeup pending for it, seems like an unnecessary thing to do. The PM core already manages these kind of things in a common way in __device_suspend(), via calling pm_runtime_barrier() and pm_wakeup_pending(). Therefore, let's simply drop this behaviour from genpd_prepare(). Note that, this change is applicable only for devices that are attached to a genpd that has the GENPD_FLAG_ACTIVE_WAKEUP set (Renesas, Mediatek, and Rockchip platforms). Moreover, a driver that needs to restore power for its device to re-configure it for a system wakeup, may still call pm_runtime_get_sync(), for example, to do this. Signed-off-by: Ulf Hansson Signed-off-by: Rafael J. Wysocki --- drivers/base/power/domain.c | 36 ------------------------------------ 1 file changed, 36 deletions(-) (limited to 'drivers') diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 78c310d3179d..b6a782c31613 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -1087,34 +1087,6 @@ static void genpd_sync_power_on(struct generic_pm_domain *genpd, bool use_lock, genpd->status = GENPD_STATE_ON; } -/** - * resume_needed - Check whether to resume a device before system suspend. - * @dev: Device to check. - * @genpd: PM domain the device belongs to. - * - * There are two cases in which a device that can wake up the system from sleep - * states should be resumed by genpd_prepare(): (1) if the device is enabled - * to wake up the system and it has to remain active for this purpose while the - * system is in the sleep state and (2) if the device is not enabled to wake up - * the system from sleep states and it generally doesn't generate wakeup signals - * by itself (those signals are generated on its behalf by other parts of the - * system). In the latter case it may be necessary to reconfigure the device's - * wakeup settings during system suspend, because it may have been set up to - * signal remote wakeup from the system's working state as needed by runtime PM. - * Return 'true' in either of the above cases. - */ -static bool resume_needed(struct device *dev, - const struct generic_pm_domain *genpd) -{ - bool active_wakeup; - - if (!device_can_wakeup(dev)) - return false; - - active_wakeup = genpd_is_active_wakeup(genpd); - return device_may_wakeup(dev) ? active_wakeup : !active_wakeup; -} - /** * genpd_prepare - Start power transition of a device in a PM domain. * @dev: Device to start the transition of. @@ -1135,14 +1107,6 @@ static int genpd_prepare(struct device *dev) if (IS_ERR(genpd)) return -EINVAL; - /* - * If a wakeup request is pending for the device, it should be woken up - * at this point and a system wakeup event should be reported if it's - * set up to wake up the system from sleep states. - */ - if (resume_needed(dev, genpd)) - pm_runtime_resume(dev); - genpd_lock(genpd); if (genpd->prepared_count++ == 0) -- cgit v1.2.3-70-g09d2 From a740561201ebf75326188e4e899fe9c2526046be Mon Sep 17 00:00:00 2001 From: Pu Wen Date: Tue, 2 Mar 2021 10:01:08 +0800 Subject: powercap: Add Hygon Fam18h RAPL support Enable Hygon Fam18h RAPL support for the power capping framework. Signed-off-by: Pu Wen Signed-off-by: Rafael J. Wysocki --- drivers/powercap/intel_rapl_common.c | 1 + drivers/powercap/intel_rapl_msr.c | 1 + 2 files changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/powercap/intel_rapl_common.c b/drivers/powercap/intel_rapl_common.c index fdda2a737186..73cf68af9770 100644 --- a/drivers/powercap/intel_rapl_common.c +++ b/drivers/powercap/intel_rapl_common.c @@ -1069,6 +1069,7 @@ static const struct x86_cpu_id rapl_ids[] __initconst = { X86_MATCH_VENDOR_FAM(AMD, 0x17, &rapl_defaults_amd), X86_MATCH_VENDOR_FAM(AMD, 0x19, &rapl_defaults_amd), + X86_MATCH_VENDOR_FAM(HYGON, 0x18, &rapl_defaults_amd), {} }; MODULE_DEVICE_TABLE(x86cpu, rapl_ids); diff --git a/drivers/powercap/intel_rapl_msr.c b/drivers/powercap/intel_rapl_msr.c index 78213d4b5b16..cc3b22881bfe 100644 --- a/drivers/powercap/intel_rapl_msr.c +++ b/drivers/powercap/intel_rapl_msr.c @@ -150,6 +150,7 @@ static int rapl_msr_probe(struct platform_device *pdev) case X86_VENDOR_INTEL: rapl_msr_priv = &rapl_msr_priv_intel; break; + case X86_VENDOR_HYGON: case X86_VENDOR_AMD: rapl_msr_priv = &rapl_msr_priv_amd; break; -- cgit v1.2.3-70-g09d2 From 4c56f573bd941dcdcbbcd58d91489e994715eb47 Mon Sep 17 00:00:00 2001 From: Sergei Trofimovich Date: Sat, 13 Mar 2021 10:42:14 +0000 Subject: ia64: fix format string for ia64-acpi-cpu-freq Fix warning with %lx / s64 mismatch: CC [M] drivers/cpufreq/ia64-acpi-cpufreq.o drivers/cpufreq/ia64-acpi-cpufreq.c: In function 'processor_get_pstate': warning: format '%lx' expects argument of type 'long unsigned int', but argument 3 has type 's64' {aka 'long long int'} [-Wformat=] Signed-off-by: Sergei Trofimovich Acked-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/ia64-acpi-cpufreq.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/cpufreq/ia64-acpi-cpufreq.c b/drivers/cpufreq/ia64-acpi-cpufreq.c index 2efe7189ccc4..c6bdc455517f 100644 --- a/drivers/cpufreq/ia64-acpi-cpufreq.c +++ b/drivers/cpufreq/ia64-acpi-cpufreq.c @@ -54,7 +54,7 @@ processor_set_pstate ( retval = ia64_pal_set_pstate((u64)value); if (retval) { - pr_debug("Failed to set freq to 0x%x, with error 0x%lx\n", + pr_debug("Failed to set freq to 0x%x, with error 0x%llx\n", value, retval); return -ENODEV; } @@ -77,7 +77,7 @@ processor_get_pstate ( if (retval) pr_debug("Failed to get current freq with " - "error 0x%lx, idx 0x%x\n", retval, *value); + "error 0x%llx, idx 0x%x\n", retval, *value); return (int)retval; } -- cgit v1.2.3-70-g09d2 From 4c38f2df71c8e33c0b64865992d693f5022eeaad Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 23 Jun 2020 15:49:40 +0530 Subject: cpufreq: CPPC: Add support for frequency invariance The Frequency Invariance Engine (FIE) is providing a frequency scaling correction factor that helps achieve more accurate load-tracking. Normally, this scaling factor can be obtained directly with the help of the cpufreq drivers as they know the exact frequency the hardware is running at. But that isn't the case for CPPC cpufreq driver. Another way of obtaining that is using the arch specific counter support, which is already present in kernel, but that hardware is optional for platforms. This patch updates the CPPC driver to register itself with the topology core to provide its own implementation (cppc_scale_freq_tick()) of topology_scale_freq_tick() which gets called by the scheduler on every tick. Note that the arch specific counters have higher priority than CPPC counters, if available, though the CPPC driver doesn't need to have any special handling for that. On an invocation of cppc_scale_freq_tick(), we schedule an irq work (since we reach here from hard-irq context), which then schedules a normal work item and cppc_scale_freq_workfn() updates the per_cpu arch_freq_scale variable based on the counter updates since the last tick. To allow platforms to disable this CPPC counter-based frequency invariance support, this is all done under CONFIG_ACPI_CPPC_CPUFREQ_FIE, which is enabled by default. This also exports sched_setattr_nocheck() as the CPPC driver can be built as a module. Cc: linux-acpi@vger.kernel.org Reviewed-by: Ionela Voinescu Tested-by: Ionela Voinescu Tested-by: Vincent Guittot Acked-by: Rafael J. Wysocki Signed-off-by: Viresh Kumar --- drivers/cpufreq/Kconfig.arm | 10 ++ drivers/cpufreq/cppc_cpufreq.c | 245 +++++++++++++++++++++++++++++++++++++++-- include/linux/arch_topology.h | 1 + kernel/sched/core.c | 1 + 4 files changed, 245 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm index e65e0a43be64..a5c5f70acfc9 100644 --- a/drivers/cpufreq/Kconfig.arm +++ b/drivers/cpufreq/Kconfig.arm @@ -19,6 +19,16 @@ config ACPI_CPPC_CPUFREQ If in doubt, say N. +config ACPI_CPPC_CPUFREQ_FIE + bool "Frequency Invariance support for CPPC cpufreq driver" + depends on ACPI_CPPC_CPUFREQ && GENERIC_ARCH_TOPOLOGY + default y + help + This extends frequency invariance support in the CPPC cpufreq driver, + by using CPPC delivered and reference performance counters. + + If in doubt, say N. + config ARM_ALLWINNER_SUN50I_CPUFREQ_NVMEM tristate "Allwinner nvmem based SUN50I CPUFreq driver" depends on ARCH_SUNXI diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c index 8a482c434ea6..b8e1b8ea628c 100644 --- a/drivers/cpufreq/cppc_cpufreq.c +++ b/drivers/cpufreq/cppc_cpufreq.c @@ -10,14 +10,18 @@ #define pr_fmt(fmt) "CPPC Cpufreq:" fmt +#include #include #include #include #include #include #include +#include +#include #include #include +#include #include @@ -57,6 +61,204 @@ static struct cppc_workaround_oem_info wa_info[] = { } }; +#ifdef CONFIG_ACPI_CPPC_CPUFREQ_FIE + +/* Frequency invariance support */ +struct cppc_freq_invariance { + int cpu; + struct irq_work irq_work; + struct kthread_work work; + struct cppc_perf_fb_ctrs prev_perf_fb_ctrs; + struct cppc_cpudata *cpu_data; +}; + +static DEFINE_PER_CPU(struct cppc_freq_invariance, cppc_freq_inv); +static struct kthread_worker *kworker_fie; +static bool fie_disabled; + +static struct cpufreq_driver cppc_cpufreq_driver; +static unsigned int hisi_cppc_cpufreq_get_rate(unsigned int cpu); +static int cppc_perf_from_fbctrs(struct cppc_cpudata *cpu_data, + struct cppc_perf_fb_ctrs fb_ctrs_t0, + struct cppc_perf_fb_ctrs fb_ctrs_t1); + +/** + * cppc_scale_freq_workfn - CPPC arch_freq_scale updater for frequency invariance + * @work: The work item. + * + * The CPPC driver register itself with the topology core to provide its own + * implementation (cppc_scale_freq_tick()) of topology_scale_freq_tick() which + * gets called by the scheduler on every tick. + * + * Note that the arch specific counters have higher priority than CPPC counters, + * if available, though the CPPC driver doesn't need to have any special + * handling for that. + * + * On an invocation of cppc_scale_freq_tick(), we schedule an irq work (since we + * reach here from hard-irq context), which then schedules a normal work item + * and cppc_scale_freq_workfn() updates the per_cpu arch_freq_scale variable + * based on the counter updates since the last tick. + */ +static void cppc_scale_freq_workfn(struct kthread_work *work) +{ + struct cppc_freq_invariance *cppc_fi; + struct cppc_perf_fb_ctrs fb_ctrs = {0}; + struct cppc_cpudata *cpu_data; + unsigned long local_freq_scale; + u64 perf; + + cppc_fi = container_of(work, struct cppc_freq_invariance, work); + cpu_data = cppc_fi->cpu_data; + + if (cppc_get_perf_ctrs(cppc_fi->cpu, &fb_ctrs)) { + pr_warn("%s: failed to read perf counters\n", __func__); + return; + } + + cppc_fi->prev_perf_fb_ctrs = fb_ctrs; + perf = cppc_perf_from_fbctrs(cpu_data, cppc_fi->prev_perf_fb_ctrs, + fb_ctrs); + + perf <<= SCHED_CAPACITY_SHIFT; + local_freq_scale = div64_u64(perf, cpu_data->perf_caps.highest_perf); + if (WARN_ON(local_freq_scale > 1024)) + local_freq_scale = 1024; + + per_cpu(arch_freq_scale, cppc_fi->cpu) = local_freq_scale; +} + +static void cppc_irq_work(struct irq_work *irq_work) +{ + struct cppc_freq_invariance *cppc_fi; + + cppc_fi = container_of(irq_work, struct cppc_freq_invariance, irq_work); + kthread_queue_work(kworker_fie, &cppc_fi->work); +} + +static void cppc_scale_freq_tick(void) +{ + struct cppc_freq_invariance *cppc_fi = &per_cpu(cppc_freq_inv, smp_processor_id()); + + /* + * cppc_get_perf_ctrs() can potentially sleep, call that from the right + * context. + */ + irq_work_queue(&cppc_fi->irq_work); +} + +static struct scale_freq_data cppc_sftd = { + .source = SCALE_FREQ_SOURCE_CPPC, + .set_freq_scale = cppc_scale_freq_tick, +}; + +static void cppc_freq_invariance_policy_init(struct cpufreq_policy *policy, + struct cppc_cpudata *cpu_data) +{ + struct cppc_perf_fb_ctrs fb_ctrs = {0}; + struct cppc_freq_invariance *cppc_fi; + int i, ret; + + if (cppc_cpufreq_driver.get == hisi_cppc_cpufreq_get_rate) + return; + + if (fie_disabled) + return; + + for_each_cpu(i, policy->cpus) { + cppc_fi = &per_cpu(cppc_freq_inv, i); + cppc_fi->cpu = i; + cppc_fi->cpu_data = cpu_data; + kthread_init_work(&cppc_fi->work, cppc_scale_freq_workfn); + init_irq_work(&cppc_fi->irq_work, cppc_irq_work); + + ret = cppc_get_perf_ctrs(i, &fb_ctrs); + if (ret) { + pr_warn("%s: failed to read perf counters: %d\n", + __func__, ret); + fie_disabled = true; + } else { + cppc_fi->prev_perf_fb_ctrs = fb_ctrs; + } + } +} + +static void __init cppc_freq_invariance_init(void) +{ + struct sched_attr attr = { + .size = sizeof(struct sched_attr), + .sched_policy = SCHED_DEADLINE, + .sched_nice = 0, + .sched_priority = 0, + /* + * Fake (unused) bandwidth; workaround to "fix" + * priority inheritance. + */ + .sched_runtime = 1000000, + .sched_deadline = 10000000, + .sched_period = 10000000, + }; + int ret; + + if (cppc_cpufreq_driver.get == hisi_cppc_cpufreq_get_rate) + return; + + if (fie_disabled) + return; + + kworker_fie = kthread_create_worker(0, "cppc_fie"); + if (IS_ERR(kworker_fie)) + return; + + ret = sched_setattr_nocheck(kworker_fie->task, &attr); + if (ret) { + pr_warn("%s: failed to set SCHED_DEADLINE: %d\n", __func__, + ret); + kthread_destroy_worker(kworker_fie); + return; + } + + /* Register for freq-invariance */ + topology_set_scale_freq_source(&cppc_sftd, cpu_present_mask); +} + +static void cppc_freq_invariance_exit(void) +{ + struct cppc_freq_invariance *cppc_fi; + int i; + + if (cppc_cpufreq_driver.get == hisi_cppc_cpufreq_get_rate) + return; + + if (fie_disabled) + return; + + topology_clear_scale_freq_source(SCALE_FREQ_SOURCE_CPPC, cpu_present_mask); + + for_each_possible_cpu(i) { + cppc_fi = &per_cpu(cppc_freq_inv, i); + irq_work_sync(&cppc_fi->irq_work); + } + + kthread_destroy_worker(kworker_fie); + kworker_fie = NULL; +} + +#else +static inline void +cppc_freq_invariance_policy_init(struct cpufreq_policy *policy, + struct cppc_cpudata *cpu_data) +{ +} + +static inline void cppc_freq_invariance_init(void) +{ +} + +static inline void cppc_freq_invariance_exit(void) +{ +} +#endif /* CONFIG_ACPI_CPPC_CPUFREQ_FIE */ + /* Callback function used to retrieve the max frequency from DMI */ static void cppc_find_dmi_mhz(const struct dmi_header *dm, void *private) { @@ -355,9 +557,12 @@ static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy) cpu_data->perf_ctrls.desired_perf = caps->highest_perf; ret = cppc_set_perf(cpu, &cpu_data->perf_ctrls); - if (ret) + if (ret) { pr_debug("Err setting perf value:%d on CPU:%d. ret:%d\n", caps->highest_perf, cpu, ret); + } else { + cppc_freq_invariance_policy_init(policy, cpu_data); + } return ret; } @@ -370,12 +575,12 @@ static inline u64 get_delta(u64 t1, u64 t0) return (u32)t1 - (u32)t0; } -static int cppc_get_rate_from_fbctrs(struct cppc_cpudata *cpu_data, - struct cppc_perf_fb_ctrs fb_ctrs_t0, - struct cppc_perf_fb_ctrs fb_ctrs_t1) +static int cppc_perf_from_fbctrs(struct cppc_cpudata *cpu_data, + struct cppc_perf_fb_ctrs fb_ctrs_t0, + struct cppc_perf_fb_ctrs fb_ctrs_t1) { u64 delta_reference, delta_delivered; - u64 reference_perf, delivered_perf; + u64 reference_perf; reference_perf = fb_ctrs_t0.reference_perf; @@ -384,12 +589,21 @@ static int cppc_get_rate_from_fbctrs(struct cppc_cpudata *cpu_data, delta_delivered = get_delta(fb_ctrs_t1.delivered, fb_ctrs_t0.delivered); - /* Check to avoid divide-by zero */ - if (delta_reference || delta_delivered) - delivered_perf = (reference_perf * delta_delivered) / - delta_reference; - else - delivered_perf = cpu_data->perf_ctrls.desired_perf; + /* Check to avoid divide-by zero and invalid delivered_perf */ + if (!delta_reference || !delta_delivered) + return cpu_data->perf_ctrls.desired_perf; + + return (reference_perf * delta_delivered) / delta_reference; +} + +static int cppc_get_rate_from_fbctrs(struct cppc_cpudata *cpu_data, + struct cppc_perf_fb_ctrs fb_ctrs_t0, + struct cppc_perf_fb_ctrs fb_ctrs_t1) +{ + u64 delivered_perf; + + delivered_perf = cppc_perf_from_fbctrs(cpu_data, fb_ctrs_t0, + fb_ctrs_t1); return cppc_cpufreq_perf_to_khz(cpu_data, delivered_perf); } @@ -514,6 +728,8 @@ static void cppc_check_hisi_workaround(void) static int __init cppc_cpufreq_init(void) { + int ret; + if ((acpi_disabled) || !acpi_cpc_valid()) return -ENODEV; @@ -521,7 +737,11 @@ static int __init cppc_cpufreq_init(void) cppc_check_hisi_workaround(); - return cpufreq_register_driver(&cppc_cpufreq_driver); + ret = cpufreq_register_driver(&cppc_cpufreq_driver); + if (!ret) + cppc_freq_invariance_init(); + + return ret; } static inline void free_cpu_data(void) @@ -538,6 +758,7 @@ static inline void free_cpu_data(void) static void __exit cppc_cpufreq_exit(void) { + cppc_freq_invariance_exit(); cpufreq_unregister_driver(&cppc_cpufreq_driver); free_cpu_data(); diff --git a/include/linux/arch_topology.h b/include/linux/arch_topology.h index 11e555cfaecb..f180240dc95f 100644 --- a/include/linux/arch_topology.h +++ b/include/linux/arch_topology.h @@ -37,6 +37,7 @@ bool topology_scale_freq_invariant(void); enum scale_freq_source { SCALE_FREQ_SOURCE_CPUFREQ = 0, SCALE_FREQ_SOURCE_ARCH, + SCALE_FREQ_SOURCE_CPPC, }; struct scale_freq_data { diff --git a/kernel/sched/core.c b/kernel/sched/core.c index ca2bb629595f..3adedc7b1725 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -6386,6 +6386,7 @@ int sched_setattr_nocheck(struct task_struct *p, const struct sched_attr *attr) { return __sched_setscheduler(p, attr, false, true); } +EXPORT_SYMBOL_GPL(sched_setattr_nocheck); /** * sched_setscheduler_nocheck - change the scheduling policy and/or RT priority of a thread from kernelspace. -- cgit v1.2.3-70-g09d2 From 15aa70a32bd03ccf2e65dcff929aff91f4e47ce8 Mon Sep 17 00:00:00 2001 From: Bhaskar Chowdhury Date: Sat, 13 Mar 2021 09:19:51 +0530 Subject: cpufreq: Rudimentary typos fix in the file s5pv210-cpufreq.c Trivial spelling fixes throughout the file. Signed-off-by: Bhaskar Chowdhury Reviewed-by: Tom Saeger Reviewed-by: Randy Dunlap [ Viresh: Capitalize two words. ] Signed-off-by: Viresh Kumar --- drivers/cpufreq/s5pv210-cpufreq.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/cpufreq/s5pv210-cpufreq.c b/drivers/cpufreq/s5pv210-cpufreq.c index 69786e5bbf05..ad7d4f272ddc 100644 --- a/drivers/cpufreq/s5pv210-cpufreq.c +++ b/drivers/cpufreq/s5pv210-cpufreq.c @@ -91,7 +91,7 @@ static DEFINE_MUTEX(set_freq_lock); /* Use 800MHz when entering sleep mode */ #define SLEEP_FREQ (800 * 1000) -/* Tracks if cpu freqency can be updated anymore */ +/* Tracks if CPU frequency can be updated anymore */ static bool no_cpufreq_access; /* @@ -190,7 +190,7 @@ static u32 clkdiv_val[5][11] = { /* * This function set DRAM refresh counter - * accoriding to operating frequency of DRAM + * according to operating frequency of DRAM * ch: DMC port number 0 or 1 * freq: Operating frequency of DRAM(KHz) */ @@ -320,7 +320,7 @@ static int s5pv210_target(struct cpufreq_policy *policy, unsigned int index) /* * 3. DMC1 refresh count for 133Mhz if (index == L4) is - * true refresh counter is already programed in upper + * true refresh counter is already programmed in upper * code. 0x287@83Mhz */ if (!bus_speed_changing) @@ -378,7 +378,7 @@ static int s5pv210_target(struct cpufreq_policy *policy, unsigned int index) /* * 6. Turn on APLL * 6-1. Set PMS values - * 6-2. Wait untile the PLL is locked + * 6-2. Wait until the PLL is locked */ if (index == L0) writel_relaxed(APLL_VAL_1000, S5P_APLL_CON); @@ -390,7 +390,7 @@ static int s5pv210_target(struct cpufreq_policy *policy, unsigned int index) } while (!(reg & (0x1 << 29))); /* - * 7. Change souce clock from SCLKMPLL(667Mhz) + * 7. Change source clock from SCLKMPLL(667Mhz) * to SCLKA2M(200Mhz) in MFC_MUX and G3D MUX * (667/4=166)->(200/4=50)Mhz */ @@ -439,8 +439,8 @@ static int s5pv210_target(struct cpufreq_policy *policy, unsigned int index) } /* - * L4 level need to change memory bus speed, hence onedram clock divier - * and memory refresh parameter should be changed + * L4 level needs to change memory bus speed, hence ONEDRAM clock + * divider and memory refresh parameter should be changed */ if (bus_speed_changing) { reg = readl_relaxed(S5P_CLK_DIV6); -- cgit v1.2.3-70-g09d2 From 2b53d1bd13e1c2b20e5f3e55788e2c09bc2197e5 Mon Sep 17 00:00:00 2001 From: Tom Saeger Date: Fri, 12 Mar 2021 19:50:06 -0700 Subject: cpufreq: cppc: simplify default delay_us setting Simplify case when setting default in cppc_cpufreq_get_transition_delay_us. Signed-off-by: Tom Saeger Signed-off-by: Viresh Kumar --- drivers/cpufreq/cppc_cpufreq.c | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c index b8e1b8ea628c..3848b4c222e1 100644 --- a/drivers/cpufreq/cppc_cpufreq.c +++ b/drivers/cpufreq/cppc_cpufreq.c @@ -418,26 +418,16 @@ static unsigned int cppc_cpufreq_get_transition_delay_us(unsigned int cpu) { unsigned long implementor = read_cpuid_implementor(); unsigned long part_num = read_cpuid_part_number(); - unsigned int delay_us = 0; switch (implementor) { case ARM_CPU_IMP_QCOM: switch (part_num) { case QCOM_CPU_PART_FALKOR_V1: case QCOM_CPU_PART_FALKOR: - delay_us = 10000; - break; - default: - delay_us = cppc_get_transition_latency(cpu) / NSEC_PER_USEC; - break; + return 10000; } - break; - default: - delay_us = cppc_get_transition_latency(cpu) / NSEC_PER_USEC; - break; } - - return delay_us; + return cppc_get_transition_latency(cpu) / NSEC_PER_USEC; } #else -- cgit v1.2.3-70-g09d2 From de5bcf404acee6218f8f49c9d6dc1f6031a62fa6 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 16 Mar 2021 16:52:43 +0100 Subject: cpufreq: intel_pstate: Clean up frequency computations Notice that some computations related to frequency in intel_pstate can be simplified if (a) intel_pstate_get_hwp_max() updates the relevant members of struct cpudata by itself and (b) the "turbo disabled" check is moved from it to its callers, so modify the code accordingly and while at it rename intel_pstate_get_hwp_max() to intel_pstate_get_hwp_cap() which better reflects its purpose and provide a simplified variat of it, __intel_pstate_get_hwp_cap(), suitable for the initialization path. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Tested-by: Chen Yu --- drivers/cpufreq/intel_pstate.c | 101 ++++++++++++++++++----------------------- 1 file changed, 45 insertions(+), 56 deletions(-) (limited to 'drivers') diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c index 5175ae3cac44..2ef9584f4802 100644 --- a/drivers/cpufreq/intel_pstate.c +++ b/drivers/cpufreq/intel_pstate.c @@ -819,19 +819,21 @@ static struct freq_attr *hwp_cpufreq_attrs[] = { NULL, }; -static void intel_pstate_get_hwp_max(struct cpudata *cpu, int *phy_max, - int *current_max) +static void __intel_pstate_get_hwp_cap(struct cpudata *cpu) { u64 cap; rdmsrl_on_cpu(cpu->cpu, MSR_HWP_CAPABILITIES, &cap); WRITE_ONCE(cpu->hwp_cap_cached, cap); - if (global.no_turbo || global.turbo_disabled) - *current_max = HWP_GUARANTEED_PERF(cap); - else - *current_max = HWP_HIGHEST_PERF(cap); + cpu->pstate.max_pstate = HWP_GUARANTEED_PERF(cap); + cpu->pstate.turbo_pstate = HWP_HIGHEST_PERF(cap); +} - *phy_max = HWP_HIGHEST_PERF(cap); +static void intel_pstate_get_hwp_cap(struct cpudata *cpu) +{ + __intel_pstate_get_hwp_cap(cpu); + cpu->pstate.max_freq = cpu->pstate.max_pstate * cpu->pstate.scaling; + cpu->pstate.turbo_freq = cpu->pstate.turbo_pstate * cpu->pstate.scaling; } static void intel_pstate_hwp_set(unsigned int cpu) @@ -1195,12 +1197,13 @@ static ssize_t store_no_turbo(struct kobject *a, struct kobj_attribute *b, static void update_qos_request(enum freq_qos_req_type type) { - int max_state, turbo_max, freq, i, perf_pct; struct freq_qos_request *req; struct cpufreq_policy *policy; + int i; for_each_possible_cpu(i) { struct cpudata *cpu = all_cpu_data[i]; + unsigned int freq, perf_pct; policy = cpufreq_cpu_get(i); if (!policy) @@ -1213,9 +1216,7 @@ static void update_qos_request(enum freq_qos_req_type type) continue; if (hwp_active) - intel_pstate_get_hwp_max(cpu, &turbo_max, &max_state); - else - turbo_max = cpu->pstate.turbo_pstate; + intel_pstate_get_hwp_cap(cpu); if (type == FREQ_QOS_MIN) { perf_pct = global.min_perf_pct; @@ -1224,8 +1225,7 @@ static void update_qos_request(enum freq_qos_req_type type) perf_pct = global.max_perf_pct; } - freq = DIV_ROUND_UP(turbo_max * perf_pct, 100); - freq *= cpu->pstate.scaling; + freq = DIV_ROUND_UP(cpu->pstate.turbo_freq * perf_pct, 100); if (freq_qos_update_request(req, freq) < 0) pr_warn("Failed to update freq constraint: CPU%d\n", i); @@ -1715,21 +1715,17 @@ static void intel_pstate_get_cpu_pstates(struct cpudata *cpu) { cpu->pstate.min_pstate = pstate_funcs.get_min(); cpu->pstate.max_pstate_physical = pstate_funcs.get_max_physical(); - cpu->pstate.turbo_pstate = pstate_funcs.get_turbo(); cpu->pstate.scaling = pstate_funcs.get_scaling(); if (hwp_active && !hwp_mode_bdw) { - unsigned int phy_max, current_max; - - intel_pstate_get_hwp_max(cpu, &phy_max, ¤t_max); - cpu->pstate.turbo_freq = phy_max * cpu->pstate.scaling; - cpu->pstate.turbo_pstate = phy_max; - cpu->pstate.max_pstate = HWP_GUARANTEED_PERF(READ_ONCE(cpu->hwp_cap_cached)); + __intel_pstate_get_hwp_cap(cpu); } else { - cpu->pstate.turbo_freq = cpu->pstate.turbo_pstate * cpu->pstate.scaling; cpu->pstate.max_pstate = pstate_funcs.get_max(); + cpu->pstate.turbo_pstate = pstate_funcs.get_turbo(); } + cpu->pstate.max_freq = cpu->pstate.max_pstate * cpu->pstate.scaling; + cpu->pstate.turbo_freq = cpu->pstate.turbo_pstate * cpu->pstate.scaling; if (pstate_funcs.get_aperf_mperf_shift) cpu->aperf_mperf_shift = pstate_funcs.get_aperf_mperf_shift(); @@ -2204,18 +2200,21 @@ static void intel_pstate_update_perf_limits(struct cpudata *cpu, int max_freq; /* - * HWP needs some special consideration, because on BDX the - * HWP_REQUEST uses abstract value to represent performance - * rather than pure ratios. + * HWP needs some special consideration, because HWP_REQUEST uses + * abstract values to represent performance rather than pure ratios. */ - if (hwp_active) { - intel_pstate_get_hwp_max(cpu, &turbo_max, &max_state); + if (hwp_active) + intel_pstate_get_hwp_cap(cpu); + + if (global.no_turbo || global.turbo_disabled) { + max_state = cpu->pstate.max_pstate; + max_freq = cpu->pstate.max_freq; } else { - max_state = global.no_turbo || global.turbo_disabled ? - cpu->pstate.max_pstate : cpu->pstate.turbo_pstate; - turbo_max = cpu->pstate.turbo_pstate; + max_state = cpu->pstate.turbo_pstate; + max_freq = cpu->pstate.turbo_freq; } - max_freq = max_state * cpu->pstate.scaling; + + turbo_max = cpu->pstate.turbo_pstate; max_policy_perf = max_state * policy_max / max_freq; if (policy_max == policy_min) { @@ -2322,10 +2321,9 @@ static void intel_pstate_verify_cpu_policy(struct cpudata *cpu, update_turbo_state(); if (hwp_active) { - int max_state, turbo_max; - - intel_pstate_get_hwp_max(cpu, &turbo_max, &max_state); - max_freq = max_state * cpu->pstate.scaling; + intel_pstate_get_hwp_cap(cpu); + max_freq = global.no_turbo || global.turbo_disabled ? + cpu->pstate.max_freq : cpu->pstate.turbo_freq; } else { max_freq = intel_pstate_get_max_freq(cpu); } @@ -2416,25 +2414,15 @@ static int __intel_pstate_cpu_init(struct cpufreq_policy *policy) cpu->max_perf_ratio = 0xFF; cpu->min_perf_ratio = 0; - policy->min = cpu->pstate.min_pstate * cpu->pstate.scaling; - policy->max = cpu->pstate.turbo_pstate * cpu->pstate.scaling; - /* cpuinfo and default policy values */ policy->cpuinfo.min_freq = cpu->pstate.min_pstate * cpu->pstate.scaling; update_turbo_state(); global.turbo_disabled_mf = global.turbo_disabled; policy->cpuinfo.max_freq = global.turbo_disabled ? - cpu->pstate.max_pstate : cpu->pstate.turbo_pstate; - policy->cpuinfo.max_freq *= cpu->pstate.scaling; - - if (hwp_active) { - unsigned int max_freq; - - max_freq = global.turbo_disabled ? cpu->pstate.max_freq : cpu->pstate.turbo_freq; - if (max_freq < policy->cpuinfo.max_freq) - policy->cpuinfo.max_freq = max_freq; - } + + policy->min = policy->cpuinfo.min_freq; + policy->max = policy->cpuinfo.max_freq; intel_pstate_init_acpi_perf_limits(policy); @@ -2683,10 +2671,10 @@ static void intel_cpufreq_adjust_perf(unsigned int cpunum, static int intel_cpufreq_cpu_init(struct cpufreq_policy *policy) { - int max_state, turbo_max, min_freq, max_freq, ret; struct freq_qos_request *req; struct cpudata *cpu; struct device *dev; + int ret, freq; dev = get_cpu_device(policy->cpu); if (!dev) @@ -2711,30 +2699,31 @@ static int intel_cpufreq_cpu_init(struct cpufreq_policy *policy) if (hwp_active) { u64 value; - intel_pstate_get_hwp_max(cpu, &turbo_max, &max_state); policy->transition_delay_us = INTEL_CPUFREQ_TRANSITION_DELAY_HWP; + + intel_pstate_get_hwp_cap(cpu); + rdmsrl_on_cpu(cpu->cpu, MSR_HWP_REQUEST, &value); WRITE_ONCE(cpu->hwp_req_cached, value); + cpu->epp_cached = intel_pstate_get_epp(cpu, value); } else { - turbo_max = cpu->pstate.turbo_pstate; policy->transition_delay_us = INTEL_CPUFREQ_TRANSITION_DELAY; } - min_freq = DIV_ROUND_UP(turbo_max * global.min_perf_pct, 100); - min_freq *= cpu->pstate.scaling; - max_freq = DIV_ROUND_UP(turbo_max * global.max_perf_pct, 100); - max_freq *= cpu->pstate.scaling; + freq = DIV_ROUND_UP(cpu->pstate.turbo_freq * global.min_perf_pct, 100); ret = freq_qos_add_request(&policy->constraints, req, FREQ_QOS_MIN, - min_freq); + freq); if (ret < 0) { dev_err(dev, "Failed to add min-freq constraint (%d)\n", ret); goto free_req; } + freq = DIV_ROUND_UP(cpu->pstate.turbo_freq * global.max_perf_pct, 100); + ret = freq_qos_add_request(&policy->constraints, req + 1, FREQ_QOS_MAX, - max_freq); + freq); if (ret < 0) { dev_err(dev, "Failed to add max-freq constraint (%d)\n", ret); goto remove_min_req; -- cgit v1.2.3-70-g09d2 From 4514d991d99211f225d83b7e640285f29f0755d0 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 16 Mar 2021 16:51:40 +0100 Subject: PCI: PM: Do not read power state in pci_enable_device_flags() It should not be necessary to update the current_state field of struct pci_dev in pci_enable_device_flags() before calling do_pci_enable_device() for the device, because none of the code between that point and the pci_set_power_state() call in do_pci_enable_device() invoked later depends on it. Moreover, doing that is actively harmful in some cases. For example, if the given PCI device depends on an ACPI power resource whose _STA method initially returns 0 ("off"), but the config space of the PCI device is accessible and the power state retrieved from the PCI_PM_CTRL register is D0, the current_state field in the struct pci_dev representing that device will get out of sync with the power.state of its ACPI companion object and that will lead to power management issues going forward. To avoid such issues it is better to leave the current_state value as is until it is changed to PCI_D0 by do_pci_enable_device() as appropriate. However, the power state of the device is not changed to PCI_D0 if it is already enabled when pci_enable_device_flags() gets called for it, so update its current_state in that case, but use pci_update_current_state() covering platform PM too for that. Link: https://lore.kernel.org/lkml/20210314000439.3138941-1-luzmaximilian@gmail.com/ Reported-by: Maximilian Luz Tested-by: Maximilian Luz Signed-off-by: Rafael J. Wysocki Reviewed-by: Mika Westerberg --- drivers/pci/pci.c | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 16a17215f633..e4d4e399004b 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1870,20 +1870,10 @@ static int pci_enable_device_flags(struct pci_dev *dev, unsigned long flags) int err; int i, bars = 0; - /* - * Power state could be unknown at this point, either due to a fresh - * boot or a device removal call. So get the current power state - * so that things like MSI message writing will behave as expected - * (e.g. if the device really is in D0 at enable time). - */ - if (dev->pm_cap) { - u16 pmcsr; - pci_read_config_word(dev, dev->pm_cap + PCI_PM_CTRL, &pmcsr); - dev->current_state = (pmcsr & PCI_PM_CTRL_STATE_MASK); - } - - if (atomic_inc_return(&dev->enable_cnt) > 1) + if (atomic_inc_return(&dev->enable_cnt) > 1) { + pci_update_current_state(dev, dev->current_state); return 0; /* already enabled */ + } bridge = pci_upstream_bridge(dev); if (bridge) -- cgit v1.2.3-70-g09d2 From c3135d28a1e099bcc12c2c2a2a154275f49ce86b Mon Sep 17 00:00:00 2001 From: Quanyang Wang Date: Thu, 25 Mar 2021 14:42:08 +0800 Subject: cpufreq: dt: dev_pm_opp_of_cpumask_add_table() may return -EPROBE_DEFER The function dev_pm_opp_of_cpumask_add_table() may return -EPROBE_DEFER, which needs to be propagated to the caller to try probing the driver later on. Signed-off-by: Quanyang Wang [ Viresh: Massage changelog/subject, improve code. ] Signed-off-by: Viresh Kumar --- drivers/cpufreq/cpufreq-dt.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/cpufreq/cpufreq-dt.c b/drivers/cpufreq/cpufreq-dt.c index b1e1bdc63b01..ece52863ba62 100644 --- a/drivers/cpufreq/cpufreq-dt.c +++ b/drivers/cpufreq/cpufreq-dt.c @@ -255,10 +255,15 @@ static int dt_cpufreq_early_init(struct device *dev, int cpu) * before updating priv->cpus. Otherwise, we will end up creating * duplicate OPPs for the CPUs. * - * OPPs might be populated at runtime, don't check for error here. + * OPPs might be populated at runtime, don't fail for error here unless + * it is -EPROBE_DEFER. */ - if (!dev_pm_opp_of_cpumask_add_table(priv->cpus)) + ret = dev_pm_opp_of_cpumask_add_table(priv->cpus); + if (!ret) { priv->have_static_opps = true; + } else if (ret == -EPROBE_DEFER) { + goto out; + } /* * The OPP table must be initialized, statically or dynamically, by this -- cgit v1.2.3-70-g09d2 From a8bb0e872bfb962de4653ce8f8723c0e1f712ce3 Mon Sep 17 00:00:00 2001 From: Yangtao Li Date: Sun, 14 Mar 2021 19:34:08 +0300 Subject: memory: samsung: exynos5422-dmc: Convert to use resource-managed OPP API Use resource-managed OPP API to simplify code. Signed-off-by: Yangtao Li Reviewed-by: Krzysztof Kozlowski Signed-off-by: Dmitry Osipenko Signed-off-by: Viresh Kumar --- drivers/memory/samsung/exynos5422-dmc.c | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/memory/samsung/exynos5422-dmc.c b/drivers/memory/samsung/exynos5422-dmc.c index 1dabb509dec3..56f6e65d40cd 100644 --- a/drivers/memory/samsung/exynos5422-dmc.c +++ b/drivers/memory/samsung/exynos5422-dmc.c @@ -343,7 +343,7 @@ static int exynos5_init_freq_table(struct exynos5_dmc *dmc, int idx; unsigned long freq; - ret = dev_pm_opp_of_add_table(dmc->dev); + ret = devm_pm_opp_of_add_table(dmc->dev); if (ret < 0) { dev_err(dmc->dev, "Failed to get OPP table\n"); return ret; @@ -354,7 +354,7 @@ static int exynos5_init_freq_table(struct exynos5_dmc *dmc, dmc->opp = devm_kmalloc_array(dmc->dev, dmc->opp_count, sizeof(struct dmc_opp_table), GFP_KERNEL); if (!dmc->opp) - goto err_opp; + return -ENOMEM; idx = dmc->opp_count - 1; for (i = 0, freq = ULONG_MAX; i < dmc->opp_count; i++, freq--) { @@ -362,7 +362,7 @@ static int exynos5_init_freq_table(struct exynos5_dmc *dmc, opp = dev_pm_opp_find_freq_floor(dmc->dev, &freq); if (IS_ERR(opp)) - goto err_opp; + return PTR_ERR(opp); dmc->opp[idx - i].freq_hz = freq; dmc->opp[idx - i].volt_uv = dev_pm_opp_get_voltage(opp); @@ -371,11 +371,6 @@ static int exynos5_init_freq_table(struct exynos5_dmc *dmc, } return 0; - -err_opp: - dev_pm_opp_of_remove_table(dmc->dev); - - return -EINVAL; } /** @@ -1567,8 +1562,6 @@ static int exynos5_dmc_remove(struct platform_device *pdev) clk_disable_unprepare(dmc->mout_bpll); clk_disable_unprepare(dmc->fout_bpll); - dev_pm_opp_remove_table(dmc->dev); - return 0; } -- cgit v1.2.3-70-g09d2 From 12c0632b25794ed4de46427811e71fee66f19f15 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 31 Mar 2021 18:26:10 -0500 Subject: PM: runtime: remove kernel-doc warnings remove make W=1 warnings drivers/base/power/runtime.c:926: warning: Function parameter or member 'timer' not described in 'pm_suspend_timer_fn' drivers/base/power/runtime.c:926: warning: Excess function parameter 'data' description in 'pm_suspend_timer_fn' Signed-off-by: Pierre-Louis Bossart Signed-off-by: Rafael J. Wysocki --- drivers/base/power/runtime.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index fe1dad68aee4..1fc1a992f90c 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c @@ -951,7 +951,7 @@ static void pm_runtime_work(struct work_struct *work) /** * pm_suspend_timer_fn - Timer function for pm_schedule_suspend(). - * @data: Device pointer passed by pm_schedule_suspend(). + * @timer: hrtimer used by pm_schedule_suspend(). * * Check if the time is right and queue a suspend request. */ -- cgit v1.2.3-70-g09d2 From beafe82b41deff86ab5d5bdc3acce7ae63c9b28b Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 31 Mar 2021 18:26:11 -0500 Subject: PM: wakeup: fix kernel-doc warnings and fix typos Remove make W=1 warnings and fit 'Itereates' typos drivers/base/power/wakeup.c:403: warning: wrong kernel-doc identifier on line: * device_wakeup_arm_wake_irqs(void) drivers/base/power/wakeup.c:419: warning: wrong kernel-doc identifier on line: * device_wakeup_disarm_wake_irqs(void) drivers/base/power/wakeup.c:537: warning: Function parameter or member 'enable' not described in 'device_set_wakeup_enable' drivers/base/power/wakeup.c:592: warning: expecting prototype for wakup_source_activate(). Prototype was for wakeup_source_activate() instead drivers/base/power/wakeup.c:697: warning: expecting prototype for wakup_source_deactivate(). Prototype was for wakeup_source_deactivate() instead drivers/base/power/wakeup.c:795: warning: Function parameter or member 't' not described in 'pm_wakeup_timer_fn' drivers/base/power/wakeup.c:795: warning: Excess function parameter 'data' description in 'pm_wakeup_timer_fn' drivers/base/power/wakeup.c:1027: warning: Function parameter or member 'set' not described in 'pm_wakep_autosleep_enabled' drivers/base/power/wakeup.c:1027: warning: Excess function parameter 'enabled' description in 'pm_wakep_autosleep_enabled' Signed-off-by: Pierre-Louis Bossart Signed-off-by: Rafael J. Wysocki --- drivers/base/power/wakeup.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c index 92073ac68473..f0b37c188514 100644 --- a/drivers/base/power/wakeup.c +++ b/drivers/base/power/wakeup.c @@ -400,9 +400,9 @@ void device_wakeup_detach_irq(struct device *dev) } /** - * device_wakeup_arm_wake_irqs(void) + * device_wakeup_arm_wake_irqs - * - * Itereates over the list of device wakeirqs to arm them. + * Iterates over the list of device wakeirqs to arm them. */ void device_wakeup_arm_wake_irqs(void) { @@ -416,9 +416,9 @@ void device_wakeup_arm_wake_irqs(void) } /** - * device_wakeup_disarm_wake_irqs(void) + * device_wakeup_disarm_wake_irqs - * - * Itereates over the list of device wakeirqs to disarm them. + * Iterates over the list of device wakeirqs to disarm them. */ void device_wakeup_disarm_wake_irqs(void) { @@ -532,6 +532,7 @@ EXPORT_SYMBOL_GPL(device_init_wakeup); /** * device_set_wakeup_enable - Enable or disable a device to wake up the system. * @dev: Device to handle. + * @enable: enable/disable flag */ int device_set_wakeup_enable(struct device *dev, bool enable) { @@ -581,7 +582,7 @@ static bool wakeup_source_not_registered(struct wakeup_source *ws) */ /** - * wakup_source_activate - Mark given wakeup source as active. + * wakeup_source_activate - Mark given wakeup source as active. * @ws: Wakeup source to handle. * * Update the @ws' statistics and, if @ws has just been activated, notify the PM @@ -686,7 +687,7 @@ static inline void update_prevent_sleep_time(struct wakeup_source *ws, #endif /** - * wakup_source_deactivate - Mark given wakeup source as inactive. + * wakeup_source_deactivate - Mark given wakeup source as inactive. * @ws: Wakeup source to handle. * * Update the @ws' statistics and notify the PM core that the wakeup source has @@ -785,7 +786,7 @@ EXPORT_SYMBOL_GPL(pm_relax); /** * pm_wakeup_timer_fn - Delayed finalization of a wakeup event. - * @data: Address of the wakeup source object associated with the event source. + * @t: timer list * * Call wakeup_source_deactivate() for the wakeup source whose address is stored * in @data if it is currently active and its timer has not been canceled and @@ -1021,7 +1022,7 @@ bool pm_save_wakeup_count(unsigned int count) #ifdef CONFIG_PM_AUTOSLEEP /** * pm_wakep_autosleep_enabled - Modify autosleep_enabled for all wakeup sources. - * @enabled: Whether to set or to clear the autosleep_enabled flags. + * @set: Whether to set or to clear the autosleep_enabled flags. */ void pm_wakep_autosleep_enabled(bool set) { -- cgit v1.2.3-70-g09d2 From 33b688e3854d6ad76d0acbeebc601ce2ddb8513d Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 31 Mar 2021 18:26:12 -0500 Subject: PM: clk: remove kernel-doc warning Remove make W=1 warning: drivers/base/power/clock_ops.c:148: warning: expecting prototype for pm_clk_enable(). Prototype was for __pm_clk_enable() instead Signed-off-by: Pierre-Louis Bossart Signed-off-by: Rafael J. Wysocki --- drivers/base/power/clock_ops.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/base/power/clock_ops.c b/drivers/base/power/clock_ops.c index 84d5acb6301b..0251f3e6e61d 100644 --- a/drivers/base/power/clock_ops.c +++ b/drivers/base/power/clock_ops.c @@ -140,7 +140,7 @@ static void pm_clk_op_unlock(struct pm_subsys_data *psd, unsigned long *flags) } /** - * pm_clk_enable - Enable a clock, reporting any errors + * __pm_clk_enable - Enable a clock, reporting any errors * @dev: The device for the given clock * @ce: PM clock entry corresponding to the clock. */ -- cgit v1.2.3-70-g09d2 From 2ab80d46fead0309d7f190d8023c8d64b2ffcbd5 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 29 Mar 2021 20:15:19 +0200 Subject: cpuidle: Use s64 as exit_latency_ns and target_residency_ns data type Subsequent changes will cause the exit_latency_ns and target_residency_ns fields in struct cpuidle_state to be used in computations in which data type conversions to u64 may turn a negative number close to zero into a verly large positive number leading to incorrect results. In preparation for that, change the data type of the fields mentioned above to s64, but ensure that they will not be negative themselves. No intentional functional impact. Signed-off-by: Rafael J. Wysocki --- drivers/cpuidle/driver.c | 4 ++++ include/linux/cpuidle.h | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/cpuidle/driver.c b/drivers/cpuidle/driver.c index 4070e573bf43..f70aa17e2a8e 100644 --- a/drivers/cpuidle/driver.c +++ b/drivers/cpuidle/driver.c @@ -181,9 +181,13 @@ static void __cpuidle_driver_init(struct cpuidle_driver *drv) */ if (s->target_residency > 0) s->target_residency_ns = s->target_residency * NSEC_PER_USEC; + else if (s->target_residency_ns < 0) + s->target_residency_ns = 0; if (s->exit_latency > 0) s->exit_latency_ns = s->exit_latency * NSEC_PER_USEC; + else if (s->exit_latency_ns < 0) + s->exit_latency_ns = 0; } } diff --git a/include/linux/cpuidle.h b/include/linux/cpuidle.h index bd605b5585cf..fce476275e16 100644 --- a/include/linux/cpuidle.h +++ b/include/linux/cpuidle.h @@ -49,8 +49,8 @@ struct cpuidle_state { char name[CPUIDLE_NAME_LEN]; char desc[CPUIDLE_DESC_LEN]; - u64 exit_latency_ns; - u64 target_residency_ns; + s64 exit_latency_ns; + s64 target_residency_ns; unsigned int flags; unsigned int exit_latency; /* in US */ int power_usage; /* in mW */ -- cgit v1.2.3-70-g09d2 From d3c33be1f350e7fd2b04381b000f3f950bd1ba77 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 29 Mar 2021 20:19:03 +0200 Subject: cpuidle: teo: Adjust handling of very short idle times If the time till the next timer event is shorter than the target residency of the first idle state (state 0), the TEO governor does not update its metrics for any idle states, but arguably it should record a "hit" for idle state 0 in that case, so modify it to do that. Accordingly, also make it record an "early hit" for idle state 0 if the measured idle duration is less than its target residency, which allows one branch more to be dropped from teo_update(). Signed-off-by: Rafael J. Wysocki --- drivers/cpuidle/governors/teo.c | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) (limited to 'drivers') diff --git a/drivers/cpuidle/governors/teo.c b/drivers/cpuidle/governors/teo.c index 6deaaf5f05b5..7dcfc9499910 100644 --- a/drivers/cpuidle/governors/teo.c +++ b/drivers/cpuidle/governors/teo.c @@ -117,7 +117,8 @@ static DEFINE_PER_CPU(struct teo_cpu, teo_cpus); static void teo_update(struct cpuidle_driver *drv, struct cpuidle_device *dev) { struct teo_cpu *cpu_data = per_cpu_ptr(&teo_cpus, dev->cpu); - int i, idx_hit = -1, idx_timer = -1; + int i, idx_hit = 0, idx_timer = 0; + unsigned int hits, misses; u64 measured_ns; if (cpu_data->time_span_ns >= cpu_data->sleep_length_ns) { @@ -174,25 +175,22 @@ static void teo_update(struct cpuidle_driver *drv, struct cpuidle_device *dev) * also increase the "early hits" metric for the state that actually * matches the measured idle duration. */ - if (idx_timer >= 0) { - unsigned int hits = cpu_data->states[idx_timer].hits; - unsigned int misses = cpu_data->states[idx_timer].misses; - - hits -= hits >> DECAY_SHIFT; - misses -= misses >> DECAY_SHIFT; - - if (idx_timer > idx_hit) { - misses += PULSE; - if (idx_hit >= 0) - cpu_data->states[idx_hit].early_hits += PULSE; - } else { - hits += PULSE; - } + hits = cpu_data->states[idx_timer].hits; + hits -= hits >> DECAY_SHIFT; + + misses = cpu_data->states[idx_timer].misses; + misses -= misses >> DECAY_SHIFT; - cpu_data->states[idx_timer].misses = misses; - cpu_data->states[idx_timer].hits = hits; + if (idx_timer == idx_hit) { + hits += PULSE; + } else { + misses += PULSE; + cpu_data->states[idx_hit].early_hits += PULSE; } + cpu_data->states[idx_timer].misses = misses; + cpu_data->states[idx_timer].hits = hits; + /* * Save idle duration values corresponding to non-timer wakeups for * pattern detection. -- cgit v1.2.3-70-g09d2 From 030adec9f68e30cbbc24c57296a141943177c148 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 29 Mar 2021 20:21:43 +0200 Subject: cpuidle: teo: Take negative "sleep length" values into account Modify the TEO governor to take possible negative return values of tick_nohz_get_next_hrtimer() into account by changing the data type of some variables used by it to s64 which allows it to carry out computations without potentially problematic data type conversions into u64. Also change the computations in teo_select() so that the negative values themselves are handled in a natural way to avoid adding extra negative value checks to that function. Signed-off-by: Rafael J. Wysocki --- drivers/cpuidle/governors/teo.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/cpuidle/governors/teo.c b/drivers/cpuidle/governors/teo.c index 7dcfc9499910..ac4bb27d69b0 100644 --- a/drivers/cpuidle/governors/teo.c +++ b/drivers/cpuidle/governors/teo.c @@ -100,8 +100,8 @@ struct teo_idle_state { * @intervals: Saved idle duration values. */ struct teo_cpu { - u64 time_span_ns; - u64 sleep_length_ns; + s64 time_span_ns; + s64 sleep_length_ns; struct teo_idle_state states[CPUIDLE_STATE_MAX]; int interval_idx; u64 intervals[INTERVALS]; @@ -214,7 +214,7 @@ static bool teo_time_ok(u64 interval_ns) */ static int teo_find_shallower_state(struct cpuidle_driver *drv, struct cpuidle_device *dev, int state_idx, - u64 duration_ns) + s64 duration_ns) { int i; @@ -240,10 +240,10 @@ static int teo_select(struct cpuidle_driver *drv, struct cpuidle_device *dev, { struct teo_cpu *cpu_data = per_cpu_ptr(&teo_cpus, dev->cpu); s64 latency_req = cpuidle_governor_latency_req(dev->cpu); - u64 duration_ns; + int max_early_idx, prev_max_early_idx, constraint_idx, idx0, idx, i; unsigned int hits, misses, early_hits; - int max_early_idx, prev_max_early_idx, constraint_idx, idx, i; ktime_t delta_tick; + s64 duration_ns; if (dev->last_state_idx >= 0) { teo_update(drv, dev); @@ -262,6 +262,7 @@ static int teo_select(struct cpuidle_driver *drv, struct cpuidle_device *dev, prev_max_early_idx = -1; constraint_idx = drv->state_count; idx = -1; + idx0 = idx; for (i = 0; i < drv->state_count; i++) { struct cpuidle_state *s = &drv->states[i]; @@ -322,6 +323,7 @@ static int teo_select(struct cpuidle_driver *drv, struct cpuidle_device *dev, idx = i; /* first enabled state */ hits = cpu_data->states[i].hits; misses = cpu_data->states[i].misses; + idx0 = i; } if (s->target_residency_ns > duration_ns) @@ -374,11 +376,16 @@ static int teo_select(struct cpuidle_driver *drv, struct cpuidle_device *dev, if (idx < 0) { idx = 0; /* No states enabled. Must use 0. */ - } else if (idx > 0) { + } else if (idx > idx0) { unsigned int count = 0; u64 sum = 0; /* + * The target residencies of at least two different enabled idle + * states are less than or equal to the current expected idle + * duration. Try to refine the selection using the most recent + * measured idle duration values. + * * Count and sum the most recent idle duration values less than * the current expected idle duration value. */ @@ -426,7 +433,8 @@ static int teo_select(struct cpuidle_driver *drv, struct cpuidle_device *dev, * till the closest timer including the tick, try to correct * that. */ - if (idx > 0 && drv->states[idx].target_residency_ns > delta_tick) + if (idx > idx0 && + drv->states[idx].target_residency_ns > delta_tick) idx = teo_find_shallower_state(drv, dev, idx, delta_tick); } -- cgit v1.2.3-70-g09d2 From 060e3535adf5c961b01421b9fdaddab8dd43ba85 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 29 Mar 2021 20:37:12 +0200 Subject: cpuidle: menu: Take negative "sleep length" values into account Make the menu governor check the tick_nohz_get_next_hrtimer() return value so as to avoid dealing with negative "sleep length" values and make it use that value directly when the tick is stopped. While at it, rename local variable delta_next in menu_select() to delta_tick which better reflects its purpose. Signed-off-by: Rafael J. Wysocki --- drivers/cpuidle/governors/menu.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/cpuidle/governors/menu.c b/drivers/cpuidle/governors/menu.c index b0a7ad566081..c3aa8d6ccee3 100644 --- a/drivers/cpuidle/governors/menu.c +++ b/drivers/cpuidle/governors/menu.c @@ -271,7 +271,7 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev, u64 predicted_ns; u64 interactivity_req; unsigned long nr_iowaiters; - ktime_t delta_next; + ktime_t delta, delta_tick; int i, idx; if (data->needs_update) { @@ -280,7 +280,12 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev, } /* determine the expected residency time, round up */ - data->next_timer_ns = tick_nohz_get_sleep_length(&delta_next); + delta = tick_nohz_get_sleep_length(&delta_tick); + if (unlikely(delta < 0)) { + delta = 0; + delta_tick = 0; + } + data->next_timer_ns = delta; nr_iowaiters = nr_iowait_cpu(dev->cpu); data->bucket = which_bucket(data->next_timer_ns, nr_iowaiters); @@ -318,7 +323,7 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev, * state selection. */ if (predicted_ns < TICK_NSEC) - predicted_ns = delta_next; + predicted_ns = data->next_timer_ns; } else { /* * Use the performance multiplier and the user-configurable @@ -377,7 +382,7 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev, * stuck in the shallow one for too long. */ if (drv->states[idx].target_residency_ns < TICK_NSEC && - s->target_residency_ns <= delta_next) + s->target_residency_ns <= delta_tick) idx = i; return idx; @@ -399,7 +404,7 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev, predicted_ns < TICK_NSEC) && !tick_nohz_tick_stopped()) { *stop_tick = false; - if (idx > 0 && drv->states[idx].target_residency_ns > delta_next) { + if (idx > 0 && drv->states[idx].target_residency_ns > delta_tick) { /* * The tick is not going to be stopped and the target * residency of the state to be returned is not within @@ -411,7 +416,7 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev, continue; idx = i; - if (drv->states[i].target_residency_ns <= delta_next) + if (drv->states[i].target_residency_ns <= delta_tick) break; } } -- cgit v1.2.3-70-g09d2 From 8b50a7995770d41a2e8d9c422cd2882aca0dedd2 Mon Sep 17 00:00:00 2001 From: Lukasz Luba Date: Mon, 15 Mar 2021 09:31:23 +0000 Subject: PM / devfreq: Unlock mutex and free devfreq struct in error path The devfreq->lock is held for time of setup. Release the lock in the error path, before jumping to the end of the function. Change the goto destination which frees the allocated memory. Cc: v5.9+ # v5.9+ Fixes: 4dc3bab8687f ("PM / devfreq: Add support delayed timer for polling mode") Signed-off-by: Lukasz Luba Signed-off-by: Chanwoo Choi --- drivers/devfreq/devfreq.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index b6d3e7db0b09..99b2eeedc238 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -822,7 +822,8 @@ struct devfreq *devfreq_add_device(struct device *dev, if (devfreq->profile->timer < 0 || devfreq->profile->timer >= DEVFREQ_TIMER_NUM) { - goto err_out; + mutex_unlock(&devfreq->lock); + goto err_dev; } if (!devfreq->profile->max_state && !devfreq->profile->freq_table) { -- cgit v1.2.3-70-g09d2 From 62453f1ba5d5def9d58e140a50f3f168f028da38 Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Tue, 23 Mar 2021 15:20:08 +0800 Subject: PM / devfreq: Use more accurate returned new_freq as resume_freq Use the more accurate returned new_freq as resume_freq. It's the same as how devfreq->previous_freq was updated. Fixes: 83f8ca45afbf0 ("PM / devfreq: add support for suspend/resume of a devfreq device") Signed-off-by: Dong Aisheng Signed-off-by: Chanwoo Choi --- drivers/devfreq/devfreq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index 99b2eeedc238..fe08c46642f7 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -388,7 +388,7 @@ static int devfreq_set_target(struct devfreq *devfreq, unsigned long new_freq, devfreq->previous_freq = new_freq; if (devfreq->suspend_freq) - devfreq->resume_freq = cur_freq; + devfreq->resume_freq = new_freq; return err; } -- cgit v1.2.3-70-g09d2 From fbf821ec632b4bb93ec9224c1e75c36b01ad16ed Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sun, 28 Feb 2021 15:52:32 +0900 Subject: PM / devfreq: rk3399_dmc: Simplify with dev_err_probe() Common pattern of handling deferred probe can be simplified with dev_err_probe(). Less code and the error value gets printed. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Chanwoo Choi --- drivers/devfreq/rk3399_dmc.c | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/devfreq/rk3399_dmc.c b/drivers/devfreq/rk3399_dmc.c index 9e9d3b4c6d48..293857ebfd75 100644 --- a/drivers/devfreq/rk3399_dmc.c +++ b/drivers/devfreq/rk3399_dmc.c @@ -324,22 +324,14 @@ static int rk3399_dmcfreq_probe(struct platform_device *pdev) mutex_init(&data->lock); data->vdd_center = devm_regulator_get(dev, "center"); - if (IS_ERR(data->vdd_center)) { - if (PTR_ERR(data->vdd_center) == -EPROBE_DEFER) - return -EPROBE_DEFER; - - dev_err(dev, "Cannot get the regulator \"center\"\n"); - return PTR_ERR(data->vdd_center); - } + if (IS_ERR(data->vdd_center)) + return dev_err_probe(dev, PTR_ERR(data->vdd_center), + "Cannot get the regulator \"center\"\n"); data->dmc_clk = devm_clk_get(dev, "dmc_clk"); - if (IS_ERR(data->dmc_clk)) { - if (PTR_ERR(data->dmc_clk) == -EPROBE_DEFER) - return -EPROBE_DEFER; - - dev_err(dev, "Cannot get the clk dmc_clk\n"); - return PTR_ERR(data->dmc_clk); - } + if (IS_ERR(data->dmc_clk)) + return dev_err_probe(dev, PTR_ERR(data->dmc_clk), + "Cannot get the clk dmc_clk\n"); data->edev = devfreq_event_get_edev_by_phandle(dev, "devfreq-events", 0); if (IS_ERR(data->edev)) -- cgit v1.2.3-70-g09d2 From 5f104f9fc1bb1423058b98b8a64b01af70f44547 Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Tue, 9 Mar 2021 20:58:33 +0800 Subject: PM / devfreq: Fix the wrong set_freq path for userspace governor in Kconfig Fix the wrong set_freq path for userspace governor in Kconfig. Signed-off-by: Dong Aisheng Signed-off-by: Chanwoo Choi --- drivers/devfreq/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/devfreq/Kconfig b/drivers/devfreq/Kconfig index 00704efe6398..20373a893b44 100644 --- a/drivers/devfreq/Kconfig +++ b/drivers/devfreq/Kconfig @@ -62,7 +62,7 @@ config DEVFREQ_GOV_USERSPACE help Sets the frequency at the user specified one. This governor returns the user configured frequency if there - has been an input to /sys/devices/.../power/devfreq_set_freq. + has been an input to /sys/devices/.../userspace/set_freq. Otherwise, the governor does not change the frequency given at the initialization. -- cgit v1.2.3-70-g09d2 From b19e13463a1054b1b51f4314a1a53da02aed776f Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Tue, 9 Mar 2021 20:58:38 +0800 Subject: PM / devfreq: Check get_dev_status in devfreq_update_stats Check .get_dev_status() in devfreq_update_stats in case it's abused when a device does not provide it. Signed-off-by: Dong Aisheng Signed-off-by: Chanwoo Choi --- drivers/devfreq/governor.h | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/devfreq/governor.h b/drivers/devfreq/governor.h index 70f44b3ca42e..244634465170 100644 --- a/drivers/devfreq/governor.h +++ b/drivers/devfreq/governor.h @@ -91,6 +91,9 @@ int devfreq_update_target(struct devfreq *devfreq, unsigned long freq); static inline int devfreq_update_stats(struct devfreq *df) { + if (!df->profile->get_dev_status) + return -EINVAL; + return df->profile->get_dev_status(df->dev.parent, &df->last_status); } #endif /* _GOVERNOR_H */ -- cgit v1.2.3-70-g09d2 From 05f15314f0895bf7dfee67142cd3f7aca3414658 Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Tue, 23 Mar 2021 15:20:09 +0800 Subject: PM / devfreq: Remove the invalid description for get_target_freq First of all, no_central_polling was removed since commit 7e6fdd4bad03 ("PM / devfreq: Core updates to support devices which can idle") Secondly, get_target_freq() is not only called only with update_devfreq() notified by OPP now, but also min/max freq qos notifier. So remove this invalid description now to avoid confusing. Signed-off-by: Dong Aisheng Signed-off-by: Chanwoo Choi --- Documentation/ABI/testing/sysfs-class-devfreq | 5 +---- drivers/devfreq/governor.h | 2 -- 2 files changed, 1 insertion(+), 6 deletions(-) (limited to 'drivers') diff --git a/Documentation/ABI/testing/sysfs-class-devfreq b/Documentation/ABI/testing/sysfs-class-devfreq index 386bc230a33d..5e6b74f30406 100644 --- a/Documentation/ABI/testing/sysfs-class-devfreq +++ b/Documentation/ABI/testing/sysfs-class-devfreq @@ -97,10 +97,7 @@ Description: object. The values are represented in ms. If the value is less than 1 jiffy, it is considered to be 0, which means no polling. This value is meaningless if the governor is - not polling; thus. If the governor is not using - devfreq-provided central polling - (/sys/class/devfreq/.../central_polling is 0), this value - may be useless. + not polling. A list of governors that support the node: - simple_ondmenad diff --git a/drivers/devfreq/governor.h b/drivers/devfreq/governor.h index 244634465170..2d69a0ce6291 100644 --- a/drivers/devfreq/governor.h +++ b/drivers/devfreq/governor.h @@ -57,8 +57,6 @@ * Basically, get_target_freq will run * devfreq_dev_profile.get_dev_status() to get the * status of the device (load = busy_time / total_time). - * If no_central_polling is set, this callback is called - * only with update_devfreq() notified by OPP. * @event_handler: Callback for devfreq core framework to notify events * to governors. Events include per device governor * init and exit, opp changes out of devfreq, suspend -- cgit v1.2.3-70-g09d2 From 6c4b264c70adcec0d4e2cdb4573ee8e4526b584d Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Tue, 23 Mar 2021 15:20:11 +0800 Subject: PM / devfreq: imx8m-ddrc: Remove imx8m_ddrc_get_dev_status Current driver actually does not support simple ondemand governor as it's unable to provide device load information. So removing the unnecessary callback to avoid confusing. Right now the driver is using userspace governor by default. polling_ms was also dropped as it's not needed for non-ondemand governor. Signed-off-by: Dong Aisheng Signed-off-by: Chanwoo Choi --- drivers/devfreq/imx8m-ddrc.c | 14 -------------- 1 file changed, 14 deletions(-) (limited to 'drivers') diff --git a/drivers/devfreq/imx8m-ddrc.c b/drivers/devfreq/imx8m-ddrc.c index bc82d3653bff..ecb9375aa877 100644 --- a/drivers/devfreq/imx8m-ddrc.c +++ b/drivers/devfreq/imx8m-ddrc.c @@ -280,18 +280,6 @@ static int imx8m_ddrc_get_cur_freq(struct device *dev, unsigned long *freq) return 0; } -static int imx8m_ddrc_get_dev_status(struct device *dev, - struct devfreq_dev_status *stat) -{ - struct imx8m_ddrc *priv = dev_get_drvdata(dev); - - stat->busy_time = 0; - stat->total_time = 0; - stat->current_frequency = clk_get_rate(priv->dram_core); - - return 0; -} - static int imx8m_ddrc_init_freq_info(struct device *dev) { struct imx8m_ddrc *priv = dev_get_drvdata(dev); @@ -429,9 +417,7 @@ static int imx8m_ddrc_probe(struct platform_device *pdev) if (ret < 0) goto err; - priv->profile.polling_ms = 1000; priv->profile.target = imx8m_ddrc_target; - priv->profile.get_dev_status = imx8m_ddrc_get_dev_status; priv->profile.exit = imx8m_ddrc_exit; priv->profile.get_cur_freq = imx8m_ddrc_get_cur_freq; priv->profile.initial_freq = clk_get_rate(priv->dram_core); -- cgit v1.2.3-70-g09d2 From ca948312e00056124e8026478a36d3b7baeb0b22 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 29 Mar 2021 09:24:24 -0300 Subject: PM / devfreq: imx-bus: Remove unneeded of_match_ptr() i.MX is a DT-only platform, so of_match_ptr() can be safely removed. Remove the unneeded of_match_ptr(). Signed-off-by: Fabio Estevam Signed-off-by: Chanwoo Choi --- drivers/devfreq/imx-bus.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/devfreq/imx-bus.c b/drivers/devfreq/imx-bus.c index 4f38455ad742..3fc3fd77492d 100644 --- a/drivers/devfreq/imx-bus.c +++ b/drivers/devfreq/imx-bus.c @@ -169,7 +169,7 @@ static struct platform_driver imx_bus_platdrv = { .probe = imx_bus_probe, .driver = { .name = "imx-bus-devfreq", - .of_match_table = of_match_ptr(imx_bus_of_match), + .of_match_table = imx_bus_of_match, }, }; module_platform_driver(imx_bus_platdrv); -- cgit v1.2.3-70-g09d2 From 0a7dc8318c2817fb33dc50946f7ca6e0ff28f036 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 29 Mar 2021 09:24:25 -0300 Subject: PM / devfreq: imx8m-ddrc: Remove unneeded of_match_ptr() i.MX is a DT-only platform, so of_match_ptr() can be safely removed. Remove the unneeded of_match_ptr(). Signed-off-by: Fabio Estevam Signed-off-by: Chanwoo Choi --- drivers/devfreq/imx8m-ddrc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/devfreq/imx8m-ddrc.c b/drivers/devfreq/imx8m-ddrc.c index ecb9375aa877..16636973eb10 100644 --- a/drivers/devfreq/imx8m-ddrc.c +++ b/drivers/devfreq/imx8m-ddrc.c @@ -447,7 +447,7 @@ static struct platform_driver imx8m_ddrc_platdrv = { .probe = imx8m_ddrc_probe, .driver = { .name = "imx8m-ddrc-devfreq", - .of_match_table = of_match_ptr(imx8m_ddrc_of_match), + .of_match_table = imx8m_ddrc_of_match, }, }; module_platform_driver(imx8m_ddrc_platdrv); -- cgit v1.2.3-70-g09d2 From 22141d5f411895bb1b0df2a6b05f702e11e63918 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Wed, 7 Apr 2021 09:10:28 +0300 Subject: intel_idle: add Iclelake-D support This patch adds Icelake Xeon D support to the intel_idle driver. Since Icelake D and Icelake SP C-state characteristics the same, we use Icelake SP C-states table for Icelake D as well. Signed-off-by: Artem Bityutskiy Acked-by: Chen Yu Signed-off-by: Rafael J. Wysocki --- drivers/idle/intel_idle.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c index 6cac0b748efa..ec1b9d306ba6 100644 --- a/drivers/idle/intel_idle.c +++ b/drivers/idle/intel_idle.c @@ -1156,6 +1156,7 @@ static const struct x86_cpu_id intel_idle_ids[] __initconst = { X86_MATCH_INTEL_FAM6_MODEL(KABYLAKE, &idle_cpu_skl), X86_MATCH_INTEL_FAM6_MODEL(SKYLAKE_X, &idle_cpu_skx), X86_MATCH_INTEL_FAM6_MODEL(ICELAKE_X, &idle_cpu_icx), + X86_MATCH_INTEL_FAM6_MODEL(ICELAKE_D, &idle_cpu_icx), X86_MATCH_INTEL_FAM6_MODEL(XEON_PHI_KNL, &idle_cpu_knl), X86_MATCH_INTEL_FAM6_MODEL(XEON_PHI_KNM, &idle_cpu_knl), X86_MATCH_INTEL_FAM6_MODEL(ATOM_GOLDMONT, &idle_cpu_bxt), -- cgit v1.2.3-70-g09d2 From 60943bbdb42e966aa4d50d587913721b95208fef Mon Sep 17 00:00:00 2001 From: Shaokun Zhang Date: Tue, 6 Apr 2021 15:00:59 +0800 Subject: cpufreq: Remove unused for_each_policy macro Macro 'for_each_policy' has become unused since commit f963735a3ca3 ("cpufreq: Create for_each_{in}active_policy()"), so remove it. Signed-off-by: Shaokun Zhang Acked-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/cpufreq.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers') diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 1d1b563cea4b..802abc925b2a 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -42,9 +42,6 @@ static LIST_HEAD(cpufreq_policy_list); #define for_each_inactive_policy(__policy) \ for_each_suitable_policy(__policy, false) -#define for_each_policy(__policy) \ - list_for_each_entry(__policy, &cpufreq_policy_list, policy_list) - /* Iterate over governors */ static LIST_HEAD(cpufreq_governor_list); #define for_each_governor(__governor) \ -- cgit v1.2.3-70-g09d2 From 32c8c34d8132b5fe8497c2538597445a0d65c29d Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Tue, 2 Mar 2021 12:54:04 +0300 Subject: cpuidle: tegra: Fix C7 idling state on Tegra114 Trusted Foundation firmware doesn't implement the do_idle call and in this case suspending should fall back to the common suspend path. In order to fix this issue we will unconditionally set the NOFLUSH_L2 mode via firmware call, which is a NO-OP on Tegra30/124, and then proceed to the C7 idling, like it was done by the older Tegra114 cpuidle driver. Fixes: 14e086baca50 ("cpuidle: tegra: Squash Tegra114 driver into the common driver") Cc: stable@vger.kernel.org # 5.7+ Reported-by: Anton Bambura # TF701 T114 Tested-by: Anton Bambura # TF701 T114 Tested-by: Matt Merhar # Ouya T30 Tested-by: Peter Geis # Ouya T30 Signed-off-by: Dmitry Osipenko Reviewed-by: Daniel Lezcano Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20210302095405.28453-1-digetx@gmail.com --- drivers/cpuidle/cpuidle-tegra.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/cpuidle/cpuidle-tegra.c b/drivers/cpuidle/cpuidle-tegra.c index 191966dc8d02..29c5e83500d3 100644 --- a/drivers/cpuidle/cpuidle-tegra.c +++ b/drivers/cpuidle/cpuidle-tegra.c @@ -135,13 +135,13 @@ static int tegra_cpuidle_c7_enter(void) { int err; - if (tegra_cpuidle_using_firmware()) { - err = call_firmware_op(prepare_idle, TF_PM_MODE_LP2_NOFLUSH_L2); - if (err) - return err; + err = call_firmware_op(prepare_idle, TF_PM_MODE_LP2_NOFLUSH_L2); + if (err && err != -ENOSYS) + return err; - return call_firmware_op(do_idle, 0); - } + err = call_firmware_op(do_idle, 0); + if (err != -ENOSYS) + return err; return cpu_suspend(0, tegra30_pm_secondary_cpu_suspend); } -- cgit v1.2.3-70-g09d2 From 2dabed47778958cca7880544c20e55984bf43d33 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Tue, 2 Mar 2021 12:54:05 +0300 Subject: cpuidle: tegra: Remove do_idle firmware call The do_idle firmware call is unused by all Tegra SoCs, hence remove it in order to keep driver's code clean. Tested-by: Anton Bambura # TF701 T114 Tested-by: Matt Merhar # Ouya T30 Tested-by: Peter Geis # Ouya T30 Signed-off-by: Dmitry Osipenko Reviewed-by: Daniel Lezcano Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20210302095405.28453-2-digetx@gmail.com --- drivers/cpuidle/cpuidle-tegra.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/cpuidle/cpuidle-tegra.c b/drivers/cpuidle/cpuidle-tegra.c index 29c5e83500d3..508bd9f23792 100644 --- a/drivers/cpuidle/cpuidle-tegra.c +++ b/drivers/cpuidle/cpuidle-tegra.c @@ -48,11 +48,6 @@ enum tegra_state { static atomic_t tegra_idle_barrier; static atomic_t tegra_abort_flag; -static inline bool tegra_cpuidle_using_firmware(void) -{ - return firmware_ops->prepare_idle && firmware_ops->do_idle; -} - static void tegra_cpuidle_report_cpus_state(void) { unsigned long cpu, lcpu, csr; @@ -139,10 +134,6 @@ static int tegra_cpuidle_c7_enter(void) if (err && err != -ENOSYS) return err; - err = call_firmware_op(do_idle, 0); - if (err != -ENOSYS) - return err; - return cpu_suspend(0, tegra30_pm_secondary_cpu_suspend); } @@ -356,9 +347,7 @@ static int tegra_cpuidle_probe(struct platform_device *pdev) * is disabled. */ if (!IS_ENABLED(CONFIG_PM_SLEEP)) { - if (!tegra_cpuidle_using_firmware()) - tegra_cpuidle_disable_state(TEGRA_C7); - + tegra_cpuidle_disable_state(TEGRA_C7); tegra_cpuidle_disable_state(TEGRA_CC6); } -- cgit v1.2.3-70-g09d2 From 498ba2a8a2756694b6f3888857426dbc8a5e6b6c Mon Sep 17 00:00:00 2001 From: He Ying Date: Tue, 6 Apr 2021 08:33:28 -0400 Subject: cpuidle: Fix ARM_QCOM_SPM_CPUIDLE configuration When CONFIG_ARM_QCOM_SPM_CPUIDLE is y and CONFIG_MMU is not set, compiling errors are encountered as follows: drivers/cpuidle/cpuidle-qcom-spm.o: In function `spm_dev_probe': cpuidle-qcom-spm.c:(.text+0x140): undefined reference to `cpu_resume_arm' cpuidle-qcom-spm.c:(.text+0x148): undefined reference to `cpu_resume_arm' Note that cpu_resume_arm is defined when MMU is set. So, add dependency on MMU in ARM_QCOM_SPM_CPUIDLE configuration. Fixes: a871be6b8eee ("cpuidle: Convert Qualcomm SPM driver to a generic CPUidle driver") Reported-by: Hulk Robot Signed-off-by: He Ying Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20210406123328.92904-1-heying24@huawei.com --- drivers/cpuidle/Kconfig.arm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/cpuidle/Kconfig.arm b/drivers/cpuidle/Kconfig.arm index 0844fadc4be8..334f83e56120 100644 --- a/drivers/cpuidle/Kconfig.arm +++ b/drivers/cpuidle/Kconfig.arm @@ -107,7 +107,7 @@ config ARM_TEGRA_CPUIDLE config ARM_QCOM_SPM_CPUIDLE bool "CPU Idle Driver for Qualcomm Subsystem Power Manager (SPM)" - depends on (ARCH_QCOM || COMPILE_TEST) && !ARM64 + depends on (ARCH_QCOM || COMPILE_TEST) && !ARM64 && MMU select ARM_CPU_SUSPEND select CPU_IDLE_MULTIPLE_DRIVERS select DT_IDLE_STATES -- cgit v1.2.3-70-g09d2 From 22592df194e31baf371906cc720da38fa0ab68f5 Mon Sep 17 00:00:00 2001 From: Marek Behún Date: Thu, 8 Apr 2021 13:42:15 +0200 Subject: cpufreq: armada-37xx: Fix setting TBG parent for load levels MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With CPU frequency determining software [1] we have discovered that after this driver does one CPU frequency change, the base frequency of the CPU is set to the frequency of TBG-A-P clock, instead of the TBG that is parent to the CPU. This can be reproduced on EspressoBIN and Turris MOX: cd /sys/devices/system/cpu/cpufreq/policy0 echo powersave >scaling_governor echo performance >scaling_governor Running the mhz tool before this driver is loaded reports 1000 MHz, and after loading the driver and executing commands above the tool reports 800 MHz. The change of TBG clock selector is supposed to happen in function armada37xx_cpufreq_dvfs_setup. Before the function returns, it does this: parent = clk_get_parent(clk); clk_set_parent(clk, parent); The armada-37xx-periph clock driver has the .set_parent method implemented correctly for this, so if the method was actually called, this would work. But since the introduction of the common clock framework in commit b2476490ef11 ("clk: introduce the common clock..."), the clk_set_parent function checks whether the parent is actually changing, and if the requested new parent is same as the old parent (which is obviously the case for the code above), the .set_parent method is not called at all. This patch fixes this issue by filling the correct TBG clock selector directly in the armada37xx_cpufreq_dvfs_setup during the filling of other registers at the same address. But the determination of CPU TBG index cannot be done via the common clock framework, therefore we need to access the North Bridge Peripheral Clock registers directly in this driver. [1] https://github.com/wtarreau/mhz Signed-off-by: Marek Behún Acked-by: Gregory CLEMENT Tested-by: Pali Rohár Tested-by: Tomasz Maciej Nowak Tested-by: Anders Trier Olesen Tested-by: Philip Soares Fixes: 92ce45fb875d ("cpufreq: Add DVFS support for Armada 37xx") Signed-off-by: Viresh Kumar --- drivers/cpufreq/armada-37xx-cpufreq.c | 35 +++++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/cpufreq/armada-37xx-cpufreq.c b/drivers/cpufreq/armada-37xx-cpufreq.c index b4af4094309b..b8dc6c849579 100644 --- a/drivers/cpufreq/armada-37xx-cpufreq.c +++ b/drivers/cpufreq/armada-37xx-cpufreq.c @@ -25,6 +25,10 @@ #include "cpufreq-dt.h" +/* Clk register set */ +#define ARMADA_37XX_CLK_TBG_SEL 0 +#define ARMADA_37XX_CLK_TBG_SEL_CPU_OFF 22 + /* Power management in North Bridge register set */ #define ARMADA_37XX_NB_L0L1 0x18 #define ARMADA_37XX_NB_L2L3 0x1C @@ -120,10 +124,15 @@ static struct armada_37xx_dvfs *armada_37xx_cpu_freq_info_get(u32 freq) * will be configured then the DVFS will be enabled. */ static void __init armada37xx_cpufreq_dvfs_setup(struct regmap *base, - struct clk *clk, u8 *divider) + struct regmap *clk_base, u8 *divider) { + u32 cpu_tbg_sel; int load_lvl; - struct clk *parent; + + /* Determine to which TBG clock is CPU connected */ + regmap_read(clk_base, ARMADA_37XX_CLK_TBG_SEL, &cpu_tbg_sel); + cpu_tbg_sel >>= ARMADA_37XX_CLK_TBG_SEL_CPU_OFF; + cpu_tbg_sel &= ARMADA_37XX_NB_TBG_SEL_MASK; for (load_lvl = 0; load_lvl < LOAD_LEVEL_NR; load_lvl++) { unsigned int reg, mask, val, offset = 0; @@ -142,6 +151,11 @@ static void __init armada37xx_cpufreq_dvfs_setup(struct regmap *base, mask = (ARMADA_37XX_NB_CLK_SEL_MASK << ARMADA_37XX_NB_CLK_SEL_OFF); + /* Set TBG index, for all levels we use the same TBG */ + val = cpu_tbg_sel << ARMADA_37XX_NB_TBG_SEL_OFF; + mask = (ARMADA_37XX_NB_TBG_SEL_MASK + << ARMADA_37XX_NB_TBG_SEL_OFF); + /* * Set cpu divider based on the pre-computed array in * order to have balanced step. @@ -160,14 +174,6 @@ static void __init armada37xx_cpufreq_dvfs_setup(struct regmap *base, regmap_update_bits(base, reg, mask, val); } - - /* - * Set cpu clock source, for all the level we keep the same - * clock source that the one already configured. For this one - * we need to use the clock framework - */ - parent = clk_get_parent(clk); - clk_set_parent(clk, parent); } /* @@ -358,11 +364,16 @@ static int __init armada37xx_cpufreq_driver_init(void) struct platform_device *pdev; unsigned long freq; unsigned int cur_frequency, base_frequency; - struct regmap *nb_pm_base, *avs_base; + struct regmap *nb_clk_base, *nb_pm_base, *avs_base; struct device *cpu_dev; int load_lvl, ret; struct clk *clk, *parent; + nb_clk_base = + syscon_regmap_lookup_by_compatible("marvell,armada-3700-periph-clock-nb"); + if (IS_ERR(nb_clk_base)) + return -ENODEV; + nb_pm_base = syscon_regmap_lookup_by_compatible("marvell,armada-3700-nb-pm"); @@ -439,7 +450,7 @@ static int __init armada37xx_cpufreq_driver_init(void) armada37xx_cpufreq_avs_configure(avs_base, dvfs); armada37xx_cpufreq_avs_setup(avs_base, dvfs); - armada37xx_cpufreq_dvfs_setup(nb_pm_base, clk, dvfs->divider); + armada37xx_cpufreq_dvfs_setup(nb_pm_base, nb_clk_base, dvfs->divider); clk_put(clk); for (load_lvl = ARMADA_37XX_DVFS_LOAD_0; load_lvl < LOAD_LEVEL_NR; -- cgit v1.2.3-70-g09d2 From 4e435a9dd26c46ac018997cc0562d50b1a96f372 Mon Sep 17 00:00:00 2001 From: Marek Behún Date: Thu, 8 Apr 2021 13:42:16 +0200 Subject: clk: mvebu: armada-37xx-periph: remove .set_parent method for CPU PM clock MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove the .set_parent method in clk_pm_cpu_ops. This method was supposed to be needed by the armada-37xx-cpufreq driver, but was never actually called due to wrong assumptions in the cpufreq driver. After this was fixed in the cpufreq driver, this method is not needed anymore. Signed-off-by: Marek Behún Acked-by: Stephen Boyd Acked-by: Gregory CLEMENT Tested-by: Pali Rohár Tested-by: Tomasz Maciej Nowak Tested-by: Anders Trier Olesen Tested-by: Philip Soares Fixes: 2089dc33ea0e ("clk: mvebu: armada-37xx-periph: add DVFS support for cpu clocks") Signed-off-by: Viresh Kumar --- drivers/clk/mvebu/armada-37xx-periph.c | 28 ---------------------------- 1 file changed, 28 deletions(-) (limited to 'drivers') diff --git a/drivers/clk/mvebu/armada-37xx-periph.c b/drivers/clk/mvebu/armada-37xx-periph.c index f5746f9ea929..6507bd2c5f31 100644 --- a/drivers/clk/mvebu/armada-37xx-periph.c +++ b/drivers/clk/mvebu/armada-37xx-periph.c @@ -440,33 +440,6 @@ static u8 clk_pm_cpu_get_parent(struct clk_hw *hw) return val; } -static int clk_pm_cpu_set_parent(struct clk_hw *hw, u8 index) -{ - struct clk_pm_cpu *pm_cpu = to_clk_pm_cpu(hw); - struct regmap *base = pm_cpu->nb_pm_base; - int load_level; - - /* - * We set the clock parent only if the DVFS is available but - * not enabled. - */ - if (IS_ERR(base) || armada_3700_pm_dvfs_is_enabled(base)) - return -EINVAL; - - /* Set the parent clock for all the load level */ - for (load_level = 0; load_level < LOAD_LEVEL_NR; load_level++) { - unsigned int reg, mask, val, - offset = ARMADA_37XX_NB_TBG_SEL_OFF; - - armada_3700_pm_dvfs_update_regs(load_level, ®, &offset); - - val = index << offset; - mask = ARMADA_37XX_NB_TBG_SEL_MASK << offset; - regmap_update_bits(base, reg, mask, val); - } - return 0; -} - static unsigned long clk_pm_cpu_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { @@ -592,7 +565,6 @@ static int clk_pm_cpu_set_rate(struct clk_hw *hw, unsigned long rate, static const struct clk_ops clk_pm_cpu_ops = { .get_parent = clk_pm_cpu_get_parent, - .set_parent = clk_pm_cpu_set_parent, .round_rate = clk_pm_cpu_round_rate, .set_rate = clk_pm_cpu_set_rate, .recalc_rate = clk_pm_cpu_recalc_rate, -- cgit v1.2.3-70-g09d2 From d118ac2062b5b8331c8768ac81e016617e0996ee Mon Sep 17 00:00:00 2001 From: Pali Rohár Date: Thu, 8 Apr 2021 13:42:17 +0200 Subject: cpufreq: armada-37xx: Fix the AVS value for load L1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The original CPU voltage value for load L1 is too low for Armada 37xx SoC when base CPU frequency is 1000 or 1200 MHz. It leads to instabilities where CPU gets stuck soon after dynamic voltage scaling from load L1 to L0. Update the CPU voltage value for load L1 accordingly when base frequency is 1000 or 1200 MHz. The minimal L1 value for base CPU frequency 1000 MHz is updated from the original 1.05V to 1.108V and for 1200 MHz is updated to 1.155V. This minimal L1 value is used only in the case when it is lower than value for L0. This change fixes CPU instability issues on 1 GHz and 1.2 GHz variants of Espressobin and 1 GHz Turris Mox. Marvell previously for 1 GHz variant of Espressobin provided a patch [1] suitable only for their Marvell Linux kernel 4.4 fork which workarounded this issue. Patch forced CPU voltage value to 1.108V in all loads. But such change does not fix CPU instability issues on 1.2 GHz variants of Armada 3720 SoC. During testing we come to the conclusion that using 1.108V as minimal value for L1 load makes 1 GHz variants of Espressobin and Turris Mox boards stable. And similarly 1.155V for 1.2 GHz variant of Espressobin. These two values 1.108V and 1.155V are documented in Armada 3700 Hardware Specifications as typical initial CPU voltage values. Discussion about this issue is also at the Armbian forum [2]. [1] - https://github.com/MarvellEmbeddedProcessors/linux-marvell/commit/dc33b62c90696afb6adc7dbcc4ebbd48bedec269 [2] - https://forum.armbian.com/topic/10429-how-to-make-espressobin-v7-stable/ Signed-off-by: Pali Rohár Acked-by: Gregory CLEMENT Tested-by: Tomasz Maciej Nowak Tested-by: Anders Trier Olesen Tested-by: Philip Soares Fixes: 1c3528232f4b ("cpufreq: armada-37xx: Add AVS support") Signed-off-by: Viresh Kumar --- drivers/cpufreq/armada-37xx-cpufreq.c | 37 +++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) (limited to 'drivers') diff --git a/drivers/cpufreq/armada-37xx-cpufreq.c b/drivers/cpufreq/armada-37xx-cpufreq.c index b8dc6c849579..c7683d447b11 100644 --- a/drivers/cpufreq/armada-37xx-cpufreq.c +++ b/drivers/cpufreq/armada-37xx-cpufreq.c @@ -73,6 +73,8 @@ #define LOAD_LEVEL_NR 4 #define MIN_VOLT_MV 1000 +#define MIN_VOLT_MV_FOR_L1_1000MHZ 1108 +#define MIN_VOLT_MV_FOR_L1_1200MHZ 1155 /* AVS value for the corresponding voltage (in mV) */ static int avs_map[] = { @@ -208,6 +210,8 @@ static u32 armada_37xx_avs_val_match(int target_vm) * - L2 & L3 voltage should be about 150mv smaller than L0 voltage. * This function calculates L1 & L2 & L3 AVS values dynamically based * on L0 voltage and fill all AVS values to the AVS value table. + * When base CPU frequency is 1000 or 1200 MHz then there is additional + * minimal avs value for load L1. */ static void __init armada37xx_cpufreq_avs_configure(struct regmap *base, struct armada_37xx_dvfs *dvfs) @@ -239,6 +243,19 @@ static void __init armada37xx_cpufreq_avs_configure(struct regmap *base, for (load_level = 1; load_level < LOAD_LEVEL_NR; load_level++) dvfs->avs[load_level] = avs_min; + /* + * Set the avs values for load L0 and L1 when base CPU frequency + * is 1000/1200 MHz to its typical initial values according to + * the Armada 3700 Hardware Specifications. + */ + if (dvfs->cpu_freq_max >= 1000*1000*1000) { + if (dvfs->cpu_freq_max >= 1200*1000*1000) + avs_min = armada_37xx_avs_val_match(MIN_VOLT_MV_FOR_L1_1200MHZ); + else + avs_min = armada_37xx_avs_val_match(MIN_VOLT_MV_FOR_L1_1000MHZ); + dvfs->avs[0] = dvfs->avs[1] = avs_min; + } + return; } @@ -258,6 +275,26 @@ static void __init armada37xx_cpufreq_avs_configure(struct regmap *base, target_vm = avs_map[l0_vdd_min] - 150; target_vm = target_vm > MIN_VOLT_MV ? target_vm : MIN_VOLT_MV; dvfs->avs[2] = dvfs->avs[3] = armada_37xx_avs_val_match(target_vm); + + /* + * Fix the avs value for load L1 when base CPU frequency is 1000/1200 MHz, + * otherwise the CPU gets stuck when switching from load L1 to load L0. + * Also ensure that avs value for load L1 is not higher than for L0. + */ + if (dvfs->cpu_freq_max >= 1000*1000*1000) { + u32 avs_min_l1; + + if (dvfs->cpu_freq_max >= 1200*1000*1000) + avs_min_l1 = armada_37xx_avs_val_match(MIN_VOLT_MV_FOR_L1_1200MHZ); + else + avs_min_l1 = armada_37xx_avs_val_match(MIN_VOLT_MV_FOR_L1_1000MHZ); + + if (avs_min_l1 > dvfs->avs[0]) + avs_min_l1 = dvfs->avs[0]; + + if (dvfs->avs[1] < avs_min_l1) + dvfs->avs[1] = avs_min_l1; + } } static void __init armada37xx_cpufreq_avs_setup(struct regmap *base, -- cgit v1.2.3-70-g09d2 From 4decb9187589f61fe9fc2bc4d9b01160b0a610c5 Mon Sep 17 00:00:00 2001 From: Pali Rohár Date: Thu, 8 Apr 2021 13:42:18 +0200 Subject: clk: mvebu: armada-37xx-periph: Fix switching CPU freq from 250 Mhz to 1 GHz MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It was observed that the workaround introduced by commit 61c40f35f5cd ("clk: mvebu: armada-37xx-periph: Fix switching CPU rate from 300Mhz to 1.2GHz") when base CPU frequency is 1.2 GHz is also required when base CPU frequency is 1 GHz. Otherwise switching CPU frequency directly from L2 (250 MHz) to L0 (1 GHz) causes a crash. When base CPU frequency is just 800 MHz no crashed were observed during switch from L2 to L0. Signed-off-by: Pali Rohár Acked-by: Stephen Boyd Acked-by: Gregory CLEMENT Tested-by: Tomasz Maciej Nowak Tested-by: Anders Trier Olesen Tested-by: Philip Soares Fixes: 2089dc33ea0e ("clk: mvebu: armada-37xx-periph: add DVFS support for cpu clocks") Signed-off-by: Viresh Kumar --- drivers/clk/mvebu/armada-37xx-periph.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/clk/mvebu/armada-37xx-periph.c b/drivers/clk/mvebu/armada-37xx-periph.c index 6507bd2c5f31..b15e177bea7e 100644 --- a/drivers/clk/mvebu/armada-37xx-periph.c +++ b/drivers/clk/mvebu/armada-37xx-periph.c @@ -487,8 +487,10 @@ static long clk_pm_cpu_round_rate(struct clk_hw *hw, unsigned long rate, } /* - * Switching the CPU from the L2 or L3 frequencies (300 and 200 Mhz - * respectively) to L0 frequency (1.2 Ghz) requires a significant + * Workaround when base CPU frequnecy is 1000 or 1200 MHz + * + * Switching the CPU from the L2 or L3 frequencies (250/300 or 200 MHz + * respectively) to L0 frequency (1/1.2 GHz) requires a significant * amount of time to let VDD stabilize to the appropriate * voltage. This amount of time is large enough that it cannot be * covered by the hardware countdown register. Due to this, the CPU @@ -498,15 +500,15 @@ static long clk_pm_cpu_round_rate(struct clk_hw *hw, unsigned long rate, * To work around this problem, we prevent switching directly from the * L2/L3 frequencies to the L0 frequency, and instead switch to the L1 * frequency in-between. The sequence therefore becomes: - * 1. First switch from L2/L3(200/300MHz) to L1(600MHZ) + * 1. First switch from L2/L3 (200/250/300 MHz) to L1 (500/600 MHz) * 2. Sleep 20ms for stabling VDD voltage - * 3. Then switch from L1(600MHZ) to L0(1200Mhz). + * 3. Then switch from L1 (500/600 MHz) to L0 (1000/1200 MHz). */ static void clk_pm_cpu_set_rate_wa(unsigned long rate, struct regmap *base) { unsigned int cur_level; - if (rate != 1200 * 1000 * 1000) + if (rate < 1000 * 1000 * 1000) return; regmap_read(base, ARMADA_37XX_NB_CPU_LOAD, &cur_level); -- cgit v1.2.3-70-g09d2 From e93033aff684641f71a436ca7a9d2a742126baaf Mon Sep 17 00:00:00 2001 From: Pali Rohár Date: Thu, 8 Apr 2021 13:42:19 +0200 Subject: clk: mvebu: armada-37xx-periph: Fix workaround for switching from L1 to L0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When CPU frequency is at 250 MHz and set_rate() is called with 500 MHz (L1) quickly followed by a call with 1 GHz (L0), the CPU does not necessarily stay in L1 for at least 20ms as is required by Marvell errata. This situation happens frequently with the ondemand cpufreq governor and can be also reproduced with userspace governor. In most cases it causes CPU to crash. This change fixes the above issue and ensures that the CPU always stays in L1 for at least 20ms when switching from any state to L0. Signed-off-by: Marek Behún Signed-off-by: Pali Rohár Acked-by: Stephen Boyd Acked-by: Gregory CLEMENT Tested-by: Tomasz Maciej Nowak Tested-by: Anders Trier Olesen Tested-by: Philip Soares Fixes: 61c40f35f5cd ("clk: mvebu: armada-37xx-periph: Fix switching CPU rate from 300Mhz to 1.2GHz") Signed-off-by: Viresh Kumar --- drivers/clk/mvebu/armada-37xx-periph.c | 45 +++++++++++++++++++++++++++++----- 1 file changed, 39 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/clk/mvebu/armada-37xx-periph.c b/drivers/clk/mvebu/armada-37xx-periph.c index b15e177bea7e..32ac6b6b7530 100644 --- a/drivers/clk/mvebu/armada-37xx-periph.c +++ b/drivers/clk/mvebu/armada-37xx-periph.c @@ -84,6 +84,7 @@ struct clk_pm_cpu { void __iomem *reg_div; u8 shift_div; struct regmap *nb_pm_base; + unsigned long l1_expiration; }; #define to_clk_double_div(_hw) container_of(_hw, struct clk_double_div, hw) @@ -504,22 +505,52 @@ static long clk_pm_cpu_round_rate(struct clk_hw *hw, unsigned long rate, * 2. Sleep 20ms for stabling VDD voltage * 3. Then switch from L1 (500/600 MHz) to L0 (1000/1200 MHz). */ -static void clk_pm_cpu_set_rate_wa(unsigned long rate, struct regmap *base) +static void clk_pm_cpu_set_rate_wa(struct clk_pm_cpu *pm_cpu, + unsigned int new_level, unsigned long rate, + struct regmap *base) { unsigned int cur_level; - if (rate < 1000 * 1000 * 1000) - return; - regmap_read(base, ARMADA_37XX_NB_CPU_LOAD, &cur_level); cur_level &= ARMADA_37XX_NB_CPU_LOAD_MASK; - if (cur_level <= ARMADA_37XX_DVFS_LOAD_1) + + if (cur_level == new_level) + return; + + /* + * System wants to go to L1 on its own. If we are going from L2/L3, + * remember when 20ms will expire. If from L0, set the value so that + * next switch to L0 won't have to wait. + */ + if (new_level == ARMADA_37XX_DVFS_LOAD_1) { + if (cur_level == ARMADA_37XX_DVFS_LOAD_0) + pm_cpu->l1_expiration = jiffies; + else + pm_cpu->l1_expiration = jiffies + msecs_to_jiffies(20); return; + } + + /* + * If we are setting to L2/L3, just invalidate L1 expiration time, + * sleeping is not needed. + */ + if (rate < 1000*1000*1000) + goto invalidate_l1_exp; + + /* + * We are going to L0 with rate >= 1GHz. Check whether we have been at + * L1 for long enough time. If not, go to L1 for 20ms. + */ + if (pm_cpu->l1_expiration && jiffies >= pm_cpu->l1_expiration) + goto invalidate_l1_exp; regmap_update_bits(base, ARMADA_37XX_NB_CPU_LOAD, ARMADA_37XX_NB_CPU_LOAD_MASK, ARMADA_37XX_DVFS_LOAD_1); msleep(20); + +invalidate_l1_exp: + pm_cpu->l1_expiration = 0; } static int clk_pm_cpu_set_rate(struct clk_hw *hw, unsigned long rate, @@ -553,7 +584,9 @@ static int clk_pm_cpu_set_rate(struct clk_hw *hw, unsigned long rate, reg = ARMADA_37XX_NB_CPU_LOAD; mask = ARMADA_37XX_NB_CPU_LOAD_MASK; - clk_pm_cpu_set_rate_wa(rate, base); + /* Apply workaround when base CPU frequency is 1000 or 1200 MHz */ + if (parent_rate >= 1000*1000*1000) + clk_pm_cpu_set_rate_wa(pm_cpu, load_level, rate, base); regmap_update_bits(base, reg, mask, load_level); -- cgit v1.2.3-70-g09d2 From 92963903a8e11b9576eb7249f8e81eefa93b6f96 Mon Sep 17 00:00:00 2001 From: Pali Rohár Date: Thu, 8 Apr 2021 13:42:20 +0200 Subject: cpufreq: armada-37xx: Fix driver cleanup when registration failed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 8db82563451f ("cpufreq: armada-37xx: fix frequency calculation for opp") changed calculation of frequency passed to the dev_pm_opp_add() function call. But the code for dev_pm_opp_remove() function call was not updated, so the driver cleanup phase does not work when registration fails. This fixes the issue by using the same frequency in both calls. Signed-off-by: Pali Rohár Acked-by: Gregory CLEMENT Tested-by: Tomasz Maciej Nowak Tested-by: Anders Trier Olesen Tested-by: Philip Soares Fixes: 8db82563451f ("cpufreq: armada-37xx: fix frequency calculation for opp") Signed-off-by: Viresh Kumar --- drivers/cpufreq/armada-37xx-cpufreq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/cpufreq/armada-37xx-cpufreq.c b/drivers/cpufreq/armada-37xx-cpufreq.c index c7683d447b11..1ab2113daef5 100644 --- a/drivers/cpufreq/armada-37xx-cpufreq.c +++ b/drivers/cpufreq/armada-37xx-cpufreq.c @@ -521,7 +521,7 @@ disable_dvfs: remove_opp: /* clean-up the already added opp before leaving */ while (load_lvl-- > ARMADA_37XX_DVFS_LOAD_0) { - freq = cur_frequency / dvfs->divider[load_lvl]; + freq = base_frequency / dvfs->divider[load_lvl]; dev_pm_opp_remove(cpu_dev, freq); } -- cgit v1.2.3-70-g09d2 From 8bad3bf23cbc40abe1d24cec08a114df6facf858 Mon Sep 17 00:00:00 2001 From: Pali Rohár Date: Thu, 8 Apr 2021 13:42:21 +0200 Subject: cpufreq: armada-37xx: Fix determining base CPU frequency MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When current CPU load is not L0 then loading armada-37xx-cpufreq.ko driver fails with following error: # modprobe armada-37xx-cpufreq [ 502.702097] Unsupported CPU frequency 250 MHz This issue was partially fixed by commit 8db82563451f ("cpufreq: armada-37xx: fix frequency calculation for opp"), but only for calculating CPU frequency for opp. Fix this also for determination of base CPU frequency. Signed-off-by: Pali Rohár Acked-by: Gregory CLEMENT Tested-by: Tomasz Maciej Nowak Tested-by: Anders Trier Olesen Tested-by: Philip Soares Fixes: 92ce45fb875d ("cpufreq: Add DVFS support for Armada 37xx") Signed-off-by: Viresh Kumar --- drivers/cpufreq/armada-37xx-cpufreq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/cpufreq/armada-37xx-cpufreq.c b/drivers/cpufreq/armada-37xx-cpufreq.c index 1ab2113daef5..e4782f562e7a 100644 --- a/drivers/cpufreq/armada-37xx-cpufreq.c +++ b/drivers/cpufreq/armada-37xx-cpufreq.c @@ -469,7 +469,7 @@ static int __init armada37xx_cpufreq_driver_init(void) return -EINVAL; } - dvfs = armada_37xx_cpu_freq_info_get(cur_frequency); + dvfs = armada_37xx_cpu_freq_info_get(base_frequency); if (!dvfs) { clk_put(clk); return -EINVAL; -- cgit v1.2.3-70-g09d2 From 5f23eb9dc0cd2a58bbfe887f335ad632368e530a Mon Sep 17 00:00:00 2001 From: Pali Rohár Date: Thu, 8 Apr 2021 13:42:22 +0200 Subject: cpufreq: armada-37xx: Remove cur_frequency variable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Variable cur_frequency in armada37xx_cpufreq_driver_init() is unused. Signed-off-by: Pali Rohár Acked-by: Gregory CLEMENT Tested-by: Tomasz Maciej Nowak Tested-by: Anders Trier Olesen Tested-by: Philip Soares Signed-off-by: Viresh Kumar --- drivers/cpufreq/armada-37xx-cpufreq.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/cpufreq/armada-37xx-cpufreq.c b/drivers/cpufreq/armada-37xx-cpufreq.c index e4782f562e7a..050abff18308 100644 --- a/drivers/cpufreq/armada-37xx-cpufreq.c +++ b/drivers/cpufreq/armada-37xx-cpufreq.c @@ -400,7 +400,7 @@ static int __init armada37xx_cpufreq_driver_init(void) struct armada_37xx_dvfs *dvfs; struct platform_device *pdev; unsigned long freq; - unsigned int cur_frequency, base_frequency; + unsigned int base_frequency; struct regmap *nb_clk_base, *nb_pm_base, *avs_base; struct device *cpu_dev; int load_lvl, ret; @@ -461,14 +461,6 @@ static int __init armada37xx_cpufreq_driver_init(void) return -EINVAL; } - /* Get nominal (current) CPU frequency */ - cur_frequency = clk_get_rate(clk); - if (!cur_frequency) { - dev_err(cpu_dev, "Failed to get clock rate for CPU\n"); - clk_put(clk); - return -EINVAL; - } - dvfs = armada_37xx_cpu_freq_info_get(base_frequency); if (!dvfs) { clk_put(clk); -- cgit v1.2.3-70-g09d2 From dbbd49bade0548db87ceb1943951dea456af2e22 Mon Sep 17 00:00:00 2001 From: Pali Rohár Date: Thu, 8 Apr 2021 13:42:23 +0200 Subject: cpufreq: armada-37xx: Fix module unloading MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This driver is missing module_exit hook. Add proper driver exit function which unregisters the platform device and cleans up the data. Signed-off-by: Pali Rohár Tested-by: Tomasz Maciej Nowak Tested-by: Anders Trier Olesen Tested-by: Philip Soares Signed-off-by: Viresh Kumar --- drivers/cpufreq/armada-37xx-cpufreq.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'drivers') diff --git a/drivers/cpufreq/armada-37xx-cpufreq.c b/drivers/cpufreq/armada-37xx-cpufreq.c index 050abff18308..3fc98a3ffd91 100644 --- a/drivers/cpufreq/armada-37xx-cpufreq.c +++ b/drivers/cpufreq/armada-37xx-cpufreq.c @@ -86,6 +86,8 @@ static int avs_map[] = { }; struct armada37xx_cpufreq_state { + struct platform_device *pdev; + struct device *cpu_dev; struct regmap *regmap; u32 nb_l0l1; u32 nb_l2l3; @@ -506,6 +508,9 @@ static int __init armada37xx_cpufreq_driver_init(void) if (ret) goto disable_dvfs; + armada37xx_cpufreq_state->cpu_dev = cpu_dev; + armada37xx_cpufreq_state->pdev = pdev; + platform_set_drvdata(pdev, dvfs); return 0; disable_dvfs: @@ -524,6 +529,26 @@ remove_opp: /* late_initcall, to guarantee the driver is loaded after A37xx clock driver */ late_initcall(armada37xx_cpufreq_driver_init); +static void __exit armada37xx_cpufreq_driver_exit(void) +{ + struct platform_device *pdev = armada37xx_cpufreq_state->pdev; + struct armada_37xx_dvfs *dvfs = platform_get_drvdata(pdev); + unsigned long freq; + int load_lvl; + + platform_device_unregister(pdev); + + armada37xx_cpufreq_disable_dvfs(armada37xx_cpufreq_state->regmap); + + for (load_lvl = ARMADA_37XX_DVFS_LOAD_0; load_lvl < LOAD_LEVEL_NR; load_lvl++) { + freq = dvfs->cpu_freq_max / dvfs->divider[load_lvl]; + dev_pm_opp_remove(armada37xx_cpufreq_state->cpu_dev, freq); + } + + kfree(armada37xx_cpufreq_state); +} +module_exit(armada37xx_cpufreq_driver_exit); + static const struct of_device_id __maybe_unused armada37xx_cpufreq_of_match[] = { { .compatible = "marvell,armada-3700-nb-pm" }, { }, -- cgit v1.2.3-70-g09d2 From b989bc0f3cf24122ec700e66eb8ffb93432f18c5 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 7 Apr 2021 16:21:55 +0200 Subject: cpufreq: intel_pstate: Simplify intel_pstate_update_perf_limits() Because pstate.max_freq is always equal to the product of pstate.max_pstate and pstate.scaling and, analogously, pstate.turbo_freq is always equal to the product of pstate.turbo_pstate and pstate.scaling, the result of the max_policy_perf computation in intel_pstate_update_perf_limits() is always equal to the quotient of policy_max and pstate.scaling, regardless of whether or not turbo is disabled. Analogously, the result of min_policy_perf in intel_pstate_update_perf_limits() is always equal to the quotient of policy_min and pstate.scaling. Accordingly, intel_pstate_update_perf_limits() need not check whether or not turbo is enabled at all and in order to compute max_policy_perf and min_policy_perf it can always divide policy_max and policy_min, respectively, by pstate.scaling. Make it do so. While at it, move the definition and initialization of the turbo_max local variable to the code branch using it. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Tested-by: Chen Yu --- drivers/cpufreq/intel_pstate.c | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c index 2ef9584f4802..f0401064d7aa 100644 --- a/drivers/cpufreq/intel_pstate.c +++ b/drivers/cpufreq/intel_pstate.c @@ -2195,9 +2195,8 @@ static void intel_pstate_update_perf_limits(struct cpudata *cpu, unsigned int policy_min, unsigned int policy_max) { + int scaling = cpu->pstate.scaling; int32_t max_policy_perf, min_policy_perf; - int max_state, turbo_max; - int max_freq; /* * HWP needs some special consideration, because HWP_REQUEST uses @@ -2206,33 +2205,24 @@ static void intel_pstate_update_perf_limits(struct cpudata *cpu, if (hwp_active) intel_pstate_get_hwp_cap(cpu); - if (global.no_turbo || global.turbo_disabled) { - max_state = cpu->pstate.max_pstate; - max_freq = cpu->pstate.max_freq; - } else { - max_state = cpu->pstate.turbo_pstate; - max_freq = cpu->pstate.turbo_freq; - } - - turbo_max = cpu->pstate.turbo_pstate; - - max_policy_perf = max_state * policy_max / max_freq; + max_policy_perf = policy_max / scaling; if (policy_max == policy_min) { min_policy_perf = max_policy_perf; } else { - min_policy_perf = max_state * policy_min / max_freq; + min_policy_perf = policy_min / scaling; min_policy_perf = clamp_t(int32_t, min_policy_perf, 0, max_policy_perf); } - pr_debug("cpu:%d max_state %d min_policy_perf:%d max_policy_perf:%d\n", - cpu->cpu, max_state, min_policy_perf, max_policy_perf); + pr_debug("cpu:%d min_policy_perf:%d max_policy_perf:%d\n", + cpu->cpu, min_policy_perf, max_policy_perf); /* Normalize user input to [min_perf, max_perf] */ if (per_cpu_limits) { cpu->min_perf_ratio = min_policy_perf; cpu->max_perf_ratio = max_policy_perf; } else { + int turbo_max = cpu->pstate.turbo_pstate; int32_t global_min, global_max; /* Global limits are in percent of the maximum turbo P-state. */ -- cgit v1.2.3-70-g09d2 From 4da6d76fb43994e558ceb82619a776e300a35e08 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 14 Apr 2021 15:53:10 +0300 Subject: PM: wakeup: use dev_set_name() directly We have open coded dev_set_name() implementation, replace that with a direct call. Signed-off-by: Andy Shevchenko Signed-off-by: Rafael J. Wysocki --- drivers/base/power/wakeup_stats.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/base/power/wakeup_stats.c b/drivers/base/power/wakeup_stats.c index d638259b829a..5ade7539ac02 100644 --- a/drivers/base/power/wakeup_stats.c +++ b/drivers/base/power/wakeup_stats.c @@ -154,7 +154,7 @@ static struct device *wakeup_source_device_create(struct device *parent, dev_set_drvdata(dev, ws); device_set_pm_not_required(dev); - retval = kobject_set_name(&dev->kobj, "wakeup%d", ws->id); + retval = dev_set_name(dev, "wakeup%d", ws->id); if (retval) goto error; -- cgit v1.2.3-70-g09d2 From 733dda9cc849895349b2a64f398aeb56e437f99f Mon Sep 17 00:00:00 2001 From: Alexander Monakov Date: Tue, 20 Apr 2021 00:57:07 +0300 Subject: cpufreq: Kconfig: fix documentation links User documentation for cpufreq governors and drivers has been moved to admin-guide; adjust references from Kconfig entries accordingly. Remove references from undocumented cpufreq drivers, as well as the 'userspace' cpufreq governor, for which no additional details are provided in the admin-guide text. Fixes: 2a0e49279850 ("cpufreq: User/admin documentation update and consolidation") Signed-off-by: Alexander Monakov Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/Kconfig | 23 ++++++----------------- 1 file changed, 6 insertions(+), 17 deletions(-) (limited to 'drivers') diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig index 85de313ddec2..c3038cdc6865 100644 --- a/drivers/cpufreq/Kconfig +++ b/drivers/cpufreq/Kconfig @@ -13,7 +13,8 @@ config CPU_FREQ clock speed, you need to either enable a dynamic cpufreq governor (see below) after boot, or use a userspace tool. - For details, take a look at . + For details, take a look at + . If in doubt, say N. @@ -140,8 +141,6 @@ config CPU_FREQ_GOV_USERSPACE To compile this driver as a module, choose M here: the module will be called cpufreq_userspace. - For details, take a look at . - If in doubt, say Y. config CPU_FREQ_GOV_ONDEMAND @@ -158,7 +157,8 @@ config CPU_FREQ_GOV_ONDEMAND To compile this driver as a module, choose M here: the module will be called cpufreq_ondemand. - For details, take a look at linux/Documentation/cpu-freq. + For details, take a look at + . If in doubt, say N. @@ -182,7 +182,8 @@ config CPU_FREQ_GOV_CONSERVATIVE To compile this driver as a module, choose M here: the module will be called cpufreq_conservative. - For details, take a look at linux/Documentation/cpu-freq. + For details, take a look at + . If in doubt, say N. @@ -246,8 +247,6 @@ config IA64_ACPI_CPUFREQ This driver adds a CPUFreq driver which utilizes the ACPI Processor Performance States. - For details, take a look at . - If in doubt, say N. endif @@ -271,8 +270,6 @@ config LOONGSON2_CPUFREQ Loongson2F and it's successors support this feature. - For details, take a look at . - If in doubt, say N. config LOONGSON1_CPUFREQ @@ -282,8 +279,6 @@ config LOONGSON1_CPUFREQ This option adds a CPUFreq driver for loongson1 processors which support software configurable cpu frequency. - For details, take a look at . - If in doubt, say N. endif @@ -293,8 +288,6 @@ config SPARC_US3_CPUFREQ help This adds the CPUFreq driver for UltraSPARC-III processors. - For details, take a look at . - If in doubt, say N. config SPARC_US2E_CPUFREQ @@ -302,8 +295,6 @@ config SPARC_US2E_CPUFREQ help This adds the CPUFreq driver for UltraSPARC-IIe processors. - For details, take a look at . - If in doubt, say N. endif @@ -318,8 +309,6 @@ config SH_CPU_FREQ will also generate a notice in the boot log before disabling itself if the CPU in question is not capable of rate rounding. - For details, take a look at . - If unsure, say N. endif -- cgit v1.2.3-70-g09d2 From e48802333acecfa3ada7b13eb55fa03b08df9e74 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 16 Apr 2021 13:43:52 +0100 Subject: PM: wakeup: remove redundant assignment to variable retval The variable retval is being initialized with a value that is never read and it is being updated later with a new value. The initialization is redundant and can be removed. Addresses-Coverity: ("Unused value") Signed-off-by: Colin Ian King Signed-off-by: Rafael J. Wysocki --- drivers/base/power/wakeup_stats.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/base/power/wakeup_stats.c b/drivers/base/power/wakeup_stats.c index 5ade7539ac02..924fac493c4f 100644 --- a/drivers/base/power/wakeup_stats.c +++ b/drivers/base/power/wakeup_stats.c @@ -137,7 +137,7 @@ static struct device *wakeup_source_device_create(struct device *parent, struct wakeup_source *ws) { struct device *dev = NULL; - int retval = -ENODEV; + int retval; dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (!dev) { -- cgit v1.2.3-70-g09d2