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