1*5b1ba0c5Sjsg /* $OpenBSD: aplcpu.c,v 1.9 2024/09/29 09:25:37 jsg Exp $ */ 242e2ec98Skettenis /* 342e2ec98Skettenis * Copyright (c) 2022 Mark Kettenis <kettenis@openbsd.org> 442e2ec98Skettenis * 542e2ec98Skettenis * Permission to use, copy, modify, and distribute this software for any 642e2ec98Skettenis * purpose with or without fee is hereby granted, provided that the above 742e2ec98Skettenis * copyright notice and this permission notice appear in all copies. 842e2ec98Skettenis * 942e2ec98Skettenis * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 1042e2ec98Skettenis * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 1142e2ec98Skettenis * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 1242e2ec98Skettenis * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 1342e2ec98Skettenis * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 1442e2ec98Skettenis * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 1542e2ec98Skettenis * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 1642e2ec98Skettenis */ 1742e2ec98Skettenis 1842e2ec98Skettenis #include <sys/param.h> 1942e2ec98Skettenis #include <sys/systm.h> 2042e2ec98Skettenis #include <sys/device.h> 2142e2ec98Skettenis #include <sys/malloc.h> 22cf0fe25bSkettenis #include <sys/sensors.h> 2342e2ec98Skettenis #include <sys/sysctl.h> 2442e2ec98Skettenis 2542e2ec98Skettenis #include <machine/bus.h> 2642e2ec98Skettenis #include <machine/fdt.h> 2742e2ec98Skettenis 2842e2ec98Skettenis #include <dev/ofw/openfirm.h> 2942e2ec98Skettenis #include <dev/ofw/fdt.h> 3042e2ec98Skettenis 31cf0fe25bSkettenis #define DVFS_CMD 0x0020 32cf0fe25bSkettenis #define DVFS_CMD_BUSY (1U << 31) 33cf0fe25bSkettenis #define DVFS_CMD_SET (1 << 25) 34b8e1a447Skettenis #define DVFS_CMD_PS2_MASK (0x1f << 12) 35cf0fe25bSkettenis #define DVFS_CMD_PS2_SHIFT 12 36b8e1a447Skettenis #define DVFS_CMD_PS1_MASK (0x1f << 0) 37cf0fe25bSkettenis #define DVFS_CMD_PS1_SHIFT 0 3842e2ec98Skettenis 39cf0fe25bSkettenis #define DVFS_STATUS 0x50 40b8e1a447Skettenis #define DVFS_T8103_STATUS_CUR_PS_MASK (0xf << 4) 41b8e1a447Skettenis #define DVFS_T8103_STATUS_CUR_PS_SHIFT 4 42b8e1a447Skettenis #define DVFS_T8112_STATUS_CUR_PS_MASK (0x1f << 5) 43b8e1a447Skettenis #define DVFS_T8112_STATUS_CUR_PS_SHIFT 5 4442e2ec98Skettenis 45c4936e80Skettenis #define APLCPU_DEEP_WFI_LATENCY 10 /* microseconds */ 46c4936e80Skettenis 4742e2ec98Skettenis struct opp { 4842e2ec98Skettenis uint64_t opp_hz; 4942e2ec98Skettenis uint32_t opp_level; 5042e2ec98Skettenis }; 5142e2ec98Skettenis 5242e2ec98Skettenis struct opp_table { 5342e2ec98Skettenis LIST_ENTRY(opp_table) ot_list; 5442e2ec98Skettenis uint32_t ot_phandle; 5542e2ec98Skettenis 5642e2ec98Skettenis struct opp *ot_opp; 5742e2ec98Skettenis u_int ot_nopp; 5842e2ec98Skettenis uint64_t ot_opp_hz_min; 5942e2ec98Skettenis uint64_t ot_opp_hz_max; 6042e2ec98Skettenis }; 6142e2ec98Skettenis 6242e2ec98Skettenis #define APLCPU_MAX_CLUSTERS 8 6342e2ec98Skettenis 6442e2ec98Skettenis struct aplcpu_softc { 6542e2ec98Skettenis struct device sc_dev; 6642e2ec98Skettenis bus_space_tag_t sc_iot; 6742e2ec98Skettenis bus_space_handle_t sc_ioh[APLCPU_MAX_CLUSTERS]; 6842e2ec98Skettenis bus_size_t sc_ios[APLCPU_MAX_CLUSTERS]; 6942e2ec98Skettenis 7042e2ec98Skettenis int sc_node; 7142e2ec98Skettenis u_int sc_nclusters; 7242e2ec98Skettenis int sc_perflevel; 7342e2ec98Skettenis 74b8e1a447Skettenis uint32_t sc_cur_ps_mask; 75b8e1a447Skettenis u_int sc_cur_ps_shift; 76b8e1a447Skettenis 7742e2ec98Skettenis LIST_HEAD(, opp_table) sc_opp_tables; 7842e2ec98Skettenis struct opp_table *sc_opp_table[APLCPU_MAX_CLUSTERS]; 7942e2ec98Skettenis uint64_t sc_opp_hz_min; 8042e2ec98Skettenis uint64_t sc_opp_hz_max; 81cf0fe25bSkettenis 82cf0fe25bSkettenis struct ksensordev sc_sensordev; 83cf0fe25bSkettenis struct ksensor sc_sensor[APLCPU_MAX_CLUSTERS]; 8442e2ec98Skettenis }; 8542e2ec98Skettenis 8642e2ec98Skettenis int aplcpu_match(struct device *, void *, void *); 8742e2ec98Skettenis void aplcpu_attach(struct device *, struct device *, void *); 8842e2ec98Skettenis 8942e2ec98Skettenis const struct cfattach aplcpu_ca = { 9042e2ec98Skettenis sizeof (struct aplcpu_softc), aplcpu_match, aplcpu_attach 9142e2ec98Skettenis }; 9242e2ec98Skettenis 9342e2ec98Skettenis struct cfdriver aplcpu_cd = { 9442e2ec98Skettenis NULL, "aplcpu", DV_DULL 9542e2ec98Skettenis }; 9642e2ec98Skettenis 9742e2ec98Skettenis void aplcpu_opp_init(struct aplcpu_softc *, int); 98b8e1a447Skettenis uint32_t aplcpu_opp_level(struct aplcpu_softc *, int); 9942e2ec98Skettenis int aplcpu_clockspeed(int *); 10042e2ec98Skettenis void aplcpu_setperf(int level); 101cf0fe25bSkettenis void aplcpu_refresh_sensors(void *); 102*5b1ba0c5Sjsg void aplcpu_idle_cycle(void); 103c4936e80Skettenis void aplcpu_deep_wfi(void); 10442e2ec98Skettenis 10542e2ec98Skettenis int 10642e2ec98Skettenis aplcpu_match(struct device *parent, void *match, void *aux) 10742e2ec98Skettenis { 10842e2ec98Skettenis struct fdt_attach_args *faa = aux; 10942e2ec98Skettenis 11099901a0dSkettenis return OF_is_compatible(faa->fa_node, "apple,soc-cpufreq") || 11199901a0dSkettenis OF_is_compatible(faa->fa_node, "apple,cluster-cpufreq"); 11242e2ec98Skettenis } 11342e2ec98Skettenis 11442e2ec98Skettenis void 11542e2ec98Skettenis aplcpu_attach(struct device *parent, struct device *self, void *aux) 11642e2ec98Skettenis { 11742e2ec98Skettenis struct aplcpu_softc *sc = (struct aplcpu_softc *)self; 11842e2ec98Skettenis struct fdt_attach_args *faa = aux; 11942e2ec98Skettenis struct cpu_info *ci; 12042e2ec98Skettenis CPU_INFO_ITERATOR cii; 12142e2ec98Skettenis int i; 12242e2ec98Skettenis 12342e2ec98Skettenis if (faa->fa_nreg < 1) { 12442e2ec98Skettenis printf(": no registers\n"); 12542e2ec98Skettenis return; 12642e2ec98Skettenis } 12742e2ec98Skettenis 12842e2ec98Skettenis if (faa->fa_nreg > APLCPU_MAX_CLUSTERS) { 12942e2ec98Skettenis printf(": too many registers\n"); 13042e2ec98Skettenis return; 13142e2ec98Skettenis } 13242e2ec98Skettenis 13342e2ec98Skettenis sc->sc_iot = faa->fa_iot; 13442e2ec98Skettenis for (i = 0; i < faa->fa_nreg; i++) { 13542e2ec98Skettenis if (bus_space_map(sc->sc_iot, faa->fa_reg[i].addr, 13642e2ec98Skettenis faa->fa_reg[i].size, 0, &sc->sc_ioh[i])) { 13742e2ec98Skettenis printf(": can't map registers\n"); 13842e2ec98Skettenis goto unmap; 13942e2ec98Skettenis } 14042e2ec98Skettenis sc->sc_ios[i] = faa->fa_reg[i].size; 14142e2ec98Skettenis } 14242e2ec98Skettenis 14342e2ec98Skettenis printf("\n"); 14442e2ec98Skettenis 14542e2ec98Skettenis sc->sc_node = faa->fa_node; 14642e2ec98Skettenis sc->sc_nclusters = faa->fa_nreg; 14742e2ec98Skettenis 14899901a0dSkettenis if (OF_is_compatible(sc->sc_node, "apple,t8103-soc-cpufreq") || 14999901a0dSkettenis OF_is_compatible(sc->sc_node, "apple,t8103-cluster-cpufreq")) { 150b8e1a447Skettenis sc->sc_cur_ps_mask = DVFS_T8103_STATUS_CUR_PS_MASK; 151b8e1a447Skettenis sc->sc_cur_ps_shift = DVFS_T8103_STATUS_CUR_PS_SHIFT; 15299901a0dSkettenis } else if (OF_is_compatible(sc->sc_node, "apple,t8112-soc-cpufreq") || 15399901a0dSkettenis OF_is_compatible(sc->sc_node, "apple,t8112-cluster-cpufreq")) { 154b8e1a447Skettenis sc->sc_cur_ps_mask = DVFS_T8112_STATUS_CUR_PS_MASK; 155b8e1a447Skettenis sc->sc_cur_ps_shift = DVFS_T8112_STATUS_CUR_PS_SHIFT; 156b8e1a447Skettenis } 157b8e1a447Skettenis 15842e2ec98Skettenis sc->sc_opp_hz_min = UINT64_MAX; 15942e2ec98Skettenis sc->sc_opp_hz_max = 0; 16042e2ec98Skettenis 16142e2ec98Skettenis LIST_INIT(&sc->sc_opp_tables); 16242e2ec98Skettenis CPU_INFO_FOREACH(cii, ci) { 16342e2ec98Skettenis aplcpu_opp_init(sc, ci->ci_node); 16442e2ec98Skettenis } 16542e2ec98Skettenis 166cf0fe25bSkettenis for (i = 0; i < sc->sc_nclusters; i++) { 167cf0fe25bSkettenis sc->sc_sensor[i].type = SENSOR_FREQ; 168cf0fe25bSkettenis sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[i]); 169cf0fe25bSkettenis } 170cf0fe25bSkettenis 171cf0fe25bSkettenis aplcpu_refresh_sensors(sc); 172cf0fe25bSkettenis 173cf0fe25bSkettenis strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname, 174cf0fe25bSkettenis sizeof(sc->sc_sensordev.xname)); 175cf0fe25bSkettenis sensordev_install(&sc->sc_sensordev); 176cf0fe25bSkettenis sensor_task_register(sc, aplcpu_refresh_sensors, 1); 177cf0fe25bSkettenis 178c4936e80Skettenis cpu_idle_cycle_fcn = aplcpu_idle_cycle; 179c4936e80Skettenis cpu_suspend_cycle_fcn = aplcpu_deep_wfi; 18042e2ec98Skettenis cpu_cpuspeed = aplcpu_clockspeed; 18142e2ec98Skettenis cpu_setperf = aplcpu_setperf; 18242e2ec98Skettenis return; 18342e2ec98Skettenis 18442e2ec98Skettenis unmap: 18542e2ec98Skettenis for (i = 0; i < faa->fa_nreg; i++) { 18642e2ec98Skettenis if (sc->sc_ios[i] == 0) 18742e2ec98Skettenis continue; 18842e2ec98Skettenis bus_space_unmap(sc->sc_iot, sc->sc_ioh[i], sc->sc_ios[i]); 18942e2ec98Skettenis } 19042e2ec98Skettenis } 19142e2ec98Skettenis 19242e2ec98Skettenis void 19342e2ec98Skettenis aplcpu_opp_init(struct aplcpu_softc *sc, int node) 19442e2ec98Skettenis { 19542e2ec98Skettenis struct opp_table *ot; 19642e2ec98Skettenis int count, child; 19742e2ec98Skettenis uint32_t freq_domain[2], phandle; 19842e2ec98Skettenis uint32_t opp_hz, opp_level; 19942e2ec98Skettenis int i, j; 20042e2ec98Skettenis 20199901a0dSkettenis freq_domain[0] = OF_getpropint(node, "performance-domains", 0); 20299901a0dSkettenis freq_domain[1] = 0; 20399901a0dSkettenis if (freq_domain[0] == 0) { 20442e2ec98Skettenis if (OF_getpropintarray(node, "apple,freq-domain", freq_domain, 20542e2ec98Skettenis sizeof(freq_domain)) != sizeof(freq_domain)) 20642e2ec98Skettenis return; 20742e2ec98Skettenis if (freq_domain[1] > APLCPU_MAX_CLUSTERS) 20842e2ec98Skettenis return; 20999901a0dSkettenis } 21099901a0dSkettenis if (freq_domain[0] != OF_getpropint(sc->sc_node, "phandle", 0)) 21199901a0dSkettenis return; 21242e2ec98Skettenis 21342e2ec98Skettenis phandle = OF_getpropint(node, "operating-points-v2", 0); 21442e2ec98Skettenis if (phandle == 0) 21542e2ec98Skettenis return; 21642e2ec98Skettenis 21742e2ec98Skettenis LIST_FOREACH(ot, &sc->sc_opp_tables, ot_list) { 21842e2ec98Skettenis if (ot->ot_phandle == phandle) { 21942e2ec98Skettenis sc->sc_opp_table[freq_domain[1]] = ot; 22042e2ec98Skettenis return; 22142e2ec98Skettenis } 22242e2ec98Skettenis } 22342e2ec98Skettenis 22442e2ec98Skettenis node = OF_getnodebyphandle(phandle); 22542e2ec98Skettenis if (node == 0) 22642e2ec98Skettenis return; 22742e2ec98Skettenis 22842e2ec98Skettenis if (!OF_is_compatible(node, "operating-points-v2")) 22942e2ec98Skettenis return; 23042e2ec98Skettenis 23142e2ec98Skettenis count = 0; 232c4936e80Skettenis for (child = OF_child(node); child != 0; child = OF_peer(child)) 23342e2ec98Skettenis count++; 23442e2ec98Skettenis if (count == 0) 23542e2ec98Skettenis return; 23642e2ec98Skettenis 23742e2ec98Skettenis ot = malloc(sizeof(struct opp_table), M_DEVBUF, M_ZERO | M_WAITOK); 23842e2ec98Skettenis ot->ot_phandle = phandle; 23942e2ec98Skettenis ot->ot_opp = mallocarray(count, sizeof(struct opp), 24042e2ec98Skettenis M_DEVBUF, M_ZERO | M_WAITOK); 24142e2ec98Skettenis ot->ot_nopp = count; 24242e2ec98Skettenis 24342e2ec98Skettenis count = 0; 24442e2ec98Skettenis for (child = OF_child(node); child != 0; child = OF_peer(child)) { 24542e2ec98Skettenis opp_hz = OF_getpropint64(child, "opp-hz", 0); 24642e2ec98Skettenis opp_level = OF_getpropint(child, "opp-level", 0); 24742e2ec98Skettenis 24842e2ec98Skettenis /* Insert into the array, keeping things sorted. */ 24942e2ec98Skettenis for (i = 0; i < count; i++) { 25042e2ec98Skettenis if (opp_hz < ot->ot_opp[i].opp_hz) 25142e2ec98Skettenis break; 25242e2ec98Skettenis } 25342e2ec98Skettenis for (j = count; j > i; j--) 25442e2ec98Skettenis ot->ot_opp[j] = ot->ot_opp[j - 1]; 25542e2ec98Skettenis ot->ot_opp[i].opp_hz = opp_hz; 25642e2ec98Skettenis ot->ot_opp[i].opp_level = opp_level; 25742e2ec98Skettenis count++; 25842e2ec98Skettenis } 25942e2ec98Skettenis 26042e2ec98Skettenis ot->ot_opp_hz_min = ot->ot_opp[0].opp_hz; 26142e2ec98Skettenis ot->ot_opp_hz_max = ot->ot_opp[count - 1].opp_hz; 26242e2ec98Skettenis 26342e2ec98Skettenis LIST_INSERT_HEAD(&sc->sc_opp_tables, ot, ot_list); 26442e2ec98Skettenis sc->sc_opp_table[freq_domain[1]] = ot; 26542e2ec98Skettenis 26642e2ec98Skettenis /* Keep track of overall min/max frequency. */ 26742e2ec98Skettenis if (sc->sc_opp_hz_min > ot->ot_opp_hz_min) 26842e2ec98Skettenis sc->sc_opp_hz_min = ot->ot_opp_hz_min; 26942e2ec98Skettenis if (sc->sc_opp_hz_max < ot->ot_opp_hz_max) 27042e2ec98Skettenis sc->sc_opp_hz_max = ot->ot_opp_hz_max; 27142e2ec98Skettenis } 27242e2ec98Skettenis 273b8e1a447Skettenis uint32_t 274b8e1a447Skettenis aplcpu_opp_level(struct aplcpu_softc *sc, int cluster) 275b8e1a447Skettenis { 276b8e1a447Skettenis uint32_t opp_level; 277b8e1a447Skettenis uint64_t pstate; 278b8e1a447Skettenis 279b8e1a447Skettenis if (sc->sc_cur_ps_mask) { 280b8e1a447Skettenis pstate = bus_space_read_8(sc->sc_iot, sc->sc_ioh[cluster], 281b8e1a447Skettenis DVFS_STATUS); 282b8e1a447Skettenis opp_level = (pstate & sc->sc_cur_ps_mask); 283b8e1a447Skettenis opp_level >>= sc->sc_cur_ps_shift; 284b8e1a447Skettenis } else { 285b8e1a447Skettenis pstate = bus_space_read_8(sc->sc_iot, sc->sc_ioh[cluster], 286b8e1a447Skettenis DVFS_CMD); 287b8e1a447Skettenis opp_level = (pstate & DVFS_CMD_PS1_MASK); 288b8e1a447Skettenis opp_level >>= DVFS_CMD_PS1_SHIFT; 289b8e1a447Skettenis } 290b8e1a447Skettenis 291b8e1a447Skettenis return opp_level; 292b8e1a447Skettenis } 293b8e1a447Skettenis 29442e2ec98Skettenis int 29542e2ec98Skettenis aplcpu_clockspeed(int *freq) 29642e2ec98Skettenis { 29799901a0dSkettenis struct aplcpu_softc *sc; 29842e2ec98Skettenis struct opp_table *ot; 29942e2ec98Skettenis uint32_t opp_hz = 0, opp_level; 30099901a0dSkettenis int i, j, k; 30142e2ec98Skettenis 30242e2ec98Skettenis /* 30342e2ec98Skettenis * Clusters can run at different frequencies. We report the 30442e2ec98Skettenis * highest frequency among all clusters. 30542e2ec98Skettenis */ 30642e2ec98Skettenis 30799901a0dSkettenis for (i = 0; i < aplcpu_cd.cd_ndevs; i++) { 30899901a0dSkettenis sc = aplcpu_cd.cd_devs[i]; 30999901a0dSkettenis if (sc == NULL) 31042e2ec98Skettenis continue; 31142e2ec98Skettenis 31299901a0dSkettenis for (j = 0; j < sc->sc_nclusters; j++) { 31399901a0dSkettenis if (sc->sc_opp_table[j] == NULL) 31499901a0dSkettenis continue; 31599901a0dSkettenis 31699901a0dSkettenis opp_level = aplcpu_opp_level(sc, j); 31742e2ec98Skettenis 31842e2ec98Skettenis /* Translate P-state to frequency. */ 31999901a0dSkettenis ot = sc->sc_opp_table[j]; 32099901a0dSkettenis for (k = 0; k < ot->ot_nopp; k++) { 32199901a0dSkettenis if (ot->ot_opp[k].opp_level != opp_level) 32299901a0dSkettenis continue; 32399901a0dSkettenis opp_hz = MAX(opp_hz, ot->ot_opp[k].opp_hz); 32442e2ec98Skettenis } 32542e2ec98Skettenis } 32699901a0dSkettenis } 32799901a0dSkettenis 32842e2ec98Skettenis if (opp_hz == 0) 32942e2ec98Skettenis return EINVAL; 33042e2ec98Skettenis 33142e2ec98Skettenis *freq = opp_hz / 1000000; 33242e2ec98Skettenis return 0; 33342e2ec98Skettenis } 33442e2ec98Skettenis 33542e2ec98Skettenis void 33642e2ec98Skettenis aplcpu_setperf(int level) 33742e2ec98Skettenis { 33899901a0dSkettenis struct aplcpu_softc *sc; 33942e2ec98Skettenis struct opp_table *ot; 34042e2ec98Skettenis uint64_t min, max; 34142e2ec98Skettenis uint64_t level_hz; 34242e2ec98Skettenis uint32_t opp_level; 34342e2ec98Skettenis uint64_t reg; 34499901a0dSkettenis int i, j, k, timo; 34542e2ec98Skettenis 34642e2ec98Skettenis /* 34742e2ec98Skettenis * We let the CPU performance level span the entire range 34842e2ec98Skettenis * between the lowest frequency on any of the clusters and the 34942e2ec98Skettenis * highest frequency on any of the clusters. We pick a 35042e2ec98Skettenis * frequency within that range based on the performance level 35142e2ec98Skettenis * and set all the clusters to the frequency that is closest 35242e2ec98Skettenis * to but less than that frequency. This isn't a particularly 35342e2ec98Skettenis * sensible method but it is easy to implement and it is hard 35442e2ec98Skettenis * to come up with something more sensible given the 35542e2ec98Skettenis * constraints of the hw.setperf sysctl interface. 35642e2ec98Skettenis */ 35799901a0dSkettenis for (i = 0; i < aplcpu_cd.cd_ndevs; i++) { 35899901a0dSkettenis sc = aplcpu_cd.cd_devs[i]; 35999901a0dSkettenis if (sc == NULL) 36099901a0dSkettenis continue; 36199901a0dSkettenis 36242e2ec98Skettenis min = sc->sc_opp_hz_min; 36342e2ec98Skettenis max = sc->sc_opp_hz_max; 36442e2ec98Skettenis level_hz = min + (level * (max - min)) / 100; 36599901a0dSkettenis } 36642e2ec98Skettenis 36799901a0dSkettenis for (i = 0; i < aplcpu_cd.cd_ndevs; i++) { 36899901a0dSkettenis sc = aplcpu_cd.cd_devs[i]; 36999901a0dSkettenis if (sc == NULL) 37099901a0dSkettenis continue; 37199901a0dSkettenis if (sc->sc_perflevel == level) 37299901a0dSkettenis continue; 37399901a0dSkettenis 37499901a0dSkettenis for (j = 0; j < sc->sc_nclusters; j++) { 37599901a0dSkettenis if (sc->sc_opp_table[j] == NULL) 37642e2ec98Skettenis continue; 37742e2ec98Skettenis 37842e2ec98Skettenis /* Translate performance level to a P-state. */ 37999901a0dSkettenis ot = sc->sc_opp_table[j]; 380d206605eSkettenis opp_level = ot->ot_opp[0].opp_level; 38199901a0dSkettenis for (k = 0; k < ot->ot_nopp; k++) { 38299901a0dSkettenis if (ot->ot_opp[k].opp_hz <= level_hz && 38399901a0dSkettenis ot->ot_opp[k].opp_level >= opp_level) 38499901a0dSkettenis opp_level = ot->ot_opp[k].opp_level; 38542e2ec98Skettenis } 38642e2ec98Skettenis 38742e2ec98Skettenis /* Wait until P-state logic isn't busy. */ 38842e2ec98Skettenis for (timo = 100; timo > 0; timo--) { 38999901a0dSkettenis reg = bus_space_read_8(sc->sc_iot, 39099901a0dSkettenis sc->sc_ioh[j], DVFS_CMD); 391cf0fe25bSkettenis if ((reg & DVFS_CMD_BUSY) == 0) 39242e2ec98Skettenis break; 39342e2ec98Skettenis delay(1); 39442e2ec98Skettenis } 395cf0fe25bSkettenis if (reg & DVFS_CMD_BUSY) 39642e2ec98Skettenis continue; 39742e2ec98Skettenis 39842e2ec98Skettenis /* Set desired P-state. */ 399cf0fe25bSkettenis reg &= ~DVFS_CMD_PS1_MASK; 400cf0fe25bSkettenis reg |= (opp_level << DVFS_CMD_PS1_SHIFT); 401cf0fe25bSkettenis reg |= DVFS_CMD_SET; 40299901a0dSkettenis bus_space_write_8(sc->sc_iot, sc->sc_ioh[j], 40399901a0dSkettenis DVFS_CMD, reg); 40442e2ec98Skettenis } 40542e2ec98Skettenis 40642e2ec98Skettenis sc->sc_perflevel = level; 40742e2ec98Skettenis } 40899901a0dSkettenis } 409cf0fe25bSkettenis 410cf0fe25bSkettenis void 411cf0fe25bSkettenis aplcpu_refresh_sensors(void *arg) 412cf0fe25bSkettenis { 413cf0fe25bSkettenis struct aplcpu_softc *sc = arg; 414cf0fe25bSkettenis struct opp_table *ot; 415cf0fe25bSkettenis uint32_t opp_level; 416cf0fe25bSkettenis int i, j; 417cf0fe25bSkettenis 418cf0fe25bSkettenis for (i = 0; i < sc->sc_nclusters; i++) { 419cf0fe25bSkettenis if (sc->sc_opp_table[i] == NULL) 420cf0fe25bSkettenis continue; 421cf0fe25bSkettenis 422b8e1a447Skettenis opp_level = aplcpu_opp_level(sc, i); 423cf0fe25bSkettenis 424cf0fe25bSkettenis /* Translate P-state to frequency. */ 425cf0fe25bSkettenis ot = sc->sc_opp_table[i]; 426cf0fe25bSkettenis for (j = 0; j < ot->ot_nopp; j++) { 427cf0fe25bSkettenis if (ot->ot_opp[j].opp_level == opp_level) { 428cf0fe25bSkettenis sc->sc_sensor[i].value = ot->ot_opp[j].opp_hz; 429cf0fe25bSkettenis break; 430cf0fe25bSkettenis } 431cf0fe25bSkettenis } 432cf0fe25bSkettenis } 433cf0fe25bSkettenis } 434c4936e80Skettenis 435c4936e80Skettenis void 436c4936e80Skettenis aplcpu_idle_cycle(void) 437c4936e80Skettenis { 438c4936e80Skettenis struct cpu_info *ci = curcpu(); 439c4936e80Skettenis struct timeval start, stop; 440c4936e80Skettenis u_long itime; 441c4936e80Skettenis 442c4936e80Skettenis microuptime(&start); 443c4936e80Skettenis 444c4936e80Skettenis if (ci->ci_prev_sleep > 3 * APLCPU_DEEP_WFI_LATENCY) 445c4936e80Skettenis aplcpu_deep_wfi(); 446c4936e80Skettenis else 447c4936e80Skettenis cpu_wfi(); 448c4936e80Skettenis 449c4936e80Skettenis microuptime(&stop); 450c4936e80Skettenis timersub(&stop, &start, &stop); 451c4936e80Skettenis itime = stop.tv_sec * 1000000 + stop.tv_usec; 452c4936e80Skettenis 453c4936e80Skettenis ci->ci_last_itime = itime; 454c4936e80Skettenis itime >>= 1; 455c4936e80Skettenis ci->ci_prev_sleep = (ci->ci_prev_sleep + (ci->ci_prev_sleep >> 1) 456c4936e80Skettenis + itime) >> 1; 457c4936e80Skettenis } 458