1 /* $OpenBSD: qccpu.c,v 1.2 2023/07/01 18:59:11 drahn 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 struct cpu_freq_tbl { 53 uint32_t driver_data; 54 uint32_t frequency; 55 }; 56 57 #define NUM_GROUP 2 58 #define MAX_LUT 40 59 60 #define XO_FREQ_HZ 19200000 61 62 struct qccpu_softc { 63 struct device sc_dev; 64 bus_space_tag_t sc_iot; 65 bus_space_handle_t sc_ioh[NUM_GROUP]; 66 67 int sc_node; 68 69 struct clock_device sc_cd; 70 uint32_t sc_freq[NUM_GROUP][MAX_LUT]; 71 int sc_num_lut[NUM_GROUP]; 72 73 struct ksensordev sc_sensordev; 74 struct ksensor sc_hz_sensor[NUM_GROUP]; 75 }; 76 77 #define DEVNAME(sc) (sc)->sc_dev.dv_xname 78 79 int qccpu_match(struct device *, void *, void *); 80 void qccpu_attach(struct device *, struct device *, void *); 81 void qccpu_enable(void *, uint32_t *, int); 82 int qccpu_set_frequency(void *, uint32_t *, uint32_t); 83 uint32_t qccpu_get_frequency(void *, uint32_t *); 84 uint32_t qccpu_lut_to_freq(struct qccpu_softc *, int, uint32_t); 85 uint32_t qccpu_lut_to_cores(struct qccpu_softc *, int, uint32_t); 86 void qccpu_refresh_sensor(void *arg); 87 88 void qccpu_collect_lut(struct qccpu_softc *sc, int); 89 90 91 const struct cfattach qccpu_ca = { 92 sizeof (struct qccpu_softc), qccpu_match, qccpu_attach 93 }; 94 95 struct cfdriver qccpu_cd = { 96 NULL, "qccpu", DV_DULL 97 }; 98 99 int 100 qccpu_match(struct device *parent, void *match, void *aux) 101 { 102 struct fdt_attach_args *faa = aux; 103 104 return OF_is_compatible(faa->fa_node, "qcom,cpufreq-epss"); 105 } 106 107 void 108 qccpu_attach(struct device *parent, struct device *self, void *aux) 109 { 110 struct qccpu_softc *sc = (struct qccpu_softc *)self; 111 struct fdt_attach_args *faa = aux; 112 113 if (faa->fa_nreg < 2) { 114 printf(": no registers\n"); 115 return; 116 } 117 118 sc->sc_iot = faa->fa_iot; 119 if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, 120 faa->fa_reg[0].size, 0, &sc->sc_ioh[0])) { 121 printf(": can't map registers (cluster0)\n"); 122 return; 123 } 124 125 if (bus_space_map(sc->sc_iot, faa->fa_reg[1].addr, 126 faa->fa_reg[1].size, 0, &sc->sc_ioh[1])) { 127 printf(": can't map registers (cluster1)\n"); 128 return; 129 } 130 sc->sc_node = faa->fa_node; 131 132 printf("\n"); 133 134 qccpu_collect_lut(sc, 0); 135 qccpu_collect_lut(sc, 1); 136 137 sc->sc_cd.cd_node = faa->fa_node; 138 sc->sc_cd.cd_cookie = sc; 139 sc->sc_cd.cd_get_frequency = qccpu_get_frequency; 140 sc->sc_cd.cd_set_frequency = qccpu_set_frequency; 141 clock_register(&sc->sc_cd); 142 143 strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname, 144 sizeof(sc->sc_sensordev.xname)); 145 146 sc->sc_hz_sensor[0].type = SENSOR_FREQ; 147 sensor_attach(&sc->sc_sensordev, &sc->sc_hz_sensor[0]); 148 sc->sc_hz_sensor[1].type = SENSOR_FREQ; 149 sensor_attach(&sc->sc_sensordev, &sc->sc_hz_sensor[1]); 150 sensordev_install(&sc->sc_sensordev); 151 sensor_task_register(sc, qccpu_refresh_sensor, 1); 152 } 153 154 void 155 qccpu_collect_lut(struct qccpu_softc *sc, int group) 156 { 157 int prev_freq = 0; 158 uint32_t freq; 159 int idx; 160 bus_space_tag_t iot = sc->sc_iot; 161 bus_space_handle_t ioh = sc->sc_ioh[group]; 162 163 for (idx = 0; ; idx++) { 164 freq = bus_space_read_4(iot, ioh, 165 CPUF_FREQ_LUT + idx * LUT_ROW_SIZE); 166 167 if (idx != 0 && prev_freq == freq) { 168 sc->sc_num_lut[group] = idx; 169 break; 170 } 171 172 sc->sc_freq[group][idx] = freq; 173 174 #ifdef DEBUG 175 printf("%s: %d: %x %u\n", DEVNAME(sc), idx, freq, 176 qccpu_lut_to_freq(sc, idx, group)); 177 #endif /* DEBUG */ 178 179 prev_freq = freq; 180 if (idx >= MAX_LUT-1) 181 break; 182 } 183 184 return; 185 } 186 187 uint32_t 188 qccpu_get_frequency(void *cookie, uint32_t *cells) 189 { 190 struct qccpu_softc *sc = cookie; 191 bus_space_tag_t iot = sc->sc_iot; 192 bus_space_handle_t ioh; 193 uint32_t lval; 194 uint32_t group; 195 196 if (cells[0] >= NUM_GROUP) { 197 printf("%s: bad cell %d\n", __func__, cells[0]); 198 return 0; 199 } 200 group = cells[0]; 201 202 ioh = sc->sc_ioh[cells[0]]; 203 204 lval = (bus_space_read_4(iot, ioh, CPUF_DOMAIN_STATE) 205 >> CPUF_DOMAIN_STATE_LVAL_S) & CPUF_DOMAIN_STATE_LVAL_M; 206 return lval *XO_FREQ_HZ; 207 } 208 209 int 210 qccpu_set_frequency(void *cookie, uint32_t *cells, uint32_t freq) 211 { 212 struct qccpu_softc *sc = cookie; 213 bus_space_tag_t iot = sc->sc_iot; 214 bus_space_handle_t ioh; 215 int index = 0; 216 int numcores, i; 217 uint32_t group; 218 219 if (cells[0] >= NUM_GROUP) { 220 printf("%s: bad cell %d\n", __func__, cells[0]); 221 return 1; 222 } 223 group = cells[0]; 224 225 ioh = sc->sc_ioh[group]; 226 227 while (index < sc->sc_num_lut[group]) { 228 if (freq == qccpu_lut_to_freq(sc, index, group)) 229 break; 230 231 if (freq < qccpu_lut_to_freq(sc, index, group)) { 232 /* select next slower if not match, not zero */ 233 if (index != 0) 234 index = index - 1; 235 break; 236 } 237 238 index++; 239 } 240 241 #ifdef DEBUG 242 printf("%s called freq %u index %d\n", __func__, freq, index); 243 #endif /* DEBUG */ 244 245 if ((bus_space_read_4(iot, ioh, CPUF_DVCS_CTRL) & 246 CPUF_DVCS_CTRL_PER_CORE) != 0) 247 numcores = qccpu_lut_to_cores(sc, index, group); 248 else 249 numcores = 1; 250 for (i = 0; i < numcores; i++) 251 bus_space_write_4(iot, ioh, CPUF_PERF_STATE + i * 4, index); 252 253 return 0; 254 } 255 256 uint32_t 257 qccpu_lut_to_freq(struct qccpu_softc *sc, int index, uint32_t group) 258 { 259 return XO_FREQ_HZ * 260 ((sc->sc_freq[group][index] >> CPUF_FREQ_LUT_LVAL_S) 261 & CPUF_FREQ_LUT_LVAL_M); 262 } 263 264 uint32_t 265 qccpu_lut_to_cores(struct qccpu_softc *sc, int index, uint32_t group) 266 { 267 return ((sc->sc_freq[group][index] >> CPUF_FREQ_LUT_CORES_S) 268 & CPUF_FREQ_LUT_CORES_M); 269 } 270 271 void 272 qccpu_refresh_sensor(void *arg) 273 { 274 struct qccpu_softc *sc = arg; 275 bus_space_tag_t iot = sc->sc_iot; 276 bus_space_handle_t ioh; 277 int idx; 278 uint32_t lval; 279 280 for (idx = 0; idx < NUM_GROUP; idx++) { 281 ioh = sc->sc_ioh[idx]; 282 283 lval = (bus_space_read_4(iot, ioh, CPUF_DOMAIN_STATE) 284 >> CPUF_DOMAIN_STATE_LVAL_S) & CPUF_DOMAIN_STATE_LVAL_M; 285 sc->sc_hz_sensor[idx].value = 1000000ULL * lval * XO_FREQ_HZ; 286 } 287 } 288