diff mbox

[1/8] cpufreq: arm_big_little: add cluster regulator support

Message ID 1429622278-12216-2-git-send-email-b.zolnierkie@samsung.com (mailing list archive)
State New, archived
Headers show

Commit Message

Bartlomiej Zolnierkiewicz April 21, 2015, 1:17 p.m. UTC
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 <kgene.kim@samsung.com>
Cc: Doug Anderson <dianders@chromium.org>
Cc: Javier Martinez Canillas <javier.martinez@collabora.co.uk>
Cc: Andreas Faerber <afaerber@suse.de>
Cc: Sachin Kamat <sachin.kamat@linaro.org>
Cc: Thomas Abraham <thomas.ab@samsung.com>
Signed-off-by: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
---
 .../bindings/cpufreq/arm_big_little_dt.txt         |    4 +
 drivers/cpufreq/arm_big_little.c                   |  153 +++++++++++++++++---
 2 files changed, 139 insertions(+), 18 deletions(-)

Comments

Lukasz Majewski April 22, 2015, 8:07 a.m. UTC | #1
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 <kgene.kim@samsung.com>
> Cc: Doug Anderson <dianders@chromium.org>
> Cc: Javier Martinez Canillas <javier.martinez@collabora.co.uk>
> Cc: Andreas Faerber <afaerber@suse.de>
> Cc: Sachin Kamat <sachin.kamat@linaro.org>
> Cc: Thomas Abraham <thomas.ab@samsung.com>
> Signed-off-by: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
> ---
>  .../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 <linux/slab.h>
>  #include <linux/topology.h>
>  #include <linux/types.h>
> +#include <linux/regulator/consumer.h>
>  #include <asm/bL_switcher.h>
>  
>  #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 new_cluster, u32
> rate) {
>  	u32 new_rate, prev_rate;
> @@ -145,22 +218,17 @@ bL_cpufreq_set_rate(u32 cpu, u32 old_cluster,
> u32 new_cluster, u32 rate) pr_debug("%s: cpu: %d, old cluster: %d,
> new cluster: %d, freq: %d\n", __func__, cpu, old_cluster,
> new_cluster, new_rate); 
> -	ret = clk_set_rate(clk[new_cluster], new_rate * 1000);
> -	if (WARN_ON(ret)) {
> -		pr_err("clk_set_rate failed: %d, new cluster: %d\n",
> ret,
> -				new_cluster);
> -		if (bLs) {
> -			per_cpu(cpu_last_req_freq, cpu) = prev_rate;
> -			per_cpu(physical_cluster, cpu) = old_cluster;
> -		}
> -
> -		mutex_unlock(&cluster_lock[new_cluster]);
> -
> -		return ret;
> +	ret = bL_cpufreq_set_rate_cluster(cpu, new_cluster,
> new_rate);
> +	if (ret && bLs) {
> +		per_cpu(cpu_last_req_freq, cpu) = prev_rate;
> +		per_cpu(physical_cluster, cpu) = old_cluster;
>  	}
>  
>  	mutex_unlock(&cluster_lock[new_cluster]);
>  
> +	if (ret)
> +		return ret;
> +
>  	/* Recalc freq for old cluster when switching clusters */
>  	if (old_cluster != new_cluster) {
>  		pr_debug("%s: cpu: %d, old cluster: %d, new cluster:
> %d\n", @@ -174,14 +242,11 @@ bL_cpufreq_set_rate(u32 cpu, u32
> old_cluster, u32 new_cluster, u32 rate) /* Set freq of old cluster if
> there are cpus left on it */ new_rate =
> find_cluster_maxfreq(old_cluster); new_rate =
> ACTUAL_FREQ(old_cluster, new_rate); -
>  		if (new_rate) {
>  			pr_debug("%s: Updating rate of old cluster:
> %d, to freq: %d\n", __func__, old_cluster, new_rate);
>  
> -			if (clk_set_rate(clk[old_cluster], new_rate
> * 1000))
> -				pr_err("%s: clk_set_rate failed: %d,
> old cluster: %d\n",
> -						__func__, ret,
> old_cluster);
> +			bL_cpufreq_set_rate_cluster(cpu,
> old_cluster, new_rate); }
>  		mutex_unlock(&cluster_lock[old_cluster]);
>  	}
> @@ -288,6 +353,8 @@ static void
> _put_cluster_clk_and_freq_table(struct device *cpu_dev) return;
>  
>  	clk_put(clk[cluster]);
> +	if (!IS_ERR(reg[cluster]))
> +		regulator_put(reg[cluster]);
>  	dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table[cluster]);
>  	if (arm_bL_ops->free_opp_table)
>  		arm_bL_ops->free_opp_table(cpu_dev);
> @@ -321,6 +388,7 @@ static void put_cluster_clk_and_freq_table(struct
> device *cpu_dev) 
>  static int _get_cluster_clk_and_freq_table(struct device *cpu_dev)
>  {
> +	unsigned long min_uV = ~0, max_uV = 0;
>  	u32 cluster = raw_cpu_to_cluster(cpu_dev->id);
>  	char name[14] = "cpu-cluster.";
>  	int ret;
> @@ -335,6 +403,51 @@ static int
> _get_cluster_clk_and_freq_table(struct device *cpu_dev) goto out;
>  	}
>  
> +	name[12] = cluster + '0';
> +	reg[cluster] = regulator_get_optional(cpu_dev, name);
> +	if (!IS_ERR(reg[cluster])) {
> +		unsigned long opp_freq = 0;
> +
> +		dev_dbg(cpu_dev, "%s: reg: %p, cluster: %d\n",
> +			__func__, reg[cluster], cluster);
> +		cpu_devs[cluster] = cpu_dev;
> +
> +		/*
> +		 * 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;
> +
> +			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();
> +
> +			if
> (regulator_is_supported_voltage(reg[cluster], opp_uV,
> +							   opp_uV)) {
> +				if (opp_uV < min_uV)
> +					min_uV = opp_uV;
> +				if (opp_uV > max_uV)
> +					max_uV = opp_uV;
> +			} else {
> +				dev_pm_opp_disable(cpu_dev,
> opp_freq);
> +			}
> +
> +			opp_freq++;
> +		}
> +
> +		ret = regulator_set_voltage_time(reg[cluster],
> min_uV, max_uV);
> +		if (ret > 0)
> +			transition_latencies[cluster] = ret * 1000;
> +	}
> +
>  	ret = dev_pm_opp_init_cpufreq_table(cpu_dev,
> &freq_table[cluster]); if (ret) {
>  		dev_err(cpu_dev, "%s: failed to init cpufreq table,
> cpu: %d, err: %d\n", @@ -342,7 +455,6 @@ static int
> _get_cluster_clk_and_freq_table(struct device *cpu_dev) goto
> free_opp_table; }
>  
> -	name[12] = cluster + '0';
>  	clk[cluster] = clk_get(cpu_dev, name);
>  	if (!IS_ERR(clk[cluster])) {
>  		dev_dbg(cpu_dev, "%s: clk: %p & freq table: %p,
> cluster: %d\n", @@ -469,6 +581,11 @@ static int
> bL_cpufreq_init(struct cpufreq_policy *policy) else
>  		policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL;
>  
> +	if (cur_cluster < MAX_CLUSTERS &&
> +	    policy->cpuinfo.transition_latency != CPUFREQ_ETERNAL)
> +		policy->cpuinfo.transition_latency
> +			+= transition_latencies[cur_cluster];
> +
>  	if (is_bL_switching_enabled())
>  		per_cpu(cpu_last_req_freq, policy->cpu) =
> clk_get_cpu_rate(policy->cpu); 

Reviewed-by: Lukasz Majewski <l.majewski@samsung.com>
Viresh Kumar April 27, 2015, 5:15 a.m. UTC | #2
On 21 April 2015 at 18:47, Bartlomiej Zolnierkiewicz
<b.zolnierkie@samsung.com> 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 <kgene.kim@samsung.com>
> Cc: Doug Anderson <dianders@chromium.org>
> Cc: Javier Martinez Canillas <javier.martinez@collabora.co.uk>
> Cc: Andreas Faerber <afaerber@suse.de>
> Cc: Sachin Kamat <sachin.kamat@linaro.org>
> Cc: Thomas Abraham <thomas.ab@samsung.com>
> Signed-off-by: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
> ---
>  .../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 <linux/slab.h>
>  #include <linux/topology.h>
>  #include <linux/types.h>
> +#include <linux/regulator/consumer.h>
>  #include <asm/bL_switcher.h>
>
>  #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

If you want to make such fixes, please add them separately.

>  bL_cpufreq_set_rate(u32 cpu, u32 old_cluster, u32 new_cluster, u32 rate)
>  {
>         u32 new_rate, prev_rate;
> @@ -145,22 +218,17 @@ bL_cpufreq_set_rate(u32 cpu, u32 old_cluster, u32 new_cluster, u32 rate)
>         pr_debug("%s: cpu: %d, old cluster: %d, new cluster: %d, freq: %d\n",
>                         __func__, cpu, old_cluster, new_cluster, new_rate);
>
> -       ret = clk_set_rate(clk[new_cluster], new_rate * 1000);
> -       if (WARN_ON(ret)) {
> -               pr_err("clk_set_rate failed: %d, new cluster: %d\n", ret,
> -                               new_cluster);
> -               if (bLs) {
> -                       per_cpu(cpu_last_req_freq, cpu) = prev_rate;
> -                       per_cpu(physical_cluster, cpu) = old_cluster;
> -               }
> -
> -               mutex_unlock(&cluster_lock[new_cluster]);
> -
> -               return ret;
> +       ret = bL_cpufreq_set_rate_cluster(cpu, new_cluster, new_rate);
> +       if (ret && bLs) {
> +               per_cpu(cpu_last_req_freq, cpu) = prev_rate;
> +               per_cpu(physical_cluster, cpu) = old_cluster;
>         }
>
>         mutex_unlock(&cluster_lock[new_cluster]);
>
> +       if (ret)
> +               return ret;
> +
>         /* Recalc freq for old cluster when switching clusters */
>         if (old_cluster != new_cluster) {
>                 pr_debug("%s: cpu: %d, old cluster: %d, new cluster: %d\n",
> @@ -174,14 +242,11 @@ bL_cpufreq_set_rate(u32 cpu, u32 old_cluster, u32 new_cluster, u32 rate)
>                 /* Set freq of old cluster if there are cpus left on it */
>                 new_rate = find_cluster_maxfreq(old_cluster);
>                 new_rate = ACTUAL_FREQ(old_cluster, new_rate);
> -

??

>                 if (new_rate) {
>                         pr_debug("%s: Updating rate of old cluster: %d, to freq: %d\n",
>                                         __func__, old_cluster, new_rate);
>
> -                       if (clk_set_rate(clk[old_cluster], new_rate * 1000))
> -                               pr_err("%s: clk_set_rate failed: %d, old cluster: %d\n",
> -                                               __func__, ret, old_cluster);
> +                       bL_cpufreq_set_rate_cluster(cpu, old_cluster, new_rate);
>                 }
>                 mutex_unlock(&cluster_lock[old_cluster]);
>         }
> @@ -288,6 +353,8 @@ static void _put_cluster_clk_and_freq_table(struct device *cpu_dev)
>                 return;
>
>         clk_put(clk[cluster]);
> +       if (!IS_ERR(reg[cluster]))
> +               regulator_put(reg[cluster]);
>         dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table[cluster]);
>         if (arm_bL_ops->free_opp_table)
>                 arm_bL_ops->free_opp_table(cpu_dev);
> @@ -321,6 +388,7 @@ static void put_cluster_clk_and_freq_table(struct device *cpu_dev)
>
>  static int _get_cluster_clk_and_freq_table(struct device *cpu_dev)
>  {
> +       unsigned long min_uV = ~0, max_uV = 0;
>         u32 cluster = raw_cpu_to_cluster(cpu_dev->id);
>         char name[14] = "cpu-cluster.";
>         int ret;
> @@ -335,6 +403,51 @@ static int _get_cluster_clk_and_freq_table(struct device *cpu_dev)
>                 goto out;
>         }
>
> +       name[12] = cluster + '0';
> +       reg[cluster] = regulator_get_optional(cpu_dev, name);

Just pass NULL instead of name.

> +       if (!IS_ERR(reg[cluster])) {
> +               unsigned long opp_freq = 0;
> +
> +               dev_dbg(cpu_dev, "%s: reg: %p, cluster: %d\n",
> +                       __func__, reg[cluster], cluster);
> +               cpu_devs[cluster] = cpu_dev;

If you want to save cpu_dev for further use, save it in bL_cpufreq_init()
not here.

> +               /*
> +                * 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;
> +
> +                       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();
> +
> +                       if (regulator_is_supported_voltage(reg[cluster], opp_uV,
> +                                                          opp_uV)) {
> +                               if (opp_uV < min_uV)
> +                                       min_uV = opp_uV;
> +                               if (opp_uV > max_uV)
> +                                       max_uV = opp_uV;
> +                       } else {
> +                               dev_pm_opp_disable(cpu_dev, opp_freq);
> +                       }
> +
> +                       opp_freq++;
> +               }
> +
> +               ret = regulator_set_voltage_time(reg[cluster], min_uV, max_uV);
> +               if (ret > 0)
> +                       transition_latencies[cluster] = ret * 1000;
> +       }
> +
>         ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table[cluster]);
>         if (ret) {
>                 dev_err(cpu_dev, "%s: failed to init cpufreq table, cpu: %d, err: %d\n",
> @@ -342,7 +455,6 @@ static int _get_cluster_clk_and_freq_table(struct device *cpu_dev)
>                 goto free_opp_table;
>         }
>
> -       name[12] = cluster + '0';
>         clk[cluster] = clk_get(cpu_dev, name);
>         if (!IS_ERR(clk[cluster])) {
>                 dev_dbg(cpu_dev, "%s: clk: %p & freq table: %p, cluster: %d\n",
> @@ -469,6 +581,11 @@ static int bL_cpufreq_init(struct cpufreq_policy *policy)
>         else
>                 policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL;
>
> +       if (cur_cluster < MAX_CLUSTERS &&
> +           policy->cpuinfo.transition_latency != CPUFREQ_ETERNAL)
> +               policy->cpuinfo.transition_latency
> +                       += transition_latencies[cur_cluster];

Instead of the crap here + a global array for transition latencies,
pass 'policy' to _get_cluster_clk_and_freq_table() and add it
directly to policy.

> +
>         if (is_bL_switching_enabled())
>                 per_cpu(cpu_last_req_freq, policy->cpu) = clk_get_cpu_rate(policy->cpu);
>
> --
> 1.7.9.5
>
diff mbox

Patch

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 <linux/slab.h>
 #include <linux/topology.h>
 #include <linux/types.h>
+#include <linux/regulator/consumer.h>
 #include <asm/bL_switcher.h>
 
 #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 new_cluster, u32 rate)
 {
 	u32 new_rate, prev_rate;
@@ -145,22 +218,17 @@  bL_cpufreq_set_rate(u32 cpu, u32 old_cluster, u32 new_cluster, u32 rate)
 	pr_debug("%s: cpu: %d, old cluster: %d, new cluster: %d, freq: %d\n",
 			__func__, cpu, old_cluster, new_cluster, new_rate);
 
-	ret = clk_set_rate(clk[new_cluster], new_rate * 1000);
-	if (WARN_ON(ret)) {
-		pr_err("clk_set_rate failed: %d, new cluster: %d\n", ret,
-				new_cluster);
-		if (bLs) {
-			per_cpu(cpu_last_req_freq, cpu) = prev_rate;
-			per_cpu(physical_cluster, cpu) = old_cluster;
-		}
-
-		mutex_unlock(&cluster_lock[new_cluster]);
-
-		return ret;
+	ret = bL_cpufreq_set_rate_cluster(cpu, new_cluster, new_rate);
+	if (ret && bLs) {
+		per_cpu(cpu_last_req_freq, cpu) = prev_rate;
+		per_cpu(physical_cluster, cpu) = old_cluster;
 	}
 
 	mutex_unlock(&cluster_lock[new_cluster]);
 
+	if (ret)
+		return ret;
+
 	/* Recalc freq for old cluster when switching clusters */
 	if (old_cluster != new_cluster) {
 		pr_debug("%s: cpu: %d, old cluster: %d, new cluster: %d\n",
@@ -174,14 +242,11 @@  bL_cpufreq_set_rate(u32 cpu, u32 old_cluster, u32 new_cluster, u32 rate)
 		/* Set freq of old cluster if there are cpus left on it */
 		new_rate = find_cluster_maxfreq(old_cluster);
 		new_rate = ACTUAL_FREQ(old_cluster, new_rate);
-
 		if (new_rate) {
 			pr_debug("%s: Updating rate of old cluster: %d, to freq: %d\n",
 					__func__, old_cluster, new_rate);
 
-			if (clk_set_rate(clk[old_cluster], new_rate * 1000))
-				pr_err("%s: clk_set_rate failed: %d, old cluster: %d\n",
-						__func__, ret, old_cluster);
+			bL_cpufreq_set_rate_cluster(cpu, old_cluster, new_rate);
 		}
 		mutex_unlock(&cluster_lock[old_cluster]);
 	}
@@ -288,6 +353,8 @@  static void _put_cluster_clk_and_freq_table(struct device *cpu_dev)
 		return;
 
 	clk_put(clk[cluster]);
+	if (!IS_ERR(reg[cluster]))
+		regulator_put(reg[cluster]);
 	dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table[cluster]);
 	if (arm_bL_ops->free_opp_table)
 		arm_bL_ops->free_opp_table(cpu_dev);
@@ -321,6 +388,7 @@  static void put_cluster_clk_and_freq_table(struct device *cpu_dev)
 
 static int _get_cluster_clk_and_freq_table(struct device *cpu_dev)
 {
+	unsigned long min_uV = ~0, max_uV = 0;
 	u32 cluster = raw_cpu_to_cluster(cpu_dev->id);
 	char name[14] = "cpu-cluster.";
 	int ret;
@@ -335,6 +403,51 @@  static int _get_cluster_clk_and_freq_table(struct device *cpu_dev)
 		goto out;
 	}
 
+	name[12] = cluster + '0';
+	reg[cluster] = regulator_get_optional(cpu_dev, name);
+	if (!IS_ERR(reg[cluster])) {
+		unsigned long opp_freq = 0;
+
+		dev_dbg(cpu_dev, "%s: reg: %p, cluster: %d\n",
+			__func__, reg[cluster], cluster);
+		cpu_devs[cluster] = cpu_dev;
+
+		/*
+		 * 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;
+
+			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();
+
+			if (regulator_is_supported_voltage(reg[cluster], opp_uV,
+							   opp_uV)) {
+				if (opp_uV < min_uV)
+					min_uV = opp_uV;
+				if (opp_uV > max_uV)
+					max_uV = opp_uV;
+			} else {
+				dev_pm_opp_disable(cpu_dev, opp_freq);
+			}
+
+			opp_freq++;
+		}
+
+		ret = regulator_set_voltage_time(reg[cluster], min_uV, max_uV);
+		if (ret > 0)
+			transition_latencies[cluster] = ret * 1000;
+	}
+
 	ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table[cluster]);
 	if (ret) {
 		dev_err(cpu_dev, "%s: failed to init cpufreq table, cpu: %d, err: %d\n",
@@ -342,7 +455,6 @@  static int _get_cluster_clk_and_freq_table(struct device *cpu_dev)
 		goto free_opp_table;
 	}
 
-	name[12] = cluster + '0';
 	clk[cluster] = clk_get(cpu_dev, name);
 	if (!IS_ERR(clk[cluster])) {
 		dev_dbg(cpu_dev, "%s: clk: %p & freq table: %p, cluster: %d\n",
@@ -469,6 +581,11 @@  static int bL_cpufreq_init(struct cpufreq_policy *policy)
 	else
 		policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL;
 
+	if (cur_cluster < MAX_CLUSTERS &&
+	    policy->cpuinfo.transition_latency != CPUFREQ_ETERNAL)
+		policy->cpuinfo.transition_latency
+			+= transition_latencies[cur_cluster];
+
 	if (is_bL_switching_enabled())
 		per_cpu(cpu_last_req_freq, policy->cpu) = clk_get_cpu_rate(policy->cpu);