PM / OPP: Introduce APIs to transition OPPs
Subsystems (6)
Viresh Kumar
maintainerv3 cover PM / OPP: Introduce APIs to transition OPPs
Hi Guys,
This patchset add APIs in OPP layer to allow OPPs transitioning from within OPP layer. Currently all OPP users need to replicate the same code to switch between OPPs. While the same can be handled easily by OPP-core.
The first 7 patches update the OPP core to introduce the new APIs and the next Nine patches update cpufreq-dt for the same.
11 out of 17 are already Reviewed by Stephen, only few are left :)
I hope this is the last version of the series :)
Testing:
- Tested on exynos 5250-arndale (dual-cortex-A15)
- Tested for both Old-V1 bindings and New V2 bindings
- Tested with regulator names as: 'cpu-supply' and 'cpu0-supply'
- Tested with Unsupported supply ranges as well, to check the opp-disable logic
V2->V3:
- Very minor updates.
- find_supply_name() doesn't return an error value now, and so its callers don't check for it.
- And so we don't need to initialize name to NULL
Viresh Kumar (16):
PM / OPP: get/put regulators from OPP core
PM / OPP: Disable OPPs that aren't supported by the regulator PM / OPP: Introduce dev_pm_opp_get_max_volt_latency()
PM / OPP: Introduce dev_pm_opp_get_max_transition_latency() PM / OPP: Parse clock-latency and voltage-tolerance for v1 bindings PM / OPP: Manage device clk
PM / OPP: Add dev_pm_opp_set_rate()
cpufreq: dt: Convert few pr_debug/err() calls to dev_dbg/err()
cpufreq: dt: Rename 'need_update' to 'opp_v1'
cpufreq: dt: OPP layers handles clock-latency for V1 bindings as well
cpufreq: dt: Pass regulator name to the OPP core
cpufreq: dt: Unsupported OPPs are already disabled
cpufreq: dt: Reuse dev_pm_opp_get_max_transition_latency()
cpufreq: dt: Use dev_pm_opp_set_rate() to switch frequency
cpufreq: dt: No need to fetch voltage-tolerance
cpufreq: dt: No need to allocate resources anymore
drivers/base/power/opp/core.c | 420 ++++++++++++++++++++++++++++++++++++++++++ drivers/base/power/opp/opp.h | 13 ++ drivers/cpufreq/cpufreq-dt.c | 300 +++++++++++------------------- include/linux/pm_opp.h | 27 +++ 4 files changed, 565 insertions(+), 195 deletions(-)
Signature · 1 line
2.7.1.370.gb2aa7f8
Viresh Kumar
maintainerPATCH v3 1/16 PM / OPP: get/put regulators from OPP core
This allows the OPP core to request/free the regulator resource, attached to a device OPP. The regulator device is fetched using the name provided by the driver, while calling: dev_pm_opp_set_regulator().
This will work for both OPP-v1 and v2 bindings.
This is a preliminary step for moving the OPP switching logic into the OPP core.
--- drivers/base/power/opp/core.c | 111 ++++++++++++++++++++++++++++++++++++++++++ drivers/base/power/opp/opp.h | 4 ++ include/linux/pm_opp.h | 9 ++++ 3 files changed, 124 insertions(+)
drivers/base/power/opp/core.c +111 -0
@@ -19,6 +19,7 @@#include <linux/device.h> #include <linux/of.h> #include <linux/export.h> +#include <linux/regulator/consumer.h> #include "opp.h"
@@ -565,6 +566,9 @@ static void _remove_device_opp(struct device_opp *dev_opp)if (dev_opp->prop_name)return ; +if (!IS_ERR_OR_NULL (dev_opp->regulator)) +return ; + list_dev = list_first_entry(&dev_opp->dev_list, struct device_list_opp, node);
@@ -1085,6 +1089,113 @@ void dev_pm_opp_put_prop_name(struct device *dev) }EXPORT_SYMBOL_GPL (dev_pm_opp_put_prop_name); +/** +* dev_pm_opp_set_regulator() - Set regulator name for the device +* @dev: Device for which regulator name is being set. +* @name: Name of the regulator. +* +* In order to support OPP switching, OPP layer needs to know the name of the +* device's regulator, as the core would be required to switch voltages as well. +* +* This must be called before any OPPs are initialized for the device. +* +* Locking: The internal device_opp and opp structures are RCU protected. +* Hence this function internally uses RCU updater strategy with mutex locks +* to keep the integrity of the internal data structures. Callers should ensure +* that this function is *NOT* called under RCU protection or in contexts where +* mutex cannot be locked. +*/ +int dev_pm_opp_set_regulator (struct device *dev,const char *name) +{ +struct device_opp *dev_opp; +struct regulator *reg; +int ret; + +mutex_lock (&dev_opp_list_lock); + + dev_opp =_add_device_opp (dev); +if (!dev_opp) { + ret = -ENOMEM ; + goto unlock; + } + +/* This should be called before OPPs are initialized */ +if (WARN_ON (!list_empty (&dev_opp->opp_list))) { + ret = -EBUSY ; + goto err; + } + +/* Already have a regulator set */ +if (WARN_ON (!IS_ERR_OR_NULL (dev_opp->regulator))) { + ret = -EBUSY ; + goto err; + } +/* Allocate the regulator */ + reg =regulator_get_optional (dev, name); +if (IS_ERR (reg)) { + ret =PTR_ERR (reg); +if (ret != -EPROBE_DEFER ) +dev_err (dev,"%s: no regulator (%s) found: %d\n" , + __func__, name, ret); + goto err; + } + + dev_opp->regulator = reg; + +mutex_unlock (&dev_opp_list_lock); +return 0 ; + +err: +_remove_device_opp (dev_opp); +unlock: +mutex_unlock (&dev_opp_list_lock); + +return ret; +} +EXPORT_SYMBOL_GPL (dev_pm_opp_set_regulator); + +/** +* dev_pm_opp_put_regulator() - Releases resources blocked for regulator +* @dev: Device for which regulator was set. +* +* Locking: The internal device_opp and opp structures are RCU protected. +* Hence this function internally uses RCU updater strategy with mutex locks +* to keep the integrity of the internal data structures. Callers should ensure +* that this function is *NOT* called under RCU protection or in contexts where +* mutex cannot be locked. +*/ +void dev_pm_opp_put_regulator (struct device *dev) +{ +struct device_opp *dev_opp; + +mutex_lock (&dev_opp_list_lock); + +/* Check for existing list for 'dev' first */ + dev_opp =_find_device_opp (dev); +if (IS_ERR (dev_opp)) { +dev_err (dev,"Failed to find dev_opp: %ld\n" ,PTR_ERR (dev_opp)); + goto unlock; + } + +if (IS_ERR_OR_NULL (dev_opp->regulator)) { +dev_err (dev,"%s: Doesn't have regulator set\n" , __func__); + goto unlock; + } + +/* Make sure there are no concurrent readers while updating dev_opp */ +WARN_ON (!list_empty (&dev_opp->opp_list)); + +regulator_put (dev_opp->regulator); + dev_opp->regulator =ERR_PTR (-EINVAL ); + +/* Try freeing device_opp if this was the last blocking resource */ +_remove_device_opp (dev_opp); + +unlock: +mutex_unlock (&dev_opp_list_lock); +} +EXPORT_SYMBOL_GPL (dev_pm_opp_put_regulator); +static bool _opp_is_supported (struct device *dev,struct device_opp *dev_opp,struct device_node *np) {
drivers/base/power/opp/opp.h +4 -0
@@ -22,6 +22,8 @@#include <linux/rculist.h> #include <linux/rcupdate.h> +struct regulator ; +/* Lock to allow exclusive modification to the device and opp lists */ extern struct mutex dev_opp_list_lock;
@@ -132,6 +134,7 @@ struct device_list_opp {* @supported_hw: Array of version number to support. * @supported_hw_count: Number of elements in supported_hw array. * @prop_name: A name to postfix to many DT properties, while parsing them. +* @regulator: Supply regulator * @dentry: debugfs dentry pointer of the real device directory (not links). * @dentry_name: Name of the real dentry. *
@@ -159,6 +162,7 @@ struct device_opp {unsigned int *supported_hw;unsigned int supported_hw_count;const char *prop_name; +struct regulator *regulator;#ifdef CONFIG_DEBUG_FS struct dentry *dentry;
include/linux/pm_opp.h +9 -0
@@ -60,6 +60,8 @@ int dev_pm_opp_set_supported_hw(struct device *dev, const u32 *versions,void dev_pm_opp_put_supported_hw (struct device *dev);int dev_pm_opp_set_prop_name (struct device *dev,const char *name);void dev_pm_opp_put_prop_name (struct device *dev); +int dev_pm_opp_set_regulator (struct device *dev,const char *name); +void dev_pm_opp_put_regulator (struct device *dev);#else static inline unsigned long dev_pm_opp_get_voltage (struct dev_pm_opp *opp) {
@@ -151,6 +153,13 @@ static inline int dev_pm_opp_set_prop_name(struct device *dev, const char *name)static inline void dev_pm_opp_put_prop_name (struct device *dev) {} +static inline int dev_pm_opp_set_regulator (struct device *dev,const char *name) +{ +return -EINVAL ; +} + +static inline void dev_pm_opp_put_regulator (struct device *dev) {} +#endif /* CONFIG_PM_OPP */ #if defined(CONFIG_PM_OPP ) && defined(CONFIG_OF )
Signature · 1 line
2.7.1.370.gb2aa7f8
Re: [PATCH V3 01/16] PM / OPP: get/put regulators from OPP core
Quoted 11 lines · Viresh Kumar
This allows the OPP core to request/free the regulator resource, attached to a device OPP. The regulator device is fetched using the name provided by the driver, while calling:
dev_pm_opp_set_regulator().This will work for both OPP-v1 and v2 bindings.
This is a preliminary step for moving the OPP switching logic into the OPP core.
Signed-off-by Viresh Kumar <[email protected]>---
Signature · 2 lines
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project
Viresh Kumar
maintainerPATCH v3 2/16 PM / OPP: Disable OPPs that aren't supported by the regulator
In reply to Viresh KumarDisable any OPPs where the connected regulator isn't able to provide the specified voltage.
--- drivers/base/power/opp/core.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+)
drivers/base/power/opp/core.c +22 -0
@@ -687,6 +687,22 @@ static struct dev_pm_opp *_allocate_opp(struct device *dev,return opp; } +static bool _opp_supported_by_regulators (struct dev_pm_opp *opp, +struct device_opp *dev_opp) +{ +struct regulator *reg = dev_opp->regulator; + +if (!IS_ERR (reg) && + !regulator_is_supported_voltage (reg, opp->u_volt_min, + opp->u_volt_max)) { +pr_warn ("%s: OPP minuV: %lu maxuV: %lu, not supported by regulator\n" , + __func__, opp->u_volt_min, opp->u_volt_max); +return false; + } + +return true; +} +static int _opp_add (struct device *dev,struct dev_pm_opp *new_opp,struct device_opp *dev_opp) {
@@ -728,6 +744,12 @@ static int _opp_add(struct device *dev, struct dev_pm_opp *new_opp,dev_err (dev,"%s: Failed to register opp to debugfs (%d)\n" , __func__, ret); +if (!_opp_supported_by_regulators (new_opp, dev_opp)) { + new_opp->available = false; +dev_warn (dev,"%s: OPP not supported by regulators (%lu)\n" , + __func__, new_opp->rate); + } +return 0 ; }
Signature · 1 line
2.7.1.370.gb2aa7f8
Viresh Kumar
maintainerPATCH v3 3/16 PM / OPP: Introduce dev_pm_opp_get_max_volt_latency()
In reply to Viresh KumarIn few use cases (like: cpufreq), it is desired to get the maximum voltage latency for changing OPPs. Add support for that.
--- drivers/base/power/opp/core.c | 59 +++++++++++++++++++++++++++++++++++++++++++ include/linux/pm_opp.h | 6 +++++ 2 files changed, 65 insertions(+)
drivers/base/power/opp/core.c +59 -0
@@ -231,6 +231,65 @@ unsigned long dev_pm_opp_get_max_clock_latency(struct device *dev)EXPORT_SYMBOL_GPL (dev_pm_opp_get_max_clock_latency);/** +* dev_pm_opp_get_max_volt_latency() - Get max voltage latency in nanoseconds +* @dev: device for which we do this operation +* +* Return: This function returns the max voltage latency in nanoseconds. +* +* Locking: This function takes rcu_read_lock(). +*/ +unsigned long dev_pm_opp_get_max_volt_latency (struct device *dev) +{ +struct device_opp *dev_opp; +struct dev_pm_opp *opp; +struct regulator *reg; +unsigned long latency_ns =0 ; +unsigned long min_uV = ~0 , max_uV =0 ; +int ret; + +rcu_read_lock (); + + dev_opp =_find_device_opp (dev); +if (IS_ERR (dev_opp)) { +rcu_read_unlock (); +return 0 ; + } + + reg = dev_opp->regulator; +if (IS_ERR_OR_NULL (reg)) { +/* Regulator may not be required for device */ +if (reg) +dev_err (dev,"%s: Invalid regulator (%ld)\n" , __func__, +PTR_ERR (reg)); +rcu_read_unlock (); +return 0 ; + } + +list_for_each_entry_rcu (opp, &dev_opp->opp_list, node) { +if (!opp->available) +continue ; + +if (opp->u_volt_min < min_uV) + min_uV = opp->u_volt_min; +if (opp->u_volt_max > max_uV) + max_uV = opp->u_volt_max; + } + +rcu_read_unlock (); + +/* +* The caller needs to ensure that dev_opp (and hence the regulator) +* isn't freed, while we are executing this routine. +*/ + ret =regulator_set_voltage_time (reg, min_uV, max_uV); +if (ret >0 ) + latency_ns = ret *1000 ; + +return latency_ns; +} +EXPORT_SYMBOL_GPL (dev_pm_opp_get_max_volt_latency); + +/** * dev_pm_opp_get_suspend_opp() - Get suspend opp * @dev: device for which we do this operation *
include/linux/pm_opp.h +6 -0
@@ -34,6 +34,7 @@ bool dev_pm_opp_is_turbo(struct dev_pm_opp *opp);int dev_pm_opp_get_opp_count (struct device *dev);unsigned long dev_pm_opp_get_max_clock_latency (struct device *dev); +unsigned long dev_pm_opp_get_max_volt_latency (struct device *dev);struct dev_pm_opp *dev_pm_opp_get_suspend_opp (struct device *dev); struct dev_pm_opp *dev_pm_opp_find_freq_exact(struct device *dev,
@@ -88,6 +89,11 @@ static inline unsigned long dev_pm_opp_get_max_clock_latency(struct device *dev)return 0 ; } +static inline unsigned long dev_pm_opp_get_max_volt_latency (struct device *dev) +{ +return 0 ; +} +static inline struct dev_pm_opp *dev_pm_opp_get_suspend_opp (struct device *dev) {return NULL ;
Signature · 1 line
2.7.1.370.gb2aa7f8
Re: [PATCH V3 03/16] PM / OPP: Introduce dev_pm_opp_get_max_volt_latency()
Quoted 5 lines · Viresh Kumar
In few use cases (like: cpufreq), it is desired to get the maximum voltage latency for changing OPPs. Add support for that.
Signed-off-by Viresh Kumar <[email protected]>---
Signature · 2 lines
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project
Viresh Kumar
maintainerPATCH v3 4/16 PM / OPP: Introduce dev_pm_opp_get_max_transition_latency()
In reply to Viresh KumarIn few use cases (like: cpufreq), it is desired to get the maximum latency for changing OPPs. Add support for that.
--- drivers/base/power/opp/core.c | 17 +++++++++++++++++ include/linux/pm_opp.h | 6 ++++++ 2 files changed, 23 insertions(+)
drivers/base/power/opp/core.c +17 -0
@@ -290,6 +290,23 @@ unsigned long dev_pm_opp_get_max_volt_latency(struct device *dev)EXPORT_SYMBOL_GPL (dev_pm_opp_get_max_volt_latency);/** +* dev_pm_opp_get_max_transition_latency() - Get max transition latency in +* nanoseconds +* @dev: device for which we do this operation +* +* Return: This function returns the max transition latency, in nanoseconds, to +* switch from one OPP to other. +* +* Locking: This function takes rcu_read_lock(). +*/ +unsigned long dev_pm_opp_get_max_transition_latency (struct device *dev) +{ +return dev_pm_opp_get_max_volt_latency (dev) + +dev_pm_opp_get_max_clock_latency (dev); +} +EXPORT_SYMBOL_GPL (dev_pm_opp_get_max_transition_latency); + +/** * dev_pm_opp_get_suspend_opp() - Get suspend opp * @dev: device for which we do this operation *
include/linux/pm_opp.h +6 -0
@@ -35,6 +35,7 @@ bool dev_pm_opp_is_turbo(struct dev_pm_opp *opp);int dev_pm_opp_get_opp_count (struct device *dev);unsigned long dev_pm_opp_get_max_clock_latency (struct device *dev);unsigned long dev_pm_opp_get_max_volt_latency (struct device *dev); +unsigned long dev_pm_opp_get_max_transition_latency (struct device *dev);struct dev_pm_opp *dev_pm_opp_get_suspend_opp (struct device *dev); struct dev_pm_opp *dev_pm_opp_find_freq_exact(struct device *dev,
@@ -94,6 +95,11 @@ static inline unsigned long dev_pm_opp_get_max_volt_latency(struct device *dev)return 0 ; } +static inline unsigned long dev_pm_opp_get_max_transition_latency (struct device *dev) +{ +return 0 ; +} +static inline struct dev_pm_opp *dev_pm_opp_get_suspend_opp (struct device *dev) {return NULL ;
Signature · 1 line
2.7.1.370.gb2aa7f8
Viresh Kumar
maintainerPATCH v3 5/16 PM / OPP: Parse clock-latency and voltage-tolerance for v1 bindings
In reply to Viresh KumarV2 bindings have better support for clock-latency and voltage-tolerance and doesn't need special care. To use callbacks, like dev_pm_opp_get_max_{transition|volt}_latency(), irrespective of the bindings, the core needs to know clock-latency/voltage-tolerance for the earlier bindings.
This patch reads clock-latency/voltage-tolerance from the device node, irrespective of the bindings (to keep it simple) and use them only for V1 bindings.
--- drivers/base/power/opp/core.c | 20 ++++++++++++++++++++ drivers/base/power/opp/opp.h | 6 ++++++ 2 files changed, 26 insertions(+)
drivers/base/power/opp/core.c +20 -0
@@ -582,6 +582,7 @@ static struct device_opp *_add_device_opp(struct device *dev) {struct device_opp *dev_opp;struct device_list_opp *list_dev; +struct device_node *np;/* Check for existing list for 'dev' first */ dev_opp =_find_device_opp (dev);
@@ -604,6 +605,21 @@ static struct device_opp *_add_device_opp(struct device *dev)return NULL ; } +/* +* Only required for backward compatibility with v1 bindings, but isn't +* harmful for other cases. And so we do it unconditionally. +*/ + np =of_node_get (dev->of_node); +if (np) { +u32 val; + +if (!of_property_read_u32 (np,"clock-latency" , &val)) + dev_opp->clock_latency_ns_max = val; +of_property_read_u32 (np,"voltage-tolerance" , + &dev_opp->voltage_tolerance_v1); +of_node_put (np); + } +srcu_init_notifier_head (&dev_opp->srcu_head);INIT_LIST_HEAD (&dev_opp->opp_list);
@@ -861,6 +877,7 @@ static int _opp_add_v1(struct device *dev, unsigned long freq, long u_volt, {struct device_opp *dev_opp;struct dev_pm_opp *new_opp; +unsigned long tol;int ret;/* Hold our list modification lock here */
@@ -874,7 +891,10 @@ static int _opp_add_v1(struct device *dev, unsigned long freq, long u_volt,/* populate the opp table */ new_opp->rate = freq; + tol = u_volt * dev_opp->voltage_tolerance_v1 /100 ; new_opp->u_volt = u_volt; + new_opp->u_volt_min = u_volt - tol; + new_opp->u_volt_max = u_volt + tol; new_opp->available = true; new_opp->dynamic = dynamic;
drivers/base/power/opp/opp.h +6 -0
@@ -138,6 +138,8 @@ struct device_list_opp {* @dentry: debugfs dentry pointer of the real device directory (not links). * @dentry_name: Name of the real dentry. * +* @voltage_tolerance_v1: In percentage, for v1 bindings only. +* * This is an internal data structure maintaining the link to opps attached to * a device. This structure is not meant to be shared to users as it is * meant for book keeping and private to OPP library.
@@ -156,6 +158,10 @@ struct device_opp {struct device_node *np;unsigned long clock_latency_ns_max; + +/* For backward compatibility with v1 bindings */ +unsigned int voltage_tolerance_v1; +bool shared_opp;struct dev_pm_opp *suspend_opp;
Signature · 1 line
2.7.1.370.gb2aa7f8
Viresh Kumar
maintainerPATCH v3 6/16 PM / OPP: Manage device clk
In reply to Viresh KumarOPP core has got almost everything now to manage device's OPP transitions, the only thing left is device's clk. Get that as well.
--- drivers/base/power/opp/core.c | 15 +++++++++++++++ drivers/base/power/opp/opp.h | 3 +++ 2 files changed, 18 insertions(+)
drivers/base/power/opp/core.c +15 -0
@@ -13,6 +13,7 @@#define pr_fmt (fmt) KBUILD_MODNAME ": " fmt +#include <linux/clk.h> #include <linux/errno.h> #include <linux/err.h> #include <linux/slab.h>
@@ -583,6 +584,7 @@ static struct device_opp *_add_device_opp(struct device *dev)struct device_opp *dev_opp;struct device_list_opp *list_dev;struct device_node *np; +int ret;/* Check for existing list for 'dev' first */ dev_opp =_find_device_opp (dev);
@@ -620,6 +622,15 @@ static struct device_opp *_add_device_opp(struct device *dev)of_node_put (np); } +/* Find clk for the device */ + dev_opp->clk =clk_get (dev,NULL ); +if (IS_ERR (dev_opp->clk)) { + ret =PTR_ERR (dev_opp->clk); +if (ret != -EPROBE_DEFER ) +dev_dbg (dev,"%s: Couldn't find clock: %d\n" , __func__, + ret); + } +srcu_init_notifier_head (&dev_opp->srcu_head);INIT_LIST_HEAD (&dev_opp->opp_list);
@@ -661,6 +672,10 @@ static void _remove_device_opp(struct device_opp *dev_opp)if (!IS_ERR_OR_NULL (dev_opp->regulator))return ; +/* Release clk */ +if (!IS_ERR (dev_opp->clk)) +clk_put (dev_opp->clk); + list_dev = list_first_entry(&dev_opp->dev_list, struct device_list_opp, node);
drivers/base/power/opp/opp.h +3 -0
@@ -22,6 +22,7 @@#include <linux/rculist.h> #include <linux/rcupdate.h> +struct clk ;struct regulator ;/* Lock to allow exclusive modification to the device and opp lists */
@@ -134,6 +135,7 @@ struct device_list_opp {* @supported_hw: Array of version number to support. * @supported_hw_count: Number of elements in supported_hw array. * @prop_name: A name to postfix to many DT properties, while parsing them. +* @clk: Device's clock handle * @regulator: Supply regulator * @dentry: debugfs dentry pointer of the real device directory (not links). * @dentry_name: Name of the real dentry.
@@ -168,6 +170,7 @@ struct device_opp {unsigned int *supported_hw;unsigned int supported_hw_count;const char *prop_name; +struct clk *clk;struct regulator *regulator;#ifdef CONFIG_DEBUG_FS
Signature · 1 line
2.7.1.370.gb2aa7f8
Viresh Kumar
maintainerPATCH v3 7/16 PM / OPP: Add dev_pm_opp_set_rate()
In reply to Viresh KumarThis adds a routine, dev_pm_opp_set_rate(), responsible for configuring power-supply and clock source for an OPP.
The OPP is found by matching against the target_freq passed to the routine. This shall replace similar code present in most of the OPP users and help simplify them a lot.
--- drivers/base/power/opp/core.c | 176 ++++++++++++++++++++++++++++++++++++++++++ include/linux/pm_opp.h | 6 ++ 2 files changed, 182 insertions(+)
drivers/base/power/opp/core.c +176 -0
@@ -529,6 +529,182 @@ struct dev_pm_opp *dev_pm_opp_find_freq_floor(struct device *dev, }EXPORT_SYMBOL_GPL (dev_pm_opp_find_freq_floor); +/* +* The caller needs to ensure that device_opp (and hence the clk) isn't freed, +* while clk returned here is used. +*/ +static struct clk *_get_opp_clk (struct device *dev) +{ +struct device_opp *dev_opp; +struct clk *clk; + +rcu_read_lock (); + + dev_opp =_find_device_opp (dev); +if (IS_ERR (dev_opp)) { +dev_err (dev,"%s: device opp doesn't exist\n" , __func__); + clk =ERR_CAST (dev_opp); + goto unlock; + } + + clk = dev_opp->clk; +if (IS_ERR (clk)) +dev_err (dev,"%s: No clock available for the device\n" , + __func__); + +unlock: +rcu_read_unlock (); +return clk; +} + +static int _set_opp_voltage (struct device *dev,struct regulator *reg, +unsigned long u_volt,unsigned long u_volt_min, +unsigned long u_volt_max) +{ +int ret; + +/* Regulator not available for device */ +if (IS_ERR (reg)) { +dev_dbg (dev,"%s: regulator not available: %ld\n" , __func__, +PTR_ERR (reg)); +return 0 ; + } + +dev_dbg (dev,"%s: voltages (mV): %lu %lu %lu\n" , __func__, u_volt_min, + u_volt, u_volt_max); + + ret =regulator_set_voltage_triplet (reg, u_volt_min, u_volt, + u_volt_max); +if (ret) +dev_err (dev,"%s: failed to set voltage (%lu %lu %lu mV): %d\n" , + __func__, u_volt_min, u_volt, u_volt_max, ret); + +return ret; +} + +/** +* dev_pm_opp_set_rate() - Configure new OPP based on frequency +* @dev: device for which we do this operation +* @target_freq: frequency to achieve +* +* This configures the power-supplies and clock source to the levels specified +* by the OPP corresponding to the target_freq. +* +* Locking: This function takes rcu_read_lock(). +*/ +int dev_pm_opp_set_rate (struct device *dev,unsigned long target_freq) +{ +struct device_opp *dev_opp; +struct dev_pm_opp *old_opp, *opp; +struct regulator *reg; +struct clk *clk; +unsigned long freq, old_freq; +unsigned long u_volt, u_volt_min, u_volt_max; +unsigned long ou_volt, ou_volt_min, ou_volt_max; +int ret; + +if (unlikely (!target_freq)) { +dev_err (dev,"%s: Invalid target frequency %lu\n" , __func__, + target_freq); +return -EINVAL ; + } + + clk =_get_opp_clk (dev); +if (IS_ERR (clk)) +return PTR_ERR (clk); + + freq =clk_round_rate (clk, target_freq); +if ((long )freq <=0 ) + freq = target_freq; + + old_freq =clk_get_rate (clk); + +/* Return early if nothing to do */ +if (old_freq == freq) { +dev_dbg (dev,"%s: old/new frequencies (%lu Hz) are same, nothing to do\n" , + __func__, freq); +return 0 ; + } + +rcu_read_lock (); + + dev_opp =_find_device_opp (dev); +if (IS_ERR (dev_opp)) { +dev_err (dev,"%s: device opp doesn't exist\n" , __func__); +rcu_read_unlock (); +return PTR_ERR (dev_opp); + } + + old_opp =dev_pm_opp_find_freq_ceil (dev, &old_freq); +if (!IS_ERR (old_opp)) { + ou_volt = old_opp->u_volt; + ou_volt_min = old_opp->u_volt_min; + ou_volt_max = old_opp->u_volt_max; + }else { +dev_err (dev,"%s: failed to find current OPP for freq %lu (%ld)\n" , + __func__, old_freq,PTR_ERR (old_opp)); + } + + opp =dev_pm_opp_find_freq_ceil (dev, &freq); +if (IS_ERR (opp)) { + ret =PTR_ERR (opp); +dev_err (dev,"%s: failed to find OPP for freq %lu (%d)\n" , + __func__, freq, ret); +rcu_read_unlock (); +return ret; + } + + u_volt = opp->u_volt; + u_volt_min = opp->u_volt_min; + u_volt_max = opp->u_volt_max; + + reg = dev_opp->regulator; + +rcu_read_unlock (); + +/* Scaling up? Scale voltage before frequency */ +if (freq > old_freq) { + ret =_set_opp_voltage (dev, reg, u_volt, u_volt_min, + u_volt_max); +if (ret) + goto restore_voltage; + } + +/* Change frequency */ + +dev_dbg (dev,"%s: switching OPP: %lu Hz --> %lu Hz\n" , + __func__, old_freq, freq); + + ret =clk_set_rate (clk, freq); +if (ret) { +dev_err (dev,"%s: failed to set clock rate: %d\n" , __func__, + ret); + goto restore_voltage; + } + +/* Scaling down? Scale voltage after frequency */ +if (freq < old_freq) { + ret =_set_opp_voltage (dev, reg, u_volt, u_volt_min, + u_volt_max); +if (ret) + goto restore_freq; + } + +return 0 ; + +restore_freq: +if (clk_set_rate (clk, old_freq)) +dev_err (dev,"%s: failed to restore old-freq (%lu Hz)\n" , + __func__, old_freq); +restore_voltage: +/* This shouldn't harm even if the voltages weren't updated earlier */ +if (!IS_ERR (old_opp)) +_set_opp_voltage (dev, reg, ou_volt, ou_volt_min, ou_volt_max); + +return ret; +} +EXPORT_SYMBOL_GPL (dev_pm_opp_set_rate); +/* List-dev Helpers */ static void _kfree_list_dev_rcu (struct rcu_head *head) {
include/linux/pm_opp.h +6 -0
@@ -64,6 +64,7 @@ int dev_pm_opp_set_prop_name(struct device *dev, const char *name);void dev_pm_opp_put_prop_name (struct device *dev);int dev_pm_opp_set_regulator (struct device *dev,const char *name);void dev_pm_opp_put_regulator (struct device *dev); +int dev_pm_opp_set_rate (struct device *dev,unsigned long target_freq);#else static inline unsigned long dev_pm_opp_get_voltage (struct dev_pm_opp *opp) {
@@ -172,6 +173,11 @@ static inline int dev_pm_opp_set_regulator(struct device *dev, const char *name)static inline void dev_pm_opp_put_regulator (struct device *dev) {} +static inline int dev_pm_opp_set_rate (struct device *dev,unsigned long target_freq) +{ +return -EINVAL ; +} +#endif /* CONFIG_PM_OPP */ #if defined(CONFIG_PM_OPP ) && defined(CONFIG_OF )
Signature · 1 line
2.7.1.370.gb2aa7f8
Re: [PATCH V3 07/16] PM / OPP: Add dev_pm_opp_set_rate()
Quoted 9 lines · Viresh Kumar
This adds a routine,
dev_pm_opp_set_rate(), responsible for configuring power-supply and clock source for an OPP.The OPP is found by matching against the
target_freqpassed to the routine. This shall replace similar code present in most of the OPP users and help simplify them a lot.Signed-off-by Viresh Kumar <[email protected]>---
Signature · 2 lines
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project
Viresh Kumar
maintainerPATCH v3 8/16 cpufreq: dt: Convert few pr_debug/err() calls to dev_dbg/err()
In reply to Viresh KumarWe have the device structure available now, lets use it for better print messages.
--- drivers/cpufreq/cpufreq-dt.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/cpufreq/cpufreq-dt.c +2 -2
@@ -246,7 +246,7 @@ static int cpufreq_init(struct cpufreq_policy *policy)*/ ret =dev_pm_opp_get_opp_count (cpu_dev);if (ret <=0 ) { -pr_debug ("OPP table is not ready, deferring probe\n" ); +dev_dbg (cpu_dev,"OPP table is not ready, deferring probe\n" ); ret = -EPROBE_DEFER ; goto out_free_opp; }
@@ -325,7 +325,7 @@ static int cpufreq_init(struct cpufreq_policy *policy) ret =dev_pm_opp_init_cpufreq_table (cpu_dev, &freq_table);if (ret) { -pr_err ("failed to init cpufreq table: %d\n" , ret); +dev_err (cpu_dev,"failed to init cpufreq table: %d\n" , ret); goto out_free_priv; }
Signature · 1 line
2.7.1.370.gb2aa7f8
Viresh Kumar
maintainerPATCH v3 9/16 cpufreq: dt: Rename 'need_update' to 'opp_v1'
In reply to Viresh KumarThat's the real purpose of this field, i.e. to take special care of old OPP V1 bindings. Lets name it accordingly, so that it can be used elsewhere.
--- drivers/cpufreq/cpufreq-dt.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
drivers/cpufreq/cpufreq-dt.c +3 -3
@@ -199,7 +199,7 @@ static int cpufreq_init(struct cpufreq_policy *policy)struct dev_pm_opp *suspend_opp;unsigned long min_uV = ~0 , max_uV =0 ;unsigned int transition_latency; -bool need_update = false; +bool opp_v1 = false;int ret; ret =allocate_resources (policy->cpu, &cpu_dev, &cpu_reg, &cpu_clk);
@@ -223,7 +223,7 @@ static int cpufreq_init(struct cpufreq_policy *policy)* finding shared-OPPs for backward compatibility. */ if (ret == -ENOENT ) - need_update = true; + opp_v1 = true;else goto out_node_put; }
@@ -251,7 +251,7 @@ static int cpufreq_init(struct cpufreq_policy *policy) goto out_free_opp; } -if (need_update) { +if (opp_v1) {struct cpufreq_dt_platform_data *pd =cpufreq_get_driver_data ();if (!pd || !pd->independent_clocks)
Signature · 1 line
2.7.1.370.gb2aa7f8
Viresh Kumar
maintainerPATCH v3 10/16 cpufreq: dt: OPP layers handles clock-latency for V1 bindings as well
In reply to Viresh Kumar"clock-latency" is handled by OPP layer for all bindings and so there is no need to make special calls for V1 bindings. Use dev_pm_opp_get_max_clock_latency() for both the cases.
--- drivers/cpufreq/cpufreq-dt.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-)
drivers/cpufreq/cpufreq-dt.c +1 -4
@@ -265,10 +265,6 @@ static int cpufreq_init(struct cpufreq_policy *policy)if (ret)dev_err (cpu_dev,"%s: failed to mark OPPs as shared: %d\n" , __func__, ret); - -of_property_read_u32 (np,"clock-latency" , &transition_latency); - }else { - transition_latency =dev_pm_opp_get_max_clock_latency (cpu_dev); } priv =kzalloc (sizeof (*priv),GFP_KERNEL );
@@ -279,6 +275,7 @@ static int cpufreq_init(struct cpufreq_policy *policy)of_property_read_u32 (np,"voltage-tolerance" , &priv->voltage_tolerance); + transition_latency =dev_pm_opp_get_max_clock_latency (cpu_dev);if (!transition_latency) transition_latency =CPUFREQ_ETERNAL ;
Signature · 1 line
2.7.1.370.gb2aa7f8
Viresh Kumar
maintainerPATCH v3 11/16 cpufreq: dt: Pass regulator name to the OPP core
In reply to Viresh KumarOPP core can handle the regulators by itself, and but it needs to know the name of the regulator to fetch. Add support for that.
--- drivers/cpufreq/cpufreq-dt.c | 46 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+)
drivers/cpufreq/cpufreq-dt.c +46 -0
@@ -34,6 +34,7 @@ struct private_data {struct regulator *cpu_reg;struct thermal_cooling_device *cdev;unsigned int voltage_tolerance;/* in percentage */ +const char *reg_name; };static struct freq_attr *cpufreq_dt_attr[] = {
@@ -119,6 +120,30 @@ static int set_target(struct cpufreq_policy *policy, unsigned int index)return ret; } +/* +* An earlier version of opp-v1 bindings used to name the regulator +* "cpu0-supply", we still need to handle that for backwards compatibility. +*/ +static const char *find_supply_name (struct device *dev,struct device_node *np) +{ +struct property *pp; +int cpu = dev->id; + +/* Try "cpu0" for older DTs */ +if (!cpu) { + pp =of_find_property (np,"cpu0-supply" ,NULL ); +if (pp) +return "cpu0" ; + } + + pp =of_find_property (np,"cpu-supply" ,NULL ); +if (pp) +return "cpu" ; + +dev_dbg (dev,"no regulator for cpu%d\n" , cpu); +return NULL ; +} +static int allocate_resources (int cpu,struct device **cdev,struct regulator **creg,struct clk **cclk) {
@@ -200,6 +225,7 @@ static int cpufreq_init(struct cpufreq_policy *policy)unsigned long min_uV = ~0 , max_uV =0 ;unsigned int transition_latency;bool opp_v1 = false; +const char *name;int ret; ret =allocate_resources (policy->cpu, &cpu_dev, &cpu_reg, &cpu_clk);
@@ -229,6 +255,20 @@ static int cpufreq_init(struct cpufreq_policy *policy) }/* +* OPP layer will be taking care of regulators now, but it needs to know +* the name of the regulator first. +*/ + name =find_supply_name (cpu_dev, np); +if (name) { + ret =dev_pm_opp_set_regulator (cpu_dev, name); +if (ret) { +dev_err (cpu_dev,"Failed to set regulator for cpu%d: %d\n" , + policy->cpu, ret); + goto out_node_put; + } + } + +/* * Initialize OPP tables for all policy->cpus. They will be shared by * all CPUs which have marked their CPUs shared with OPP bindings. *
@@ -273,6 +313,7 @@ static int cpufreq_init(struct cpufreq_policy *policy) goto out_free_opp; } + priv->reg_name = name;of_property_read_u32 (np,"voltage-tolerance" , &priv->voltage_tolerance); transition_latency =dev_pm_opp_get_max_clock_latency (cpu_dev);
@@ -366,6 +407,8 @@ static int cpufreq_init(struct cpufreq_policy *policy)kfree (priv); out_free_opp:dev_pm_opp_of_cpumask_remove_table (policy->cpus); +if (name) +dev_pm_opp_put_regulator (cpu_dev); out_node_put:of_node_put (np); out_put_reg_clk:
@@ -383,6 +426,9 @@ static int cpufreq_exit(struct cpufreq_policy *policy)cpufreq_cooling_unregister (priv->cdev);dev_pm_opp_free_cpufreq_table (priv->cpu_dev, &policy->freq_table);dev_pm_opp_of_cpumask_remove_table (policy->related_cpus); +if (priv->reg_name) +dev_pm_opp_put_regulator (priv->cpu_dev); +clk_put (policy->clk);if (!IS_ERR (priv->cpu_reg))regulator_put (priv->cpu_reg);
Signature · 1 line
2.7.1.370.gb2aa7f8
Re: [PATCH V3 11/16] cpufreq: dt: Pass regulator name to the OPP core
Quoted 5 lines · Viresh Kumar
OPP core can handle the regulators by itself, and but it needs to know the name of the regulator to fetch. Add support for that.
Signed-off-by Viresh Kumar <[email protected]>---
Signature · 2 lines
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project
Re: [PATCH V3 11/16] cpufreq: dt: Pass regulator name to the OPP core
In reply to Viresh KumarHi Viresh,
CC device-tree folks
Replying to an old email, because that's the most accurate reference I could find.
Quoted 26 lines · Viresh Kumar
OPP core can handle the regulators by itself, and but it needs to know the name of the regulator to fetch. Add support for that.
Signed-off-by Viresh Kumar <[email protected]>--- drivers/cpufreq/cpufreq-dt.c | 46 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+)drivers/cpufreq/cpufreq-dt.c +9
-0@@ -119,6 +120,30 @@ static int set_target(struct cpufreq_policy *policy, unsigned int index)return ret; } +/* +* An earlier version of opp-v1 bindings used to name the regulator +* "cpu0-supply", we still need to handle that for backwards compatibility. +*/ +static const char *find_supply_name (struct device *dev,struct device_node *np) +{ +struct property *pp; +int cpu = dev->id; +
drivers/cpufreq/cpufreq-dt.c static int set_target(struct cpufreq_policy *policy, unsigned int index) · line 141
Quoted 10 lines
drivers/cpufreq/cpufreq-dt.c +10
-0+/* Try "cpu0" for older DTs */ +if (!cpu) { + pp =of_find_property (np,"cpu0-supply" ,NULL ); +if (pp) +return "cpu0" ; + } + + pp =of_find_property (np,"cpu-supply" ,NULL ); +if (pp) +return "cpu" ;
Despite the existence of lots of users of these properties, I couldn't find both the "earlier version" and the "current version" of the opp-v1 bindings documenting the "cpu0-supply" and "cpu-supply" properties?
Even for opp-v2, they are not documented in Documentation/devicetree/bindings/opp/opp.txt, but cpu-supply is used in the examples?
For v2, I did find "[PATCH 01/16] PM / OPP: Add 'supply-names' binding"
https://lore.kernel.org/lkml/2b87b162eabd1570ae2311e1ef8655acda72f678.1441972771.git.viresh.kumar@linaro.org/
but presumably that's an even further evolution?
Can you please document these properties?
Thanks!
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- [email protected]
In personal conversations with technical people, I call myself a hacker. But when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
Viresh Kumar
maintainerRe: [PATCH V3 11/16] cpufreq: dt: Pass regulator name to the OPP core
Quoted 40 lines · Geert Uytterhoeven
Hi Viresh,
CC device-tree folks
Replying to an old email, because that's the most accurate reference I could find.
Quoted 32 lines · Viresh Kumar
OPP core can handle the regulators by itself, and but it needs to know the name of the regulator to fetch. Add support for that.
Signed-off-by Viresh Kumar <[email protected]>--- drivers/cpufreq/cpufreq-dt.c | 46 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+)drivers/cpufreq/cpufreq-dt.c +15
-0@@ -119,6 +120,30 @@ static int set_target(struct cpufreq_policy *policy, unsigned int index)return ret; } +/* +* An earlier version of opp-v1 bindings used to name the regulator +* "cpu0-supply", we still need to handle that for backwards compatibility. +*/ +static const char *find_supply_name (struct device *dev,struct device_node *np) +{ +struct property *pp; +int cpu = dev->id; + +/* Try "cpu0" for older DTs */ +if (!cpu) { + pp =of_find_property (np,"cpu0-supply" ,NULL ); +if (pp) +return "cpu0" ; + }
Quoted 8 lines
drivers/cpufreq/cpufreq-dt.c static int set_target(struct cpufreq_policy *policy, unsigned int index) · line 141
Quoted 4 lines
drivers/cpufreq/cpufreq-dt.c +4
-0+ + pp =of_find_property (np,"cpu-supply" ,NULL ); +if (pp) +return "cpu" ;Despite the existence of lots of users of these properties, I couldn't find both the "earlier version" and the "current version" of the opp-v1 bindings documenting the "cpu0-supply" and "cpu-supply" properties?
They are part of the device nodes and don't fall under the jurisdiction of OPP tables and so aren't defined there. We rely on the "<name>-supply" property from the regulator bindings for the devices.
Quoted 3 lines
Even for opp-v2, they are not documented in
Documentation/devicetree/bindings/opp/opp.txt, but cpu-supply is used in the examples?
Same reasoning here as well.
Quoted 3 lines
For v2, I did find "[PATCH 01/16] PM / OPP: Add 'supply-names' binding"
https://lore.kernel.org/lkml/2b87b162eabd1570ae2311e1ef8655acda72f678.1441972771.git.viresh.kumar@linaro.org/
but presumably that's an even further evolution?
Yeah, that never made it to mainline is abandoned.
Quoted 1 line
Can you please document these properties?
I don't think we need to, do we ?
Signature · 1 line
viresh
Re: [PATCH V3 11/16] cpufreq: dt: Pass regulator name to the OPP core
Hi Viresh,
Quoted 52 lines · Viresh Kumar
Quoted 38 lines · Geert Uytterhoeven
CC device-tree folks
Replying to an old email, because that's the most accurate reference I could find.
Quoted 32 lines · Viresh Kumar
OPP core can handle the regulators by itself, and but it needs to know the name of the regulator to fetch. Add support for that.
Signed-off-by Viresh Kumar <[email protected]>--- drivers/cpufreq/cpufreq-dt.c | 46 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+)drivers/cpufreq/cpufreq-dt.c +15
-0@@ -119,6 +120,30 @@ static int set_target(struct cpufreq_policy *policy, unsigned int index)return ret; } +/* +* An earlier version of opp-v1 bindings used to name the regulator +* "cpu0-supply", we still need to handle that for backwards compatibility. +*/ +static const char *find_supply_name (struct device *dev,struct device_node *np) +{ +struct property *pp; +int cpu = dev->id; + +/* Try "cpu0" for older DTs */ +if (!cpu) { + pp =of_find_property (np,"cpu0-supply" ,NULL ); +if (pp) +return "cpu0" ; + }Quoted 8 lines
drivers/cpufreq/cpufreq-dt.c static int set_target(struct cpufreq_policy *policy, unsigned int index) · line 141
Quoted 4 lines
drivers/cpufreq/cpufreq-dt.c +4
-0+ + pp =of_find_property (np,"cpu-supply" ,NULL ); +if (pp) +return "cpu" ;Despite the existence of lots of users of these properties, I couldn't find both the "earlier version" and the "current version" of the opp-v1 bindings documenting the "cpu0-supply" and "cpu-supply" properties?
They are part of the device nodes and don't fall under the jurisdiction of OPP tables and so aren't defined there. We rely on the "<name>-supply" property from the regulator bindings for the devices.
Quoted 5 lines
Quoted 3 lines
Even for opp-v2, they are not documented in
Documentation/devicetree/bindings/opp/opp.txt, but cpu-supply is used in the examples?Same reasoning here as well.
Right, they are part of the cpu nodes, not of the operating-points properties (which are part of the cpu nodes) or the opp_table nodes (for v2).
Quoted 3 lines
Quoted 1 line
Can you please document these properties?
I don't think we need to, do we ?
IMHO they should be documented somewhere, with a link to Documentation/devicetree/bindings/regulator/regulator.txt. How else can a DTS writer know he/she needs them? Without cpu-supply (and clock!) properties, cpufreq can't work.
Candidates:
- Documentation/devicetree/bindings/cpufreq/cpufreq-dt.txt This has operating points without clocks nor cpu-supply, but with clock-latency (for which clock? ;-)
- Documentation/devicetree/bindings/cpufreq/arm_big_little_dt.txt Again, operating points without clocks nor cpu-supply, but with clock-latency.
Thanks!
Gr{oetje,eeting}s,
Geert
Signature · 5 lines
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- [email protected] In personal conversations with technical people, I call myself a hacker. But when I'm talking to journalists I just say "programmer" or something like that. -- Linus Torvalds
Viresh Kumar
maintainerRe: [PATCH V3 11/16] cpufreq: dt: Pass regulator name to the OPP core
Quoted 5 lines · Geert Uytterhoeven
IMHO they should be documented somewhere, with a link to
Documentation/devicetree/bindings/regulator/regulator.txt. How else can a DTS writer know he/she needs them? Without cpu-supply (and clock!) properties, cpufreq can't work.
Quoted 7 lines
Candidates:
-Documentation/devicetree/bindings/cpufreq/cpufreq-dt.txtThis has operating points without clocks nor cpu-supply, but with clock-latency (for which clock? ;-)
-Documentation/devicetree/bindings/cpufreq/arm_big_little_dt.txtAgain, operating points without clocks nor cpu-supply, but with clock-latency.
Sounds reasonable. Will cook patches for that soon. Thanks.
Signature · 1 line
viresh
Viresh Kumar
maintainerPATCH v3 12/16 cpufreq: dt: Unsupported OPPs are already disabled
In reply to Viresh KumarThe core already have a valid regulator set for the device opp and the unsupported OPPs are already disabled by the core. There is no need to repeat that in the user drivers, get rid of it.
--- drivers/cpufreq/cpufreq-dt.c | 2 -- 1 file changed, 2 deletions(-)
drivers/cpufreq/cpufreq-dt.c +0 -2
@@ -349,8 +349,6 @@ static int cpufreq_init(struct cpufreq_policy *policy) min_uV = opp_uV;if (opp_uV > max_uV) max_uV = opp_uV; - }else { -dev_pm_opp_disable (cpu_dev, opp_freq); } opp_freq++;
Signature · 1 line
2.7.1.370.gb2aa7f8
Viresh Kumar
maintainerPATCH v3 13/16 cpufreq: dt: Reuse dev_pm_opp_get_max_transition_latency()
In reply to Viresh KumarOPP layer has all the information now to calculate transition latency (clock_latency + voltage_latency). Lets reuse the OPP layer helper dev_pm_opp_get_max_transition_latency() instead of open coding the same in cpufreq-dt driver.
--- drivers/cpufreq/cpufreq-dt.c | 48 ++++---------------------------------------- 1 file changed, 4 insertions(+), 44 deletions(-)
drivers/cpufreq/cpufreq-dt.c +4 -44
@@ -222,7 +222,6 @@ static int cpufreq_init(struct cpufreq_policy *policy)struct regulator *cpu_reg;struct clk *cpu_clk;struct dev_pm_opp *suspend_opp; -unsigned long min_uV = ~0 , max_uV =0 ;unsigned int transition_latency;bool opp_v1 = false;const char *name;
@@ -316,49 +315,6 @@ static int cpufreq_init(struct cpufreq_policy *policy) priv->reg_name = name;of_property_read_u32 (np,"voltage-tolerance" , &priv->voltage_tolerance); - transition_latency =dev_pm_opp_get_max_clock_latency (cpu_dev); -if (!transition_latency) - transition_latency =CPUFREQ_ETERNAL ; - -if (!IS_ERR (cpu_reg)) { -unsigned long opp_freq =0 ; - -/* -* Disable any OPPs where the connected regulator isn't able to -* provide the specified voltage and record minimum and maximum -* voltage levels. -*/ -while (1 ) { -struct dev_pm_opp *opp; -unsigned long opp_uV, tol_uV; - -rcu_read_lock (); - opp =dev_pm_opp_find_freq_ceil (cpu_dev, &opp_freq); -if (IS_ERR (opp)) { -rcu_read_unlock (); -break ; - } - opp_uV =dev_pm_opp_get_voltage (opp); -rcu_read_unlock (); - - tol_uV = opp_uV * priv->voltage_tolerance /100 ; -if (regulator_is_supported_voltage (cpu_reg, - opp_uV - tol_uV, - opp_uV + tol_uV)) { -if (opp_uV < min_uV) - min_uV = opp_uV; -if (opp_uV > max_uV) - max_uV = opp_uV; - } - - opp_freq++; - } - - ret =regulator_set_voltage_time (cpu_reg, min_uV, max_uV); -if (ret >0 ) - transition_latency += ret *1000 ; - } - ret =dev_pm_opp_init_cpufreq_table (cpu_dev, &freq_table);if (ret) {dev_err (cpu_dev,"failed to init cpufreq table: %d\n" , ret);
@@ -393,6 +349,10 @@ static int cpufreq_init(struct cpufreq_policy *policy) cpufreq_dt_attr[1 ] = &cpufreq_freq_attr_scaling_boost_freqs; } + transition_latency =dev_pm_opp_get_max_transition_latency (cpu_dev); +if (!transition_latency) + transition_latency =CPUFREQ_ETERNAL ; + policy->cpuinfo.transition_latency = transition_latency;of_node_put (np);
Signature · 1 line
2.7.1.370.gb2aa7f8
Viresh Kumar
maintainerPATCH v3 14/16 cpufreq: dt: Use dev_pm_opp_set_rate() to switch frequency
In reply to Viresh KumarOPP core supports frequency/voltage changes based on the target frequency now, use that instead of open coding the same in cpufreq-dt driver.
--- drivers/cpufreq/cpufreq-dt.c | 73 ++------------------------------------------ 1 file changed, 2 insertions(+), 71 deletions(-)
drivers/cpufreq/cpufreq-dt.c +2 -71
@@ -45,79 +45,10 @@ static struct freq_attr *cpufreq_dt_attr[] = {static int set_target (struct cpufreq_policy *policy,unsigned int index) { -struct dev_pm_opp *opp; -struct cpufreq_frequency_table *freq_table = policy->freq_table; -struct clk *cpu_clk = policy->clk;struct private_data *priv = policy->driver_data; -struct device *cpu_dev = priv->cpu_dev; -struct regulator *cpu_reg = priv->cpu_reg; -unsigned long volt =0 , tol =0 ; -int volt_old =0 ; -unsigned int old_freq, new_freq; -long freq_Hz, freq_exact; -int ret; - - freq_Hz =clk_round_rate (cpu_clk, freq_table[index].frequency *1000 ); -if (freq_Hz <=0 ) - freq_Hz = freq_table[index].frequency *1000 ; - - freq_exact = freq_Hz; - new_freq = freq_Hz /1000 ; - old_freq =clk_get_rate (cpu_clk) /1000 ; - -if (!IS_ERR (cpu_reg)) { -unsigned long opp_freq; - -rcu_read_lock (); - opp =dev_pm_opp_find_freq_ceil (cpu_dev, &freq_Hz); -if (IS_ERR (opp)) { -rcu_read_unlock (); -dev_err (cpu_dev,"failed to find OPP for %ld\n" , - freq_Hz); -return PTR_ERR (opp); - } - volt =dev_pm_opp_get_voltage (opp); - opp_freq =dev_pm_opp_get_freq (opp); -rcu_read_unlock (); - tol = volt * priv->voltage_tolerance /100 ; - volt_old =regulator_get_voltage (cpu_reg); -dev_dbg (cpu_dev,"Found OPP: %ld kHz, %ld uV\n" , - opp_freq /1000 , volt); - } - -dev_dbg (cpu_dev,"%u MHz, %d mV --> %u MHz, %ld mV\n" , - old_freq /1000 , (volt_old >0 ) ? volt_old /1000 :-1 , - new_freq /1000 , volt ? volt /1000 :-1 ); - -/* scaling up? scale voltage before frequency */ -if (!IS_ERR (cpu_reg) && new_freq > old_freq) { - ret =regulator_set_voltage_tol (cpu_reg, volt, tol); -if (ret) { -dev_err (cpu_dev,"failed to scale voltage up: %d\n" , - ret); -return ret; - } - } - - ret =clk_set_rate (cpu_clk, freq_exact); -if (ret) { -dev_err (cpu_dev,"failed to set clock rate: %d\n" , ret); -if (!IS_ERR (cpu_reg) && volt_old >0 ) -regulator_set_voltage_tol (cpu_reg, volt_old, tol); -return ret; - } -/* scaling down? scale voltage after frequency */ -if (!IS_ERR (cpu_reg) && new_freq < old_freq) { - ret =regulator_set_voltage_tol (cpu_reg, volt, tol); -if (ret) { -dev_err (cpu_dev,"failed to scale voltage down: %d\n" , - ret); -clk_set_rate (cpu_clk, old_freq *1000 ); - } - } - -return ret; +return dev_pm_opp_set_rate (priv->cpu_dev, + policy->freq_table[index].frequency *1000 ); }/*
Signature · 1 line
2.7.1.370.gb2aa7f8
Viresh Kumar
maintainerPATCH v3 15/16 cpufreq: dt: No need to fetch voltage-tolerance
In reply to Viresh KumarIts already done by core and we don't need to get it anymore. And so, we don't need to get of node in cpufreq_init() anymore, move that to find_supply_name() instead.
--- drivers/cpufreq/cpufreq-dt.c | 46 ++++++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 23 deletions(-)
drivers/cpufreq/cpufreq-dt.c +23 -23
@@ -33,7 +33,6 @@ struct private_data {struct device *cpu_dev;struct regulator *cpu_reg;struct thermal_cooling_device *cdev; -unsigned int voltage_tolerance;/* in percentage */ const char *reg_name; };
@@ -55,24 +54,38 @@ static int set_target(struct cpufreq_policy *policy, unsigned int index)* An earlier version of opp-v1 bindings used to name the regulator * "cpu0-supply", we still need to handle that for backwards compatibility. */ -static const char *find_supply_name (struct device *dev,struct device_node *np) +static const char *find_supply_name (struct device *dev) { +struct device_node *np;struct property *pp;int cpu = dev->id; +const char *name =NULL ; + + np =of_node_get (dev->of_node); + +/* This must be valid for sure */ +if (WARN_ON (!np)) +return NULL ;/* Try "cpu0" for older DTs */ if (!cpu) { pp =of_find_property (np,"cpu0-supply" ,NULL ); -if (pp) -return "cpu0" ; +if (pp) { + name ="cpu0" ; + goto node_put; + } } pp =of_find_property (np,"cpu-supply" ,NULL ); -if (pp) -return "cpu" ; +if (pp) { + name ="cpu" ; + goto node_put; + }dev_dbg (dev,"no regulator for cpu%d\n" , cpu); -return NULL ; +node_put: +of_node_put (np); +return name; } static int allocate_resources(int cpu, struct device **cdev,
@@ -147,7 +160,6 @@ static int allocate_resources(int cpu, struct device **cdev,static int cpufreq_init (struct cpufreq_policy *policy) {struct cpufreq_frequency_table *freq_table; -struct device_node *np;struct private_data *priv;struct device *cpu_dev;struct regulator *cpu_reg;
@@ -164,13 +176,6 @@ static int cpufreq_init(struct cpufreq_policy *policy)return ret; } - np =of_node_get (cpu_dev->of_node); -if (!np) { -dev_err (cpu_dev,"failed to find cpu%d node\n" , policy->cpu); - ret = -ENOENT ; - goto out_put_reg_clk; - } -/* Get OPP-sharing information from "operating-points-v2" bindings */ ret =dev_pm_opp_of_get_sharing_cpus (cpu_dev, policy->cpus);if (ret) {
@@ -181,20 +186,20 @@ static int cpufreq_init(struct cpufreq_policy *policy)if (ret == -ENOENT ) opp_v1 = true;else - goto out_node_put; + goto out_put_reg_clk; }/* * OPP layer will be taking care of regulators now, but it needs to know * the name of the regulator first. */ - name =find_supply_name (cpu_dev, np); + name =find_supply_name (cpu_dev);if (name) { ret =dev_pm_opp_set_regulator (cpu_dev, name);if (ret) {dev_err (cpu_dev,"Failed to set regulator for cpu%d: %d\n" , policy->cpu, ret); - goto out_node_put; + goto out_put_reg_clk; } }
@@ -244,7 +249,6 @@ static int cpufreq_init(struct cpufreq_policy *policy) } priv->reg_name = name; -of_property_read_u32 (np,"voltage-tolerance" , &priv->voltage_tolerance); ret =dev_pm_opp_init_cpufreq_table (cpu_dev, &freq_table);if (ret) {
@@ -286,8 +290,6 @@ static int cpufreq_init(struct cpufreq_policy *policy) policy->cpuinfo.transition_latency = transition_latency; -of_node_put (np); -return 0 ; out_free_cpufreq_table:
@@ -298,8 +300,6 @@ static int cpufreq_init(struct cpufreq_policy *policy)dev_pm_opp_of_cpumask_remove_table (policy->cpus);if (name)dev_pm_opp_put_regulator (cpu_dev); -out_node_put: -of_node_put (np); out_put_reg_clk:clk_put (cpu_clk);if (!IS_ERR (cpu_reg))
Signature · 1 line
2.7.1.370.gb2aa7f8
Viresh Kumar
maintainerPATCH v3 16/16 cpufreq: dt: No need to allocate resources anymore
In reply to Viresh KumarOPP layer manages it now and cpufreq-dt driver doesn't need it. But, we still need to check for availability of resources for deferred probing.
--- drivers/cpufreq/cpufreq-dt.c | 112 +++++++++++++++++-------------------------- 1 file changed, 45 insertions(+), 67 deletions(-)
drivers/cpufreq/cpufreq-dt.c +45 -67
@@ -31,7 +31,6 @@struct private_data {struct device *cpu_dev; -struct regulator *cpu_reg;struct thermal_cooling_device *cdev;const char *reg_name; };
@@ -88,73 +87,59 @@ static const char *find_supply_name(struct device *dev)return name; } -static int allocate_resources (int cpu,struct device **cdev, -struct regulator **creg,struct clk **cclk) +static int resources_available (void ) {struct device *cpu_dev;struct regulator *cpu_reg;struct clk *cpu_clk;int ret =0 ; -char *reg_cpu0 ="cpu0" , *reg_cpu ="cpu" , *reg; +const char *name; - cpu_dev =get_cpu_device (cpu); + cpu_dev =get_cpu_device (0 );if (!cpu_dev) { -pr_err ("failed to get cpu%d device\n" , cpu); +pr_err ("failed to get cpu0 device\n" );return -ENODEV ; } -/* Try "cpu0" for older DTs */ -if (!cpu) - reg = reg_cpu0; -else - reg = reg_cpu; - -try_again: - cpu_reg =regulator_get_optional (cpu_dev, reg); - ret =PTR_ERR_OR_ZERO (cpu_reg); + cpu_clk =clk_get (cpu_dev,NULL ); + ret =PTR_ERR_OR_ZERO (cpu_clk);if (ret) {/* -* If cpu's regulator supply node is present, but regulator is -* not yet registered, we should try defering probe. +* If cpu's clk node is present, but clock is not yet +* registered, we should try defering probe. */ -if (ret == -EPROBE_DEFER ) { -dev_dbg (cpu_dev,"cpu%d regulator not ready, retry\n" , - cpu); -return ret; - } - -/* Try with "cpu-supply" */ -if (reg == reg_cpu0) { - reg = reg_cpu; - goto try_again; - } +if (ret == -EPROBE_DEFER ) +dev_dbg (cpu_dev,"clock not ready, retry\n" ); +else +dev_err (cpu_dev,"failed to get clock: %d\n" , ret); -dev_dbg (cpu_dev,"no regulator for cpu%d: %d\n" , cpu, ret); +return ret; } - cpu_clk =clk_get (cpu_dev,NULL ); - ret =PTR_ERR_OR_ZERO (cpu_clk); -if (ret) { -/* put regulator */ -if (!IS_ERR (cpu_reg)) -regulator_put (cpu_reg); +clk_put (cpu_clk); + + name =find_supply_name (cpu_dev); +/* Platform doesn't require regulator */ +if (!name) +return 0 ; + cpu_reg =regulator_get_optional (cpu_dev, name); + ret =PTR_ERR_OR_ZERO (cpu_reg); +if (ret) {/* -* If cpu's clk node is present, but clock is not yet -* registered, we should try defering probe. +* If cpu's regulator supply node is present, but regulator is +* not yet registered, we should try defering probe. */ if (ret == -EPROBE_DEFER ) -dev_dbg (cpu_dev,"cpu%d clock not ready, retry\n" , cpu); +dev_dbg (cpu_dev,"cpu0 regulator not ready, retry\n" );else -dev_err (cpu_dev,"failed to get cpu%d clock: %d\n" , cpu, - ret); - }else { - *cdev = cpu_dev; - *creg = cpu_reg; - *cclk = cpu_clk; +dev_dbg (cpu_dev,"no regulator for cpu0: %d\n" , ret); + +return ret; } -return ret; +regulator_put (cpu_reg); +return 0 ; }static int cpufreq_init (struct cpufreq_policy *policy)
@@ -162,7 +147,6 @@ static int cpufreq_init(struct cpufreq_policy *policy)struct cpufreq_frequency_table *freq_table;struct private_data *priv;struct device *cpu_dev; -struct regulator *cpu_reg;struct clk *cpu_clk;struct dev_pm_opp *suspend_opp;unsigned int transition_latency;
@@ -170,9 +154,16 @@ static int cpufreq_init(struct cpufreq_policy *policy)const char *name;int ret; - ret =allocate_resources (policy->cpu, &cpu_dev, &cpu_reg, &cpu_clk); -if (ret) { -pr_err ("%s: Failed to allocate resources: %d\n" , __func__, ret); + cpu_dev =get_cpu_device (policy->cpu); +if (!cpu_dev) { +pr_err ("failed to get cpu%d device\n" , policy->cpu); +return -ENODEV ; + } + + cpu_clk =clk_get (cpu_dev,NULL ); +if (IS_ERR (cpu_clk)) { + ret =PTR_ERR (cpu_clk); +dev_err (cpu_dev,"%s: failed to get clk: %d\n" , __func__, ret);return ret; }
@@ -186,7 +177,7 @@ static int cpufreq_init(struct cpufreq_policy *policy)if (ret == -ENOENT ) opp_v1 = true;else - goto out_put_reg_clk; + goto out_put_clk; }/*
@@ -199,7 +190,7 @@ static int cpufreq_init(struct cpufreq_policy *policy)if (ret) {dev_err (cpu_dev,"Failed to set regulator for cpu%d: %d\n" , policy->cpu, ret); - goto out_put_reg_clk; + goto out_put_clk; } }
@@ -257,9 +248,7 @@ static int cpufreq_init(struct cpufreq_policy *policy) } priv->cpu_dev = cpu_dev; - priv->cpu_reg = cpu_reg; policy->driver_data = priv; - policy->clk = cpu_clk;rcu_read_lock ();
@@ -300,10 +289,8 @@ static int cpufreq_init(struct cpufreq_policy *policy)dev_pm_opp_of_cpumask_remove_table (policy->cpus);if (name)dev_pm_opp_put_regulator (cpu_dev); -out_put_reg_clk: +out_put_clk:clk_put (cpu_clk); -if (!IS_ERR (cpu_reg)) -regulator_put (cpu_reg);return ret; }
@@ -319,8 +306,6 @@ static int cpufreq_exit(struct cpufreq_policy *policy)dev_pm_opp_put_regulator (priv->cpu_dev);clk_put (policy->clk); -if (!IS_ERR (priv->cpu_reg)) -regulator_put (priv->cpu_reg);kfree (priv);return 0 ;
@@ -373,9 +358,6 @@ static struct cpufreq_driver dt_cpufreq_driver = {static int dt_cpufreq_probe (struct platform_device *pdev) { -struct device *cpu_dev; -struct regulator *cpu_reg; -struct clk *cpu_clk;int ret;/*
@@ -385,19 +367,15 @@ static int dt_cpufreq_probe(struct platform_device *pdev)* * FIXME: Is checking this only for CPU0 sufficient ? */ - ret =allocate_resources (0 , &cpu_dev, &cpu_reg, &cpu_clk); + ret =resources_available ();if (ret)return ret; -clk_put (cpu_clk); -if (!IS_ERR (cpu_reg)) -regulator_put (cpu_reg); - dt_cpufreq_driver.driver_data =dev_get_platdata (&pdev->dev); ret =cpufreq_register_driver (&dt_cpufreq_driver);if (ret) -dev_err (cpu_dev,"failed register driver: %d\n" , ret); +dev_err (&pdev->dev,"failed register driver: %d\n" , ret);return ret; }
Signature · 1 line
2.7.1.370.gb2aa7f8
Re: [PATCH V3 16/16] cpufreq: dt: No need to allocate resources anymore
Quoted 5 lines · Viresh Kumar
OPP layer manages it now and cpufreq-dt driver doesn't need it. But, we still need to check for availability of resources for deferred probing.
Signed-off-by Viresh Kumar <[email protected]>---
Signature · 2 lines
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project
Rafael J. Wysocki
maintainerQuoted 45 lines · Viresh Kumar
Hi Guys,
This patchset add APIs in OPP layer to allow OPPs transitioning from within OPP layer. Currently all OPP users need to replicate the same code to switch between OPPs. While the same can be handled easily by OPP-core.
The first 7 patches update the OPP core to introduce the new APIs and the next Nine patches update cpufreq-dt for the same.
11 out of 17 are already Reviewed by Stephen, only few are left :)
I hope this is the last version of the series :)
Testing:
- Tested on exynos 5250-arndale (dual-cortex-A15)
- Tested for both Old-V1 bindings and New V2 bindings
- Tested with regulator names as: 'cpu-supply' and 'cpu0-supply'
- Tested with Unsupported supply ranges as well, to check the opp-disable logic
V2->V3:
- Very minor updates.
-find_supply_name()doesn't return an error value now, and so its callers don't check for it.
- And so we don't need to initialize name to NULLViresh Kumar (16):
PM / OPP: get/put regulators from OPP core
PM / OPP: Disable OPPs that aren't supported by the regulator PM / OPP: Introducedev_pm_opp_get_max_volt_latency()
PM / OPP: Introducedev_pm_opp_get_max_transition_latency()PM / OPP: Parse clock-latency and voltage-tolerance for v1 bindings PM / OPP: Manage device clk
PM / OPP: Adddev_pm_opp_set_rate()
cpufreq: dt: Convert few pr_debug/err() calls to dev_dbg/err()
cpufreq: dt: Rename 'need_update' to 'opp_v1'
cpufreq: dt: OPP layers handles clock-latency for V1 bindings as well
cpufreq: dt: Pass regulator name to the OPP core
cpufreq: dt: Unsupported OPPs are already disabled
cpufreq: dt: Reusedev_pm_opp_get_max_transition_latency()
cpufreq: dt: Usedev_pm_opp_set_rate()to switch frequency
cpufreq: dt: No need to fetch voltage-tolerance
cpufreq: dt: No need to allocate resources anymore
Quoted 5 lines
drivers/base/power/opp/core.c | 420 ++++++++++++++++++++++++++++++++++++++++++ drivers/base/power/opp/opp.h | 13 ++ drivers/cpufreq/cpufreq-dt.c | 300 +++++++++++------------------- include/linux/pm_opp.h | 27 +++ 4 files changed, 565 insertions(+), 195 deletions(-)
Since this has all been reviewed now, I've applied the series (and it has broken something already in linux-next, JFTR).
Thanks,
Rafael