Index | Thread | Search

From:
Tobias Heider <tobias.heider@stusta.de>
Subject:
scmi: hook up to cpu_* to get apm working
To:
tech@openbsd.org
Cc:
patrick@openbsd.org, kettenis@openbsd.org
Date:
Thu, 22 May 2025 07:16:17 +0200

Download raw body.

Thread
On Snapdragon X Elite chips we have to use ARM SCMI to control cpu performance.
Our driver now supports reading the current performance level, translating
it to HZ and exporting it as a sensor as well as setting initial levels.

I think it is time to hook everything up to cpu_setperf and cpu_clockspeed to
make those controls available to apm and hw.setperf.
Loosely inspired by aplcpu which deals with a similar problem on Apple machines.

ok? test feedback?

Index: scmi.c
===================================================================
RCS file: /cvs/src/sys/dev/fdt/scmi.c,v
diff -u -p -r1.3 scmi.c
--- scmi.c	22 May 2025 03:04:01 -0000	1.3
+++ scmi.c	22 May 2025 03:37:38 -0000
@@ -484,6 +484,9 @@ int	scmi_perf_level_get(struct scmi_soft
 int	scmi_perf_level_set(struct scmi_softc *, int, int);
 void	scmi_perf_refresh_sensor(void *);
 
+int	scmi_perf_cpuspeed(int *);
+void	scmi_perf_cpusetperf(int);
+
 void
 scmi_attach_perf(struct scmi_softc *sc, int node)
 {
@@ -558,6 +561,10 @@ scmi_attach_perf(struct scmi_softc *sc, 
 	}
 	sensordev_install(&sc->sc_perf_sensordev);
 	sensor_task_register(sc, scmi_perf_refresh_sensor, 1);
+
+	cpu_setperf = scmi_perf_cpusetperf;
+	cpu_cpuspeed = scmi_perf_cpuspeed;
+
 	return;
 err:
 	free(sc->sc_perf_fsensors, M_DEVBUF,
@@ -647,6 +654,63 @@ scmi_perf_level_set(struct scmi_softc *s
 		return -1;
 	}
 	return 0;
+}
+
+int
+scmi_perf_cpuspeed(int *freq)
+{
+	struct scmi_softc *sc;
+	int i, level = -1;
+	uint64_t opp_hz = 0;
+
+        for (i = 0; i < scmi_cd.cd_ndevs; i++) {
+                sc = scmi_cd.cd_devs[i];
+                if (sc == NULL)
+                        continue;
+
+		if (sc->sc_perf_domains == NULL)
+			continue;
+
+		for (i = 0; i < sc->sc_perf_ndomains; i++) {
+			if (sc->sc_perf_domains[i].pd_levels == NULL)
+				return EINVAL;
+
+			level = scmi_perf_level_get(sc, i);
+			opp_hz = MAX(opp_hz, (uint64_t)sc->sc_perf_domains[i].
+		    	    pd_levels[level].pl_ifreq * 1000);
+		}
+	}
+
+	if (opp_hz == 0)
+		return EINVAL;
+
+	*freq = opp_hz / 1000000;
+	return 0;
+}
+
+void
+scmi_perf_cpusetperf(int level)
+{
+	struct scmi_softc *sc;
+	size_t nlevels;
+	int i;
+
+        for (i = 0; i < scmi_cd.cd_ndevs; i++) {
+                sc = scmi_cd.cd_devs[i];
+                if (sc == NULL)
+			return;
+		if (sc->sc_perf_domains == NULL)
+			continue;
+
+		/* Find number of levels per domain */
+		for (i = 0; i < sc->sc_perf_ndomains; i++) {
+			nlevels = sc->sc_perf_domains[i].pd_nlevels;
+			if (nlevels == 0)
+				continue;
+			scmi_perf_level_set(sc, i,
+			    (level * (nlevels - 1) / 100));
+		}
+	}
 }
 
 void