xref: /openbsd/sys/arch/powerpc64/dev/opal.c (revision 0701a158)
1*0701a158Skettenis /*	$OpenBSD: opal.c,v 1.14 2022/10/12 13:39:50 kettenis Exp $	*/
2d59f9b5aSkettenis /*
3d59f9b5aSkettenis  * Copyright (c) 2020 Mark Kettenis <kettenis@openbsd.org>
4d59f9b5aSkettenis  *
5d59f9b5aSkettenis  * Permission to use, copy, modify, and distribute this software for any
6d59f9b5aSkettenis  * purpose with or without fee is hereby granted, provided that the above
7d59f9b5aSkettenis  * copyright notice and this permission notice appear in all copies.
8d59f9b5aSkettenis  *
9d59f9b5aSkettenis  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10d59f9b5aSkettenis  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11d59f9b5aSkettenis  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12d59f9b5aSkettenis  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13d59f9b5aSkettenis  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14d59f9b5aSkettenis  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15d59f9b5aSkettenis  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16d59f9b5aSkettenis  */
17d59f9b5aSkettenis 
18d59f9b5aSkettenis #include <sys/param.h>
19d59f9b5aSkettenis #include <sys/device.h>
20430c8281Skettenis #include <sys/malloc.h>
21b286a873Sgkoehler #include <sys/sysctl.h>
22430c8281Skettenis #include <sys/systm.h>
23d59f9b5aSkettenis 
24d59f9b5aSkettenis #include <machine/bus.h>
25c7966c0cSgkoehler #include <machine/cpu.h>
26d59f9b5aSkettenis #include <machine/fdt.h>
27d59f9b5aSkettenis #include <machine/opal.h>
28d59f9b5aSkettenis 
29d59f9b5aSkettenis #include <dev/clock_subr.h>
30d59f9b5aSkettenis 
31d59f9b5aSkettenis #include <dev/ofw/openfirm.h>
32d59f9b5aSkettenis #include <dev/ofw/fdt.h>
33d59f9b5aSkettenis 
34430c8281Skettenis #define OPAL_NUM_HANDLERS	4
35430c8281Skettenis 
36430c8281Skettenis struct opal_intr {
37430c8281Skettenis 	struct opal_softc	*oi_sc;
38430c8281Skettenis 	uint32_t		oi_isn;
39430c8281Skettenis };
40430c8281Skettenis 
41430c8281Skettenis struct intrhand {
42430c8281Skettenis 	uint64_t		ih_events;
43430c8281Skettenis 	int			(*ih_func)(void *);
44430c8281Skettenis 	void			*ih_arg;
45430c8281Skettenis };
46430c8281Skettenis 
47d59f9b5aSkettenis struct opal_softc {
48d59f9b5aSkettenis 	struct device		sc_dev;
49430c8281Skettenis 
50430c8281Skettenis 	struct opal_intr	*sc_intr;
51430c8281Skettenis 	int			sc_nintr;
52430c8281Skettenis 
53430c8281Skettenis 	struct intrhand		*sc_handler[OPAL_NUM_HANDLERS];
54430c8281Skettenis 
55d59f9b5aSkettenis 	struct todr_chip_handle	sc_todr;
56b286a873Sgkoehler 
57b286a873Sgkoehler 	int			*sc_pstate;
58b286a873Sgkoehler 	int			*sc_freq;
59b286a873Sgkoehler 	int			sc_npstate;
60d59f9b5aSkettenis };
61d59f9b5aSkettenis 
62430c8281Skettenis struct opal_softc *opal_sc;
63430c8281Skettenis 
64d59f9b5aSkettenis int	opal_match(struct device *, void *, void *);
65d59f9b5aSkettenis void	opal_attach(struct device *, struct device *, void *);
66d59f9b5aSkettenis 
67471aeecfSnaddy const struct cfattach opal_ca = {
68d59f9b5aSkettenis 	sizeof (struct opal_softc), opal_match, opal_attach
69d59f9b5aSkettenis };
70d59f9b5aSkettenis 
71d59f9b5aSkettenis struct cfdriver opal_cd = {
72d59f9b5aSkettenis 	NULL, "opal", DV_DULL
73d59f9b5aSkettenis };
74d59f9b5aSkettenis 
75430c8281Skettenis void	opal_attach_deferred(struct device *);
769cff15efSkettenis void	opal_attach_node(struct opal_softc *, int);
77d59f9b5aSkettenis int	opal_gettime(struct todr_chip_handle *, struct timeval *);
78d59f9b5aSkettenis int	opal_settime(struct todr_chip_handle *, struct timeval *);
79196daab3Sgkoehler void	opal_configure_idle_states(struct opal_softc *, int);
80196daab3Sgkoehler void	opal_found_stop_state(struct opal_softc *, uint64_t);
81d59f9b5aSkettenis 
82b286a873Sgkoehler extern int perflevel;
83b286a873Sgkoehler 
84b286a873Sgkoehler void	opalpm_init(struct opal_softc *, int);
85b286a873Sgkoehler int	opalpm_find_index(struct opal_softc *);
86b286a873Sgkoehler int	opalpm_cpuspeed(int *);
87b286a873Sgkoehler void	opalpm_setperf(int);
88b286a873Sgkoehler 
89d59f9b5aSkettenis int
opal_match(struct device * parent,void * match,void * aux)90d59f9b5aSkettenis opal_match(struct device *parent, void *match, void *aux)
91d59f9b5aSkettenis {
92d59f9b5aSkettenis 	struct fdt_attach_args *faa = aux;
93d59f9b5aSkettenis 
94d59f9b5aSkettenis 	return OF_is_compatible(faa->fa_node, "ibm,opal-v3");
95d59f9b5aSkettenis }
96d59f9b5aSkettenis 
97d59f9b5aSkettenis void
opal_attach(struct device * parent,struct device * self,void * aux)98d59f9b5aSkettenis opal_attach(struct device *parent, struct device *self, void *aux)
99d59f9b5aSkettenis {
100d59f9b5aSkettenis 	struct opal_softc *sc = (struct opal_softc *)self;
101d59f9b5aSkettenis 	struct fdt_attach_args *faa = aux;
102430c8281Skettenis 	uint32_t *interrupts;
103430c8281Skettenis 	int len, i;
104d59f9b5aSkettenis 	int node;
105d59f9b5aSkettenis 
106d59f9b5aSkettenis 	node = OF_getnodebyname(faa->fa_node, "firmware");
107d59f9b5aSkettenis 	if (node) {
108d59f9b5aSkettenis 		char version[64];
109d59f9b5aSkettenis 
110d59f9b5aSkettenis 		version[0] = 0;
111d59f9b5aSkettenis 		OF_getprop(node, "version", version, sizeof(version));
112d59f9b5aSkettenis 		version[sizeof(version) - 1] = 0;
113d59f9b5aSkettenis 		printf(": %s", version);
114d59f9b5aSkettenis 	}
115d59f9b5aSkettenis 
116430c8281Skettenis 	len = OF_getproplen(faa->fa_node, "opal-interrupts");
117430c8281Skettenis 	if (len > 0 && (len % sizeof(uint32_t)) != 0) {
118430c8281Skettenis 		printf(": can't parse interrupts\n");
119430c8281Skettenis 		return;
120430c8281Skettenis 	}
121430c8281Skettenis 
122d59f9b5aSkettenis 	printf("\n");
123d59f9b5aSkettenis 
124430c8281Skettenis 	/* There can be only one. */
125430c8281Skettenis 	KASSERT(opal_sc == NULL);
126430c8281Skettenis 	opal_sc = sc;
127430c8281Skettenis 
128430c8281Skettenis 	if (len > 0) {
129430c8281Skettenis 		interrupts = malloc(len, M_TEMP, M_WAITOK);
130430c8281Skettenis 		OF_getpropintarray(faa->fa_node, "opal-interrupts",
131430c8281Skettenis 		    interrupts, len);
132430c8281Skettenis 		sc->sc_nintr = len / sizeof(uint32_t);
133430c8281Skettenis 
134430c8281Skettenis 		sc->sc_intr = mallocarray(sc->sc_nintr,
135430c8281Skettenis 		    sizeof(struct opal_intr), M_DEVBUF, M_WAITOK);
136430c8281Skettenis 
137430c8281Skettenis 		for (i = 0; i < sc->sc_nintr; i++) {
138430c8281Skettenis 			sc->sc_intr[i].oi_sc = sc;
139430c8281Skettenis 			sc->sc_intr[i].oi_isn = interrupts[i];
140430c8281Skettenis 		}
141430c8281Skettenis 
142430c8281Skettenis 		free(interrupts, M_TEMP, len);
143430c8281Skettenis 
144430c8281Skettenis 		config_defer(self, opal_attach_deferred);
145430c8281Skettenis 	}
146430c8281Skettenis 
147d59f9b5aSkettenis 	sc->sc_todr.todr_gettime = opal_gettime;
148d59f9b5aSkettenis 	sc->sc_todr.todr_settime = opal_settime;
149*0701a158Skettenis 	sc->sc_todr.todr_quality = 0;
150d59f9b5aSkettenis 	todr_attach(&sc->sc_todr);
1519cff15efSkettenis 
152196daab3Sgkoehler 	node = OF_getnodebyname(faa->fa_node, "power-mgt");
153196daab3Sgkoehler 	if (node) {
154196daab3Sgkoehler 		opal_configure_idle_states(sc, node);
155196daab3Sgkoehler 		opalpm_init(sc, node);
156196daab3Sgkoehler 	}
157b286a873Sgkoehler 
1589cff15efSkettenis 	node = OF_getnodebyname(faa->fa_node, "consoles");
1599cff15efSkettenis 	if (node) {
1609cff15efSkettenis 		for (node = OF_child(node); node; node = OF_peer(node))
1619cff15efSkettenis 			opal_attach_node(sc, node);
1629cff15efSkettenis 	}
163eb0540bfSkettenis 
164eb0540bfSkettenis 	node = OF_getnodebyname(faa->fa_node, "sensors");
165eb0540bfSkettenis 	if (node) {
166eb0540bfSkettenis 		for (node = OF_child(node); node; node = OF_peer(node))
167eb0540bfSkettenis 			opal_attach_node(sc, node);
168eb0540bfSkettenis 	}
169c5fc2d49Skettenis 
170c5fc2d49Skettenis 	for (node = OF_child(faa->fa_node); node; node = OF_peer(node)) {
171c5fc2d49Skettenis 		if (OF_is_compatible(node, "ibm,opal-ipmi"))
172c5fc2d49Skettenis 			opal_attach_node(sc, node);
173c5fc2d49Skettenis 	}
1749cff15efSkettenis }
1759cff15efSkettenis 
1769cff15efSkettenis int
opal_intr(void * arg)177430c8281Skettenis opal_intr(void *arg)
178430c8281Skettenis {
179430c8281Skettenis 	struct opal_intr *oi = arg;
180430c8281Skettenis 	struct opal_softc *sc = oi->oi_sc;
181430c8281Skettenis 	uint64_t events = 0;
182430c8281Skettenis 	int i;
183430c8281Skettenis 
184430c8281Skettenis 	opal_handle_interrupt(oi->oi_isn, opal_phys(&events));
185430c8281Skettenis 
186430c8281Skettenis 	/* Handle registered events. */
187430c8281Skettenis 	for (i = 0; i < OPAL_NUM_HANDLERS; i++) {
188430c8281Skettenis 		struct intrhand *ih = sc->sc_handler[i];
189430c8281Skettenis 
190430c8281Skettenis 		if (ih == NULL)
191430c8281Skettenis 			continue;
192430c8281Skettenis 		if ((events & ih->ih_events) == 0)
193430c8281Skettenis 			continue;
194430c8281Skettenis 
195430c8281Skettenis 		ih->ih_func(ih->ih_arg);
196430c8281Skettenis 	}
197430c8281Skettenis 
198430c8281Skettenis 	return 1;
199430c8281Skettenis }
200430c8281Skettenis 
201430c8281Skettenis void *
opal_intr_establish(uint64_t events,int level,int (* func)(void *),void * arg)202430c8281Skettenis opal_intr_establish(uint64_t events, int level, int (*func)(void *), void *arg)
203430c8281Skettenis {
204430c8281Skettenis 	struct opal_softc *sc = opal_sc;
205430c8281Skettenis 	struct intrhand *ih;
206430c8281Skettenis 	int i;
207430c8281Skettenis 
208430c8281Skettenis 	for (i = 0; i < OPAL_NUM_HANDLERS; i++) {
209430c8281Skettenis 		if (sc->sc_handler[i] == NULL)
210430c8281Skettenis 			break;
211430c8281Skettenis 	}
212430c8281Skettenis 	if (i == OPAL_NUM_HANDLERS)
213430c8281Skettenis 		return NULL;
214430c8281Skettenis 
215430c8281Skettenis 	ih = malloc(sizeof(*ih), M_DEVBUF, M_WAITOK);
216430c8281Skettenis 	ih->ih_events = events;
217430c8281Skettenis 	ih->ih_func = func;
218430c8281Skettenis 	ih->ih_arg = arg;
219430c8281Skettenis 	sc->sc_handler[i] = ih;
220430c8281Skettenis 
221430c8281Skettenis 	return ih;
222430c8281Skettenis }
223430c8281Skettenis 
224430c8281Skettenis void
opal_attach_deferred(struct device * self)225430c8281Skettenis opal_attach_deferred(struct device *self)
226430c8281Skettenis {
227430c8281Skettenis 	struct opal_softc *sc = (struct opal_softc *)self;
228430c8281Skettenis 	int i;
229430c8281Skettenis 
230430c8281Skettenis 	for (i = 0; i < sc->sc_nintr; i++) {
23139ef823bSkettenis 		intr_establish(sc->sc_intr[i].oi_isn, IST_LEVEL, IPL_TTY,
232a940e3deSkettenis 		    NULL, opal_intr, &sc->sc_intr[i], sc->sc_dev.dv_xname);
233430c8281Skettenis 	}
234430c8281Skettenis }
235430c8281Skettenis 
236430c8281Skettenis int
opal_print(void * aux,const char * pnp)2379cff15efSkettenis opal_print(void *aux, const char *pnp)
2389cff15efSkettenis {
2399cff15efSkettenis 	struct fdt_attach_args *faa = aux;
2409cff15efSkettenis 	char name[32];
2419cff15efSkettenis 
2429cff15efSkettenis 	if (!pnp)
2439cff15efSkettenis 		return (QUIET);
2449cff15efSkettenis 
2459cff15efSkettenis 	if (OF_getprop(faa->fa_node, "name", name, sizeof(name)) > 0) {
2469cff15efSkettenis 		name[sizeof(name) - 1] = 0;
2479cff15efSkettenis 		printf("\"%s\"", name);
2489cff15efSkettenis 	} else
2499cff15efSkettenis 		printf("node %u", faa->fa_node);
2509cff15efSkettenis 
2519cff15efSkettenis 	printf(" at %s", pnp);
2529cff15efSkettenis 
2539cff15efSkettenis 	return (UNCONF);
2549cff15efSkettenis }
2559cff15efSkettenis 
2569cff15efSkettenis void
opal_attach_node(struct opal_softc * sc,int node)2579cff15efSkettenis opal_attach_node(struct opal_softc *sc, int node)
2589cff15efSkettenis {
2599cff15efSkettenis 	struct fdt_attach_args faa;
2609cff15efSkettenis 	char buf[32];
2619cff15efSkettenis 
2629cff15efSkettenis 	if (OF_getproplen(node, "compatible") <= 0)
2639cff15efSkettenis 		return;
2649cff15efSkettenis 
2659cff15efSkettenis 	if (OF_getprop(node, "status", buf, sizeof(buf)) > 0 &&
2669cff15efSkettenis 	    strcmp(buf, "disabled") == 0)
2679cff15efSkettenis 		return;
2689cff15efSkettenis 
2699cff15efSkettenis 	memset(&faa, 0, sizeof(faa));
2709cff15efSkettenis 	faa.fa_name = "";
2719cff15efSkettenis 	faa.fa_node = node;
2729cff15efSkettenis 
2739cff15efSkettenis 	config_found(&sc->sc_dev, &faa, opal_print);
274d59f9b5aSkettenis }
275d59f9b5aSkettenis 
276d59f9b5aSkettenis int
opal_gettime(struct todr_chip_handle * ch,struct timeval * tv)277d59f9b5aSkettenis opal_gettime(struct todr_chip_handle *ch, struct timeval *tv)
278d59f9b5aSkettenis {
279d59f9b5aSkettenis 	struct clock_ymdhms dt;
280d59f9b5aSkettenis 	uint64_t time;
281d59f9b5aSkettenis 	uint32_t date;
282d59f9b5aSkettenis 	int64_t error;
283d59f9b5aSkettenis 
284d59f9b5aSkettenis 	do {
2850d8e59b5Skettenis 		error = opal_rtc_read(opal_phys(&date), opal_phys(&time));
286d59f9b5aSkettenis 		if (error == OPAL_BUSY_EVENT)
287d59f9b5aSkettenis 			opal_poll_events(NULL);
288d59f9b5aSkettenis 	} while (error == OPAL_BUSY_EVENT);
289d59f9b5aSkettenis 
290d59f9b5aSkettenis 	if (error != OPAL_SUCCESS)
291d59f9b5aSkettenis 		return EIO;
292d59f9b5aSkettenis 
293d59f9b5aSkettenis 	dt.dt_sec = FROMBCD((time >> 40) & 0xff);
294d59f9b5aSkettenis 	dt.dt_min = FROMBCD((time >> 48) & 0xff);
295d59f9b5aSkettenis 	dt.dt_hour = FROMBCD((time >> 56) & 0xff);
296d59f9b5aSkettenis 	dt.dt_day = FROMBCD((date >> 0) & 0xff);
297d59f9b5aSkettenis 	dt.dt_mon = FROMBCD((date >> 8) & 0xff);
298d59f9b5aSkettenis 	dt.dt_year = FROMBCD((date >> 16) & 0xff);
299ee574247Skettenis 	dt.dt_year += 100 * FROMBCD((date >> 24) & 0xff);
300d59f9b5aSkettenis 
301d59f9b5aSkettenis 	tv->tv_sec = clock_ymdhms_to_secs(&dt);
302d59f9b5aSkettenis 	tv->tv_usec = 0;
303d59f9b5aSkettenis 
304d59f9b5aSkettenis 	return 0;
305d59f9b5aSkettenis }
306d59f9b5aSkettenis 
307d59f9b5aSkettenis int
opal_settime(struct todr_chip_handle * ch,struct timeval * tv)308d59f9b5aSkettenis opal_settime(struct todr_chip_handle *ch, struct timeval *tv)
309d59f9b5aSkettenis {
310d59f9b5aSkettenis 	struct clock_ymdhms dt;
311d59f9b5aSkettenis 	uint64_t time = 0;
312d59f9b5aSkettenis 	uint32_t date = 0;
313d59f9b5aSkettenis 	int64_t error;
314d59f9b5aSkettenis 
315d59f9b5aSkettenis 	clock_secs_to_ymdhms(tv->tv_sec, &dt);
316d59f9b5aSkettenis 
317d59f9b5aSkettenis 	time |= (uint64_t)TOBCD(dt.dt_sec) << 40;
318d59f9b5aSkettenis 	time |= (uint64_t)TOBCD(dt.dt_min) << 48;
319d59f9b5aSkettenis 	time |= (uint64_t)TOBCD(dt.dt_hour) << 56;
320d59f9b5aSkettenis 	date |= (uint32_t)TOBCD(dt.dt_day);
321d59f9b5aSkettenis 	date |= (uint32_t)TOBCD(dt.dt_mon) << 8;
322d59f9b5aSkettenis 	date |= (uint32_t)TOBCD(dt.dt_year) << 16;
323d59f9b5aSkettenis 	date |= (uint32_t)TOBCD(dt.dt_year / 100) << 24;
324d59f9b5aSkettenis 
325d59f9b5aSkettenis 	do {
326d59f9b5aSkettenis 		error = opal_rtc_write(date, time);
327d59f9b5aSkettenis 		if (error == OPAL_BUSY_EVENT)
328d59f9b5aSkettenis 			opal_poll_events(NULL);
329d59f9b5aSkettenis 	} while (error == OPAL_BUSY_EVENT);
330d59f9b5aSkettenis 
331d59f9b5aSkettenis 	if (error != OPAL_SUCCESS)
332d59f9b5aSkettenis 		return EIO;
333d59f9b5aSkettenis 
334d59f9b5aSkettenis 	return 0;
335d59f9b5aSkettenis }
336b286a873Sgkoehler 
337196daab3Sgkoehler #define OPAL_PM_LOSE_USER_CONTEXT	0x00001000
338196daab3Sgkoehler #define OPAL_PM_STOP_INST_FAST		0x00100000
339196daab3Sgkoehler 
340196daab3Sgkoehler void
opal_configure_idle_states(struct opal_softc * sc,int node)341196daab3Sgkoehler opal_configure_idle_states(struct opal_softc *sc, int node)
342196daab3Sgkoehler {
343196daab3Sgkoehler 	uint64_t *states;
344196daab3Sgkoehler 	uint32_t accept, *flags;
345196daab3Sgkoehler 	int count, flen, i, slen;
346196daab3Sgkoehler 	char *prop;
347196daab3Sgkoehler 
348196daab3Sgkoehler 	prop = "ibm,cpu-idle-state-flags";
349196daab3Sgkoehler 	flen = OF_getproplen(node, prop);
350196daab3Sgkoehler 	if (flen <= 0 || flen % sizeof(flags[0]) != 0)
351196daab3Sgkoehler 		return;
352196daab3Sgkoehler 	count = flen / sizeof(flags[0]);
353196daab3Sgkoehler 	slen = count * sizeof(states[0]);
354196daab3Sgkoehler 
355196daab3Sgkoehler 	flags = malloc(flen, M_DEVBUF, M_WAITOK);
356196daab3Sgkoehler 	states = malloc(slen, M_DEVBUF, M_WAITOK);
357196daab3Sgkoehler 	OF_getpropintarray(node, prop, flags, flen);
358196daab3Sgkoehler 
359196daab3Sgkoehler 	/* Power ISA v3 uses the psscr with the stop instruction. */
360196daab3Sgkoehler 	prop = "ibm,cpu-idle-state-psscr";
361196daab3Sgkoehler 	if (OF_getpropint64array(node, prop, states, slen) == slen) {
362196daab3Sgkoehler 		/*
363196daab3Sgkoehler 		 * Find the deepest idle state that doesn't lose too
364196daab3Sgkoehler 		 * much context.
365196daab3Sgkoehler 		 */
366196daab3Sgkoehler 		accept = OPAL_PM_LOSE_USER_CONTEXT | OPAL_PM_STOP_INST_FAST;
367196daab3Sgkoehler 		for (i = count - 1; i >= 0; i--) {
368196daab3Sgkoehler 			if ((flags[i] & ~accept) == 0) {
369196daab3Sgkoehler 				opal_found_stop_state(sc, states[i]);
370196daab3Sgkoehler 				break;
371196daab3Sgkoehler 			}
372196daab3Sgkoehler 		}
373196daab3Sgkoehler 	}
374196daab3Sgkoehler 
375196daab3Sgkoehler 	free(flags, M_DEVBUF, flen);
376196daab3Sgkoehler 	free(states, M_DEVBUF, slen);
377196daab3Sgkoehler }
378196daab3Sgkoehler 
379196daab3Sgkoehler void cpu_idle_stop(void);
380196daab3Sgkoehler #ifdef MULTIPROCESSOR
381196daab3Sgkoehler void cpu_hatch_and_stop(void);
382196daab3Sgkoehler #endif
383196daab3Sgkoehler 
384196daab3Sgkoehler void
opal_found_stop_state(struct opal_softc * sc,uint64_t state)385196daab3Sgkoehler opal_found_stop_state(struct opal_softc *sc, uint64_t state)
386196daab3Sgkoehler {
387196daab3Sgkoehler #ifdef MULTIPROCESSOR
388196daab3Sgkoehler 	uint32_t pirs[8];
389196daab3Sgkoehler 	int i, len, node;
390196daab3Sgkoehler 	char buf[32];
391196daab3Sgkoehler #endif
392196daab3Sgkoehler 
393196daab3Sgkoehler 	cpu_idle_state_psscr = state;
394196daab3Sgkoehler 	cpu_idle_cycle_fcn = &cpu_idle_stop;
395196daab3Sgkoehler 	printf("%s: idle psscr %llx\n", sc->sc_dev.dv_xname,
396196daab3Sgkoehler 	    (unsigned long long)state);
397196daab3Sgkoehler 
398196daab3Sgkoehler #ifdef MULTIPROCESSOR
399196daab3Sgkoehler 	/*
400196daab3Sgkoehler 	 * Idle the other hardware threads.  We use only one thread of
401196daab3Sgkoehler 	 * each cpu core.  The other threads are idle in OPAL.  If we
402196daab3Sgkoehler 	 * move them to a deeper idle state, then the core might
403196daab3Sgkoehler 	 * switch to single-thread mode, increase performance.
404196daab3Sgkoehler 	 */
405196daab3Sgkoehler 	node = OF_parent(curcpu()->ci_node);
406196daab3Sgkoehler 	for (node = OF_child(node); node != 0; node = OF_peer(node)) {
407196daab3Sgkoehler 		if (OF_getprop(node, "device_type", buf, sizeof(buf)) <= 0 ||
408196daab3Sgkoehler 		    strcmp(buf, "cpu") != 0)
409196daab3Sgkoehler 			continue;
410196daab3Sgkoehler 		len = OF_getpropintarray(node, "ibm,ppc-interrupt-server#s",
411196daab3Sgkoehler 		    pirs, sizeof(pirs));
412196daab3Sgkoehler 		if (len > 0 && len % 4 == 0) {
413196daab3Sgkoehler 			/* Skip i = 0, the first hardware thread. */
414196daab3Sgkoehler 			for (i = 1; i < len / 4; i++)
415196daab3Sgkoehler 				opal_start_cpu(pirs[i],
416196daab3Sgkoehler 				    (vaddr_t)cpu_hatch_and_stop);
417196daab3Sgkoehler 		}
418196daab3Sgkoehler 	}
419196daab3Sgkoehler #endif
420196daab3Sgkoehler }
421196daab3Sgkoehler 
422b286a873Sgkoehler void
opalpm_init(struct opal_softc * sc,int node)423b286a873Sgkoehler opalpm_init(struct opal_softc *sc, int node)
424b286a873Sgkoehler {
425b286a873Sgkoehler 	int i, len;
426b286a873Sgkoehler 
427b286a873Sgkoehler 	len = OF_getproplen(node, "ibm,pstate-ids");
428b286a873Sgkoehler 	if (len <= 0 || len % sizeof(int) != 0 ||
429b286a873Sgkoehler 	    len != OF_getproplen(node, "ibm,pstate-frequencies-mhz")) {
430196daab3Sgkoehler 		printf("%s: can't parse pstates\n", sc->sc_dev.dv_xname);
431b286a873Sgkoehler 		return;
432b286a873Sgkoehler 	}
433b286a873Sgkoehler 	sc->sc_pstate = malloc(len, M_DEVBUF, M_WAITOK);
434b286a873Sgkoehler 	sc->sc_freq = malloc(len, M_DEVBUF, M_WAITOK);
435b286a873Sgkoehler 	sc->sc_npstate = len / sizeof(int);
436b286a873Sgkoehler 	OF_getprop(node, "ibm,pstate-ids", sc->sc_pstate, len);
437b286a873Sgkoehler 	OF_getprop(node, "ibm,pstate-frequencies-mhz", sc->sc_freq, len);
438b286a873Sgkoehler 
439b286a873Sgkoehler 	if ((i = opalpm_find_index(sc)) != -1)
440b286a873Sgkoehler 		perflevel = (sc->sc_npstate - 1 - i) * 100 / sc->sc_npstate;
441b286a873Sgkoehler 	cpu_cpuspeed = opalpm_cpuspeed;
442c7966c0cSgkoehler #ifndef MULTIPROCESSOR
443b286a873Sgkoehler 	cpu_setperf = opalpm_setperf;
444c7966c0cSgkoehler #else
445c7966c0cSgkoehler 	ul_setperf = opalpm_setperf;
446c7966c0cSgkoehler 	cpu_setperf = mp_setperf;
447c7966c0cSgkoehler #endif
448b286a873Sgkoehler }
449b286a873Sgkoehler 
450b286a873Sgkoehler int
opalpm_find_index(struct opal_softc * sc)451b286a873Sgkoehler opalpm_find_index(struct opal_softc *sc)
452b286a873Sgkoehler {
453b286a873Sgkoehler 	int i, pstate;
454b286a873Sgkoehler 
455b286a873Sgkoehler 	/*
456b286a873Sgkoehler 	 * POWER9 23.5.8.3 Power Management Status Register (PMSR)
457b286a873Sgkoehler 	 * 8:15 Local Actual Pstate
458b286a873Sgkoehler 	 */
459b286a873Sgkoehler 	pstate = (mfpmsr() >> 48) & 0xff;
460b286a873Sgkoehler 	for (i = 0; i < sc->sc_npstate; i++) {
461b286a873Sgkoehler 		if (sc->sc_pstate[i] == pstate)
462b286a873Sgkoehler 			return i;
463b286a873Sgkoehler 	}
464b286a873Sgkoehler 	return -1;
465b286a873Sgkoehler }
466b286a873Sgkoehler 
467b286a873Sgkoehler int
opalpm_cpuspeed(int * freq)468b286a873Sgkoehler opalpm_cpuspeed(int *freq)
469b286a873Sgkoehler {
470b286a873Sgkoehler 	struct opal_softc *sc = opal_sc;
471b286a873Sgkoehler 	int i;
472b286a873Sgkoehler 
473b286a873Sgkoehler 	if ((i = opalpm_find_index(sc)) == -1)
474b286a873Sgkoehler 		return 1;
475b286a873Sgkoehler 	*freq = sc->sc_freq[i];
476b286a873Sgkoehler 	return 0;
477b286a873Sgkoehler }
478b286a873Sgkoehler 
479b286a873Sgkoehler void
opalpm_setperf(int level)480b286a873Sgkoehler opalpm_setperf(int level)
481b286a873Sgkoehler {
482b286a873Sgkoehler 	struct opal_softc *sc = opal_sc;
483b286a873Sgkoehler 	uint64_t pstate;
484b286a873Sgkoehler 	int i;
485b286a873Sgkoehler 
486b286a873Sgkoehler 	/*
487b286a873Sgkoehler 	 * Assume that "ibm,pstate-frequencies-mhz" is sorted from
488b286a873Sgkoehler 	 * fastest to slowest.
489b286a873Sgkoehler 	 */
490b286a873Sgkoehler 	i = (100 - level) * (sc->sc_npstate - 1) / 100;
491b286a873Sgkoehler 	pstate = sc->sc_pstate[i];
492b286a873Sgkoehler 
493b286a873Sgkoehler 	/*
494b286a873Sgkoehler 	 * POWER9 23.5.8.1 Power Management Control Register (PMCR)
495b286a873Sgkoehler 	 *  0:7  Upper Pstate request
496b286a873Sgkoehler 	 *  8:15 Lower Pstate request
497b286a873Sgkoehler 	 * 60:63 Version
498b286a873Sgkoehler 	 */
499b286a873Sgkoehler 	mtpmcr((pstate << 56) | (pstate << 48) | 0);
500b286a873Sgkoehler }
501