Hi,
This patch series adds generic arm_big_little_dt cpufreq driver
support for Exynos5420/5800 (using the new CPU clock type which
allows it). It also:
- enhances arm_big_little[_dt] driver with CPU cluster regulator
support
- fixes CPU clock configuration data and CPU operating points
setup for Exynos5800
- adds CPU cluster regulator supplies for ODROID-XU3 board
This patch series has been tested on Exynos5800 based ODROID-XU3
board.
Depends on:
- next-20150330 branch of linux-next kernel tree
- "[PATCH 0/6] cpufreq: use generic cpufreq drivers for Exynos4210
platform" [1]
- "[PATCH 0/6] cpufreq: use generic cpufreq drivers for Exynos4x12
platform" [2]
- "[PATCH] cpufreq: exynos: remove dead ->need_apll_change method" [3]
- "[PATCH 0/4] cpufreq: use generic cpufreq drivers for Exynos5250
platform" [4]
[1] http://www.kernelhub.org/?msg=721136&p=2
[2] http://marc.info/?l=linux-pm&m=142868881101873&w=2
[3] https://lkml.org/lkml/2015/3/27/574
[4] https://lkml.org/lkml/2015/4/13/611
Changes over Thomas' original v12 code:
- split Exynos5420 and Exynos5800 support
- moved E5420_[EGL,KFC]_DIV0() macros to clk-exynos5420.c
- disabled cpufreq if big.LITTLE switcher support is enabled
- enhanced arm_big_little[_dt] driver with CPU cluster regulator
support
- fixed CPU clock configuration data for Exynos5800
- fixed CPU operating points setup for Exynos5800
- added CPU cluster regulator supplies for ODROID-XU3 board
Best regards,
--
Bartlomiej Zolnierkiewicz
Samsung R&D Institute Poland
Samsung Electronics
Bartlomiej Zolnierkiewicz (4):
cpufreq: arm_big_little: add cluster regulator support
ARM: dts: add cluster regulator supply properties for
exynos5422-odroidxu3
clk: samsung: exynos5800: fix cpu clock configuration data
ARM: dts: Exynos5800: fix CPU OPP
Thomas Abraham (4):
clk: samsung: exynos5420: add cpu clock configuration data and
instantiate cpu clock
ARM: dts: Exynos5420: add CPU OPP and regulator supply property
ARM: Exynos: use generic cpufreq driver for Exynos5420
ARM: Exynos: use generic cpufreq driver for Exynos5800
.../bindings/cpufreq/arm_big_little_dt.txt | 4 +
arch/arm/boot/dts/exynos5420.dtsi | 38 +++++
arch/arm/boot/dts/exynos5422-odroidxu3.dts | 8 +
arch/arm/boot/dts/exynos5800.dtsi | 39 +++++
arch/arm/mach-exynos/exynos.c | 8 +
drivers/clk/samsung/clk-exynos5420.c | 88 ++++++++++-
drivers/cpufreq/arm_big_little.c | 153 +++++++++++++++++---
include/dt-bindings/clock/exynos5420.h | 2 +
8 files changed, 320 insertions(+), 20 deletions(-)
--
1.7.9.5
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 <[email protected]>
Cc: Doug Anderson <[email protected]>
Cc: Javier Martinez Canillas <[email protected]>
Cc: Andreas Faerber <[email protected]>
Cc: Sachin Kamat <[email protected]>
Cc: Thomas Abraham <[email protected]>
Signed-off-by: Bartlomiej Zolnierkiewicz <[email protected]>
---
.../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);
--
1.7.9.5
Add cluster regulator supply properties as a preparation to
adding generic arm_big_little_dt cpufreq driver support for
ODROID-XU3 board.
Cc: Kukjin Kim <[email protected]>
Cc: Doug Anderson <[email protected]>
Cc: Javier Martinez Canillas <[email protected]>
Cc: Andreas Faerber <[email protected]>
Cc: Sachin Kamat <[email protected]>
Cc: Thomas Abraham <[email protected]>
Signed-off-by: Bartlomiej Zolnierkiewicz <[email protected]>
---
arch/arm/boot/dts/exynos5422-odroidxu3.dts | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/arch/arm/boot/dts/exynos5422-odroidxu3.dts b/arch/arm/boot/dts/exynos5422-odroidxu3.dts
index edc25cf..e876016 100644
--- a/arch/arm/boot/dts/exynos5422-odroidxu3.dts
+++ b/arch/arm/boot/dts/exynos5422-odroidxu3.dts
@@ -355,6 +355,14 @@
dr_mode = "otg";
};
+&cpu0 {
+ cpu-cluster.0-supply = <&buck2_reg>;
+};
+
+&cpu4 {
+ cpu-cluster.1-supply = <&buck6_reg>;
+};
+
&i2c_0 {
status = "okay";
--
1.7.9.5
From: Thomas Abraham <[email protected]>
With the addition of the new Samsung specific cpu-clock type, the
arm clock can be represented as a cpu-clock type. Add the CPU clock
configuration data and instantiate the CPU clock type for Exynos5420.
Changes by Bartlomiej:
- split Exynos5420 support from the original patches
- moved E5420_[EGL,KFC]_DIV0() macros to clk-exynos5420.c
Cc: Tomasz Figa <[email protected]>
Cc: Mike Turquette <[email protected]>
Cc: Javier Martinez Canillas <[email protected]>
Signed-off-by: Thomas Abraham <[email protected]>
Signed-off-by: Bartlomiej Zolnierkiewicz <[email protected]>
---
drivers/clk/samsung/clk-exynos5420.c | 58 ++++++++++++++++++++++++++++++--
include/dt-bindings/clock/exynos5420.h | 2 ++
2 files changed, 58 insertions(+), 2 deletions(-)
diff --git a/drivers/clk/samsung/clk-exynos5420.c b/drivers/clk/samsung/clk-exynos5420.c
index 07d666c..9398a2d 100644
--- a/drivers/clk/samsung/clk-exynos5420.c
+++ b/drivers/clk/samsung/clk-exynos5420.c
@@ -19,6 +19,7 @@
#include <linux/syscore_ops.h>
#include "clk.h"
+#include "clk-cpu.h"
#define APLL_LOCK 0x0
#define APLL_CON0 0x100
@@ -616,9 +617,11 @@ static struct samsung_mux_clock exynos5x_mux_clks[] __initdata = {
MUX(0, "mout_mspll_kfc", mout_mspll_cpu_p, SRC_TOP7, 8, 2),
MUX(0, "mout_mspll_cpu", mout_mspll_cpu_p, SRC_TOP7, 12, 2),
- MUX(0, "mout_apll", mout_apll_p, SRC_CPU, 0, 1),
+ MUX_F(0, "mout_apll", mout_apll_p, SRC_CPU, 0, 1,
+ CLK_SET_RATE_PARENT, 0),
MUX(0, "mout_cpu", mout_cpu_p, SRC_CPU, 16, 1),
- MUX(0, "mout_kpll", mout_kpll_p, SRC_KFC, 0, 1),
+ MUX_F(0, "mout_kpll", mout_kpll_p, SRC_KFC, 0, 1,
+ CLK_SET_RATE_PARENT, 0),
MUX(0, "mout_kfc", mout_kfc_p, SRC_KFC, 16, 1),
MUX(0, "mout_aclk200", mout_group1_p, SRC_TOP0, 8, 2),
@@ -1246,6 +1249,50 @@ static struct samsung_pll_clock exynos5x_plls[nr_plls] __initdata = {
KPLL_CON0, NULL),
};
+#define E5420_EGL_DIV0(apll, pclk_dbg, atb, cpud) \
+ ((((apll) << 24) | ((pclk_dbg) << 20) | ((atb) << 16) | \
+ ((cpud) << 4)))
+
+static const struct exynos_cpuclk_cfg_data exynos5420_eglclk_d[] __initconst = {
+ { 1800000, E5420_EGL_DIV0(3, 7, 7, 4), },
+ { 1700000, E5420_EGL_DIV0(3, 7, 7, 3), },
+ { 1600000, E5420_EGL_DIV0(3, 7, 7, 3), },
+ { 1500000, E5420_EGL_DIV0(3, 7, 7, 3), },
+ { 1400000, E5420_EGL_DIV0(3, 7, 7, 3), },
+ { 1300000, E5420_EGL_DIV0(3, 7, 7, 2), },
+ { 1200000, E5420_EGL_DIV0(3, 7, 7, 2), },
+ { 1100000, E5420_EGL_DIV0(3, 7, 7, 2), },
+ { 1000000, E5420_EGL_DIV0(3, 6, 6, 2), },
+ { 900000, E5420_EGL_DIV0(3, 6, 6, 2), },
+ { 800000, E5420_EGL_DIV0(3, 5, 5, 2), },
+ { 700000, E5420_EGL_DIV0(3, 5, 5, 2), },
+ { 600000, E5420_EGL_DIV0(3, 4, 4, 2), },
+ { 500000, E5420_EGL_DIV0(3, 3, 3, 2), },
+ { 400000, E5420_EGL_DIV0(3, 3, 3, 2), },
+ { 300000, E5420_EGL_DIV0(3, 3, 3, 2), },
+ { 200000, E5420_EGL_DIV0(3, 3, 3, 2), },
+ { 0 },
+};
+
+#define E5420_KFC_DIV(kpll, pclk, aclk) \
+ ((((kpll) << 24) | ((pclk) << 20) | ((aclk) << 4)))
+
+static const struct exynos_cpuclk_cfg_data exynos5420_kfcclk_d[] __initconst = {
+ { 1300000, E5420_KFC_DIV(3, 5, 2), },
+ { 1200000, E5420_KFC_DIV(3, 5, 2), },
+ { 1100000, E5420_KFC_DIV(3, 5, 2), },
+ { 1000000, E5420_KFC_DIV(3, 5, 2), },
+ { 900000, E5420_KFC_DIV(3, 5, 2), },
+ { 800000, E5420_KFC_DIV(3, 5, 2), },
+ { 700000, E5420_KFC_DIV(3, 4, 2), },
+ { 600000, E5420_KFC_DIV(3, 4, 2), },
+ { 500000, E5420_KFC_DIV(3, 4, 2), },
+ { 400000, E5420_KFC_DIV(3, 3, 2), },
+ { 300000, E5420_KFC_DIV(3, 3, 2), },
+ { 200000, E5420_KFC_DIV(3, 3, 2), },
+ { 0 },
+};
+
static const struct of_device_id ext_clk_match[] __initconst = {
{ .compatible = "samsung,exynos5420-oscclk", .data = (void *)0, },
{ },
@@ -1310,6 +1357,13 @@ static void __init exynos5x_clk_init(struct device_node *np,
ARRAY_SIZE(exynos5800_gate_clks));
}
+ exynos_register_cpu_clock(ctx, CLK_ARM_CLK, "armclk",
+ mout_cpu_p[0], mout_cpu_p[1], 0x200,
+ exynos5420_eglclk_d, ARRAY_SIZE(exynos5420_eglclk_d), 0);
+ exynos_register_cpu_clock(ctx, CLK_KFC_CLK, "kfcclk",
+ mout_kfc_p[0], mout_kfc_p[1], 0x28200,
+ exynos5420_kfcclk_d, ARRAY_SIZE(exynos5420_kfcclk_d), 0);
+
exynos5420_clk_sleep_init();
samsung_clk_of_add_provider(np, ctx);
diff --git a/include/dt-bindings/clock/exynos5420.h b/include/dt-bindings/clock/exynos5420.h
index 99da0d1..dde9664 100644
--- a/include/dt-bindings/clock/exynos5420.h
+++ b/include/dt-bindings/clock/exynos5420.h
@@ -25,6 +25,8 @@
#define CLK_FOUT_MPLL 10
#define CLK_FOUT_BPLL 11
#define CLK_FOUT_KPLL 12
+#define CLK_ARM_CLK 13
+#define CLK_KFC_CLK 14
/* gate for special clocks (sclk) */
#define CLK_SCLK_UART0 128
--
1.7.9.5
From: Thomas Abraham <[email protected]>
For Exynos5420 platforms, add CPU operating points and CPU
regulator supply properties for migrating from Exynos specific
cpufreq driver to using generic cpufreq driver.
Changes by Bartlomiej:
- split Exynos5420 support from the original patch
Cc: Kukjin Kim <[email protected]>
Cc: Doug Anderson <[email protected]>
Cc: Javier Martinez Canillas <[email protected]>
Cc: Andreas Faerber <[email protected]>
Cc: Sachin Kamat <[email protected]>
Signed-off-by: Thomas Abraham <[email protected]>
Signed-off-by: Bartlomiej Zolnierkiewicz <[email protected]>
---
arch/arm/boot/dts/exynos5420.dtsi | 38 +++++++++++++++++++++++++++++++++++++
1 file changed, 38 insertions(+)
diff --git a/arch/arm/boot/dts/exynos5420.dtsi b/arch/arm/boot/dts/exynos5420.dtsi
index f67b23f..85b9cfc 100644
--- a/arch/arm/boot/dts/exynos5420.dtsi
+++ b/arch/arm/boot/dts/exynos5420.dtsi
@@ -59,8 +59,26 @@
device_type = "cpu";
compatible = "arm,cortex-a15";
reg = <0x0>;
+ clocks = <&clock CLK_ARM_CLK>;
+ clock-names = "cpu-cluster.0";
clock-frequency = <1800000000>;
cci-control-port = <&cci_control1>;
+ clock-latency = <140000>;
+
+ operating-points = <
+ 1800000 1250000
+ 1700000 1212500
+ 1600000 1175000
+ 1500000 1137500
+ 1400000 1112500
+ 1300000 1062500
+ 1200000 1037500
+ 1100000 1012500
+ 1000000 987500
+ 900000 962500
+ 800000 937500
+ 700000 912500
+ >;
};
cpu1: cpu@1 {
@@ -69,6 +87,7 @@
reg = <0x1>;
clock-frequency = <1800000000>;
cci-control-port = <&cci_control1>;
+ clock-latency = <140000>;
};
cpu2: cpu@2 {
@@ -77,6 +96,7 @@
reg = <0x2>;
clock-frequency = <1800000000>;
cci-control-port = <&cci_control1>;
+ clock-latency = <140000>;
};
cpu3: cpu@3 {
@@ -85,14 +105,29 @@
reg = <0x3>;
clock-frequency = <1800000000>;
cci-control-port = <&cci_control1>;
+ clock-latency = <140000>;
};
cpu4: cpu@100 {
device_type = "cpu";
compatible = "arm,cortex-a7";
reg = <0x100>;
+ clocks = <&clock CLK_KFC_CLK>;
+ clock-names = "cpu-cluster.1";
clock-frequency = <1000000000>;
cci-control-port = <&cci_control0>;
+ clock-latency = <140000>;
+
+ operating-points = <
+ 1300000 1275000
+ 1200000 1212500
+ 1100000 1162500
+ 1000000 1112500
+ 900000 1062500
+ 800000 1025000
+ 700000 975000
+ 600000 937500
+ >;
};
cpu5: cpu@101 {
@@ -101,6 +136,7 @@
reg = <0x101>;
clock-frequency = <1000000000>;
cci-control-port = <&cci_control0>;
+ clock-latency = <140000>;
};
cpu6: cpu@102 {
@@ -109,6 +145,7 @@
reg = <0x102>;
clock-frequency = <1000000000>;
cci-control-port = <&cci_control0>;
+ clock-latency = <140000>;
};
cpu7: cpu@103 {
@@ -117,6 +154,7 @@
reg = <0x103>;
clock-frequency = <1000000000>;
cci-control-port = <&cci_control0>;
+ clock-latency = <140000>;
};
};
--
1.7.9.5
From: Thomas Abraham <[email protected]>
The new CPU clock type allows the use of generic arm_big_little_dt
cpufreq driver for Exynos5420.
Changes by Bartlomiej:
- split Exynos5420 support from the original patch
- disable cpufreq if big.LITTLE switcher support is enabled
Cc: Tomasz Figa <[email protected]>
Cc: Kukjin Kim <[email protected]>
Cc: Javier Martinez Canillas <[email protected]>
Signed-off-by: Thomas Abraham <[email protected]>
Signed-off-by: Bartlomiej Zolnierkiewicz <[email protected]>
---
arch/arm/mach-exynos/exynos.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/arch/arm/mach-exynos/exynos.c b/arch/arm/mach-exynos/exynos.c
index 4b03a32..11ac7fb 100644
--- a/arch/arm/mach-exynos/exynos.c
+++ b/arch/arm/mach-exynos/exynos.c
@@ -207,6 +207,13 @@ struct cpufreq_dt_platform_data cpufreq_dt_pd = {
static const struct of_device_id exynos_cpufreq_matches[] = {
{ .compatible = "samsung,exynos4210", .data = "cpufreq-dt" },
{ .compatible = "samsung,exynos5250", .data = "cpufreq-dt" },
+/*
+ * FIXME: When big.LITTLE switcher is enabled system lockups during
+ * ondemand governor stress testing (observed on ODROID-XU3 board).
+ */
+#ifndef CONFIG_BL_SWITCHER
+ { .compatible = "samsung,exynos5420", .data = "arm-bL-cpufreq-dt" },
+#endif
{ /* sentinel */ }
};
--
1.7.9.5
Fix cpu clock configuration data for Exynos5800 (it uses
higher PCLK_DBG divider values than Exynos5420 and supports
additional frequencies).
Based on Hardkernel's kernel for ODROID-XU3 board.
Cc: Tomasz Figa <[email protected]>
Cc: Mike Turquette <[email protected]>
Cc: Javier Martinez Canillas <[email protected]>
Cc: Thomas Abraham <[email protected]>
Signed-off-by: Bartlomiej Zolnierkiewicz <[email protected]>
---
drivers/clk/samsung/clk-exynos5420.c | 36 +++++++++++++++++++++++++++++++---
1 file changed, 33 insertions(+), 3 deletions(-)
diff --git a/drivers/clk/samsung/clk-exynos5420.c b/drivers/clk/samsung/clk-exynos5420.c
index 9398a2d..462aaee 100644
--- a/drivers/clk/samsung/clk-exynos5420.c
+++ b/drivers/clk/samsung/clk-exynos5420.c
@@ -1274,10 +1274,34 @@ static const struct exynos_cpuclk_cfg_data exynos5420_eglclk_d[] __initconst = {
{ 0 },
};
+static const struct exynos_cpuclk_cfg_data exynos5800_eglclk_d[] __initconst = {
+ { 2000000, E5420_EGL_DIV0(3, 7, 7, 4), },
+ { 1900000, E5420_EGL_DIV0(3, 7, 7, 4), },
+ { 1800000, E5420_EGL_DIV0(3, 7, 7, 4), },
+ { 1700000, E5420_EGL_DIV0(3, 7, 7, 3), },
+ { 1600000, E5420_EGL_DIV0(3, 7, 7, 3), },
+ { 1500000, E5420_EGL_DIV0(3, 7, 7, 3), },
+ { 1400000, E5420_EGL_DIV0(3, 7, 7, 3), },
+ { 1300000, E5420_EGL_DIV0(3, 7, 7, 2), },
+ { 1200000, E5420_EGL_DIV0(3, 7, 7, 2), },
+ { 1100000, E5420_EGL_DIV0(3, 7, 7, 2), },
+ { 1000000, E5420_EGL_DIV0(3, 7, 6, 2), },
+ { 900000, E5420_EGL_DIV0(3, 7, 6, 2), },
+ { 800000, E5420_EGL_DIV0(3, 7, 5, 2), },
+ { 700000, E5420_EGL_DIV0(3, 7, 5, 2), },
+ { 600000, E5420_EGL_DIV0(3, 7, 4, 2), },
+ { 500000, E5420_EGL_DIV0(3, 7, 3, 2), },
+ { 400000, E5420_EGL_DIV0(3, 7, 3, 2), },
+ { 300000, E5420_EGL_DIV0(3, 7, 3, 2), },
+ { 200000, E5420_EGL_DIV0(3, 7, 3, 2), },
+ { 0 },
+};
+
#define E5420_KFC_DIV(kpll, pclk, aclk) \
((((kpll) << 24) | ((pclk) << 20) | ((aclk) << 4)))
static const struct exynos_cpuclk_cfg_data exynos5420_kfcclk_d[] __initconst = {
+ { 1400000, E5420_KFC_DIV(3, 5, 3), }, /* for Exynos5800 */
{ 1300000, E5420_KFC_DIV(3, 5, 2), },
{ 1200000, E5420_KFC_DIV(3, 5, 2), },
{ 1100000, E5420_KFC_DIV(3, 5, 2), },
@@ -1357,9 +1381,15 @@ static void __init exynos5x_clk_init(struct device_node *np,
ARRAY_SIZE(exynos5800_gate_clks));
}
- exynos_register_cpu_clock(ctx, CLK_ARM_CLK, "armclk",
- mout_cpu_p[0], mout_cpu_p[1], 0x200,
- exynos5420_eglclk_d, ARRAY_SIZE(exynos5420_eglclk_d), 0);
+ if (soc == EXYNOS5420) {
+ exynos_register_cpu_clock(ctx, CLK_ARM_CLK, "armclk",
+ mout_cpu_p[0], mout_cpu_p[1], 0x200,
+ exynos5420_eglclk_d, ARRAY_SIZE(exynos5420_eglclk_d), 0);
+ } else {
+ exynos_register_cpu_clock(ctx, CLK_ARM_CLK, "armclk",
+ mout_cpu_p[0], mout_cpu_p[1], 0x200,
+ exynos5800_eglclk_d, ARRAY_SIZE(exynos5800_eglclk_d), 0);
+ }
exynos_register_cpu_clock(ctx, CLK_KFC_CLK, "kfcclk",
mout_kfc_p[0], mout_kfc_p[1], 0x28200,
exynos5420_kfcclk_d, ARRAY_SIZE(exynos5420_kfcclk_d), 0);
--
1.7.9.5
Fix CPU operating points for Exynos5800 (it uses different
voltages than Exynos5420 and supports additional frequencies).
However don't use 2000MHz & 1900MHz OPPs (for A15 cores) and
1400MHz OPP (for A7 cores) until there is a separate DTS for
ODROID-XU3 Lite board (which doesn't support these higher
OPPs).
Based on Hardkernel's kernel for ODROID-XU3 board.
Cc: Kukjin Kim <[email protected]>
Cc: Doug Anderson <[email protected]>
Cc: Javier Martinez Canillas <[email protected]>
Cc: Andreas Faerber <[email protected]>
Cc: Sachin Kamat <[email protected]>
Cc: Thomas Abraham <[email protected]>
Signed-off-by: Bartlomiej Zolnierkiewicz <[email protected]>
---
arch/arm/boot/dts/exynos5800.dtsi | 39 +++++++++++++++++++++++++++++++++++++
1 file changed, 39 insertions(+)
diff --git a/arch/arm/boot/dts/exynos5800.dtsi b/arch/arm/boot/dts/exynos5800.dtsi
index c0bb356..ba7ecd2 100644
--- a/arch/arm/boot/dts/exynos5800.dtsi
+++ b/arch/arm/boot/dts/exynos5800.dtsi
@@ -19,6 +19,45 @@
compatible = "samsung,exynos5800", "samsung,exynos5";
};
+&cpu0 {
+ operating-points = <
+ 1800000 1250000
+ 1700000 1250000
+ 1600000 1250000
+ 1500000 1100000
+ 1400000 1100000
+ 1300000 1100000
+ 1200000 1000000
+ 1100000 1000000
+ 1000000 1000000
+ 900000 1000000
+ 800000 900000
+ 700000 900000
+ 600000 900000
+ 500000 900000
+ 400000 900000
+ 300000 900000
+ 200000 900000
+ >;
+};
+
+&cpu4 {
+ operating-points = <
+ 1300000 1250000
+ 1200000 1250000
+ 1100000 1250000
+ 1000000 1100000
+ 900000 1100000
+ 800000 1100000
+ 700000 1000000
+ 600000 1000000
+ 500000 1000000
+ 400000 1000000
+ 300000 900000
+ 200000 900000
+ >;
+};
+
&clock {
compatible = "samsung,exynos5800-clock";
};
--
1.7.9.5
From: Thomas Abraham <[email protected]>
The new CPU clock type allows the use of generic arm_big_little_dt
cpufreq driver for Exynos5800.
Changes by Bartlomiej:
- split Exynos5800 support from the original patch
- disable cpufreq if big.LITTLE switcher support is enabled
Cc: Tomasz Figa <[email protected]>
Cc: Kukjin Kim <[email protected]>
Cc: Javier Martinez Canillas <[email protected]>
Signed-off-by: Thomas Abraham <[email protected]>
Signed-off-by: Bartlomiej Zolnierkiewicz <[email protected]>
---
arch/arm/mach-exynos/exynos.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/arch/arm/mach-exynos/exynos.c b/arch/arm/mach-exynos/exynos.c
index 11ac7fb..7892062 100644
--- a/arch/arm/mach-exynos/exynos.c
+++ b/arch/arm/mach-exynos/exynos.c
@@ -213,6 +213,7 @@ static const struct of_device_id exynos_cpufreq_matches[] = {
*/
#ifndef CONFIG_BL_SWITCHER
{ .compatible = "samsung,exynos5420", .data = "arm-bL-cpufreq-dt" },
+ { .compatible = "samsung,exynos5800", .data = "arm-bL-cpufreq-dt" },
#endif
{ /* sentinel */ }
};
--
1.7.9.5
On Tuesday, April 21, 2015 03:17:50 PM Bartlomiej Zolnierkiewicz wrote:
> Hi,
>
> This patch series adds generic arm_big_little_dt cpufreq driver
> support for Exynos5420/5800 (using the new CPU clock type which
> allows it). It also:
> - enhances arm_big_little[_dt] driver with CPU cluster regulator
> support
> - fixes CPU clock configuration data and CPU operating points
> setup for Exynos5800
> - adds CPU cluster regulator supplies for ODROID-XU3 board
>
> This patch series has been tested on Exynos5800 based ODROID-XU3
> board.
>
> Depends on:
> - next-20150330 branch of linux-next kernel tree
> - "[PATCH 0/6] cpufreq: use generic cpufreq drivers for Exynos4210
> platform" [1]
> - "[PATCH 0/6] cpufreq: use generic cpufreq drivers for Exynos4x12
> platform" [2]
> - "[PATCH] cpufreq: exynos: remove dead ->need_apll_change method" [3]
> - "[PATCH 0/4] cpufreq: use generic cpufreq drivers for Exynos5250
> platform" [4]
>
> [1] http://www.kernelhub.org/?msg=721136&p=2
> [2] http://marc.info/?l=linux-pm&m=142868881101873&w=2
> [3] https://lkml.org/lkml/2015/3/27/574
> [4] https://lkml.org/lkml/2015/4/13/611
>
> Changes over Thomas' original v12 code:
> - split Exynos5420 and Exynos5800 support
> - moved E5420_[EGL,KFC]_DIV0() macros to clk-exynos5420.c
> - disabled cpufreq if big.LITTLE switcher support is enabled
> - enhanced arm_big_little[_dt] driver with CPU cluster regulator
> support
> - fixed CPU clock configuration data for Exynos5800
> - fixed CPU operating points setup for Exynos5800
> - added CPU cluster regulator supplies for ODROID-XU3 board
>
> Best regards,
> --
> Bartlomiej Zolnierkiewicz
> Samsung R&D Institute Poland
> Samsung Electronics
>
>
> Bartlomiej Zolnierkiewicz (4):
> cpufreq: arm_big_little: add cluster regulator support
> ARM: dts: add cluster regulator supply properties for
> exynos5422-odroidxu3
> clk: samsung: exynos5800: fix cpu clock configuration data
> ARM: dts: Exynos5800: fix CPU OPP
>
> Thomas Abraham (4):
> clk: samsung: exynos5420: add cpu clock configuration data and
> instantiate cpu clock
> ARM: dts: Exynos5420: add CPU OPP and regulator supply property
> ARM: Exynos: use generic cpufreq driver for Exynos5420
> ARM: Exynos: use generic cpufreq driver for Exynos5800
>
> .../bindings/cpufreq/arm_big_little_dt.txt | 4 +
> arch/arm/boot/dts/exynos5420.dtsi | 38 +++++
> arch/arm/boot/dts/exynos5422-odroidxu3.dts | 8 +
> arch/arm/boot/dts/exynos5800.dtsi | 39 +++++
> arch/arm/mach-exynos/exynos.c | 8 +
> drivers/clk/samsung/clk-exynos5420.c | 88 ++++++++++-
> drivers/cpufreq/arm_big_little.c | 153 +++++++++++++++++---
> include/dt-bindings/clock/exynos5420.h | 2 +
> 8 files changed, 320 insertions(+), 20 deletions(-)
If you are using exynos5420-arndale-octa.dts, exynos5420-peach-pit.dts,
exynos5420-smdk5420.dts or exynos5800-peach-pi.dts please use the attached
patch instead of patch #2/8. Sorry for the inconvenience.
From: Bartlomiej Zolnierkiewicz <[email protected]>
Subject: [PATCH v2 2/8] ARM: dts: Exynos5420/5800: add cluster regulator supply properties
Add cluster regulator supply properties as a preparation to
adding generic arm_big_little_dt cpufreq driver support for
Exynos5420 and Exynos5800 based boards.
Cc: Kukjin Kim <[email protected]>
Cc: Doug Anderson <[email protected]>
Cc: Javier Martinez Canillas <[email protected]>
Cc: Andreas Faerber <[email protected]>
Cc: Sachin Kamat <[email protected]>
Cc: Thomas Abraham <[email protected]>
Signed-off-by: Bartlomiej Zolnierkiewicz <[email protected]>
---
arch/arm/boot/dts/exynos5420-arndale-octa.dts | 8 ++++++++
arch/arm/boot/dts/exynos5420-peach-pit.dts | 8 ++++++++
arch/arm/boot/dts/exynos5420-smdk5420.dts | 8 ++++++++
arch/arm/boot/dts/exynos5422-odroidxu3.dts | 8 ++++++++
arch/arm/boot/dts/exynos5800-peach-pi.dts | 8 ++++++++
5 files changed, 40 insertions(+)
Index: b/arch/arm/boot/dts/exynos5420-arndale-octa.dts
===================================================================
--- a/arch/arm/boot/dts/exynos5420-arndale-octa.dts 2015-04-21 16:26:53.022509881 +0200
+++ b/arch/arm/boot/dts/exynos5420-arndale-octa.dts 2015-04-21 17:25:30.250396715 +0200
@@ -369,6 +369,14 @@
};
};
+&cpu0 {
+ cpu-cluster.0-supply = <&buck2_reg>;
+};
+
+&cpu4 {
+ cpu-cluster.1-supply = <&buck6_reg>;
+};
+
&usbdrd_dwc3_1 {
dr_mode = "host";
};
Index: b/arch/arm/boot/dts/exynos5420-peach-pit.dts
===================================================================
--- a/arch/arm/boot/dts/exynos5420-peach-pit.dts 2015-04-21 16:26:53.022509881 +0200
+++ b/arch/arm/boot/dts/exynos5420-peach-pit.dts 2015-04-21 17:26:32.034394727 +0200
@@ -676,6 +676,14 @@
};
};
+&cpu0 {
+ cpu-cluster.0-supply = <&buck2_reg>;
+};
+
+&cpu4 {
+ cpu-cluster.1-supply = <&buck6_reg>;
+};
+
&i2c_2 {
status = "okay";
samsung,i2c-sda-delay = <100>;
Index: b/arch/arm/boot/dts/exynos5420-smdk5420.dts
===================================================================
--- a/arch/arm/boot/dts/exynos5420-smdk5420.dts 2015-04-21 16:26:53.022509881 +0200
+++ b/arch/arm/boot/dts/exynos5420-smdk5420.dts 2015-04-21 17:27:04.338393689 +0200
@@ -420,3 +420,11 @@
};
};
};
+
+&cpu0 {
+ cpu-cluster.0-supply = <&buck2_reg>;
+};
+
+&cpu4 {
+ cpu-cluster.1-supply = <&buck6_reg>;
+};
Index: b/arch/arm/boot/dts/exynos5422-odroidxu3.dts
===================================================================
--- a/arch/arm/boot/dts/exynos5422-odroidxu3.dts 2015-04-21 17:23:44.334400124 +0200
+++ b/arch/arm/boot/dts/exynos5422-odroidxu3.dts 2015-04-21 17:24:05.454399445 +0200
@@ -355,6 +355,14 @@
dr_mode = "otg";
};
+&cpu0 {
+ cpu-cluster.0-supply = <&buck2_reg>;
+};
+
+&cpu4 {
+ cpu-cluster.1-supply = <&buck6_reg>;
+};
+
&i2c_0 {
status = "okay";
Index: b/arch/arm/boot/dts/exynos5800-peach-pi.dts
===================================================================
--- a/arch/arm/boot/dts/exynos5800-peach-pi.dts 2015-04-21 16:26:53.030509881 +0200
+++ b/arch/arm/boot/dts/exynos5800-peach-pi.dts 2015-04-21 17:27:47.674392295 +0200
@@ -638,6 +638,14 @@
};
};
+&cpu0 {
+ cpu-cluster.0-supply = <&buck2_reg>;
+};
+
+&cpu4 {
+ cpu-cluster.1-supply = <&buck6_reg>;
+};
+
&i2c_2 {
status = "okay";
samsung,i2c-sda-delay = <100>;
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 <[email protected]>
> Cc: Doug Anderson <[email protected]>
> Cc: Javier Martinez Canillas <[email protected]>
> Cc: Andreas Faerber <[email protected]>
> Cc: Sachin Kamat <[email protected]>
> Cc: Thomas Abraham <[email protected]>
> Signed-off-by: Bartlomiej Zolnierkiewicz <[email protected]>
> ---
> .../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 <[email protected]>
--
Best regards,
Lukasz Majewski
Samsung R&D Institute Poland (SRPOL) | Linux Platform Group
Hi Bartlomiej,
> Add cluster regulator supply properties as a preparation to
> adding generic arm_big_little_dt cpufreq driver support for
> ODROID-XU3 board.
>
> Cc: Kukjin Kim <[email protected]>
> Cc: Doug Anderson <[email protected]>
> Cc: Javier Martinez Canillas <[email protected]>
> Cc: Andreas Faerber <[email protected]>
> Cc: Sachin Kamat <[email protected]>
> Cc: Thomas Abraham <[email protected]>
> Signed-off-by: Bartlomiej Zolnierkiewicz <[email protected]>
> ---
> arch/arm/boot/dts/exynos5422-odroidxu3.dts | 8 ++++++++
> 1 file changed, 8 insertions(+)
>
> diff --git a/arch/arm/boot/dts/exynos5422-odroidxu3.dts
> b/arch/arm/boot/dts/exynos5422-odroidxu3.dts index edc25cf..e876016
> 100644 --- a/arch/arm/boot/dts/exynos5422-odroidxu3.dts
> +++ b/arch/arm/boot/dts/exynos5422-odroidxu3.dts
> @@ -355,6 +355,14 @@
> dr_mode = "otg";
> };
>
> +&cpu0 {
> + cpu-cluster.0-supply = <&buck2_reg>;
> +};
> +
> +&cpu4 {
> + cpu-cluster.1-supply = <&buck6_reg>;
> +};
> +
> &i2c_0 {
> status = "okay";
>
Reviewed-by: Lukasz Majewski <[email protected]>
--
Best regards,
Lukasz Majewski
Samsung R&D Institute Poland (SRPOL) | Linux Platform Group
Hi Bartlomiej,
> From: Thomas Abraham <[email protected]>
>
> With the addition of the new Samsung specific cpu-clock type, the
> arm clock can be represented as a cpu-clock type. Add the CPU clock
> configuration data and instantiate the CPU clock type for Exynos5420.
>
> Changes by Bartlomiej:
> - split Exynos5420 support from the original patches
> - moved E5420_[EGL,KFC]_DIV0() macros to clk-exynos5420.c
>
> Cc: Tomasz Figa <[email protected]>
> Cc: Mike Turquette <[email protected]>
> Cc: Javier Martinez Canillas <[email protected]>
> Signed-off-by: Thomas Abraham <[email protected]>
> Signed-off-by: Bartlomiej Zolnierkiewicz <[email protected]>
> ---
> drivers/clk/samsung/clk-exynos5420.c | 58
> ++++++++++++++++++++++++++++++--
> include/dt-bindings/clock/exynos5420.h | 2 ++ 2 files changed, 58
> insertions(+), 2 deletions(-)
>
> diff --git a/drivers/clk/samsung/clk-exynos5420.c
> b/drivers/clk/samsung/clk-exynos5420.c index 07d666c..9398a2d 100644
> --- a/drivers/clk/samsung/clk-exynos5420.c
> +++ b/drivers/clk/samsung/clk-exynos5420.c
> @@ -19,6 +19,7 @@
> #include <linux/syscore_ops.h>
>
> #include "clk.h"
> +#include "clk-cpu.h"
>
> #define APLL_LOCK 0x0
> #define APLL_CON0 0x100
> @@ -616,9 +617,11 @@ static struct samsung_mux_clock
> exynos5x_mux_clks[] __initdata = { MUX(0, "mout_mspll_kfc",
> mout_mspll_cpu_p, SRC_TOP7, 8, 2), MUX(0, "mout_mspll_cpu",
> mout_mspll_cpu_p, SRC_TOP7, 12, 2),
> - MUX(0, "mout_apll", mout_apll_p, SRC_CPU, 0, 1),
> + MUX_F(0, "mout_apll", mout_apll_p, SRC_CPU, 0, 1,
> + CLK_SET_RATE_PARENT, 0),
> MUX(0, "mout_cpu", mout_cpu_p, SRC_CPU, 16, 1),
> - MUX(0, "mout_kpll", mout_kpll_p, SRC_KFC, 0, 1),
> + MUX_F(0, "mout_kpll", mout_kpll_p, SRC_KFC, 0, 1,
> + CLK_SET_RATE_PARENT, 0),
> MUX(0, "mout_kfc", mout_kfc_p, SRC_KFC, 16, 1),
>
> MUX(0, "mout_aclk200", mout_group1_p, SRC_TOP0, 8, 2),
> @@ -1246,6 +1249,50 @@ static struct samsung_pll_clock
> exynos5x_plls[nr_plls] __initdata = { KPLL_CON0, NULL),
> };
>
> +#define E5420_EGL_DIV0(apll, pclk_dbg, atb,
> cpud) \
> + ((((apll) << 24) | ((pclk_dbg) << 20) | ((atb) <<
> 16) | \
> + ((cpud) << 4)))
> +
> +static const struct exynos_cpuclk_cfg_data exynos5420_eglclk_d[]
> __initconst = {
> + { 1800000, E5420_EGL_DIV0(3, 7, 7, 4), },
> + { 1700000, E5420_EGL_DIV0(3, 7, 7, 3), },
> + { 1600000, E5420_EGL_DIV0(3, 7, 7, 3), },
> + { 1500000, E5420_EGL_DIV0(3, 7, 7, 3), },
> + { 1400000, E5420_EGL_DIV0(3, 7, 7, 3), },
> + { 1300000, E5420_EGL_DIV0(3, 7, 7, 2), },
> + { 1200000, E5420_EGL_DIV0(3, 7, 7, 2), },
> + { 1100000, E5420_EGL_DIV0(3, 7, 7, 2), },
> + { 1000000, E5420_EGL_DIV0(3, 6, 6, 2), },
> + { 900000, E5420_EGL_DIV0(3, 6, 6, 2), },
> + { 800000, E5420_EGL_DIV0(3, 5, 5, 2), },
> + { 700000, E5420_EGL_DIV0(3, 5, 5, 2), },
> + { 600000, E5420_EGL_DIV0(3, 4, 4, 2), },
> + { 500000, E5420_EGL_DIV0(3, 3, 3, 2), },
> + { 400000, E5420_EGL_DIV0(3, 3, 3, 2), },
> + { 300000, E5420_EGL_DIV0(3, 3, 3, 2), },
> + { 200000, E5420_EGL_DIV0(3, 3, 3, 2), },
> + { 0 },
> +};
> +
> +#define E5420_KFC_DIV(kpll, pclk,
> aclk) \
> + ((((kpll) << 24) | ((pclk) << 20) | ((aclk) << 4)))
> +
> +static const struct exynos_cpuclk_cfg_data exynos5420_kfcclk_d[]
> __initconst = {
> + { 1300000, E5420_KFC_DIV(3, 5, 2), },
> + { 1200000, E5420_KFC_DIV(3, 5, 2), },
> + { 1100000, E5420_KFC_DIV(3, 5, 2), },
> + { 1000000, E5420_KFC_DIV(3, 5, 2), },
> + { 900000, E5420_KFC_DIV(3, 5, 2), },
> + { 800000, E5420_KFC_DIV(3, 5, 2), },
> + { 700000, E5420_KFC_DIV(3, 4, 2), },
> + { 600000, E5420_KFC_DIV(3, 4, 2), },
> + { 500000, E5420_KFC_DIV(3, 4, 2), },
> + { 400000, E5420_KFC_DIV(3, 3, 2), },
> + { 300000, E5420_KFC_DIV(3, 3, 2), },
> + { 200000, E5420_KFC_DIV(3, 3, 2), },
> + { 0 },
> +};
> +
> static const struct of_device_id ext_clk_match[] __initconst = {
> { .compatible = "samsung,exynos5420-oscclk", .data = (void
> *)0, }, { },
> @@ -1310,6 +1357,13 @@ static void __init exynos5x_clk_init(struct
> device_node *np, ARRAY_SIZE(exynos5800_gate_clks));
> }
>
> + exynos_register_cpu_clock(ctx, CLK_ARM_CLK, "armclk",
> + mout_cpu_p[0], mout_cpu_p[1], 0x200,
> + exynos5420_eglclk_d,
> ARRAY_SIZE(exynos5420_eglclk_d), 0);
> + exynos_register_cpu_clock(ctx, CLK_KFC_CLK, "kfcclk",
> + mout_kfc_p[0], mout_kfc_p[1], 0x28200,
> + exynos5420_kfcclk_d,
> ARRAY_SIZE(exynos5420_kfcclk_d), 0); +
> exynos5420_clk_sleep_init();
>
> samsung_clk_of_add_provider(np, ctx);
> diff --git a/include/dt-bindings/clock/exynos5420.h
> b/include/dt-bindings/clock/exynos5420.h index 99da0d1..dde9664 100644
> --- a/include/dt-bindings/clock/exynos5420.h
> +++ b/include/dt-bindings/clock/exynos5420.h
> @@ -25,6 +25,8 @@
> #define CLK_FOUT_MPLL 10
> #define CLK_FOUT_BPLL 11
> #define CLK_FOUT_KPLL 12
> +#define CLK_ARM_CLK 13
> +#define CLK_KFC_CLK 14
>
> /* gate for special clocks (sclk) */
> #define CLK_SCLK_UART0 128
Reviewed-by: Lukasz Majewski <[email protected]>
--
Best regards,
Lukasz Majewski
Samsung R&D Institute Poland (SRPOL) | Linux Platform Group
Hi Bartlomiej,
> From: Thomas Abraham <[email protected]>
>
> For Exynos5420 platforms, add CPU operating points and CPU
> regulator supply properties for migrating from Exynos specific
> cpufreq driver to using generic cpufreq driver.
>
> Changes by Bartlomiej:
> - split Exynos5420 support from the original patch
>
> Cc: Kukjin Kim <[email protected]>
> Cc: Doug Anderson <[email protected]>
> Cc: Javier Martinez Canillas <[email protected]>
> Cc: Andreas Faerber <[email protected]>
> Cc: Sachin Kamat <[email protected]>
> Signed-off-by: Thomas Abraham <[email protected]>
> Signed-off-by: Bartlomiej Zolnierkiewicz <[email protected]>
> ---
> arch/arm/boot/dts/exynos5420.dtsi | 38
> +++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+)
>
> diff --git a/arch/arm/boot/dts/exynos5420.dtsi
> b/arch/arm/boot/dts/exynos5420.dtsi index f67b23f..85b9cfc 100644
> --- a/arch/arm/boot/dts/exynos5420.dtsi
> +++ b/arch/arm/boot/dts/exynos5420.dtsi
> @@ -59,8 +59,26 @@
> device_type = "cpu";
> compatible = "arm,cortex-a15";
> reg = <0x0>;
> + clocks = <&clock CLK_ARM_CLK>;
> + clock-names = "cpu-cluster.0";
> clock-frequency = <1800000000>;
> cci-control-port = <&cci_control1>;
> + clock-latency = <140000>;
> +
> + operating-points = <
> + 1800000 1250000
> + 1700000 1212500
> + 1600000 1175000
> + 1500000 1137500
> + 1400000 1112500
> + 1300000 1062500
> + 1200000 1037500
> + 1100000 1012500
> + 1000000 987500
> + 900000 962500
> + 800000 937500
> + 700000 912500
> + >;
> };
>
> cpu1: cpu@1 {
> @@ -69,6 +87,7 @@
> reg = <0x1>;
> clock-frequency = <1800000000>;
> cci-control-port = <&cci_control1>;
> + clock-latency = <140000>;
> };
>
> cpu2: cpu@2 {
> @@ -77,6 +96,7 @@
> reg = <0x2>;
> clock-frequency = <1800000000>;
> cci-control-port = <&cci_control1>;
> + clock-latency = <140000>;
> };
>
> cpu3: cpu@3 {
> @@ -85,14 +105,29 @@
> reg = <0x3>;
> clock-frequency = <1800000000>;
> cci-control-port = <&cci_control1>;
> + clock-latency = <140000>;
> };
>
> cpu4: cpu@100 {
> device_type = "cpu";
> compatible = "arm,cortex-a7";
> reg = <0x100>;
> + clocks = <&clock CLK_KFC_CLK>;
> + clock-names = "cpu-cluster.1";
> clock-frequency = <1000000000>;
> cci-control-port = <&cci_control0>;
> + clock-latency = <140000>;
> +
> + operating-points = <
> + 1300000 1275000
> + 1200000 1212500
> + 1100000 1162500
> + 1000000 1112500
> + 900000 1062500
> + 800000 1025000
> + 700000 975000
> + 600000 937500
> + >;
> };
>
> cpu5: cpu@101 {
> @@ -101,6 +136,7 @@
> reg = <0x101>;
> clock-frequency = <1000000000>;
> cci-control-port = <&cci_control0>;
> + clock-latency = <140000>;
> };
>
> cpu6: cpu@102 {
> @@ -109,6 +145,7 @@
> reg = <0x102>;
> clock-frequency = <1000000000>;
> cci-control-port = <&cci_control0>;
> + clock-latency = <140000>;
> };
>
> cpu7: cpu@103 {
> @@ -117,6 +154,7 @@
> reg = <0x103>;
> clock-frequency = <1000000000>;
> cci-control-port = <&cci_control0>;
> + clock-latency = <140000>;
> };
> };
>
Reviewed-by: Lukasz Majewski <[email protected]>
--
Best regards,
Lukasz Majewski
Samsung R&D Institute Poland (SRPOL) | Linux Platform Group
Hi Bartlomiej,
> From: Thomas Abraham <[email protected]>
>
> The new CPU clock type allows the use of generic arm_big_little_dt
> cpufreq driver for Exynos5420.
>
> Changes by Bartlomiej:
> - split Exynos5420 support from the original patch
> - disable cpufreq if big.LITTLE switcher support is enabled
>
> Cc: Tomasz Figa <[email protected]>
> Cc: Kukjin Kim <[email protected]>
> Cc: Javier Martinez Canillas <[email protected]>
> Signed-off-by: Thomas Abraham <[email protected]>
> Signed-off-by: Bartlomiej Zolnierkiewicz <[email protected]>
> ---
> arch/arm/mach-exynos/exynos.c | 7 +++++++
> 1 file changed, 7 insertions(+)
>
> diff --git a/arch/arm/mach-exynos/exynos.c
> b/arch/arm/mach-exynos/exynos.c index 4b03a32..11ac7fb 100644
> --- a/arch/arm/mach-exynos/exynos.c
> +++ b/arch/arm/mach-exynos/exynos.c
> @@ -207,6 +207,13 @@ struct cpufreq_dt_platform_data cpufreq_dt_pd = {
> static const struct of_device_id exynos_cpufreq_matches[] = {
> { .compatible = "samsung,exynos4210", .data = "cpufreq-dt" },
> { .compatible = "samsung,exynos5250", .data = "cpufreq-dt" },
> +/*
> + * FIXME: When big.LITTLE switcher is enabled system lockups during
> + * ondemand governor stress testing (observed on ODROID-XU3 board).
> + */
> +#ifndef CONFIG_BL_SWITCHER
> + { .compatible = "samsung,exynos5420", .data =
> "arm-bL-cpufreq-dt" }, +#endif
> { /* sentinel */ }
> };
>
Reviewed-by: Lukasz Majewski <[email protected]>
--
Best regards,
Lukasz Majewski
Samsung R&D Institute Poland (SRPOL) | Linux Platform Group
Hi Bartlomiej,
> Fix cpu clock configuration data for Exynos5800 (it uses
> higher PCLK_DBG divider values than Exynos5420 and supports
> additional frequencies).
>
> Based on Hardkernel's kernel for ODROID-XU3 board.
>
> Cc: Tomasz Figa <[email protected]>
> Cc: Mike Turquette <[email protected]>
> Cc: Javier Martinez Canillas <[email protected]>
> Cc: Thomas Abraham <[email protected]>
> Signed-off-by: Bartlomiej Zolnierkiewicz <[email protected]>
> ---
> drivers/clk/samsung/clk-exynos5420.c | 36
> +++++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+),
> 3 deletions(-)
>
> diff --git a/drivers/clk/samsung/clk-exynos5420.c
> b/drivers/clk/samsung/clk-exynos5420.c index 9398a2d..462aaee 100644
> --- a/drivers/clk/samsung/clk-exynos5420.c
> +++ b/drivers/clk/samsung/clk-exynos5420.c
> @@ -1274,10 +1274,34 @@ static const struct exynos_cpuclk_cfg_data
> exynos5420_eglclk_d[] __initconst = { { 0 },
> };
>
> +static const struct exynos_cpuclk_cfg_data exynos5800_eglclk_d[]
> __initconst = {
> + { 2000000, E5420_EGL_DIV0(3, 7, 7, 4), },
> + { 1900000, E5420_EGL_DIV0(3, 7, 7, 4), },
> + { 1800000, E5420_EGL_DIV0(3, 7, 7, 4), },
> + { 1700000, E5420_EGL_DIV0(3, 7, 7, 3), },
> + { 1600000, E5420_EGL_DIV0(3, 7, 7, 3), },
> + { 1500000, E5420_EGL_DIV0(3, 7, 7, 3), },
> + { 1400000, E5420_EGL_DIV0(3, 7, 7, 3), },
> + { 1300000, E5420_EGL_DIV0(3, 7, 7, 2), },
> + { 1200000, E5420_EGL_DIV0(3, 7, 7, 2), },
> + { 1100000, E5420_EGL_DIV0(3, 7, 7, 2), },
> + { 1000000, E5420_EGL_DIV0(3, 7, 6, 2), },
> + { 900000, E5420_EGL_DIV0(3, 7, 6, 2), },
> + { 800000, E5420_EGL_DIV0(3, 7, 5, 2), },
> + { 700000, E5420_EGL_DIV0(3, 7, 5, 2), },
> + { 600000, E5420_EGL_DIV0(3, 7, 4, 2), },
> + { 500000, E5420_EGL_DIV0(3, 7, 3, 2), },
> + { 400000, E5420_EGL_DIV0(3, 7, 3, 2), },
> + { 300000, E5420_EGL_DIV0(3, 7, 3, 2), },
> + { 200000, E5420_EGL_DIV0(3, 7, 3, 2), },
> + { 0 },
> +};
> +
> #define E5420_KFC_DIV(kpll, pclk,
> aclk) \ ((((kpll) << 24) |
> ((pclk) << 20) | ((aclk) << 4)))
> static const struct exynos_cpuclk_cfg_data exynos5420_kfcclk_d[]
> __initconst = {
> + { 1400000, E5420_KFC_DIV(3, 5, 3), }, /* for Exynos5800 */
> { 1300000, E5420_KFC_DIV(3, 5, 2), },
> { 1200000, E5420_KFC_DIV(3, 5, 2), },
> { 1100000, E5420_KFC_DIV(3, 5, 2), },
> @@ -1357,9 +1381,15 @@ static void __init exynos5x_clk_init(struct
> device_node *np, ARRAY_SIZE(exynos5800_gate_clks));
> }
>
> - exynos_register_cpu_clock(ctx, CLK_ARM_CLK, "armclk",
> - mout_cpu_p[0], mout_cpu_p[1], 0x200,
> - exynos5420_eglclk_d,
> ARRAY_SIZE(exynos5420_eglclk_d), 0);
> + if (soc == EXYNOS5420) {
> + exynos_register_cpu_clock(ctx, CLK_ARM_CLK, "armclk",
> + mout_cpu_p[0], mout_cpu_p[1], 0x200,
> + exynos5420_eglclk_d,
> ARRAY_SIZE(exynos5420_eglclk_d), 0);
> + } else {
> + exynos_register_cpu_clock(ctx, CLK_ARM_CLK, "armclk",
> + mout_cpu_p[0], mout_cpu_p[1], 0x200,
> + exynos5800_eglclk_d,
> ARRAY_SIZE(exynos5800_eglclk_d), 0);
> + }
> exynos_register_cpu_clock(ctx, CLK_KFC_CLK, "kfcclk",
> mout_kfc_p[0], mout_kfc_p[1], 0x28200,
> exynos5420_kfcclk_d,
> ARRAY_SIZE(exynos5420_kfcclk_d), 0);
Reviewed-by: Lukasz Majewski <[email protected]>
--
Best regards,
Lukasz Majewski
Samsung R&D Institute Poland (SRPOL) | Linux Platform Group
On 21 April 2015 at 18:47, Bartlomiej Zolnierkiewicz
<[email protected]> 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 <[email protected]>
> Cc: Doug Anderson <[email protected]>
> Cc: Javier Martinez Canillas <[email protected]>
> Cc: Andreas Faerber <[email protected]>
> Cc: Sachin Kamat <[email protected]>
> Cc: Thomas Abraham <[email protected]>
> Signed-off-by: Bartlomiej Zolnierkiewicz <[email protected]>
> ---
> .../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
>
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 <[email protected]>
> Cc: Doug Anderson <[email protected]>
> Cc: Javier Martinez Canillas <[email protected]>
> Cc: Andreas Faerber <[email protected]>
> Cc: Sachin Kamat <[email protected]>
> Cc: Thomas Abraham <[email protected]>
> Signed-off-by: Bartlomiej Zolnierkiewicz <[email protected]>
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 <[email protected]>
Do you plan to continue working on this?
Thanks
Heiko
[0] ---------------- 8< -----------------------------
From: Heiko Stuebner <[email protected]>
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 <[email protected]>
---
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 cpu, u32 cluster, u32 new_rate)
{
- unsigned long volt = 0, volt_old = 0;
+ unsigned long volt = 0, volt_max = 0, volt_old = 0;
long freq_Hz;
u32 old_rate;
- int ret;
+ int ret = 0;
freq_Hz = new_rate * 1000;
old_rate = clk_get_rate(clk[cluster]) / 1000;
@@ -144,13 +210,18 @@ bL_cpufreq_set_rate_cluster(u32 cpu, u32 cluster, u32 new_rate)
struct dev_pm_opp *opp;
unsigned long opp_freq;
+ ret = bL_lock_shared_regulators(cluster);
+ if(ret)
+ return 0;
+
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);
+ ret = PTR_ERR(opp);
+ goto unlock;
}
volt = dev_pm_opp_get_voltage(opp);
opp_freq = dev_pm_opp_get_freq(opp);
@@ -158,20 +229,25 @@ bL_cpufreq_set_rate_cluster(u32 cpu, u32 cluster, u32 new_rate)
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);
+
+ volt_max = volt;
+ ret = bL_adapt_shared_regulators(cluster, &volt_max);
+ if (ret)
+ goto unlock;
}
- pr_debug("%s: cpu %d, cluster: %d, %u MHz, %ld mV --> %u MHz, %ld mV\n",
+ pr_debug("%s: cpu %d, cluster: %d, %u MHz, %ld mV --> %u MHz, %ld-%ld mV\n",
__func__, cpu, cluster,
old_rate / 1000, (volt_old > 0) ? volt_old / 1000 : -1,
- new_rate / 1000, volt ? volt / 1000 : -1);
+ new_rate / 1000, volt ? volt / 1000 : -1, volt_max ? volt_max / 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);
+ ret = regulator_set_voltage(reg[cluster], volt, volt_max);
if (ret) {
pr_err("%s: cpu: %d, cluster: %d, failed to scale voltage up: %d\n",
__func__, cpu, cluster, ret);
- return ret;
+ goto unlock;
}
}
@@ -181,21 +257,25 @@ bL_cpufreq_set_rate_cluster(u32 cpu, u32 cluster, u32 new_rate)
__func__, cluster, ret);
if (!IS_ERR(reg[cluster]) && volt_old > 0)
regulator_set_voltage_tol(reg[cluster], volt_old, 0);
- return ret;
+ goto unlock;
}
/* scaling down? scale voltage after frequency */
if (!IS_ERR(reg[cluster]) && new_rate < old_rate) {
- ret = regulator_set_voltage_tol(reg[cluster], volt, 0);
+ ret = regulator_set_voltage(reg[cluster], volt, volt_max);
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;
+ goto unlock;
}
}
- return 0;
+unlock:
+ if (!IS_ERR(reg[cluster]))
+ bL_unlock_shared_regulators(cluster);
+
+ return ret;
}
static int
--
2.1.4
---------------- 8< ----------------
From: Heiko Stuebner <[email protected]>
Subject: [PATCH] regulator: add a regulator_is_match function
Another stolen concept from the common clock framework. At some points it
can be useful to check if two regulator structs are actually pointing to
the same regulator_dev.
The usecase in question was to check if the supplying regulators of two
cpu clusters are actually the same and the regulator is thus shared
between these cpu clusters.
Therefore add regulator_is_match() that compares the rdev pointers
of two regulators and emits a bool stating if they match.
Signed-off-by: Heiko Stuebner <[email protected]>
---
drivers/regulator/core.c | 23 +++++++++++++++++++++++
include/linux/regulator/consumer.h | 7 +++++++
2 files changed, 30 insertions(+)
diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index 443eaab..6bb7e70 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -1591,6 +1591,29 @@ void regulator_put(struct regulator *regulator)
EXPORT_SYMBOL_GPL(regulator_put);
/**
+ * regulator_is_match - check if two regulator's point to the same rdev
+ * @p: regulator compared against q
+ * @q: regulator compared against p
+ *
+ * Returns true if the two struct regulator pointers both point to the same rdev
+ * Returns false otherwise. Note that two NULL clks are treated as matching.
+ */
+bool regulator_is_match(const struct regulator *p, const struct regulator *q)
+{
+ /* trivial case: identical struct regulator's or both NULL */
+ if (p == q)
+ return true;
+
+ /* true if clk->core pointers match. Avoid derefing garbage */
+ if (!IS_ERR_OR_NULL(p) && !IS_ERR_OR_NULL(q))
+ if (p->rdev == q->rdev)
+ return true;
+
+ return false;
+}
+EXPORT_SYMBOL_GPL(regulator_is_match);
+
+/**
* regulator_register_supply_alias - Provide device alias for supply lookup
*
* @dev: device that will be given as the regulator "consumer"
diff --git a/include/linux/regulator/consumer.h b/include/linux/regulator/consumer.h
index f8a689e..38ffaa0 100644
--- a/include/linux/regulator/consumer.h
+++ b/include/linux/regulator/consumer.h
@@ -172,6 +172,7 @@ struct regulator *__must_check devm_regulator_get_optional(struct device *dev,
const char *id);
void regulator_put(struct regulator *regulator);
void devm_regulator_put(struct regulator *regulator);
+bool regulator_is_match(const struct regulator *p, const struct regulator *q);
int regulator_register_supply_alias(struct device *dev, const char *id,
struct device *alias_dev,
@@ -316,6 +317,12 @@ static inline void devm_regulator_put(struct regulator *regulator)
{
}
+static inline bool regulator_is_match(const struct regulator *p,
+ const struct regulator *q)
+{
+ return true;
+}
+
static inline int regulator_register_supply_alias(struct device *dev,
const char *id,
struct device *alias_dev,
--
2.1.4