[PATCH 1/8] cpufreq: arm_big_little: add cluster regulator support
Add cluster regulator support as a preparation to adding generic arm_big_little_dt cpufreq_dt driver support for ODROID-XU3 board. This allows arm_big_little[_dt] driver to set not only the frequency but also the voltage (which is obtained from operating point's voltage value) for CPU clusters. Cc: Kukjin Kim Cc: Doug Anderson Cc: Javier Martinez Canillas Cc: Andreas Faerber Cc: Sachin Kamat Cc: Thomas Abraham Signed-off-by: Bartlomiej Zolnierkiewicz --- .../bindings/cpufreq/arm_big_little_dt.txt |4 + drivers/cpufreq/arm_big_little.c | 153 +--- 2 files changed, 139 insertions(+), 18 deletions(-) diff --git a/Documentation/devicetree/bindings/cpufreq/arm_big_little_dt.txt b/Documentation/devicetree/bindings/cpufreq/arm_big_little_dt.txt index 0715695..8ca4a12 100644 --- a/Documentation/devicetree/bindings/cpufreq/arm_big_little_dt.txt +++ b/Documentation/devicetree/bindings/cpufreq/arm_big_little_dt.txt @@ -18,6 +18,10 @@ Required properties: Optional properties: - clock-latency: Specify the possible maximum transition latency for clock, in unit of nanoseconds. +- cpu-cluster.0-supply: Provides the regulator node supplying voltage to CPU + cluster 0. +- cpu-cluster.1-supply: Provides the regulator node supplying voltage to CPU + cluster 1. Examples: diff --git a/drivers/cpufreq/arm_big_little.c b/drivers/cpufreq/arm_big_little.c index e1a6ba6..edb461b 100644 --- a/drivers/cpufreq/arm_big_little.c +++ b/drivers/cpufreq/arm_big_little.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include "arm_big_little.h" @@ -54,6 +55,9 @@ static bool bL_switching_enabled; static struct cpufreq_arm_bL_ops *arm_bL_ops; static struct clk *clk[MAX_CLUSTERS]; +static struct regulator *reg[MAX_CLUSTERS]; +static struct device *cpu_devs[MAX_CLUSTERS]; +static int transition_latencies[MAX_CLUSTERS]; static struct cpufreq_frequency_table *freq_table[MAX_CLUSTERS + 1]; static atomic_t cluster_usage[MAX_CLUSTERS + 1]; @@ -122,7 +126,76 @@ static unsigned int bL_cpufreq_get_rate(unsigned int cpu) } } -static unsigned int +static int +bL_cpufreq_set_rate_cluster(u32 cpu, u32 cluster, u32 new_rate) +{ + unsigned long volt = 0, volt_old = 0; + long freq_Hz; + u32 old_rate; + int ret; + + freq_Hz = new_rate * 1000; + old_rate = clk_get_rate(clk[cluster]) / 1000; + + if (!IS_ERR(reg[cluster])) { + struct dev_pm_opp *opp; + unsigned long opp_freq; + + rcu_read_lock(); + opp = dev_pm_opp_find_freq_ceil(cpu_devs[cluster], &freq_Hz); + if (IS_ERR(opp)) { + rcu_read_unlock(); + pr_err("%s: cpu %d, cluster: %d, failed to find OPP for %ld\n", + __func__, cpu, cluster, freq_Hz); + return PTR_ERR(opp); + } + volt = dev_pm_opp_get_voltage(opp); + opp_freq = dev_pm_opp_get_freq(opp); + rcu_read_unlock(); + volt_old = regulator_get_voltage(reg[cluster]); + pr_debug("%s: cpu %d, cluster: %d, Found OPP: %ld kHz, %ld uV\n", + __func__, cpu, cluster, opp_freq / 1000, volt); + } + + pr_debug("%s: cpu %d, cluster: %d, %u MHz, %ld mV --> %u MHz, %ld mV\n", + __func__, cpu, cluster, + old_rate / 1000, (volt_old > 0) ? volt_old / 1000 : -1, + new_rate / 1000, volt ? volt / 1000 : -1); + + /* scaling up? scale voltage before frequency */ + if (!IS_ERR(reg[cluster]) && new_rate > old_rate) { + ret = regulator_set_voltage_tol(reg[cluster], volt, 0); + if (ret) { + pr_err("%s: cpu: %d, cluster: %d, failed to scale voltage up: %d\n", + __func__, cpu, cluster, ret); + return ret; + } + } + + ret = clk_set_rate(clk[cluster], new_rate * 1000); + if (WARN_ON(ret)) { + pr_err("%s: clk_set_rate failed: %d, cluster: %d\n", + __func__, cluster, ret); + if (!IS_ERR(reg[cluster]) && volt_old > 0) + regulator_set_voltage_tol(reg[cluster], volt_old, 0); + return ret; + } + + /* scaling down? scale voltage after frequency */ + if (!IS_ERR(reg[cluster]) && new_rate < old_rate) { + ret = regulator_set_voltage_tol(reg[cluster], volt, 0); + if (ret) { + pr_err("%s: cpu: %d, cluster: %d, failed to scale voltage down: %d\n", + __func__, cpu, cluster, ret); + clk_set_rate(clk[cluster], old_rate * 1000); + return ret; + } + } + + return 0; +} + +static int bL_cpufreq_set_rate(u32 cpu, u32 old_cluster, u32
Re: [PATCH 1/8] cpufreq: arm_big_little: add cluster regulator support
Hi Bartlomiej, > Add cluster regulator support as a preparation to adding > generic arm_big_little_dt cpufreq_dt driver support for > ODROID-XU3 board. This allows arm_big_little[_dt] driver > to set not only the frequency but also the voltage (which > is obtained from operating point's voltage value) for CPU > clusters. > > Cc: Kukjin Kim > Cc: Doug Anderson > Cc: Javier Martinez Canillas > Cc: Andreas Faerber > Cc: Sachin Kamat > Cc: Thomas Abraham > Signed-off-by: Bartlomiej Zolnierkiewicz > --- > .../bindings/cpufreq/arm_big_little_dt.txt |4 + > drivers/cpufreq/arm_big_little.c | 153 > +--- 2 files changed, 139 insertions(+), 18 > deletions(-) > > diff --git > a/Documentation/devicetree/bindings/cpufreq/arm_big_little_dt.txt > b/Documentation/devicetree/bindings/cpufreq/arm_big_little_dt.txt > index 0715695..8ca4a12 100644 --- > a/Documentation/devicetree/bindings/cpufreq/arm_big_little_dt.txt +++ > b/Documentation/devicetree/bindings/cpufreq/arm_big_little_dt.txt @@ > -18,6 +18,10 @@ Required properties: Optional properties: > - clock-latency: Specify the possible maximum transition latency for > clock, in unit of nanoseconds. > +- cpu-cluster.0-supply: Provides the regulator node supplying > voltage to CPU > + cluster 0. > +- cpu-cluster.1-supply: Provides the regulator node supplying > voltage to CPU > + cluster 1. > > Examples: > > diff --git a/drivers/cpufreq/arm_big_little.c > b/drivers/cpufreq/arm_big_little.c index e1a6ba6..edb461b 100644 > --- a/drivers/cpufreq/arm_big_little.c > +++ b/drivers/cpufreq/arm_big_little.c > @@ -31,6 +31,7 @@ > #include > #include > #include > +#include > #include > > #include "arm_big_little.h" > @@ -54,6 +55,9 @@ static bool bL_switching_enabled; > > static struct cpufreq_arm_bL_ops *arm_bL_ops; > static struct clk *clk[MAX_CLUSTERS]; > +static struct regulator *reg[MAX_CLUSTERS]; > +static struct device *cpu_devs[MAX_CLUSTERS]; > +static int transition_latencies[MAX_CLUSTERS]; > static struct cpufreq_frequency_table *freq_table[MAX_CLUSTERS + 1]; > static atomic_t cluster_usage[MAX_CLUSTERS + 1]; > > @@ -122,7 +126,76 @@ static unsigned int bL_cpufreq_get_rate(unsigned > int cpu) } > } > > -static unsigned int > +static int > +bL_cpufreq_set_rate_cluster(u32 cpu, u32 cluster, u32 new_rate) > +{ > + unsigned long volt = 0, volt_old = 0; > + long freq_Hz; > + u32 old_rate; > + int ret; > + > + freq_Hz = new_rate * 1000; > + old_rate = clk_get_rate(clk[cluster]) / 1000; > + > + if (!IS_ERR(reg[cluster])) { > + struct dev_pm_opp *opp; > + unsigned long opp_freq; > + > + rcu_read_lock(); > + opp = dev_pm_opp_find_freq_ceil(cpu_devs[cluster], > &freq_Hz); > + if (IS_ERR(opp)) { > + rcu_read_unlock(); > + pr_err("%s: cpu %d, cluster: %d, failed to > find OPP for %ld\n", > + __func__, cpu, cluster, freq_Hz); > + return PTR_ERR(opp); > + } > + volt = dev_pm_opp_get_voltage(opp); > + opp_freq = dev_pm_opp_get_freq(opp); > + rcu_read_unlock(); > + volt_old = regulator_get_voltage(reg[cluster]); > + pr_debug("%s: cpu %d, cluster: %d, Found OPP: %ld > kHz, %ld uV\n", > + __func__, cpu, cluster, opp_freq / 1000, > volt); > + } > + > + pr_debug("%s: cpu %d, cluster: %d, %u MHz, %ld mV --> %u > MHz, %ld mV\n", > + __func__, cpu, cluster, > + old_rate / 1000, (volt_old > 0) ? volt_old / 1000 : > -1, > + new_rate / 1000, volt ? volt / 1000 : -1); > + > + /* scaling up? scale voltage before frequency */ > + if (!IS_ERR(reg[cluster]) && new_rate > old_rate) { > + ret = regulator_set_voltage_tol(reg[cluster], volt, > 0); > + if (ret) { > + pr_err("%s: cpu: %d, cluster: %d, failed to > scale voltage up: %d\n", > + __func__, cpu, cluster, ret); > + return ret; > + } > + } > + > + ret = clk_set_rate(clk[cluster], new_rate * 1000); > + if (WARN_ON(ret)) { > + pr_err("%s: clk_set_rate failed: %d, cluster: %d\n", > + __func__, cluster, ret); > + if (!IS_ERR(reg[cluster]) && volt_old > 0) > + regulator_set_voltage_tol(reg[cluster], > volt_old, 0); > + return ret; > + } > + > + /* scaling down? scale voltage after frequency */ > + if (!IS_ERR(reg[cluster]) && new_rate < old_rate) { > + ret = regulator_set_voltage_tol(reg[cluster], volt, > 0); > + if (ret) { > + pr_err("%s: cpu: %d, cluster: %d, failed to > scale voltage down: %d\n", > + __func__, cpu, cluster, ret); > + clk_set_rate(clk[cluster],
Re: [PATCH 1/8] cpufreq: arm_big_little: add cluster regulator support
On 21 April 2015 at 18:47, Bartlomiej Zolnierkiewicz wrote: > Add cluster regulator support as a preparation to adding > generic arm_big_little_dt cpufreq_dt driver support for > ODROID-XU3 board. This allows arm_big_little[_dt] driver This is irrelevant here, its not about XU3 but any board that wants to use it.. > to set not only the frequency but also the voltage (which > is obtained from operating point's voltage value) for CPU > clusters. > > Cc: Kukjin Kim > Cc: Doug Anderson > Cc: Javier Martinez Canillas > Cc: Andreas Faerber > Cc: Sachin Kamat > Cc: Thomas Abraham > Signed-off-by: Bartlomiej Zolnierkiewicz > --- > .../bindings/cpufreq/arm_big_little_dt.txt |4 + > drivers/cpufreq/arm_big_little.c | 153 > +--- > 2 files changed, 139 insertions(+), 18 deletions(-) > > diff --git a/Documentation/devicetree/bindings/cpufreq/arm_big_little_dt.txt > b/Documentation/devicetree/bindings/cpufreq/arm_big_little_dt.txt > index 0715695..8ca4a12 100644 > --- a/Documentation/devicetree/bindings/cpufreq/arm_big_little_dt.txt > +++ b/Documentation/devicetree/bindings/cpufreq/arm_big_little_dt.txt > @@ -18,6 +18,10 @@ Required properties: > Optional properties: > - clock-latency: Specify the possible maximum transition latency for clock, >in unit of nanoseconds. > +- cpu-cluster.0-supply: Provides the regulator node supplying voltage to CPU > + cluster 0. > +- cpu-cluster.1-supply: Provides the regulator node supplying voltage to CPU > + cluster 1. I don't think you need these.. http://permalink.gmane.org/gmane.linux.power-management.general/58548 > Examples: > > diff --git a/drivers/cpufreq/arm_big_little.c > b/drivers/cpufreq/arm_big_little.c > index e1a6ba6..edb461b 100644 > --- a/drivers/cpufreq/arm_big_little.c > +++ b/drivers/cpufreq/arm_big_little.c > @@ -31,6 +31,7 @@ > #include > #include > #include > +#include > #include > > #include "arm_big_little.h" > @@ -54,6 +55,9 @@ static bool bL_switching_enabled; > > static struct cpufreq_arm_bL_ops *arm_bL_ops; > static struct clk *clk[MAX_CLUSTERS]; > +static struct regulator *reg[MAX_CLUSTERS]; > +static struct device *cpu_devs[MAX_CLUSTERS]; > +static int transition_latencies[MAX_CLUSTERS]; > static struct cpufreq_frequency_table *freq_table[MAX_CLUSTERS + 1]; > static atomic_t cluster_usage[MAX_CLUSTERS + 1]; > > @@ -122,7 +126,76 @@ static unsigned int bL_cpufreq_get_rate(unsigned int cpu) > } > } > > -static unsigned int > +static int > +bL_cpufreq_set_rate_cluster(u32 cpu, u32 cluster, u32 new_rate) > +{ > + unsigned long volt = 0, volt_old = 0; > + long freq_Hz; > + u32 old_rate; > + int ret; > + > + freq_Hz = new_rate * 1000; > + old_rate = clk_get_rate(clk[cluster]) / 1000; > + > + if (!IS_ERR(reg[cluster])) { > + struct dev_pm_opp *opp; > + unsigned long opp_freq; > + > + rcu_read_lock(); > + opp = dev_pm_opp_find_freq_ceil(cpu_devs[cluster], &freq_Hz); > + if (IS_ERR(opp)) { > + rcu_read_unlock(); > + pr_err("%s: cpu %d, cluster: %d, failed to find OPP > for %ld\n", > + __func__, cpu, cluster, freq_Hz); > + return PTR_ERR(opp); > + } > + volt = dev_pm_opp_get_voltage(opp); > + opp_freq = dev_pm_opp_get_freq(opp); > + rcu_read_unlock(); > + volt_old = regulator_get_voltage(reg[cluster]); > + pr_debug("%s: cpu %d, cluster: %d, Found OPP: %ld kHz, %ld > uV\n", > + __func__, cpu, cluster, opp_freq / 1000, volt); > + } > + > + pr_debug("%s: cpu %d, cluster: %d, %u MHz, %ld mV --> %u MHz, %ld > mV\n", > + __func__, cpu, cluster, > + old_rate / 1000, (volt_old > 0) ? volt_old / 1000 : -1, > + new_rate / 1000, volt ? volt / 1000 : -1); > + > + /* scaling up? scale voltage before frequency */ > + if (!IS_ERR(reg[cluster]) && new_rate > old_rate) { > + ret = regulator_set_voltage_tol(reg[cluster], volt, 0); > + if (ret) { > + pr_err("%s: cpu: %d, cluster: %d, failed to scale > voltage up: %d\n", > + __func__, cpu, cluster, ret); > + return ret; > + } > + } > + > + ret = clk_set_rate(clk[cluster], new_rate * 1000); > + if (WARN_ON(ret)) { > + pr_err("%s: clk_set_rate failed: %d, cluster: %d\n", > + __func__, cluster, ret); > + if (!IS_ERR(reg[cluster]) && volt_old > 0) > + regulator_set_voltage_tol(reg[cluster], volt_old, 0); > + return ret; > + } > + > + /* scaling down? scale voltage after frequency */ > + if (!IS_ERR(reg[cluster]) && new_rate < old_ra
Re: [PATCH 1/8] cpufreq: arm_big_little: add cluster regulator support
Hi, Am Dienstag, 21. April 2015, 15:17:51 schrieb Bartlomiej Zolnierkiewicz: > Add cluster regulator support as a preparation to adding > generic arm_big_little_dt cpufreq_dt driver support for > ODROID-XU3 board. This allows arm_big_little[_dt] driver > to set not only the frequency but also the voltage (which > is obtained from operating point's voltage value) for CPU > clusters. > > Cc: Kukjin Kim > Cc: Doug Anderson > Cc: Javier Martinez Canillas > Cc: Andreas Faerber > Cc: Sachin Kamat > Cc: Thomas Abraham > Signed-off-by: Bartlomiej Zolnierkiewicz I gave this a spin on the rk3368 arm64 soc from Rockchip, mainly to check if my armclk handling was correct. Your patch here only supports individual supplies per cluster but my current board shares the supplies over both cpu clusters, so I've cooked up a patch to also try to support shared supplies [0]. Nevertheless, Tested-by: Heiko Stuebner Do you plan to continue working on this? Thanks Heiko [0] 8< - From: Heiko Stuebner Subject: [PATCH] cpufreq: arm_big_little: add support for shared cluster regulators In some socs or board designs the supplying regulator is shared between more than one cluster but the current regulator support for big_little sets the target voltage without any tolerance. So when cluster0 requests 0.9V and cluster1 1.3V no suitable frequency span is available that fits both. To accomodate this, look for shared regulators and calculate the maximum voltage necessary. If the regulator of the remote cluster has a lower voltage, its maximum also gets increased. If cluster supplies are not shared, the behaviour is the same as before with one specific voltage being set instead of a voltage-range. When adapting shared voltages the remote clusters need to be locked too, because cpufreq can very well try to change more than one cluster at the same time. While the used mutex_trylock prevents deadlocks reliably, it might also prevent some (or a lot) frequency changes from succeeding: lock cluster0 lock cluster1 trylock cluster1 trylock cluster0 both fail I'm probably simply overlooking some better way currently. Signed-off-by: Heiko Stuebner --- drivers/cpufreq/arm_big_little.c | 102 ++- 1 file changed, 91 insertions(+), 11 deletions(-) diff --git a/drivers/cpufreq/arm_big_little.c b/drivers/cpufreq/arm_big_little.c index e04ca0c..c65b111 100644 --- a/drivers/cpufreq/arm_big_little.c +++ b/drivers/cpufreq/arm_big_little.c @@ -130,12 +130,78 @@ static unsigned int bL_cpufreq_get_rate(unsigned int cpu) } static int +bL_adapt_shared_regulators(u32 cluster, unsigned long *volt_max) +{ + unsigned long other_volt; + int ret, i; + + for (i = 0; i < MAX_CLUSTERS; i++) { + if (i == cluster || IS_ERR_OR_NULL(reg[i])) + continue; + + if (regulator_is_match(reg[cluster], reg[i])) { + other_volt = regulator_get_voltage(reg[i]); + if (other_volt > *volt_max) { + *volt_max = other_volt; + } else { + pr_debug("%s: adapting shared regulator in cluster %d to %lu-%lu mV\n", +__func__, i, other_volt / 1000, *volt_max / 1000); + ret = regulator_set_voltage(reg[i], other_volt, *volt_max); + if (ret) { + pr_err("%s: shared-supply for cluster: %d, failed to scale voltage up: %d\n", + __func__, cluster, ret); + return ret; + } + } + } + } + + return 0; +} + +static int +bL_lock_shared_regulators(u32 cluster) +{ + int ret, i; + + for (i = 0; i < MAX_CLUSTERS; i++) { + if (i == cluster || IS_ERR_OR_NULL(reg[i])) + continue; + + if (regulator_is_match(reg[cluster], reg[i])) { + ret = mutex_trylock(&cluster_lock[i]); + if (!ret) { + for (i--; i >= 0; i--) + mutex_unlock(&cluster_lock[i]); + return -EBUSY; + } + } + } + + return 0; +} + +static void +bL_unlock_shared_regulators(u32 cluster) +{ + int i; + + for (i = 0; i < MAX_CLUSTERS; i++) { + if (i == cluster || IS_ERR_OR_NULL(reg[i])) + continue; + + if (regulator_is_match(reg[cluster], reg[i])) + mutex_unlock(&cluster_lock[i]); + } +} + +static int bL_cpufreq_set_rate_cluster(u32 c