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
opal_match(struct device * parent,void * match,void * aux)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
opal_attach(struct device * parent,struct device * self,void * aux)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
opal_intr(void * arg)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 *
opal_intr_establish(uint64_t events,int level,int (* func)(void *),void * arg)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
opal_attach_deferred(struct device * self)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
opal_print(void * aux,const char * pnp)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
opal_attach_node(struct opal_softc * sc,int node)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
opal_gettime(struct todr_chip_handle * ch,struct timeval * tv)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
opal_settime(struct todr_chip_handle * ch,struct timeval * tv)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
opal_configure_idle_states(struct opal_softc * sc,int node)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
opal_found_stop_state(struct opal_softc * sc,uint64_t state)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
opalpm_init(struct opal_softc * sc,int node)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
opalpm_find_index(struct opal_softc * sc)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
opalpm_cpuspeed(int * freq)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
opalpm_setperf(int level)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