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