From: Tobias Heider 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 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