[PATCH] cpufreq: dt: Set the initial OPP to apply its voltage constraints

Natanael Copa ncopa at alpinelinux.org
Wed Nov 11 05:04:26 PST 2026


The cpufreq core skips requests for the current frequency, so a policy
has no voltage constraint on its supply until its first frequency
change. Another policy sharing the supply can then lower the voltage
too far.

On the SpacemiT K1, both clusters share one supply and boot at 1.6 GHz.
When policy0 scales down, the supply drops to 0.95 V while policy4
still runs at 1.6 GHz, causing random crashes during boot.

Set the OPP for the current clock rate of all CPUs before registering
the driver.

Fixes: 3b9c34556fc1 ("riscv: dts: spacemit: Add cpu scaling for K1 SoC")
Cc: stable at vger.kernel.org
Assisted-by: Claude:claude-opus-5-5
Assisted-by: Claude:claude-fable-5-2
Signed-off-by: Natanael Copa <ncopa at alpinelinux.org>
---
Reproducer, booted with cpufreq.default_governor=performance:

  # echo 614400 > /sys/devices/system/cpu/cpufreq/policy0/scaling_max_freq

buck1 drops to 950000 uV while policy4 is still at 1600000 kHz, which
crashed my board within seconds.

Tested on a Banana Pi BPI-F3 with v7.3.3 and v7.4-rc2. Without the
patch, v7.3.3 crashed on every boot with schedutil (three). With the
patch, the reproducer keeps buck1 at 1050000 uV, and five boots with
schedutil were clean on each kernel. Not tested on other SoCs.

Other OPP users that never set their current OPP have the same gap. If
you would rather fix this in the OPP core, I can rework it.

Found and fixed with the help of an AI assistant (Claude). I reviewed
and tested the result.

 drivers/cpufreq/cpufreq-dt.c | 26 ++++++++++++++++++++++++++
 1 file changed, 26 insertions(+)

diff --git a/drivers/cpufreq/cpufreq-dt.c b/drivers/cpufreq/cpufreq-dt.c
index eb15650f99bf..2646dda10aac 100644
--- a/drivers/cpufreq/cpufreq-dt.c
+++ b/drivers/cpufreq/cpufreq-dt.c
@@ -153,6 +153,30 @@ static struct cpufreq_driver dt_cpufreq_driver = {
 	.suspend = cpufreq_generic_suspend,
 };
 
+/*
+ * Set the OPP for the current clock rate, so that its voltage constraints
+ * apply before any policy can change frequency. Otherwise another policy
+ * sharing the supply could lower the voltage below what this one needs.
+ */
+static void dt_cpufreq_set_initial_opp(struct device *cpu_dev)
+{
+	struct clk *clk = clk_get(cpu_dev, NULL);
+	unsigned long rate;
+	int ret;
+
+	if (IS_ERR(clk))
+		return;
+
+	rate = clk_get_rate(clk);
+	clk_put(clk);
+	if (!rate)
+		return;
+
+	ret = dev_pm_opp_set_rate(cpu_dev, rate);
+	if (ret)
+		dev_warn(cpu_dev, "failed to set initial OPP: %d\n", ret);
+}
+
 static int dt_cpufreq_early_init(struct device *dev, int cpu)
 {
 	struct private_data *priv;
@@ -251,6 +275,8 @@ static int dt_cpufreq_early_init(struct device *dev, int cpu)
 		goto out;
 	}
 
+	dt_cpufreq_set_initial_opp(cpu_dev);
+
 	list_add(&priv->node, &priv_list);
 	return 0;
 

base-commit: 067c5c24cef23d913b67eaaf71ae67e7169a6620
-- 
2.56.1




More information about the linux-riscv mailing list