ipq806x: introduce dedicated krait cpufreq

- Drop cpufreq patchs that tweak the cpufreq-dt driver
- Add dedicated krait cpufreq driver

Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
This commit is contained in:
Ansuel Smith 2021-03-01 01:11:16 +01:00 committed by Petr Štetiar
parent e17fb62293
commit 5dbbefcbcc
10 changed files with 994 additions and 608 deletions

View File

@ -1,153 +0,0 @@
From: Sylwester Nawrocki <s.nawrocki@samsung.com>
To: krzk@kernel.org, vireshk@kernel.org, robh+dt@kernel.org
Cc: sboyd@kernel.org, roger.lu@mediatek.com,
linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
linux-samsung-soc@vger.kernel.org, devicetree@vger.kernel.org,
b.zolnierkie@samsung.com, m.szyprowski@samsung.com,
Stephen Boyd <sboyd@codeaurora.org>,
Sylwester Nawrocki <s.nawrocki@samsung.com>
Subject: [PATCH v5 1/4] PM / OPP: Support adjusting OPP voltages at runtime
Date: Wed, 16 Oct 2019 16:57:53 +0200
Message-ID: <20191016145756.16004-2-s.nawrocki@samsung.com> (raw)
In-Reply-To: <20191016145756.16004-1-s.nawrocki@samsung.com>
From: Stephen Boyd <sboyd@codeaurora.org>
On some SoCs the Adaptive Voltage Scaling (AVS) technique is
employed to optimize the operating voltage of a device. At a
given frequency, the hardware monitors dynamic factors and either
makes a suggestion for how much to adjust a voltage for the
current frequency, or it automatically adjusts the voltage
without software intervention. Add an API to the OPP library for
the former case, so that AVS type devices can update the voltages
for an OPP when the hardware determines the voltage should
change. The assumption is that drivers like CPUfreq or devfreq
will register for the OPP notifiers and adjust the voltage
according to suggestions that AVS makes.
This patch is derived from [1] submitted by Stephen.
[1] https://lore.kernel.org/patchwork/patch/599279/
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
Signed-off-by: Roger Lu <roger.lu@mediatek.com>
[s.nawrocki@samsung.com: added handling of OPP min/max voltage]
Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com>
---
drivers/opp/core.c | 69 ++++++++++++++++++++++++++++++++++++++++++
include/linux/pm_opp.h | 13 ++++++++
2 files changed, 82 insertions(+)
--- a/drivers/opp/core.c
+++ b/drivers/opp/core.c
@@ -2102,6 +2102,75 @@ put_table:
}
/**
+ * dev_pm_opp_adjust_voltage() - helper to change the voltage of an OPP
+ * @dev: device for which we do this operation
+ * @freq: OPP frequency to adjust voltage of
+ * @u_volt: new OPP target voltage
+ * @u_volt_min: new OPP min voltage
+ * @u_volt_max: new OPP max voltage
+ *
+ * Return: -EINVAL for bad pointers, -ENOMEM if no memory available for the
+ * copy operation, returns 0 if no modifcation was done OR modification was
+ * successful.
+ */
+int dev_pm_opp_adjust_voltage(struct device *dev, unsigned long freq,
+ unsigned long u_volt, unsigned long u_volt_min,
+ unsigned long u_volt_max)
+
+{
+ struct opp_table *opp_table;
+ struct dev_pm_opp *tmp_opp, *opp = ERR_PTR(-ENODEV);
+ int r = 0;
+
+ /* Find the opp_table */
+ opp_table = _find_opp_table(dev);
+ if (IS_ERR(opp_table)) {
+ r = PTR_ERR(opp_table);
+ dev_warn(dev, "%s: Device OPP not found (%d)\n", __func__, r);
+ return r;
+ }
+
+ mutex_lock(&opp_table->lock);
+
+ /* Do we have the frequency? */
+ list_for_each_entry(tmp_opp, &opp_table->opp_list, node) {
+ if (tmp_opp->rate == freq) {
+ opp = tmp_opp;
+ break;
+ }
+ }
+
+ if (IS_ERR(opp)) {
+ r = PTR_ERR(opp);
+ goto adjust_unlock;
+ }
+
+ /* Is update really needed? */
+ if (opp->supplies->u_volt == u_volt)
+ goto adjust_unlock;
+
+ opp->supplies->u_volt = u_volt;
+ opp->supplies->u_volt_min = u_volt_min;
+ opp->supplies->u_volt_max = u_volt_max;
+
+ dev_pm_opp_get(opp);
+ mutex_unlock(&opp_table->lock);
+
+ /* Notify the voltage change of the OPP */
+ blocking_notifier_call_chain(&opp_table->head, OPP_EVENT_ADJUST_VOLTAGE,
+ opp);
+
+ dev_pm_opp_put(opp);
+ goto adjust_put_table;
+
+adjust_unlock:
+ mutex_unlock(&opp_table->lock);
+adjust_put_table:
+ dev_pm_opp_put_opp_table(opp_table);
+ return r;
+}
+
+/**
* dev_pm_opp_enable() - Enable a specific OPP
* @dev: device for which we do this operation
* @freq: OPP frequency to enable
--- a/include/linux/pm_opp.h
+++ b/include/linux/pm_opp.h
@@ -22,6 +22,7 @@ struct opp_table;
enum dev_pm_opp_event {
OPP_EVENT_ADD, OPP_EVENT_REMOVE, OPP_EVENT_ENABLE, OPP_EVENT_DISABLE,
+ OPP_EVENT_ADJUST_VOLTAGE,
};
/**
@@ -113,6 +114,10 @@ int dev_pm_opp_add(struct device *dev, u
void dev_pm_opp_remove(struct device *dev, unsigned long freq);
void dev_pm_opp_remove_all_dynamic(struct device *dev);
+int dev_pm_opp_adjust_voltage(struct device *dev, unsigned long freq,
+ unsigned long u_volt, unsigned long u_volt_min,
+ unsigned long u_volt_max);
+
int dev_pm_opp_enable(struct device *dev, unsigned long freq);
int dev_pm_opp_disable(struct device *dev, unsigned long freq);
@@ -242,6 +247,14 @@ static inline void dev_pm_opp_remove_all
{
}
+static inline int
+dev_pm_opp_adjust_voltage(struct device *dev, unsigned long freq,
+ unsigned long u_volt, unsigned long u_volt_min,
+ unsigned long u_volt_max)
+{
+ return 0;
+}
+
static inline int dev_pm_opp_enable(struct device *dev, unsigned long freq)
{
return 0;

View File

@ -1,52 +0,0 @@
From d06ca5e7a3cf726f5be5ffd96e93ccd798b8c09a Mon Sep 17 00:00:00 2001
From: Georgi Djakov <georgi.djakov@linaro.org>
Date: Thu, 12 May 2016 14:41:33 +0300
Subject: [PATCH 51/69] PM / OPP: Add a helper to get an opp regulator for
device
Signed-off-by: Georgi Djakov <georgi.djakov@linaro.org>
---
drivers/opp/core.c | 21 +++++++++++++++++++++
include/linux/pm_opp.h | 1 +
2 files changed, 22 insertions(+)
--- a/drivers/opp/core.c
+++ b/drivers/opp/core.c
@@ -127,6 +127,27 @@ unsigned long dev_pm_opp_get_freq(struct
}
EXPORT_SYMBOL_GPL(dev_pm_opp_get_freq);
+struct regulator *dev_pm_opp_get_regulator(struct device *dev)
+{
+ struct opp_table *opp_table;
+ struct regulator *reg;
+
+ rcu_read_lock();
+
+ opp_table = _find_opp_table(dev);
+ if (IS_ERR(opp_table)) {
+ rcu_read_unlock();
+ return ERR_CAST(opp_table);
+ }
+
+ reg = opp_table->regulators[0];
+
+ rcu_read_unlock();
+
+ return reg;
+}
+EXPORT_SYMBOL_GPL(dev_pm_opp_get_regulator);
+
/**
* dev_pm_opp_get_level() - Gets the level corresponding to an available opp
* @opp: opp for which level value has to be returned for
--- a/include/linux/pm_opp.h
+++ b/include/linux/pm_opp.h
@@ -83,6 +83,7 @@ void dev_pm_opp_put_opp_table(struct opp
unsigned long dev_pm_opp_get_voltage(struct dev_pm_opp *opp);
unsigned long dev_pm_opp_get_freq(struct dev_pm_opp *opp);
+struct regulator *dev_pm_opp_get_regulator(struct device *dev);
unsigned int dev_pm_opp_get_level(struct dev_pm_opp *opp);

View File

@ -1,47 +0,0 @@
From 4533c285c2aedce6d4434d7b877066de3b1ecb33 Mon Sep 17 00:00:00 2001
From: Georgi Djakov <georgi.djakov@linaro.org>
Date: Thu, 25 Aug 2016 18:43:35 +0300
Subject: [PATCH 52/69] PM / OPP: Update the voltage tolerance when adjusting
the OPP
When the voltage is adjusted, the voltage tolerance is not updated.
This can lead to situations where the voltage min value is greater
than the voltage max value. The final result is triggering a BUG()
in the regulator core.
Fix this by updating the voltage tolerance values too.
Signed-off-by: Georgi Djakov <georgi.djakov@linaro.org>
---
drivers/opp/core.c | 5 +++++
1 file changed, 5 insertions(+)
--- a/drivers/opp/core.c
+++ b/drivers/opp/core.c
@@ -2142,6 +2142,7 @@ int dev_pm_opp_adjust_voltage(struct dev
struct opp_table *opp_table;
struct dev_pm_opp *tmp_opp, *opp = ERR_PTR(-ENODEV);
int r = 0;
+ unsigned long tol;
/* Find the opp_table */
opp_table = _find_opp_table(dev);
@@ -2171,8 +2172,17 @@ int dev_pm_opp_adjust_voltage(struct dev
goto adjust_unlock;
opp->supplies->u_volt = u_volt;
- opp->supplies->u_volt_min = u_volt_min;
- opp->supplies->u_volt_max = u_volt_max;
+
+ tol = u_volt * opp_table->voltage_tolerance_v1 / 100;
+ if ( u_volt_min == u_volt )
+ opp->supplies->u_volt_min = u_volt - tol;
+ else
+ opp->supplies->u_volt_min = u_volt_min;
+
+ if ( u_volt_max == u_volt )
+ opp->supplies->u_volt_max = u_volt + tol;
+ else
+ opp->supplies->u_volt_max = u_volt_max;
dev_pm_opp_get(opp);
mutex_unlock(&opp_table->lock);

View File

@ -1,118 +0,0 @@
From 10577f74c35bd395951d1b2382c8d821089b5745 Mon Sep 17 00:00:00 2001
From: Stephen Boyd <sboyd@codeaurora.org>
Date: Fri, 18 Sep 2015 17:52:08 -0700
Subject: [PATCH 54/69] cpufreq-dt: Handle OPP voltage adjust events
On some SoCs the Adaptive Voltage Scaling (AVS) technique is
employed to optimize the operating voltage of a device. At a
given frequency, the hardware monitors dynamic factors and either
makes a suggestion for how much to adjust a voltage for the
current frequency, or it automatically adjusts the voltage
without software intervention.
In the former case, an AVS driver will call
dev_pm_opp_modify_voltage() and update the voltage for the
particular OPP the CPUs are using. Add an OPP notifier to
cpufreq-dt so that we can adjust the voltage of the CPU when AVS
updates the OPP.
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
Acked-by: Viresh Kumar <viresh.kumar@linaro.org>
Signed-off-by: Georgi Djakov <georgi.djakov@linaro.org>
---
drivers/cpufreq/cpufreq-dt.c | 68 ++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 65 insertions(+), 3 deletions(-)
--- a/drivers/cpufreq/cpufreq-dt.c
+++ b/drivers/cpufreq/cpufreq-dt.c
@@ -27,6 +27,9 @@ struct private_data {
struct opp_table *opp_table;
struct device *cpu_dev;
const char *reg_name;
+ struct notifier_block opp_nb;
+ struct mutex lock;
+ unsigned long opp_freq;
bool have_static_opps;
};
@@ -42,12 +45,15 @@ static int set_target(struct cpufreq_pol
unsigned long freq = policy->freq_table[index].frequency;
int ret;
+ mutex_lock(&priv->lock);
ret = dev_pm_opp_set_rate(priv->cpu_dev, freq * 1000);
if (!ret) {
+ priv->opp_freq = freq * 1000;
arch_set_freq_scale(policy->related_cpus, freq,
policy->cpuinfo.max_freq);
}
+ mutex_unlock(&priv->lock);
return ret;
}
@@ -90,6 +96,39 @@ node_put:
return name;
}
+static int opp_notifier(struct notifier_block *nb, unsigned long event,
+ void *data)
+{
+ struct dev_pm_opp *opp = data;
+ struct private_data *priv = container_of(nb, struct private_data,
+ opp_nb);
+ struct device *cpu_dev = priv->cpu_dev;
+ struct regulator *cpu_reg;
+ unsigned long volt, freq;
+ int ret = 0;
+
+ if (event == OPP_EVENT_ADJUST_VOLTAGE) {
+ cpu_reg = dev_pm_opp_get_regulator(cpu_dev);
+ if (IS_ERR(cpu_reg)) {
+ ret = PTR_ERR(cpu_reg);
+ goto out;
+ }
+ volt = dev_pm_opp_get_voltage(opp);
+ freq = dev_pm_opp_get_freq(opp);
+
+ mutex_lock(&priv->lock);
+ if (freq == priv->opp_freq) {
+ ret = regulator_set_voltage_triplet(cpu_reg, volt, volt, volt);
+ }
+ mutex_unlock(&priv->lock);
+ if (ret)
+ dev_err(cpu_dev, "failed to scale voltage: %d\n", ret);
+ }
+
+out:
+ return notifier_from_errno(ret);
+}
+
static int resources_available(void)
{
struct device *cpu_dev;
@@ -246,10 +285,14 @@ static int cpufreq_init(struct cpufreq_p
__func__, ret);
}
+ mutex_init(&priv->lock);
+ priv->opp_nb.notifier_call = opp_notifier;
+ dev_pm_opp_register_notifier(cpu_dev, &priv->opp_nb);
+
ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table);
if (ret) {
dev_err(cpu_dev, "failed to init cpufreq table: %d\n", ret);
- goto out_free_opp;
+ goto out_unregister_nb;
}
priv->cpu_dev = cpu_dev;
@@ -281,6 +324,8 @@ static int cpufreq_init(struct cpufreq_p
out_free_cpufreq_table:
dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table);
+out_unregister_nb:
+ dev_pm_opp_unregister_notifier(cpu_dev, &priv->opp_nb);
out_free_opp:
if (priv->have_static_opps)
dev_pm_opp_of_cpumask_remove_table(policy->cpus);

View File

@ -1,199 +0,0 @@
--- a/drivers/cpufreq/cpufreq-dt.c
+++ b/drivers/cpufreq/cpufreq-dt.c
@@ -39,20 +39,85 @@ static struct freq_attr *cpufreq_dt_attr
NULL,
};
+struct shared_data {
+ unsigned long *curr_freq_table;
+ unsigned long curr_l2_freq;
+ unsigned long curr_l2_volt;
+};
+
+static struct shared_data *cpus_shared_data;
+
static int set_target(struct cpufreq_policy *policy, unsigned int index)
{
struct private_data *priv = policy->driver_data;
unsigned long freq = policy->freq_table[index].frequency;
+ struct clk *l2_clk = policy->l2_clk;
+ struct regulator *l2_regulator = policy->l2_regulator;
+ unsigned long l2_freq, target_l2_freq;
+ unsigned long l2_vol, target_l2_volt;
+ unsigned long target_freq;
int ret;
mutex_lock(&priv->lock);
ret = dev_pm_opp_set_rate(priv->cpu_dev, freq * 1000);
if (!ret) {
+ if (policy->l2_rate_set) {
+ int cpu, l2_index, tol = 0;
+ target_freq = freq * 1000;
+
+ cpus_shared_data->curr_freq_table[policy->cpu] = target_freq;
+ for_each_present_cpu(cpu)
+ if (cpu != policy->cpu)
+ target_freq = max(target_freq, cpus_shared_data->curr_freq_table[cpu]);
+
+ for (l2_index = 2; l2_index > 0; l2_index--)
+ if (target_freq >= policy->l2_cpufreq[l2_index])
+ break;
+
+ l2_freq = cpus_shared_data->curr_l2_freq;
+ target_l2_freq = policy->l2_rate[l2_index];
+
+ if (l2_freq != target_l2_freq) {
+
+ /*
+ * Set to idle bin if switching from normal to high bin
+ * or vice versa
+ */
+ if ( (l2_index == 2 && l2_freq == policy->l2_rate[1]) ||
+ (l2_index == 1 && l2_freq == policy->l2_rate[2]) ) {
+ ret = clk_set_rate(l2_clk, policy->l2_rate[0]);
+ if (ret)
+ goto exit;
+ }
+
+ /* scale l2 with the core */
+ ret = clk_set_rate(l2_clk, target_l2_freq);
+ if (ret)
+ goto exit;
+ cpus_shared_data->curr_l2_freq = target_l2_freq;
+
+ if (policy->l2_volt_set) {
+
+ l2_vol = cpus_shared_data->curr_l2_volt;
+ target_l2_volt = policy->l2_volt[l2_index];
+
+ if (l2_vol != target_l2_volt) {
+ tol = target_l2_volt * policy->l2_volt_tol / 100;
+ ret = regulator_set_voltage_tol(l2_regulator,target_l2_volt,tol);
+ if (ret)
+ goto exit;
+ cpus_shared_data->curr_l2_volt = target_l2_volt;
+ }
+ }
+ }
+ }
priv->opp_freq = freq * 1000;
arch_set_freq_scale(policy->related_cpus, freq,
policy->cpuinfo.max_freq);
}
+
+exit:
mutex_unlock(&priv->lock);
return ret;
@@ -195,6 +260,9 @@ static int cpufreq_init(struct cpufreq_p
bool fallback = false;
const char *name;
int ret;
+ struct device_node *np, *l2_np;
+ struct clk *l2_clk = NULL;
+ struct regulator *l2_regulator = NULL;
cpu_dev = get_cpu_device(policy->cpu);
if (!cpu_dev) {
@@ -302,6 +370,57 @@ static int cpufreq_init(struct cpufreq_p
policy->suspend_freq = dev_pm_opp_get_suspend_opp_freq(cpu_dev) / 1000;
+ policy->l2_rate_set = false;
+ policy->l2_volt_set = false;
+
+ l2_clk = clk_get(cpu_dev, "l2");
+ if (!IS_ERR(l2_clk))
+ policy->l2_clk = l2_clk;
+
+ l2_np = of_find_node_by_name(NULL, "qcom,l2");
+ if (l2_np) {
+ struct device_node *vdd;
+ np = of_node_get(priv->cpu_dev->of_node);
+
+ if (np)
+ of_property_read_u32(np, "voltage-tolerance", &policy->l2_volt_tol);
+
+ of_property_read_u32_array(l2_np, "qcom,l2-rates", policy->l2_rate, 3);
+ if (policy->l2_rate[0] && policy->l2_rate[1] && policy->l2_rate[2]) {
+ policy->l2_rate_set = true;
+ of_property_read_u32_array(l2_np, "qcom,l2-cpufreq", policy->l2_cpufreq, 3);
+ of_property_read_u32_array(l2_np, "qcom,l2-volt", policy->l2_volt, 3);
+ } else
+ pr_warn("L2: failed to parse L2 rates\n");
+
+ if (!policy->l2_cpufreq[0] && !policy->l2_cpufreq[1] &&
+ !policy->l2_cpufreq[2] && policy->l2_rate_set) {
+ int i;
+
+ pr_warn("L2: failed to parse target cpu freq, using defaults\n");
+ for (i = 0; i < 3; i++)
+ policy->l2_cpufreq[i] = policy->l2_rate[i];
+ }
+
+ if (policy->l2_volt[0] && policy->l2_volt[1] && policy->l2_volt[2] &&
+ policy->l2_volt_tol && policy->l2_rate_set) {
+ vdd = of_parse_phandle(l2_np, "qcom,l2-supply", 0);
+
+ if (vdd) {
+ l2_regulator = devm_regulator_get(cpu_dev, vdd->name);
+ if (!IS_ERR(l2_regulator)) {
+ policy->l2_regulator = l2_regulator;
+ policy->l2_volt_set = true;
+ } else {
+ pr_warn("failed to get l2 supply\n");
+ l2_regulator = NULL;
+ }
+
+ of_node_put(vdd);
+ }
+ }
+ }
+
/* Support turbo/boost mode */
if (policy_has_boost_freq(policy)) {
/* This gets disabled by core on driver unregister */
@@ -401,6 +520,14 @@ static int dt_cpufreq_probe(struct platf
if (ret)
return ret;
+ cpus_shared_data = kzalloc(sizeof(struct shared_data), GFP_KERNEL);
+ if (!cpus_shared_data)
+ return -ENOMEM;
+
+ cpus_shared_data->curr_freq_table = kzalloc(sizeof(int) * CONFIG_NR_CPUS, GFP_KERNEL);
+ if (!cpus_shared_data->curr_freq_table)
+ return -ENOMEM;
+
if (data) {
if (data->have_governor_per_policy)
dt_cpufreq_driver.flags |= CPUFREQ_HAVE_GOVERNOR_PER_POLICY;
@@ -419,6 +546,8 @@ static int dt_cpufreq_probe(struct platf
static int dt_cpufreq_remove(struct platform_device *pdev)
{
+ kfree(cpus_shared_data->curr_freq_table);
+ kfree(cpus_shared_data);
cpufreq_unregister_driver(&dt_cpufreq_driver);
return 0;
}
--- a/include/linux/cpufreq.h
+++ b/include/linux/cpufreq.h
@@ -58,7 +58,15 @@ struct cpufreq_policy {
should set cpufreq */
unsigned int cpu; /* cpu managing this policy, must be online */
- struct clk *clk;
+ struct clk *clk;
+ struct clk *l2_clk; /* L2 clock */
+ struct regulator *l2_regulator; /* L2 supply */
+ unsigned int l2_rate[3]; /* L2 bus clock rate */
+ bool l2_rate_set;
+ unsigned int l2_cpufreq[3]; /* L2 target CPU frequency */
+ unsigned int l2_volt[3]; /* L2 voltage array */
+ bool l2_volt_set;
+ unsigned int l2_volt_tol; /* L2 voltage tolerance */
struct cpufreq_cpuinfo cpuinfo;/* see above */
unsigned int min; /* in kHz */

View File

@ -1,23 +0,0 @@
From 001a8dcb56ced58c518aaa10a4f0ba5e878705b6 Mon Sep 17 00:00:00 2001
From: Georgi Djakov <georgi.djakov@linaro.org>
Date: Tue, 17 May 2016 16:15:43 +0300
Subject: [PATCH 56/69] cpufreq-dt: Add missing rcu locks
Signed-off-by: Georgi Djakov <georgi.djakov@linaro.org>
---
drivers/cpufreq/cpufreq-dt.c | 2 ++
1 file changed, 2 insertions(+)
--- a/drivers/cpufreq/cpufreq-dt.c
+++ b/drivers/cpufreq/cpufreq-dt.c
@@ -178,8 +178,10 @@ static int opp_notifier(struct notifier_
ret = PTR_ERR(cpu_reg);
goto out;
}
+ rcu_read_lock();
volt = dev_pm_opp_get_voltage(opp);
freq = dev_pm_opp_get_freq(opp);
+ rcu_read_unlock();
mutex_lock(&priv->lock);
if (freq == priv->opp_freq) {

View File

@ -0,0 +1,56 @@
From a206d4061f1cc2c5cd17ee45c53a0ba711e48e6d Mon Sep 17 00:00:00 2001
From: Ansuel Smith <ansuelsmth@gmail.com>
Date: Sun, 7 Feb 2021 16:42:52 +0100
Subject: [PATCH 3/3] drivers: cpufreq: qcom-cpufreq-nvmem: support specific
cpufreq driver
Add support for specific cpufreq driver for qcom-cpufreq-nvmem driver.
Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
---
drivers/cpufreq/qcom-cpufreq-nvmem.c | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/drivers/cpufreq/qcom-cpufreq-nvmem.c b/drivers/cpufreq/qcom-cpufreq-nvmem.c
index d1744b5d9619..4866c74ead0f 100644
--- a/drivers/cpufreq/qcom-cpufreq-nvmem.c
+++ b/drivers/cpufreq/qcom-cpufreq-nvmem.c
@@ -52,6 +52,7 @@ struct qcom_cpufreq_match_data {
char **pvs_name,
struct qcom_cpufreq_drv *drv);
const char **genpd_names;
+ const char *cpufreq_driver;
};
struct qcom_cpufreq_drv {
@@ -250,6 +251,7 @@ static const struct qcom_cpufreq_match_data match_data_kryo = {
static const struct qcom_cpufreq_match_data match_data_krait = {
.get_version = qcom_cpufreq_krait_name_version,
+ .cpufreq_driver = "krait-cpufreq",
};
static const char *qcs404_genpd_names[] = { "cpr", NULL };
@@ -385,6 +387,19 @@ static int qcom_cpufreq_probe(struct platform_device *pdev)
}
}
+ if (drv->data->cpufreq_driver) {
+ cpufreq_dt_pdev = platform_device_register_simple(
+ drv->data->cpufreq_driver, -1, NULL, 0);
+ if (!IS_ERR(cpufreq_dt_pdev)) {
+ platform_set_drvdata(pdev, drv);
+ return 0;
+ } else {
+ dev_err(cpu_dev,
+ "Failed to register dedicated %s cpufreq\n",
+ drv->data->cpufreq_driver);
+ }
+ }
+
cpufreq_dt_pdev = platform_device_register_simple("cpufreq-dt", -1,
NULL, 0);
if (!IS_ERR(cpufreq_dt_pdev)) {
--
2.29.2

View File

@ -0,0 +1,679 @@
From cc41a266280cad0b55319e614167c88dff344248 Mon Sep 17 00:00:00 2001
From: Ansuel Smith <ansuelsmth@gmail.com>
Date: Sat, 22 Feb 2020 16:33:10 +0100
Subject: [PATCH 1/8] cpufreq: add Krait dedicated scaling driver
This new driver is based on generic cpufreq-dt driver.
Krait SoCs have 2-4 cpu and one shared L2 cache that can
operate at different frequency based on the maximum cpu clk
across all core.
L2 frequency and voltage are scaled on every frequency change
if needed. On Krait SoCs is present a bug that can cause
transition problem between frequency bin, to workaround this
on more than one transition, the L2 frequency is first set to the
base rate and then to the target rate.
The L2 frequency use the OPP framework and use the opp-level
bindings to link the l2 freq to different cpu freq. This is needed
as the Krait l2 clk are note mapped 1:1 to the core clks and some
of the l2 clk is set based on a range of the cpu clks. If the driver
find a broken config (for example no opp-level set) the l2 scaling is
skipped.
Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
---
drivers/cpufreq/Kconfig.arm | 14 +-
drivers/cpufreq/Makefile | 2 +
drivers/cpufreq/qcom-cpufreq-krait.c | 589 +++++++++++++++++++++++++++
3 files changed, 604 insertions(+), 1 deletion(-)
create mode 100644 drivers/cpufreq/qcom-cpufreq-krait.c
--- a/drivers/cpufreq/Kconfig.arm
+++ b/drivers/cpufreq/Kconfig.arm
@@ -150,6 +150,18 @@ config ARM_QCOM_CPUFREQ_HW
The driver implements the cpufreq interface for this HW engine.
Say Y if you want to support CPUFreq HW.
+config ARM_QCOM_CPUFREQ_KRAIT
+ tristate "CPU Frequency scaling support for Krait SoCs"
+ depends on ARCH_QCOM || COMPILE_TEST
+ select PM_OPP
+ select ARM_QCOM_CPUFREQ_NVMEM
+ help
+ This adds the CPUFreq driver for Qualcomm Krait SoC based boards.
+ This scale the cache clk and regulator based on the different cpu
+ clks when scaling the different cores clk.
+
+ If in doubt, say N.
+
config ARM_RASPBERRYPI_CPUFREQ
tristate "Raspberry Pi cpufreq support"
depends on CLK_RASPBERRYPI || COMPILE_TEST
@@ -339,4 +351,4 @@ config ARM_PXA2xx_CPUFREQ
help
This add the CPUFreq driver support for Intel PXA2xx SOCs.
- If in doubt, say N.
+ If in doubt, say N.
\ No newline at end of file
--- a/drivers/cpufreq/Makefile
+++ b/drivers/cpufreq/Makefile
@@ -63,6 +63,7 @@ obj-$(CONFIG_ARM_PXA2xx_CPUFREQ) += pxa2
obj-$(CONFIG_PXA3xx) += pxa3xx-cpufreq.o
obj-$(CONFIG_ARM_QCOM_CPUFREQ_HW) += qcom-cpufreq-hw.o
obj-$(CONFIG_ARM_QCOM_CPUFREQ_NVMEM) += qcom-cpufreq-nvmem.o
+obj-$(CONFIG_ARM_QCOM_CPUFREQ_KRAIT) += qcom-cpufreq-krait.o
obj-$(CONFIG_ARM_RASPBERRYPI_CPUFREQ) += raspberrypi-cpufreq.o
obj-$(CONFIG_ARM_S3C2410_CPUFREQ) += s3c2410-cpufreq.o
obj-$(CONFIG_ARM_S3C2412_CPUFREQ) += s3c2412-cpufreq.o
@@ -86,6 +87,7 @@ obj-$(CONFIG_ARM_TEGRA186_CPUFREQ) += te
obj-$(CONFIG_ARM_TEGRA194_CPUFREQ) += tegra194-cpufreq.o
obj-$(CONFIG_ARM_TI_CPUFREQ) += ti-cpufreq.o
obj-$(CONFIG_ARM_VEXPRESS_SPC_CPUFREQ) += vexpress-spc-cpufreq.o
+obj-$(CONFIG_ARM_KRAIT_CPUFREQ) += krait-cpufreq.o
##################################################################################
--- /dev/null
+++ b/drivers/cpufreq/qcom-cpufreq-krait.c
@@ -0,0 +1,601 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/clk.h>
+#include <linux/cpu.h>
+#include <linux/cpufreq.h>
+#include <linux/cpumask.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/pm_opp.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/thermal.h>
+
+#include "cpufreq-dt.h"
+
+static struct platform_device *l2_pdev;
+
+struct private_data {
+ struct opp_table *opp_table;
+ struct device *cpu_dev;
+ const char *reg_name;
+ bool have_static_opps;
+};
+
+static int set_target(struct cpufreq_policy *policy, unsigned int index)
+{
+ struct private_data *priv = policy->driver_data;
+ unsigned long freq = policy->freq_table[index].frequency;
+ unsigned long target_freq = freq * 1000;
+ struct dev_pm_opp *opp;
+ unsigned int level;
+ int cpu, ret;
+
+ if (l2_pdev) {
+ /* find the max freq across all core */
+ for_each_present_cpu(cpu)
+ if (cpu != index)
+ target_freq = max(
+ target_freq,
+ (unsigned long)cpufreq_quick_get(cpu));
+
+ opp = dev_pm_opp_find_freq_exact(priv->cpu_dev, target_freq,
+ true);
+ if (IS_ERR(opp)) {
+ dev_err(&l2_pdev->dev, "failed to find OPP for %ld\n",
+ target_freq);
+ return PTR_ERR(opp);
+ }
+ level = dev_pm_opp_get_level(opp);
+ dev_pm_opp_put(opp);
+
+ opp = dev_pm_opp_find_level_exact(&l2_pdev->dev, level);
+ if (IS_ERR(opp)) {
+ dev_err(&l2_pdev->dev,
+ "failed to find level OPP for %d\n", level);
+ return PTR_ERR(opp);
+ }
+ target_freq = dev_pm_opp_get_freq(opp);
+ dev_pm_opp_put(opp);
+
+ ret = dev_pm_opp_set_rate(&l2_pdev->dev, target_freq);
+ if (ret)
+ return ret;
+
+ /*
+ * Hardware constraint:
+ * Krait CPU cannot operate at 384MHz with L2 at 1Ghz.
+ * Assume index 0 with the idle freq and level > 0 as
+ * any L2 freq > 384MHz.
+ * Skip CPU freq change in this corner case.
+ */
+ if (unlikely(index == 0 && level != 0)) {
+ dev_err(priv->cpu_dev, "Krait CPU can't operate at idle freq with L2 at 1GHz");
+ return -EINVAL;
+ }
+ }
+
+ ret = dev_pm_opp_set_rate(priv->cpu_dev, freq * 1000);
+ if (ret)
+ return ret;
+
+ arch_set_freq_scale(policy->related_cpus, freq,
+ policy->cpuinfo.max_freq);
+
+ return 0;
+}
+
+/*
+ * An earlier version of opp-v1 bindings used to name the regulator
+ * "cpu0-supply", we still need to handle that for backwards compatibility.
+ */
+static const char *find_supply_name(struct device *dev)
+{
+ struct device_node *np;
+ struct property *pp;
+ int cpu = dev->id;
+ const char *name = NULL;
+
+ np = of_node_get(dev->of_node);
+
+ /* This must be valid for sure */
+ if (WARN_ON(!np))
+ return NULL;
+
+ /* Try "cpu0" for older DTs */
+ if (!cpu) {
+ pp = of_find_property(np, "cpu0-supply", NULL);
+ if (pp) {
+ name = "cpu0";
+ goto node_put;
+ }
+ }
+
+ pp = of_find_property(np, "cpu-supply", NULL);
+ if (pp) {
+ name = "cpu";
+ goto node_put;
+ }
+
+ dev_dbg(dev, "no regulator for cpu%d\n", cpu);
+node_put:
+ of_node_put(np);
+ return name;
+}
+
+static int resources_available(void)
+{
+ struct device *cpu_dev;
+ struct regulator *cpu_reg;
+ struct clk *cpu_clk;
+ int ret = 0;
+ const char *name;
+
+ cpu_dev = get_cpu_device(0);
+ if (!cpu_dev) {
+ pr_err("failed to get cpu0 device\n");
+ return -ENODEV;
+ }
+
+ cpu_clk = clk_get(cpu_dev, NULL);
+ ret = PTR_ERR_OR_ZERO(cpu_clk);
+ if (ret) {
+ /*
+ * If cpu's clk node is present, but clock is not yet
+ * registered, we should try defering probe.
+ */
+ if (ret == -EPROBE_DEFER)
+ dev_dbg(cpu_dev, "clock not ready, retry\n");
+ else
+ dev_err(cpu_dev, "failed to get clock: %d\n", ret);
+
+ return ret;
+ }
+
+ clk_put(cpu_clk);
+
+ name = find_supply_name(cpu_dev);
+ /* Platform doesn't require regulator */
+ if (!name)
+ return 0;
+
+ cpu_reg = regulator_get_optional(cpu_dev, name);
+ ret = PTR_ERR_OR_ZERO(cpu_reg);
+ if (ret) {
+ /*
+ * If cpu's regulator supply node is present, but regulator is
+ * not yet registered, we should try defering probe.
+ */
+ if (ret == -EPROBE_DEFER)
+ dev_dbg(cpu_dev, "cpu0 regulator not ready, retry\n");
+ else
+ dev_dbg(cpu_dev, "no regulator for cpu0: %d\n", ret);
+
+ return ret;
+ }
+
+ regulator_put(cpu_reg);
+ return 0;
+}
+
+static int cpufreq_init(struct cpufreq_policy *policy)
+{
+ struct cpufreq_frequency_table *freq_table;
+ struct opp_table *opp_table = NULL;
+ unsigned int transition_latency;
+ struct private_data *priv;
+ struct device *cpu_dev;
+ bool fallback = false;
+ struct clk *cpu_clk;
+ const char *name;
+ int ret;
+
+ cpu_dev = get_cpu_device(policy->cpu);
+ if (!cpu_dev) {
+ pr_err("failed to get cpu%d device\n", policy->cpu);
+ return -ENODEV;
+ }
+
+ cpu_clk = clk_get(cpu_dev, NULL);
+ if (IS_ERR(cpu_clk)) {
+ ret = PTR_ERR(cpu_clk);
+ dev_err(cpu_dev, "%s: failed to get clk: %d\n", __func__, ret);
+ return ret;
+ }
+
+ /* Get OPP-sharing information from "operating-points-v2" bindings */
+ ret = dev_pm_opp_of_get_sharing_cpus(cpu_dev, policy->cpus);
+ if (ret) {
+ if (ret != -ENOENT)
+ goto out_put_clk;
+
+ /*
+ * operating-points-v2 not supported, fallback to old method of
+ * finding shared-OPPs for backward compatibility if the
+ * platform hasn't set sharing CPUs.
+ */
+ if (dev_pm_opp_get_sharing_cpus(cpu_dev, policy->cpus))
+ fallback = true;
+ }
+
+ /*
+ * OPP layer will be taking care of regulators now, but it needs to know
+ * the name of the regulator first.
+ */
+ name = find_supply_name(cpu_dev);
+ if (name) {
+ opp_table = dev_pm_opp_set_regulators(cpu_dev, &name, 1);
+ if (IS_ERR(opp_table)) {
+ ret = PTR_ERR(opp_table);
+ dev_err(cpu_dev,
+ "Failed to set regulator for cpu%d: %d\n",
+ policy->cpu, ret);
+ goto out_put_clk;
+ }
+ }
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv) {
+ ret = -ENOMEM;
+ goto out_put_regulator;
+ }
+
+ priv->reg_name = name;
+ priv->opp_table = opp_table;
+
+ /*
+ * Initialize OPP tables for all policy->cpus. They will be shared by
+ * all CPUs which have marked their CPUs shared with OPP bindings.
+ *
+ * For platforms not using operating-points-v2 bindings, we do this
+ * before updating policy->cpus. Otherwise, we will end up creating
+ * duplicate OPPs for policy->cpus.
+ *
+ * OPPs might be populated at runtime, don't check for error here
+ */
+ if (!dev_pm_opp_of_cpumask_add_table(policy->cpus))
+ priv->have_static_opps = true;
+
+ /*
+ * But we need OPP table to function so if it is not there let's
+ * give platform code chance to provide it for us.
+ */
+ ret = dev_pm_opp_get_opp_count(cpu_dev);
+ if (ret <= 0) {
+ dev_dbg(cpu_dev, "OPP table is not ready, deferring probe\n");
+ ret = -EPROBE_DEFER;
+ goto out_free_opp;
+ }
+
+ if (fallback) {
+ cpumask_setall(policy->cpus);
+
+ /*
+ * OPP tables are initialized only for policy->cpu, do it for
+ * others as well.
+ */
+ ret = dev_pm_opp_set_sharing_cpus(cpu_dev, policy->cpus);
+ if (ret)
+ dev_err(cpu_dev,
+ "%s: failed to mark OPPs as shared: %d\n",
+ __func__, ret);
+ }
+
+ ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table);
+ if (ret) {
+ dev_err(cpu_dev, "failed to init cpufreq table: %d\n", ret);
+ goto out_free_opp;
+ }
+
+ priv->cpu_dev = cpu_dev;
+
+ policy->driver_data = priv;
+ policy->clk = cpu_clk;
+ policy->freq_table = freq_table;
+
+ policy->suspend_freq = dev_pm_opp_get_suspend_opp_freq(cpu_dev) / 1000;
+
+ transition_latency = dev_pm_opp_get_max_transition_latency(cpu_dev);
+ if (!transition_latency)
+ transition_latency = CPUFREQ_ETERNAL;
+
+ policy->cpuinfo.transition_latency = transition_latency;
+ policy->dvfs_possible_from_any_cpu = true;
+
+ dev_pm_opp_of_register_em(cpu_dev, policy->cpus);
+
+ return 0;
+
+out_free_opp:
+ if (priv->have_static_opps)
+ dev_pm_opp_of_cpumask_remove_table(policy->cpus);
+ kfree(priv);
+out_put_regulator:
+ if (name)
+ dev_pm_opp_put_regulators(opp_table);
+out_put_clk:
+ clk_put(cpu_clk);
+
+ return ret;
+}
+
+static int cpufreq_online(struct cpufreq_policy *policy)
+{
+ /* We did light-weight tear down earlier, nothing to do here */
+ return 0;
+}
+
+static int cpufreq_offline(struct cpufreq_policy *policy)
+{
+ /*
+ * Preserve policy->driver_data and don't free resources on light-weight
+ * tear down.
+ */
+ return 0;
+}
+
+static int cpufreq_exit(struct cpufreq_policy *policy)
+{
+ struct private_data *priv = policy->driver_data;
+
+ dev_pm_opp_free_cpufreq_table(priv->cpu_dev, &policy->freq_table);
+ if (priv->have_static_opps)
+ dev_pm_opp_of_cpumask_remove_table(policy->related_cpus);
+ if (priv->reg_name)
+ dev_pm_opp_put_regulators(priv->opp_table);
+
+ clk_put(policy->clk);
+ kfree(priv);
+
+ return 0;
+}
+
+static struct cpufreq_driver krait_cpufreq_driver = {
+ .flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK |
+ CPUFREQ_IS_COOLING_DEV,
+ .verify = cpufreq_generic_frequency_table_verify,
+ .target_index = set_target,
+ .get = cpufreq_generic_get,
+ .init = cpufreq_init,
+ .exit = cpufreq_exit,
+ .online = cpufreq_online,
+ .offline = cpufreq_offline,
+ .name = "krait-cpufreq",
+ .suspend = cpufreq_generic_suspend,
+};
+
+struct krait_data {
+ unsigned long idle_freq;
+ bool regulator_enabled;
+};
+
+static int krait_cache_set_opp(struct dev_pm_set_opp_data *data)
+{
+ unsigned long old_freq = data->old_opp.rate, freq = data->new_opp.rate;
+ struct dev_pm_opp_supply *supply = &data->new_opp.supplies[0];
+ struct regulator *reg = data->regulators[0];
+ struct clk *clk = data->clk;
+ struct krait_data *kdata;
+ unsigned long idle_freq;
+ int ret;
+
+ kdata = (struct krait_data *)dev_get_drvdata(data->dev);
+ idle_freq = kdata->idle_freq;
+
+ /* Scaling up? Scale voltage before frequency */
+ if (freq >= old_freq) {
+ ret = regulator_set_voltage_triplet(reg, supply->u_volt_min,
+ supply->u_volt,
+ supply->u_volt_max);
+ if (ret)
+ goto exit;
+ }
+
+ /*
+ * Set to idle bin if switching from normal to high bin
+ * or vice versa. It has been notice that a bug is triggered
+ * in cache scaling when more than one bin is scaled, to fix
+ * this we first need to transition to the base rate and then
+ * to target rate
+ */
+ if (likely(freq != idle_freq && old_freq != idle_freq)) {
+ ret = clk_set_rate(clk, idle_freq);
+ if (ret)
+ goto exit;
+ }
+
+ ret = clk_set_rate(clk, freq);
+ if (ret)
+ goto exit;
+
+ /* Scaling down? Scale voltage after frequency */
+ if (freq < old_freq) {
+ ret = regulator_set_voltage_triplet(reg, supply->u_volt_min,
+ supply->u_volt,
+ supply->u_volt_max);
+ }
+
+ if (unlikely(!kdata->regulator_enabled)) {
+ ret = regulator_enable(reg);
+ if (ret < 0)
+ dev_warn(data->dev, "Failed to enable regulator: %d", ret);
+ else
+ kdata->regulator_enabled = true;
+ }
+
+exit:
+ return ret;
+};
+
+static int krait_cache_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct krait_data *data;
+ struct opp_table *table;
+ struct dev_pm_opp *opp;
+ struct device *cpu_dev;
+ int ret;
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ table = dev_pm_opp_set_regulators(dev, (const char *[]){ "l2" }, 1);
+ if (IS_ERR(table)) {
+ ret = PTR_ERR(table);
+ if (ret != -EPROBE_DEFER)
+ dev_err(dev, "failed to set regulators %d\n", ret);
+
+ return ret;
+ }
+
+ ret = PTR_ERR_OR_ZERO(
+ dev_pm_opp_register_set_opp_helper(dev, krait_cache_set_opp));
+ if (ret)
+ return ret;
+
+ ret = dev_pm_opp_of_add_table(dev);
+ if (ret) {
+ dev_err(dev, "failed to parse L2 freq thresholds\n");
+ return ret;
+ }
+
+ opp = dev_pm_opp_find_freq_ceil(dev, &data->idle_freq);
+ dev_pm_opp_put(opp);
+
+ /*
+ * Check opp-level configuration
+ * At least 2 level must be set or the cache will always be scaled
+ * the idle freq causing some performance problem
+ *
+ * In case of invalid configuration, the l2 scaling is skipped
+ */
+ cpu_dev = get_cpu_device(0);
+ if (!cpu_dev) {
+ pr_err("failed to get cpu0 device\n");
+ return -ENODEV;
+ }
+
+ /*
+ * Check if we have at least opp-level 1, 0 should always be set to
+ * the idle freq
+ */
+ opp = dev_pm_opp_find_level_exact(dev, 1);
+ if (IS_ERR(opp)) {
+ dev_err(dev,
+ "Invalid configuration found of l2 opp. Can't find opp-level 1");
+ goto invalid_conf;
+ }
+ dev_pm_opp_put(opp);
+
+ /*
+ * Check if we have at least opp-level 1 in the cpu opp, 0 should always
+ * be set to the idle freq
+ */
+ opp = dev_pm_opp_find_level_exact(cpu_dev, 1);
+ if (IS_ERR(opp)) {
+ dev_err(dev,
+ "Invalid configuration found of cpu opp. Can't find opp-level 1");
+ goto invalid_conf;
+ }
+ dev_pm_opp_put(opp);
+
+ platform_set_drvdata(pdev, data);
+
+ /* The l2 scaling is enabled by linking the cpufreq driver */
+ l2_pdev = pdev;
+
+ return 0;
+
+invalid_conf:
+ dev_pm_opp_remove_table(dev);
+ dev_pm_opp_put_regulators(table);
+ dev_pm_opp_unregister_set_opp_helper(table);
+
+ return -EINVAL;
+};
+
+static int krait_cache_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct opp_table *table = dev_pm_opp_get_opp_table(dev);
+
+ dev_pm_opp_remove_table(dev);
+ dev_pm_opp_put_regulators(table);
+ dev_pm_opp_unregister_set_opp_helper(table);
+
+ return 0;
+};
+
+static const struct of_device_id krait_cache_match_table[] = {
+ { .compatible = "qcom,krait-cache" },
+ {}
+};
+
+static struct platform_driver krait_cache_driver = {
+ .driver = {
+ .name = "krait-cache",
+ .of_match_table = krait_cache_match_table,
+ },
+ .probe = krait_cache_probe,
+ .remove = krait_cache_remove,
+};
+module_platform_driver(krait_cache_driver);
+
+static int krait_cpufreq_probe(struct platform_device *pdev)
+{
+ struct cpufreq_dt_platform_data *data = dev_get_platdata(&pdev->dev);
+ int ret;
+
+ /*
+ * All per-cluster (CPUs sharing clock/voltages) initialization is done
+ * from ->init(). In probe(), we just need to make sure that clk and
+ * regulators are available. Else defer probe and retry.
+ *
+ * FIXME: Is checking this only for CPU0 sufficient ?
+ */
+ ret = resources_available();
+ if (ret)
+ return ret;
+
+ if (data) {
+ if (data->have_governor_per_policy)
+ krait_cpufreq_driver.flags |=
+ CPUFREQ_HAVE_GOVERNOR_PER_POLICY;
+
+ krait_cpufreq_driver.resume = data->resume;
+ if (data->suspend)
+ krait_cpufreq_driver.suspend = data->suspend;
+ }
+
+ ret = cpufreq_register_driver(&krait_cpufreq_driver);
+ if (ret)
+ dev_err(&pdev->dev, "failed register driver: %d\n", ret);
+
+ return ret;
+}
+
+static int krait_cpufreq_remove(struct platform_device *pdev)
+{
+ cpufreq_unregister_driver(&krait_cpufreq_driver);
+ return 0;
+}
+
+static struct platform_driver krait_cpufreq_platdrv = {
+ .driver = {
+ .name = "krait-cpufreq",
+ },
+ .probe = krait_cpufreq_probe,
+ .remove = krait_cpufreq_remove,
+};
+module_platform_driver(krait_cpufreq_platdrv);
+
+MODULE_ALIAS("platform:krait-cpufreq");
+MODULE_AUTHOR("Ansuel Smith <ansuelsmth@gmail.com>");
+MODULE_DESCRIPTION("Dedicated Krait SoC cpufreq driver");
+MODULE_LICENSE("GPL");

View File

@ -0,0 +1,243 @@
From c9ecd920324a647bf1f2b47f771c8f599cc7b551 Mon Sep 17 00:00:00 2001
From: Ansuel Smith <ansuelsmth@gmail.com>
Date: Sat, 22 Feb 2020 18:02:17 +0100
Subject: [PATCH 2/8] Documentation: cpufreq: add qcom,krait-cache bindings
Document dedicated cpufreq for Krait CPUs.
Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
---
.../bindings/cpufreq/qcom-cpufreq-krait.yaml | 221 ++++++++++++++++++
1 file changed, 221 insertions(+)
create mode 100644 Documentation/devicetree/bindings/cpufreq/qcom-cpufreq-krait.yaml
diff --git a/Documentation/devicetree/bindings/cpufreq/qcom-cpufreq-krait.yaml b/Documentation/devicetree/bindings/cpufreq/qcom-cpufreq-krait.yaml
new file mode 100644
index 000000000000..f6bcca863d9a
--- /dev/null
+++ b/Documentation/devicetree/bindings/cpufreq/qcom-cpufreq-krait.yaml
@@ -0,0 +1,221 @@
+# SPDX-License-Identifier: GPL-2.0
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/cpufreq/qcom-cpufreq-krait.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: CPU Frequency scaling driver for Krait SoCs
+
+maintainers:
+ - Ansuel Smith <ansuelsmth@gmail.com>
+
+description: |
+ The krait cpufreq driver is a dedicated frequency scaling driver
+ based on cpufreq-dt generic driver that scale L2 cache and the
+ cores. TEST
+
+ The L2 cache is scaled based on the max clk across all cores and
+ the clock is decided based on the opp-level set in the device tree.
+
+ Different core freq can be linked to a specific l2 freq and the driver
+ on frequency change will scale the core and the l2 clk based of the
+ linked freq.
+
+ On Krait SoC is present a bug and on every L2 clk change the driver
+ needs to set the clk to the idle freq before changing it to the new value.
+
+ This requires the qcom cpufreq nvmem driver to parse the different opp
+ core clk and an additional opp table for the l2 scaling.
+
+ If the driver detect broken config (for example missing opp-level) the
+ cpufreq driver skips the l2 scaling
+
+ Referring to this example opp-level can be used to link a range of cpu freq
+ to a specific l2 freq:
+ cpu opp freq 384000000 has opp-level 0
+ l2 opp freq 384000000 has opp-level 0
+ The driver will scale l2 to 384000000
+
+ cpu opp freq 600000000-1000000000 has opp-level 1
+ l2 opp freq 1000000000 has opp-level 1
+ The driver will scale l2 to 1000000000
+
+allOf:
+ - $ref: /schemas/cache-controller.yaml#
+
+select:
+ properties:
+ compatible:
+ items:
+ - enum:
+ - qcom,krait-cache
+
+ required:
+ - compatible
+
+properties:
+ compatible:
+ items:
+ - const: qcom,krait-cache
+ - const: cache
+
+ cache-level:
+ const: 2
+
+ clocks:
+ maxItems: 1
+
+ clock-names:
+ const: l2
+
+ l2-supply: true
+
+ operating-points-v2: true
+
+required:
+ - compatible
+ - cache-level
+ - clocks
+ - clock-names
+ - l2-supply
+ - operating-points-v2
+
+additionalProperties: false
+
+examples:
+ - |
+ cpus {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ cpu0: cpu@0 {
+ compatible = "qcom,krait";
+ enable-method = "qcom,kpss-acc-v1";
+ device_type = "cpu";
+ reg = <0>;
+ next-level-cache = <&L2>;
+ qcom,acc = <&acc0>;
+ qcom,saw = <&saw0>;
+ clocks = <&kraitcc 0>, <&kraitcc 4>;
+ clock-names = "cpu", "l2";
+ clock-latency = <100000>;
+ cpu-supply = <&smb208_s2a>;
+ operating-points-v2 = <&opp_table0>;
+ voltage-tolerance = <5>;
+ cooling-min-state = <0>;
+ cooling-max-state = <10>;
+ #cooling-cells = <2>;
+ cpu-idle-states = <&CPU_SPC>;
+ };
+
+ /* ... */
+
+ };
+
+ opp_table0: opp_table0 {
+ compatible = "operating-points-v2-kryo-cpu";
+ nvmem-cells = <&speedbin_efuse>;
+
+ opp-384000000 {
+ opp-hz = /bits/ 64 <384000000>;
+ opp-microvolt-speed0-pvs0-v0 = <1000000>;
+ opp-microvolt-speed0-pvs1-v0 = <925000>;
+ opp-microvolt-speed0-pvs2-v0 = <875000>;
+ opp-microvolt-speed0-pvs3-v0 = <800000>;
+ opp-supported-hw = <0x1>;
+ clock-latency-ns = <100000>;
+ opp-level = <0>;
+ };
+
+ opp-600000000 {
+ opp-hz = /bits/ 64 <600000000>;
+ opp-microvolt-speed0-pvs0-v0 = <1050000>;
+ opp-microvolt-speed0-pvs1-v0 = <975000>;
+ opp-microvolt-speed0-pvs2-v0 = <925000>;
+ opp-microvolt-speed0-pvs3-v0 = <850000>;
+ opp-supported-hw = <0x1>;
+ clock-latency-ns = <100000>;
+ opp-level = <1>;
+ };
+
+ opp-800000000 {
+ opp-hz = /bits/ 64 <800000000>;
+ opp-microvolt-speed0-pvs0-v0 = <1100000>;
+ opp-microvolt-speed0-pvs1-v0 = <1025000>;
+ opp-microvolt-speed0-pvs2-v0 = <995000>;
+ opp-microvolt-speed0-pvs3-v0 = <900000>;
+ opp-supported-hw = <0x1>;
+ clock-latency-ns = <100000>;
+ opp-level = <1>;
+ };
+
+ opp-1000000000 {
+ opp-hz = /bits/ 64 <1000000000>;
+ opp-microvolt-speed0-pvs0-v0 = <1150000>;
+ opp-microvolt-speed0-pvs1-v0 = <1075000>;
+ opp-microvolt-speed0-pvs2-v0 = <1025000>;
+ opp-microvolt-speed0-pvs3-v0 = <950000>;
+ opp-supported-hw = <0x1>;
+ clock-latency-ns = <100000>;
+ opp-level = <1>;
+ };
+
+ opp-1200000000 {
+ opp-hz = /bits/ 64 <1200000000>;
+ opp-microvolt-speed0-pvs0-v0 = <1200000>;
+ opp-microvolt-speed0-pvs1-v0 = <1125000>;
+ opp-microvolt-speed0-pvs2-v0 = <1075000>;
+ opp-microvolt-speed0-pvs3-v0 = <1000000>;
+ opp-supported-hw = <0x1>;
+ clock-latency-ns = <100000>;
+ opp-level = <2>;
+ };
+
+ opp-1400000000 {
+ opp-hz = /bits/ 64 <1400000000>;
+ opp-microvolt-speed0-pvs0-v0 = <1250000>;
+ opp-microvolt-speed0-pvs1-v0 = <1175000>;
+ opp-microvolt-speed0-pvs2-v0 = <1125000>;
+ opp-microvolt-speed0-pvs3-v0 = <1050000>;
+ opp-supported-hw = <0x1>;
+ clock-latency-ns = <100000>;
+ opp-level = <2>;
+ };
+ };
+
+ opp_table_l2: opp_table_l2 {
+ compatible = "operating-points-v2";
+
+ opp-384000000 {
+ opp-hz = /bits/ 64 <384000000>;
+ opp-microvolt = <1100000>;
+ clock-latency-ns = <100000>;
+ opp-level = <0>;
+ };
+ opp-1000000000 {
+ opp-hz = /bits/ 64 <1000000000>;
+ opp-microvolt = <1100000>;
+ clock-latency-ns = <100000>;
+ opp-level = <1>;
+ };
+ opp-1200000000 {
+ opp-hz = /bits/ 64 <1200000000>;
+ opp-microvolt = <1150000>;
+ clock-latency-ns = <100000>;
+ opp-level = <2>;
+ };
+ };
+
+ soc {
+ L2: l2-cache {
+ compatible = "qcom,krait-cache", "cache";
+ cache-level = <2>;
+
+ clocks = <&kraitcc 4>;
+ clock-names = "l2";
+ l2-supply = <&smb208_s1a>;
+ operating-points-v2 = <&opp_table_l2>;
+ };
+ };
+
+...
--
2.29.2

View File

@ -217,9 +217,9 @@
+int scale_fabrics(unsigned long max_cpu_freq);
+
+#endif
--- a/drivers/cpufreq/cpufreq-dt.c
+++ b/drivers/cpufreq/cpufreq-dt.c
@@ -20,6 +20,7 @@
--- a/drivers/cpufreq/qcom-cpufreq-krait.c
+++ b/drivers/cpufreq/qcom-cpufreq-krait.c
@@ -15,6 +15,7 @@
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <linux/thermal.h>
@ -227,17 +227,17 @@
#include "cpufreq-dt.h"
@@ -111,6 +112,13 @@ static int set_target(struct cpufreq_pol
}
}
}
@@ -58,6 +59,13 @@ static int set_target(struct cpufreq_policy *policy, unsigned int index)
level = dev_pm_opp_get_level(opp);
dev_pm_opp_put(opp);
+ /*
+ * Scale fabrics with max freq across all cores
+ */
+ ret = scale_fabrics(target_freq);
+ if (ret)
+ return ret;
+
+ /*
+ * Scale fabrics with max freq across all cores
+ */
+ ret = scale_fabrics(target_freq);
+ if (ret)
+ goto exit;
}
priv->opp_freq = freq * 1000;
arch_set_freq_scale(policy->related_cpus, freq,
opp = dev_pm_opp_find_level_exact(&l2_pdev->dev, level);
if (IS_ERR(opp)) {
dev_err(&l2_pdev->dev,