1 /* $OpenBSD: opal.c,v 1.14 2022/10/12 13:39:50 kettenis 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 const 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 void opal_configure_idle_states(struct opal_softc *, int); 80 void opal_found_stop_state(struct opal_softc *, uint64_t); 81 82 extern int perflevel; 83 84 void opalpm_init(struct opal_softc *, int); 85 int opalpm_find_index(struct opal_softc *); 86 int opalpm_cpuspeed(int *); 87 void opalpm_setperf(int); 88 89 int 90 opal_match(struct device *parent, void *match, void *aux) 91 { 92 struct fdt_attach_args *faa = aux; 93 94 return OF_is_compatible(faa->fa_node, "ibm,opal-v3"); 95 } 96 97 void 98 opal_attach(struct device *parent, struct device *self, void *aux) 99 { 100 struct opal_softc *sc = (struct opal_softc *)self; 101 struct fdt_attach_args *faa = aux; 102 uint32_t *interrupts; 103 int len, i; 104 int node; 105 106 node = OF_getnodebyname(faa->fa_node, "firmware"); 107 if (node) { 108 char version[64]; 109 110 version[0] = 0; 111 OF_getprop(node, "version", version, sizeof(version)); 112 version[sizeof(version) - 1] = 0; 113 printf(": %s", version); 114 } 115 116 len = OF_getproplen(faa->fa_node, "opal-interrupts"); 117 if (len > 0 && (len % sizeof(uint32_t)) != 0) { 118 printf(": can't parse interrupts\n"); 119 return; 120 } 121 122 printf("\n"); 123 124 /* There can be only one. */ 125 KASSERT(opal_sc == NULL); 126 opal_sc = sc; 127 128 if (len > 0) { 129 interrupts = malloc(len, M_TEMP, M_WAITOK); 130 OF_getpropintarray(faa->fa_node, "opal-interrupts", 131 interrupts, len); 132 sc->sc_nintr = len / sizeof(uint32_t); 133 134 sc->sc_intr = mallocarray(sc->sc_nintr, 135 sizeof(struct opal_intr), M_DEVBUF, M_WAITOK); 136 137 for (i = 0; i < sc->sc_nintr; i++) { 138 sc->sc_intr[i].oi_sc = sc; 139 sc->sc_intr[i].oi_isn = interrupts[i]; 140 } 141 142 free(interrupts, M_TEMP, len); 143 144 config_defer(self, opal_attach_deferred); 145 } 146 147 sc->sc_todr.todr_gettime = opal_gettime; 148 sc->sc_todr.todr_settime = opal_settime; 149 sc->sc_todr.todr_quality = 0; 150 todr_attach(&sc->sc_todr); 151 152 node = OF_getnodebyname(faa->fa_node, "power-mgt"); 153 if (node) { 154 opal_configure_idle_states(sc, node); 155 opalpm_init(sc, node); 156 } 157 158 node = OF_getnodebyname(faa->fa_node, "consoles"); 159 if (node) { 160 for (node = OF_child(node); node; node = OF_peer(node)) 161 opal_attach_node(sc, node); 162 } 163 164 node = OF_getnodebyname(faa->fa_node, "sensors"); 165 if (node) { 166 for (node = OF_child(node); node; node = OF_peer(node)) 167 opal_attach_node(sc, node); 168 } 169 170 for (node = OF_child(faa->fa_node); node; node = OF_peer(node)) { 171 if (OF_is_compatible(node, "ibm,opal-ipmi")) 172 opal_attach_node(sc, node); 173 } 174 } 175 176 int 177 opal_intr(void *arg) 178 { 179 struct opal_intr *oi = arg; 180 struct opal_softc *sc = oi->oi_sc; 181 uint64_t events = 0; 182 int i; 183 184 opal_handle_interrupt(oi->oi_isn, opal_phys(&events)); 185 186 /* Handle registered events. */ 187 for (i = 0; i < OPAL_NUM_HANDLERS; i++) { 188 struct intrhand *ih = sc->sc_handler[i]; 189 190 if (ih == NULL) 191 continue; 192 if ((events & ih->ih_events) == 0) 193 continue; 194 195 ih->ih_func(ih->ih_arg); 196 } 197 198 return 1; 199 } 200 201 void * 202 opal_intr_establish(uint64_t events, int level, int (*func)(void *), void *arg) 203 { 204 struct opal_softc *sc = opal_sc; 205 struct intrhand *ih; 206 int i; 207 208 for (i = 0; i < OPAL_NUM_HANDLERS; i++) { 209 if (sc->sc_handler[i] == NULL) 210 break; 211 } 212 if (i == OPAL_NUM_HANDLERS) 213 return NULL; 214 215 ih = malloc(sizeof(*ih), M_DEVBUF, M_WAITOK); 216 ih->ih_events = events; 217 ih->ih_func = func; 218 ih->ih_arg = arg; 219 sc->sc_handler[i] = ih; 220 221 return ih; 222 } 223 224 void 225 opal_attach_deferred(struct device *self) 226 { 227 struct opal_softc *sc = (struct opal_softc *)self; 228 int i; 229 230 for (i = 0; i < sc->sc_nintr; i++) { 231 intr_establish(sc->sc_intr[i].oi_isn, IST_LEVEL, IPL_TTY, 232 NULL, opal_intr, &sc->sc_intr[i], sc->sc_dev.dv_xname); 233 } 234 } 235 236 int 237 opal_print(void *aux, const char *pnp) 238 { 239 struct fdt_attach_args *faa = aux; 240 char name[32]; 241 242 if (!pnp) 243 return (QUIET); 244 245 if (OF_getprop(faa->fa_node, "name", name, sizeof(name)) > 0) { 246 name[sizeof(name) - 1] = 0; 247 printf("\"%s\"", name); 248 } else 249 printf("node %u", faa->fa_node); 250 251 printf(" at %s", pnp); 252 253 return (UNCONF); 254 } 255 256 void 257 opal_attach_node(struct opal_softc *sc, int node) 258 { 259 struct fdt_attach_args faa; 260 char buf[32]; 261 262 if (OF_getproplen(node, "compatible") <= 0) 263 return; 264 265 if (OF_getprop(node, "status", buf, sizeof(buf)) > 0 && 266 strcmp(buf, "disabled") == 0) 267 return; 268 269 memset(&faa, 0, sizeof(faa)); 270 faa.fa_name = ""; 271 faa.fa_node = node; 272 273 config_found(&sc->sc_dev, &faa, opal_print); 274 } 275 276 int 277 opal_gettime(struct todr_chip_handle *ch, struct timeval *tv) 278 { 279 struct clock_ymdhms dt; 280 uint64_t time; 281 uint32_t date; 282 int64_t error; 283 284 do { 285 error = opal_rtc_read(opal_phys(&date), opal_phys(&time)); 286 if (error == OPAL_BUSY_EVENT) 287 opal_poll_events(NULL); 288 } while (error == OPAL_BUSY_EVENT); 289 290 if (error != OPAL_SUCCESS) 291 return EIO; 292 293 dt.dt_sec = FROMBCD((time >> 40) & 0xff); 294 dt.dt_min = FROMBCD((time >> 48) & 0xff); 295 dt.dt_hour = FROMBCD((time >> 56) & 0xff); 296 dt.dt_day = FROMBCD((date >> 0) & 0xff); 297 dt.dt_mon = FROMBCD((date >> 8) & 0xff); 298 dt.dt_year = FROMBCD((date >> 16) & 0xff); 299 dt.dt_year += 100 * FROMBCD((date >> 24) & 0xff); 300 301 tv->tv_sec = clock_ymdhms_to_secs(&dt); 302 tv->tv_usec = 0; 303 304 return 0; 305 } 306 307 int 308 opal_settime(struct todr_chip_handle *ch, struct timeval *tv) 309 { 310 struct clock_ymdhms dt; 311 uint64_t time = 0; 312 uint32_t date = 0; 313 int64_t error; 314 315 clock_secs_to_ymdhms(tv->tv_sec, &dt); 316 317 time |= (uint64_t)TOBCD(dt.dt_sec) << 40; 318 time |= (uint64_t)TOBCD(dt.dt_min) << 48; 319 time |= (uint64_t)TOBCD(dt.dt_hour) << 56; 320 date |= (uint32_t)TOBCD(dt.dt_day); 321 date |= (uint32_t)TOBCD(dt.dt_mon) << 8; 322 date |= (uint32_t)TOBCD(dt.dt_year) << 16; 323 date |= (uint32_t)TOBCD(dt.dt_year / 100) << 24; 324 325 do { 326 error = opal_rtc_write(date, time); 327 if (error == OPAL_BUSY_EVENT) 328 opal_poll_events(NULL); 329 } while (error == OPAL_BUSY_EVENT); 330 331 if (error != OPAL_SUCCESS) 332 return EIO; 333 334 return 0; 335 } 336 337 #define OPAL_PM_LOSE_USER_CONTEXT 0x00001000 338 #define OPAL_PM_STOP_INST_FAST 0x00100000 339 340 void 341 opal_configure_idle_states(struct opal_softc *sc, int node) 342 { 343 uint64_t *states; 344 uint32_t accept, *flags; 345 int count, flen, i, slen; 346 char *prop; 347 348 prop = "ibm,cpu-idle-state-flags"; 349 flen = OF_getproplen(node, prop); 350 if (flen <= 0 || flen % sizeof(flags[0]) != 0) 351 return; 352 count = flen / sizeof(flags[0]); 353 slen = count * sizeof(states[0]); 354 355 flags = malloc(flen, M_DEVBUF, M_WAITOK); 356 states = malloc(slen, M_DEVBUF, M_WAITOK); 357 OF_getpropintarray(node, prop, flags, flen); 358 359 /* Power ISA v3 uses the psscr with the stop instruction. */ 360 prop = "ibm,cpu-idle-state-psscr"; 361 if (OF_getpropint64array(node, prop, states, slen) == slen) { 362 /* 363 * Find the deepest idle state that doesn't lose too 364 * much context. 365 */ 366 accept = OPAL_PM_LOSE_USER_CONTEXT | OPAL_PM_STOP_INST_FAST; 367 for (i = count - 1; i >= 0; i--) { 368 if ((flags[i] & ~accept) == 0) { 369 opal_found_stop_state(sc, states[i]); 370 break; 371 } 372 } 373 } 374 375 free(flags, M_DEVBUF, flen); 376 free(states, M_DEVBUF, slen); 377 } 378 379 void cpu_idle_stop(void); 380 #ifdef MULTIPROCESSOR 381 void cpu_hatch_and_stop(void); 382 #endif 383 384 void 385 opal_found_stop_state(struct opal_softc *sc, uint64_t state) 386 { 387 #ifdef MULTIPROCESSOR 388 uint32_t pirs[8]; 389 int i, len, node; 390 char buf[32]; 391 #endif 392 393 cpu_idle_state_psscr = state; 394 cpu_idle_cycle_fcn = &cpu_idle_stop; 395 printf("%s: idle psscr %llx\n", sc->sc_dev.dv_xname, 396 (unsigned long long)state); 397 398 #ifdef MULTIPROCESSOR 399 /* 400 * Idle the other hardware threads. We use only one thread of 401 * each cpu core. The other threads are idle in OPAL. If we 402 * move them to a deeper idle state, then the core might 403 * switch to single-thread mode, increase performance. 404 */ 405 node = OF_parent(curcpu()->ci_node); 406 for (node = OF_child(node); node != 0; node = OF_peer(node)) { 407 if (OF_getprop(node, "device_type", buf, sizeof(buf)) <= 0 || 408 strcmp(buf, "cpu") != 0) 409 continue; 410 len = OF_getpropintarray(node, "ibm,ppc-interrupt-server#s", 411 pirs, sizeof(pirs)); 412 if (len > 0 && len % 4 == 0) { 413 /* Skip i = 0, the first hardware thread. */ 414 for (i = 1; i < len / 4; i++) 415 opal_start_cpu(pirs[i], 416 (vaddr_t)cpu_hatch_and_stop); 417 } 418 } 419 #endif 420 } 421 422 void 423 opalpm_init(struct opal_softc *sc, int node) 424 { 425 int i, len; 426 427 len = OF_getproplen(node, "ibm,pstate-ids"); 428 if (len <= 0 || len % sizeof(int) != 0 || 429 len != OF_getproplen(node, "ibm,pstate-frequencies-mhz")) { 430 printf("%s: can't parse pstates\n", sc->sc_dev.dv_xname); 431 return; 432 } 433 sc->sc_pstate = malloc(len, M_DEVBUF, M_WAITOK); 434 sc->sc_freq = malloc(len, M_DEVBUF, M_WAITOK); 435 sc->sc_npstate = len / sizeof(int); 436 OF_getprop(node, "ibm,pstate-ids", sc->sc_pstate, len); 437 OF_getprop(node, "ibm,pstate-frequencies-mhz", sc->sc_freq, len); 438 439 if ((i = opalpm_find_index(sc)) != -1) 440 perflevel = (sc->sc_npstate - 1 - i) * 100 / sc->sc_npstate; 441 cpu_cpuspeed = opalpm_cpuspeed; 442 #ifndef MULTIPROCESSOR 443 cpu_setperf = opalpm_setperf; 444 #else 445 ul_setperf = opalpm_setperf; 446 cpu_setperf = mp_setperf; 447 #endif 448 } 449 450 int 451 opalpm_find_index(struct opal_softc *sc) 452 { 453 int i, pstate; 454 455 /* 456 * POWER9 23.5.8.3 Power Management Status Register (PMSR) 457 * 8:15 Local Actual Pstate 458 */ 459 pstate = (mfpmsr() >> 48) & 0xff; 460 for (i = 0; i < sc->sc_npstate; i++) { 461 if (sc->sc_pstate[i] == pstate) 462 return i; 463 } 464 return -1; 465 } 466 467 int 468 opalpm_cpuspeed(int *freq) 469 { 470 struct opal_softc *sc = opal_sc; 471 int i; 472 473 if ((i = opalpm_find_index(sc)) == -1) 474 return 1; 475 *freq = sc->sc_freq[i]; 476 return 0; 477 } 478 479 void 480 opalpm_setperf(int level) 481 { 482 struct opal_softc *sc = opal_sc; 483 uint64_t pstate; 484 int i; 485 486 /* 487 * Assume that "ibm,pstate-frequencies-mhz" is sorted from 488 * fastest to slowest. 489 */ 490 i = (100 - level) * (sc->sc_npstate - 1) / 100; 491 pstate = sc->sc_pstate[i]; 492 493 /* 494 * POWER9 23.5.8.1 Power Management Control Register (PMCR) 495 * 0:7 Upper Pstate request 496 * 8:15 Lower Pstate request 497 * 60:63 Version 498 */ 499 mtpmcr((pstate << 56) | (pstate << 48) | 0); 500 } 501