1 /* $OpenBSD: opal.c,v 1.10 2020/09/23 03:03:11 gkoehler Exp $ */ 2 /* 3 * Copyright (c) 2020 Mark Kettenis <kettenis@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/device.h> 20 #include <sys/malloc.h> 21 #include <sys/sysctl.h> 22 #include <sys/systm.h> 23 24 #include <machine/bus.h> 25 #include <machine/cpu.h> 26 #include <machine/fdt.h> 27 #include <machine/opal.h> 28 29 #include <dev/clock_subr.h> 30 31 #include <dev/ofw/openfirm.h> 32 #include <dev/ofw/fdt.h> 33 34 #define OPAL_NUM_HANDLERS 4 35 36 struct opal_intr { 37 struct opal_softc *oi_sc; 38 uint32_t oi_isn; 39 }; 40 41 struct intrhand { 42 uint64_t ih_events; 43 int (*ih_func)(void *); 44 void *ih_arg; 45 }; 46 47 struct opal_softc { 48 struct device sc_dev; 49 50 struct opal_intr *sc_intr; 51 int sc_nintr; 52 53 struct intrhand *sc_handler[OPAL_NUM_HANDLERS]; 54 55 struct todr_chip_handle sc_todr; 56 57 int *sc_pstate; 58 int *sc_freq; 59 int sc_npstate; 60 }; 61 62 struct opal_softc *opal_sc; 63 64 int opal_match(struct device *, void *, void *); 65 void opal_attach(struct device *, struct device *, void *); 66 67 struct cfattach opal_ca = { 68 sizeof (struct opal_softc), opal_match, opal_attach 69 }; 70 71 struct cfdriver opal_cd = { 72 NULL, "opal", DV_DULL 73 }; 74 75 void opal_attach_deferred(struct device *); 76 void opal_attach_node(struct opal_softc *, int); 77 int opal_gettime(struct todr_chip_handle *, struct timeval *); 78 int opal_settime(struct todr_chip_handle *, struct timeval *); 79 80 extern int perflevel; 81 82 void opalpm_init(struct opal_softc *, int); 83 int opalpm_find_index(struct opal_softc *); 84 int opalpm_cpuspeed(int *); 85 void opalpm_setperf(int); 86 87 int 88 opal_match(struct device *parent, void *match, void *aux) 89 { 90 struct fdt_attach_args *faa = aux; 91 92 return OF_is_compatible(faa->fa_node, "ibm,opal-v3"); 93 } 94 95 void 96 opal_attach(struct device *parent, struct device *self, void *aux) 97 { 98 struct opal_softc *sc = (struct opal_softc *)self; 99 struct fdt_attach_args *faa = aux; 100 uint32_t *interrupts; 101 int len, i; 102 int node; 103 104 node = OF_getnodebyname(faa->fa_node, "firmware"); 105 if (node) { 106 char version[64]; 107 108 version[0] = 0; 109 OF_getprop(node, "version", version, sizeof(version)); 110 version[sizeof(version) - 1] = 0; 111 printf(": %s", version); 112 } 113 114 len = OF_getproplen(faa->fa_node, "opal-interrupts"); 115 if (len > 0 && (len % sizeof(uint32_t)) != 0) { 116 printf(": can't parse interrupts\n"); 117 return; 118 } 119 120 printf("\n"); 121 122 /* There can be only one. */ 123 KASSERT(opal_sc == NULL); 124 opal_sc = sc; 125 126 if (len > 0) { 127 interrupts = malloc(len, M_TEMP, M_WAITOK); 128 OF_getpropintarray(faa->fa_node, "opal-interrupts", 129 interrupts, len); 130 sc->sc_nintr = len / sizeof(uint32_t); 131 132 sc->sc_intr = mallocarray(sc->sc_nintr, 133 sizeof(struct opal_intr), M_DEVBUF, M_WAITOK); 134 135 for (i = 0; i < sc->sc_nintr; i++) { 136 sc->sc_intr[i].oi_sc = sc; 137 sc->sc_intr[i].oi_isn = interrupts[i]; 138 } 139 140 free(interrupts, M_TEMP, len); 141 142 config_defer(self, opal_attach_deferred); 143 } 144 145 sc->sc_todr.todr_gettime = opal_gettime; 146 sc->sc_todr.todr_settime = opal_settime; 147 todr_attach(&sc->sc_todr); 148 149 opalpm_init(sc, OF_getnodebyname(faa->fa_node, "power-mgt")); 150 151 node = OF_getnodebyname(faa->fa_node, "consoles"); 152 if (node) { 153 for (node = OF_child(node); node; node = OF_peer(node)) 154 opal_attach_node(sc, node); 155 } 156 157 node = OF_getnodebyname(faa->fa_node, "sensors"); 158 if (node) { 159 for (node = OF_child(node); node; node = OF_peer(node)) 160 opal_attach_node(sc, node); 161 } 162 } 163 164 int 165 opal_intr(void *arg) 166 { 167 struct opal_intr *oi = arg; 168 struct opal_softc *sc = oi->oi_sc; 169 uint64_t events = 0; 170 int i; 171 172 opal_handle_interrupt(oi->oi_isn, opal_phys(&events)); 173 174 /* Handle registered events. */ 175 for (i = 0; i < OPAL_NUM_HANDLERS; i++) { 176 struct intrhand *ih = sc->sc_handler[i]; 177 178 if (ih == NULL) 179 continue; 180 if ((events & ih->ih_events) == 0) 181 continue; 182 183 ih->ih_func(ih->ih_arg); 184 } 185 186 return 1; 187 } 188 189 void * 190 opal_intr_establish(uint64_t events, int level, int (*func)(void *), void *arg) 191 { 192 struct opal_softc *sc = opal_sc; 193 struct intrhand *ih; 194 int i; 195 196 for (i = 0; i < OPAL_NUM_HANDLERS; i++) { 197 if (sc->sc_handler[i] == NULL) 198 break; 199 } 200 if (i == OPAL_NUM_HANDLERS) 201 return NULL; 202 203 ih = malloc(sizeof(*ih), M_DEVBUF, M_WAITOK); 204 ih->ih_events = events; 205 ih->ih_func = func; 206 ih->ih_arg = arg; 207 sc->sc_handler[i] = ih; 208 209 return ih; 210 } 211 212 void 213 opal_attach_deferred(struct device *self) 214 { 215 struct opal_softc *sc = (struct opal_softc *)self; 216 int i; 217 218 for (i = 0; i < sc->sc_nintr; i++) { 219 intr_establish(sc->sc_intr[i].oi_isn, IST_LEVEL, IPL_TTY, 220 NULL, opal_intr, &sc->sc_intr[i], sc->sc_dev.dv_xname); 221 } 222 } 223 224 int 225 opal_print(void *aux, const char *pnp) 226 { 227 struct fdt_attach_args *faa = aux; 228 char name[32]; 229 230 if (!pnp) 231 return (QUIET); 232 233 if (OF_getprop(faa->fa_node, "name", name, sizeof(name)) > 0) { 234 name[sizeof(name) - 1] = 0; 235 printf("\"%s\"", name); 236 } else 237 printf("node %u", faa->fa_node); 238 239 printf(" at %s", pnp); 240 241 return (UNCONF); 242 } 243 244 void 245 opal_attach_node(struct opal_softc *sc, int node) 246 { 247 struct fdt_attach_args faa; 248 char buf[32]; 249 250 if (OF_getproplen(node, "compatible") <= 0) 251 return; 252 253 if (OF_getprop(node, "status", buf, sizeof(buf)) > 0 && 254 strcmp(buf, "disabled") == 0) 255 return; 256 257 memset(&faa, 0, sizeof(faa)); 258 faa.fa_name = ""; 259 faa.fa_node = node; 260 261 config_found(&sc->sc_dev, &faa, opal_print); 262 } 263 264 int 265 opal_gettime(struct todr_chip_handle *ch, struct timeval *tv) 266 { 267 struct clock_ymdhms dt; 268 uint64_t time; 269 uint32_t date; 270 int64_t error; 271 272 do { 273 error = opal_rtc_read(opal_phys(&date), opal_phys(&time)); 274 if (error == OPAL_BUSY_EVENT) 275 opal_poll_events(NULL); 276 } while (error == OPAL_BUSY_EVENT); 277 278 if (error != OPAL_SUCCESS) 279 return EIO; 280 281 dt.dt_sec = FROMBCD((time >> 40) & 0xff); 282 dt.dt_min = FROMBCD((time >> 48) & 0xff); 283 dt.dt_hour = FROMBCD((time >> 56) & 0xff); 284 dt.dt_day = FROMBCD((date >> 0) & 0xff); 285 dt.dt_mon = FROMBCD((date >> 8) & 0xff); 286 dt.dt_year = FROMBCD((date >> 16) & 0xff); 287 dt.dt_year += 100 * FROMBCD((date >> 24) & 0xff); 288 289 tv->tv_sec = clock_ymdhms_to_secs(&dt); 290 tv->tv_usec = 0; 291 292 return 0; 293 } 294 295 int 296 opal_settime(struct todr_chip_handle *ch, struct timeval *tv) 297 { 298 struct clock_ymdhms dt; 299 uint64_t time = 0; 300 uint32_t date = 0; 301 int64_t error; 302 303 clock_secs_to_ymdhms(tv->tv_sec, &dt); 304 305 time |= (uint64_t)TOBCD(dt.dt_sec) << 40; 306 time |= (uint64_t)TOBCD(dt.dt_min) << 48; 307 time |= (uint64_t)TOBCD(dt.dt_hour) << 56; 308 date |= (uint32_t)TOBCD(dt.dt_day); 309 date |= (uint32_t)TOBCD(dt.dt_mon) << 8; 310 date |= (uint32_t)TOBCD(dt.dt_year) << 16; 311 date |= (uint32_t)TOBCD(dt.dt_year / 100) << 24; 312 313 do { 314 error = opal_rtc_write(date, time); 315 if (error == OPAL_BUSY_EVENT) 316 opal_poll_events(NULL); 317 } while (error == OPAL_BUSY_EVENT); 318 319 if (error != OPAL_SUCCESS) 320 return EIO; 321 322 return 0; 323 } 324 325 void 326 opalpm_init(struct opal_softc *sc, int node) 327 { 328 int i, len; 329 330 if (!node) { 331 printf("%s: no power-mgt\n", sc->sc_dev.dv_xname); 332 return; 333 } 334 len = OF_getproplen(node, "ibm,pstate-ids"); 335 if (len <= 0 || len % sizeof(int) != 0 || 336 len != OF_getproplen(node, "ibm,pstate-frequencies-mhz")) { 337 printf("%s: can't parse power-mgt\n", sc->sc_dev.dv_xname); 338 return; 339 } 340 sc->sc_pstate = malloc(len, M_DEVBUF, M_WAITOK); 341 sc->sc_freq = malloc(len, M_DEVBUF, M_WAITOK); 342 sc->sc_npstate = len / sizeof(int); 343 OF_getprop(node, "ibm,pstate-ids", sc->sc_pstate, len); 344 OF_getprop(node, "ibm,pstate-frequencies-mhz", sc->sc_freq, len); 345 346 if ((i = opalpm_find_index(sc)) != -1) 347 perflevel = (sc->sc_npstate - 1 - i) * 100 / sc->sc_npstate; 348 cpu_cpuspeed = opalpm_cpuspeed; 349 #ifndef MULTIPROCESSOR 350 cpu_setperf = opalpm_setperf; 351 #else 352 ul_setperf = opalpm_setperf; 353 cpu_setperf = mp_setperf; 354 #endif 355 } 356 357 int 358 opalpm_find_index(struct opal_softc *sc) 359 { 360 int i, pstate; 361 362 /* 363 * POWER9 23.5.8.3 Power Management Status Register (PMSR) 364 * 8:15 Local Actual Pstate 365 */ 366 pstate = (mfpmsr() >> 48) & 0xff; 367 for (i = 0; i < sc->sc_npstate; i++) { 368 if (sc->sc_pstate[i] == pstate) 369 return i; 370 } 371 return -1; 372 } 373 374 int 375 opalpm_cpuspeed(int *freq) 376 { 377 struct opal_softc *sc = opal_sc; 378 int i; 379 380 if ((i = opalpm_find_index(sc)) == -1) 381 return 1; 382 *freq = sc->sc_freq[i]; 383 return 0; 384 } 385 386 void 387 opalpm_setperf(int level) 388 { 389 struct opal_softc *sc = opal_sc; 390 uint64_t pstate; 391 int i; 392 393 /* 394 * Assume that "ibm,pstate-frequencies-mhz" is sorted from 395 * fastest to slowest. 396 */ 397 i = (100 - level) * (sc->sc_npstate - 1) / 100; 398 pstate = sc->sc_pstate[i]; 399 400 /* 401 * POWER9 23.5.8.1 Power Management Control Register (PMCR) 402 * 0:7 Upper Pstate request 403 * 8:15 Lower Pstate request 404 * 60:63 Version 405 */ 406 mtpmcr((pstate << 56) | (pstate << 48) | 0); 407 } 408