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