NetBSD Problem Report #60671
From www@netbsd.org Mon Aug 31 06:11:08 2026
Return-Path: <www@netbsd.org>
Received: from mail.netbsd.org (mail.netbsd.org [199.233.217.200])
(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)
key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256
client-signature RSA-PSS (2048 bits) client-digest SHA256)
(Client CN "mail.netbsd.org", Issuer "YR2" (not verified))
by mollari.NetBSD.org (Postfix) with ESMTPS id E9BAC1A923B
for <gnats-bugs@gnats.NetBSD.org>; Mon, 31 Aug 2026 06:11:07 +0000 (UTC)
Message-Id: <20260831061106.2AD811A923C@mollari.NetBSD.org>
Date: Mon, 31 Aug 2026 06:11:06 +0000 (UTC)
From: stefano@dragas.it
Reply-To: stefano@dragas.it
To: gnats-bugs@NetBSD.org
Subject: cpufreq_dt ignores min/max voltage ranges in opp-microvolt, breaking DVFS
X-Send-Pr-Version: www-1.0
X-From4GNATS: "stefano@dragas.it via gnats" <gnats-admin@NetBSD.org>
>Number: 60671
>Category: kern
>Synopsis: cpufreq_dt ignores min/max voltage ranges in opp-microvolt, breaking DVFS
>Confidential: no
>Severity: serious
>Priority: medium
>Responsible: yurix
>State: feedback
>Class: sw-bug
>Submitter-Id: net
>Arrival-Date: Mon Aug 31 06:15:00 +0000 2026
>Closed-Date:
>Last-Modified: Sun Oct 18 21:25:01 +0000 2026
>Originator: Stefano Marinelli
>Release: NetBSD 11.0_STABLE / netbsd-11
>Organization:
>Environment:
NetBSD opionenb 11.0_STABLE NetBSD 11.0_STABLE (GENERIC) #0: Tue Aug 25 20:15:20 UTC 2026 mkrepro@mkrepro.NetBSD.org:/usr/src/sys/arch/evbarm/compile/GENERIC evbarm
>Description:
The FDT cpufreq driver only stores the first value of the
"opp-microvolt" property when parsing operating-points-v2.
An OPP may specify three values:
opp-microvolt = <target min max>;
However, cpufreq_dt currently only stores opp_uv[0], and later requests
that exact voltage from the regulator using:
fdtbus_regulator_set_voltage(sc->sc_supply, new_uv, new_uv);
This causes CPU frequency transitions to fail when the regulator does not
provide the exact target voltage, but does provide a valid voltage within
the range specified by the OPP.
This can be reproduced on the Allwinner H3 Orange Pi boards.
For example, the available CPU frequencies are:
machdep.cpufreq.cpu0.available = 1008 816 648
On these boards the OPP table contains voltage ranges, while the GPIO
regulator provides a limited set of discrete voltages.
As a consequence, with the unmodified kernel:
# sysctl -w machdep.cpufreq.cpu0.target=648
sysctl: machdep.cpufreq.cpu0.target: Invalid argument
Interestingly, the frequency may already have changed before the
regulator operation fails:
# sysctl machdep.cpufreq.cpu0.current
machdep.cpufreq.cpu0.current = 648
Going upwards can fail before the clock transition:
# sysctl -w machdep.cpufreq.cpu0.target=816
machdep.cpufreq.cpu0.target: 648 -> 816
# sysctl -w machdep.cpufreq.cpu0.target=1008
sysctl: machdep.cpufreq.cpu0.target: Invalid argument
# sysctl machdep.cpufreq.cpu0.current
machdep.cpufreq.cpu0.current = 816
This also breaks estd. estd correctly selects a different CPU frequency,
but when the sysctl operation returns an error it exits with:
estd: Cannot set CPU frequency (maybe you aren't root?)
The problem appears to be that cpufreq_dt loses the min/max voltage
information supplied by operating-points-v2.
>How-To-Repeat:
On an Orange Pi One running NetBSD 11_STABLE:
# sysctl machdep.cpufreq
machdep.cpufreq.cpu0.available = 1008 816 648
Stop estd if it is running (it will crash anyway), then try:
# sysctl -w machdep.cpufreq.cpu0.target=816
# sysctl -w machdep.cpufreq.cpu0.target=648
# sysctl -w machdep.cpufreq.cpu0.target=816
# sysctl -w machdep.cpufreq.cpu0.target=1008
Some transitions return EINVAL.
Running estd and generating CPU load also reproduces the problem, as
estd exits when one of these transitions returns an error.
>Fix:
Modify cpufreq_dt so that it preserves the target, minimum and maximum
values from:
opp-microvolt = <target min max>;
and pass the valid voltage range to fdtbus_regulator_set_voltage()
instead of requiring the exact target voltage.
For legacy OPP tables containing only one voltage, min and max remain
equal to the target voltage, preserving the existing behaviour.
With this change applied to netbsd-11, I rebuilt a GENERIC
evbarm/earmv7hf kernel and tested it on an Orange Pi One.
All transitions now complete successfully:
1008 -> 816
816 -> 648
648 -> 816
816 -> 1008
All sysctl operations return success, and estd can dynamically scale
between 648, 816 and 1008 MHz without terminating.
Patch follows:
diff --git a/sys/dev/fdt/cpufreq_dt.c b/sys/dev/fdt/cpufreq_dt.c
index d80eb90a5..741db01da 100644
--- a/sys/dev/fdt/cpufreq_dt.c
+++ b/sys/dev/fdt/cpufreq_dt.c
@@ -55,6 +55,8 @@ static kmutex_t cpufreq_dt_tables_lock;
struct cpufreq_dt_opp {
u_int freq_khz;
u_int voltage_uv;
+ u_int voltage_min_uv;
+ u_int voltage_max_uv;
u_int latency_ns;
};
@@ -94,6 +96,7 @@ cpufreq_dt_set_rate(struct cpufreq_dt_softc *sc, u_int freq_khz)
{
struct cpufreq_dt_opp *opp = NULL;
u_int old_rate, new_rate, old_uv, new_uv;
+ u_int new_min_uv, new_max_uv;
uint64_t xc;
int error;
ssize_t n;
@@ -109,6 +112,8 @@ cpufreq_dt_set_rate(struct cpufreq_dt_softc *sc, u_int freq_khz)
old_rate = clk_get_rate(sc->sc_clk);
new_rate = freq_khz * 1000;
new_uv = opp->voltage_uv;
+ new_min_uv = opp->voltage_min_uv;
+ new_max_uv = opp->voltage_max_uv;
if (old_rate == new_rate)
return 0;
@@ -120,7 +125,7 @@ cpufreq_dt_set_rate(struct cpufreq_dt_softc *sc, u_int freq_khz)
if (new_uv > old_uv) {
error = fdtbus_regulator_set_voltage(sc->sc_supply,
- new_uv, new_uv);
+ new_min_uv, new_max_uv);
if (error != 0)
return error;
}
@@ -137,7 +142,7 @@ cpufreq_dt_set_rate(struct cpufreq_dt_softc *sc, u_int freq_khz)
if (sc->sc_supply != NULL) {
if (new_uv < old_uv) {
error = fdtbus_regulator_set_voltage(sc->sc_supply,
- new_uv, new_uv);
+ new_min_uv, new_max_uv);
if (error != 0)
return error;
}
@@ -351,6 +356,10 @@ cpufreq_dt_parse_opp(struct cpufreq_dt_softc *sc)
for (i = 0; i < sc->sc_nopp; i++, opp += 2) {
sc->sc_opp[i].freq_khz = be32toh(opp[0]);
sc->sc_opp[i].voltage_uv = be32toh(opp[1]);
+ sc->sc_opp[i].voltage_min_uv =
+ sc->sc_opp[i].voltage_uv;
+ sc->sc_opp[i].voltage_max_uv =
+ sc->sc_opp[i].voltage_uv;
}
return 0;
@@ -444,11 +453,22 @@ cpufreq_dt_parse_opp_v2(struct cpufreq_dt_softc *sc)
if (of_getprop_uint64(opp_node, "opp-hz", &opp_hz) != 0)
return EINVAL;
opp_uv = fdtbus_get_prop(opp_node, "opp-microvolt", &len);
- if (opp_uv == NULL || len < 1)
+ if (opp_uv == NULL || len < sizeof(*opp_uv))
return EINVAL;
/* Table is in reverse order */
sc->sc_opp[index].freq_khz = (u_int)(opp_hz / 1000);
sc->sc_opp[index].voltage_uv = be32toh(opp_uv[0]);
+ if (len >= 3 * sizeof(*opp_uv)) {
+ sc->sc_opp[index].voltage_min_uv =
+ be32toh(opp_uv[1]);
+ sc->sc_opp[index].voltage_max_uv =
+ be32toh(opp_uv[2]);
+ } else {
+ sc->sc_opp[index].voltage_min_uv =
+ sc->sc_opp[index].voltage_uv;
+ sc->sc_opp[index].voltage_max_uv =
+ sc->sc_opp[index].voltage_uv;
+ }
of_getprop_uint32(opp_node, "clock-latency-ns", &sc->sc_opp[index].latency_ns);
--index;
}
Tested-by: Stefano Marinelli <stefano@dragas.it>
>Release-Note:
>Audit-Trail:
From: Yuri Honegger <yuri.honegger@icloud.com>
To: "gnats-bugs@netbsd.org" <gnats-bugs@NetBSD.org>
Cc:
Subject: Re: kern/60671
Date: Mon, 14 Sep 2026 21:36:04 +0200
About your patch: It looks correct, but it doesn=E2=80=99t modify the =
aprint calls. If we only communicate the range to the regulator, we =
should also update the printfs to report a range instead of a specific =
value to avoid confusion. And at that point, voltage_uv is basically =
unused and can be removed.
However, I think a better approach would be to change the fdt_regulator =
framework so that it accepts preferred_uv, min_uv and max_uv instead of =
only min_uv and max_uv.=20
Looking through some regulator implementations, most always use min_uv =
or the average as a substitute for a preferred_uv.
For example, tps65217 uses uv_min and does some rounding that can lead =
to values outside uv_min. Having a preferred_uv doesn=E2=80=99t solve =
the rounding issue, but it can add some margin.
In terms of scope, it looks doable (around 15 callsites and =
implementations).
I also thinks it is slightly less risky because it doesn=E2=80=99t =
change the value that reaches the regulator, so all regulators should =
continue to behave exactly as they do right now on boards unaffected by =
your issue.
From: "Yuri Honegger Yurix" <yurix@netbsd.org>
To: gnats-bugs@gnats.NetBSD.org
Cc:
Subject: PR/60671 CVS commit: src/sys/dev/fdt
Date: Sat, 17 Oct 2026 21:03:46 +0000
Module Name: src
Committed By: yurix
Date: Sat Oct 17 21:03:46 UTC 2026
Modified Files:
src/sys/dev/fdt: fdt_regulator.c fdt_regulator.h
Log Message:
fdtbus_regulator_set_voltage_triple: support target/min/max
Some dt-bindings specify voltages as target/min/max, while the NetBSD fdt subsystem only supported min/max.
This PR introduces fdtbus_regulator_set_voltage_triple that tries to set the voltage to an optimal value before falling back to the full range.
See PR 60671
To generate a diff of this commit:
cvs rdiff -u -r1.9 -r1.10 src/sys/dev/fdt/fdt_regulator.c
cvs rdiff -u -r1.1 -r1.2 src/sys/dev/fdt/fdt_regulator.h
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
From: "Yuri Honegger Yurix" <yurix@netbsd.org>
To: gnats-bugs@gnats.NetBSD.org
Cc:
Subject: PR/60671 CVS commit: src/sys/dev/fdt
Date: Sat, 17 Oct 2026 21:04:44 +0000
Module Name: src
Committed By: yurix
Date: Sat Oct 17 21:04:44 UTC 2026
Modified Files:
src/sys/dev/fdt: cpufreq_dt.c
Log Message:
cpufreq_dt: communicate full voltage range to regulators
In the last commit, fdtbus_regulator_set_voltage got changed to accept the voltage as a target, min, max triple from a min, max pair. This commit switched the cpufreq code over to sending the full voltage triple to regulators if it is available in the device tree.
This fixes PR 60671
To generate a diff of this commit:
cvs rdiff -u -r1.20 -r1.21 src/sys/dev/fdt/cpufreq_dt.c
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Responsible-Changed-From-To: kern-bug-people->yurix
Responsible-Changed-By: yurix@NetBSD.org
Responsible-Changed-When: Sun, 18 Oct 2026 18:43:02 +0000
Responsible-Changed-Why:
yurix has been devloping fixes for the issue
State-Changed-From-To: open->feedback
State-Changed-By: yurix@NetBSD.org
State-Changed-When: Sun, 18 Oct 2026 18:43:02 +0000
State-Changed-Why:
a fix has been commited
From: Yuri Honegger <yurix@NetBSD.org>
To: gnats-bugs@netbsd.org
Cc:
Subject: Re: kern/60671 (cpufreq_dt ignores min/max voltage ranges in
opp-microvolt, breaking DVFS)
Date: Sun, 18 Oct 2026 20:45:40 +0200
I=E2=80=99ve committed a fix that should solve your issue. Would you =
mind to test and report the result?=
From: stefano@dragas.it
To: gnats-bugs@netbsd.org
Cc: yurix@netbsd.org, gnats-admin@netbsd.org, netbsd-bugs@netbsd.org
Subject: Re: kern/60671 (cpufreq_dt ignores min/max voltage ranges in opp-microvolt, breaking DVFS)
Date: Sun, 18 Oct 2026 23:13:20 +0200
Thank you! I will test it ASAP.
Stefano
> Il giorno 18 ott 2026, alle ore 20:50, Yuri Honegger via gnats <gnats-admi=
n@netbsd.org> ha scritto:
>=20
> =EF=BB=BFThe following reply was made to PR kern/60671; it has been noted b=
y GNATS.
>=20
> From: Yuri Honegger <yurix@NetBSD.org>
> To: gnats-bugs@netbsd.org
> Cc:
> Subject: Re: kern/60671 (cpufreq_dt ignores min/max voltage ranges in
> opp-microvolt, breaking DVFS)
> Date: Sun, 18 Oct 2026 20:45:40 +0200
>=20
> I=3DE2=3D80=3D99ve committed a fix that should solve your issue. Would you=
=3D
> mind to test and report the result?=3D
>=20
>Unformatted:
(Contact us)
$NetBSD: query-full-pr,v 1.51 2026/08/10 02:28:17 riastradh Exp $
$NetBSD: gnats_config.sh,v 1.10 2026/05/13 22:00:09 riastradh Exp $
Copyright © 1994-2026
The NetBSD Foundation, Inc. ALL RIGHTS RESERVED.