xref: /openbsd-src/sys/arch/powerpc64/dev/opal.c (revision a0747c9f67a4ae71ccb71e62a28d1ea19e06a63c)
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