1 /* $OpenBSD: qccpu.c,v 1.4 2024/10/10 23:15:27 jsg Exp $ */ 2 /* 3 * Copyright (c) 2023 Dale Rahn <drahn@openbsd.org> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include <sys/param.h> 19 #include <sys/systm.h> 20 #include <sys/device.h> 21 #include <sys/sensors.h> 22 23 #include <machine/intr.h> 24 #include <machine/bus.h> 25 #include <machine/fdt.h> 26 27 #include <dev/ofw/openfirm.h> 28 #include <dev/ofw/ofw_clock.h> 29 #include <dev/ofw/fdt.h> 30 31 #define CPUF_ENABLE 0x000 32 #define CPUF_DOMAIN_STATE 0x020 33 #define CPUF_DOMAIN_STATE_LVAL_M 0xff 34 #define CPUF_DOMAIN_STATE_LVAL_S 0 35 #define CPUF_DVCS_CTRL 0x0b0 36 #define CPUF_DVCS_CTRL_PER_CORE 0x1 37 #define CPUF_FREQ_LUT 0x100 38 #define CPUF_FREQ_LUT_SRC_M 0x1 39 #define CPUF_FREQ_LUT_SRC_S 30 40 #define CPUF_FREQ_LUT_CORES_M 0x7 41 #define CPUF_FREQ_LUT_CORES_S 16 42 #define CPUF_FREQ_LUT_LVAL_M 0xff 43 #define CPUF_FREQ_LUT_LVAL_S 0 44 #define CPUF_VOLT_LUT 0x200 45 #define CPUF_VOLT_LUT_IDX_M 0x2f 46 #define CPUF_VOLT_LUT_IDX_S 16 47 #define CPUF_VOLT_LUT_VOLT_M 0xfff 48 #define CPUF_VOLT_LUT_VOLT_S 0 49 #define CPUF_PERF_STATE 0x320 50 #define LUT_ROW_SIZE 4 51 52 #define NUM_GROUP 2 53 #define MAX_LUT 40 54 55 #define XO_FREQ_HZ 19200000 56 57 struct qccpu_softc { 58 struct device sc_dev; 59 bus_space_tag_t sc_iot; 60 bus_space_handle_t sc_ioh[NUM_GROUP]; 61 62 int sc_node; 63 64 struct clock_device sc_cd; 65 uint32_t sc_freq[NUM_GROUP][MAX_LUT]; 66 int sc_num_lut[NUM_GROUP]; 67 68 struct ksensordev sc_sensordev; 69 struct ksensor sc_hz_sensor[NUM_GROUP]; 70 }; 71 72 #define DEVNAME(sc) (sc)->sc_dev.dv_xname 73 74 int qccpu_match(struct device *, void *, void *); 75 void qccpu_attach(struct device *, struct device *, void *); 76 int qccpu_set_frequency(void *, uint32_t *, uint32_t); 77 uint32_t qccpu_get_frequency(void *, uint32_t *); 78 uint32_t qccpu_lut_to_freq(struct qccpu_softc *, int, uint32_t); 79 uint32_t qccpu_lut_to_cores(struct qccpu_softc *, int, uint32_t); 80 void qccpu_refresh_sensor(void *arg); 81 82 void qccpu_collect_lut(struct qccpu_softc *sc, int); 83 84 85 const struct cfattach qccpu_ca = { 86 sizeof (struct qccpu_softc), qccpu_match, qccpu_attach 87 }; 88 89 struct cfdriver qccpu_cd = { 90 NULL, "qccpu", DV_DULL 91 }; 92 93 int 94 qccpu_match(struct device *parent, void *match, void *aux) 95 { 96 struct fdt_attach_args *faa = aux; 97 98 return OF_is_compatible(faa->fa_node, "qcom,cpufreq-epss"); 99 } 100 101 void 102 qccpu_attach(struct device *parent, struct device *self, void *aux) 103 { 104 struct qccpu_softc *sc = (struct qccpu_softc *)self; 105 struct fdt_attach_args *faa = aux; 106 107 if (faa->fa_nreg < 2) { 108 printf(": no registers\n"); 109 return; 110 } 111 112 sc->sc_iot = faa->fa_iot; 113 if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, 114 faa->fa_reg[0].size, 0, &sc->sc_ioh[0])) { 115 printf(": can't map registers (cluster0)\n"); 116 return; 117 } 118 119 if (bus_space_map(sc->sc_iot, faa->fa_reg[1].addr, 120 faa->fa_reg[1].size, 0, &sc->sc_ioh[1])) { 121 printf(": can't map registers (cluster1)\n"); 122 return; 123 } 124 sc->sc_node = faa->fa_node; 125 126 printf("\n"); 127 128 qccpu_collect_lut(sc, 0); 129 qccpu_collect_lut(sc, 1); 130 131 sc->sc_cd.cd_node = faa->fa_node; 132 sc->sc_cd.cd_cookie = sc; 133 sc->sc_cd.cd_get_frequency = qccpu_get_frequency; 134 sc->sc_cd.cd_set_frequency = qccpu_set_frequency; 135 clock_register(&sc->sc_cd); 136 137 strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname, 138 sizeof(sc->sc_sensordev.xname)); 139 140 sc->sc_hz_sensor[0].type = SENSOR_FREQ; 141 sensor_attach(&sc->sc_sensordev, &sc->sc_hz_sensor[0]); 142 sc->sc_hz_sensor[1].type = SENSOR_FREQ; 143 sensor_attach(&sc->sc_sensordev, &sc->sc_hz_sensor[1]); 144 sensordev_install(&sc->sc_sensordev); 145 sensor_task_register(sc, qccpu_refresh_sensor, 1); 146 } 147 148 void 149 qccpu_collect_lut(struct qccpu_softc *sc, int group) 150 { 151 int prev_freq = 0; 152 uint32_t freq; 153 int idx; 154 bus_space_tag_t iot = sc->sc_iot; 155 bus_space_handle_t ioh = sc->sc_ioh[group]; 156 157 for (idx = 0; ; idx++) { 158 freq = bus_space_read_4(iot, ioh, 159 CPUF_FREQ_LUT + idx * LUT_ROW_SIZE); 160 161 if (idx != 0 && prev_freq == freq) { 162 sc->sc_num_lut[group] = idx; 163 break; 164 } 165 166 sc->sc_freq[group][idx] = freq; 167 168 #ifdef DEBUG 169 printf("%s: %d: %x %u\n", DEVNAME(sc), idx, freq, 170 qccpu_lut_to_freq(sc, idx, group)); 171 #endif /* DEBUG */ 172 173 prev_freq = freq; 174 if (idx >= MAX_LUT-1) 175 break; 176 } 177 178 return; 179 } 180 181 uint32_t 182 qccpu_get_frequency(void *cookie, uint32_t *cells) 183 { 184 struct qccpu_softc *sc = cookie; 185 bus_space_tag_t iot = sc->sc_iot; 186 bus_space_handle_t ioh; 187 uint32_t lval; 188 uint32_t group; 189 190 if (cells[0] >= NUM_GROUP) { 191 printf("%s: bad cell %d\n", __func__, cells[0]); 192 return 0; 193 } 194 group = cells[0]; 195 196 ioh = sc->sc_ioh[cells[0]]; 197 198 lval = (bus_space_read_4(iot, ioh, CPUF_DOMAIN_STATE) 199 >> CPUF_DOMAIN_STATE_LVAL_S) & CPUF_DOMAIN_STATE_LVAL_M; 200 return lval *XO_FREQ_HZ; 201 } 202 203 int 204 qccpu_set_frequency(void *cookie, uint32_t *cells, uint32_t freq) 205 { 206 struct qccpu_softc *sc = cookie; 207 bus_space_tag_t iot = sc->sc_iot; 208 bus_space_handle_t ioh; 209 int index = 0; 210 int numcores, i; 211 uint32_t group; 212 213 if (cells[0] >= NUM_GROUP) { 214 printf("%s: bad cell %d\n", __func__, cells[0]); 215 return 1; 216 } 217 group = cells[0]; 218 219 ioh = sc->sc_ioh[group]; 220 221 while (index < sc->sc_num_lut[group]) { 222 if (freq == qccpu_lut_to_freq(sc, index, group)) 223 break; 224 225 if (freq < qccpu_lut_to_freq(sc, index, group)) { 226 /* select next slower if not match, not zero */ 227 if (index != 0) 228 index = index - 1; 229 break; 230 } 231 232 index++; 233 } 234 235 #ifdef DEBUG 236 printf("%s called freq %u index %d\n", __func__, freq, index); 237 #endif /* DEBUG */ 238 239 if ((bus_space_read_4(iot, ioh, CPUF_DVCS_CTRL) & 240 CPUF_DVCS_CTRL_PER_CORE) != 0) 241 numcores = qccpu_lut_to_cores(sc, index, group); 242 else 243 numcores = 1; 244 for (i = 0; i < numcores; i++) 245 bus_space_write_4(iot, ioh, CPUF_PERF_STATE + i * 4, index); 246 247 return 0; 248 } 249 250 uint32_t 251 qccpu_lut_to_freq(struct qccpu_softc *sc, int index, uint32_t group) 252 { 253 return XO_FREQ_HZ * 254 ((sc->sc_freq[group][index] >> CPUF_FREQ_LUT_LVAL_S) 255 & CPUF_FREQ_LUT_LVAL_M); 256 } 257 258 uint32_t 259 qccpu_lut_to_cores(struct qccpu_softc *sc, int index, uint32_t group) 260 { 261 return ((sc->sc_freq[group][index] >> CPUF_FREQ_LUT_CORES_S) 262 & CPUF_FREQ_LUT_CORES_M); 263 } 264 265 void 266 qccpu_refresh_sensor(void *arg) 267 { 268 struct qccpu_softc *sc = arg; 269 bus_space_tag_t iot = sc->sc_iot; 270 bus_space_handle_t ioh; 271 int idx; 272 uint32_t lval; 273 274 for (idx = 0; idx < NUM_GROUP; idx++) { 275 ioh = sc->sc_ioh[idx]; 276 277 lval = (bus_space_read_4(iot, ioh, CPUF_DOMAIN_STATE) 278 >> CPUF_DOMAIN_STATE_LVAL_S) & CPUF_DOMAIN_STATE_LVAL_M; 279 sc->sc_hz_sensor[idx].value = 1000000ULL * lval * XO_FREQ_HZ; 280 } 281 } 282