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:

NetBSD Home
NetBSD PR Database Search

(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.