xref: /openbsd-src/sys/dev/acpi/acpicpu.c (revision a28daedfc357b214be5c701aa8ba8adb29a7f1c2)
1 /* $OpenBSD: acpicpu.c,v 1.53 2009/02/24 13:20:02 deraadt Exp $ */
2 /*
3  * Copyright (c) 2005 Marco Peereboom <marco@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/proc.h>
20 #include <sys/signalvar.h>
21 #include <sys/sysctl.h>
22 #include <sys/systm.h>
23 #include <sys/device.h>
24 #include <sys/malloc.h>
25 #include <sys/queue.h>
26 
27 #include <machine/bus.h>
28 #include <machine/cpu.h>
29 #include <machine/cpufunc.h>
30 #include <machine/specialreg.h>
31 
32 #include <dev/acpi/acpireg.h>
33 #include <dev/acpi/acpivar.h>
34 #include <dev/acpi/acpidev.h>
35 #include <dev/acpi/amltypes.h>
36 #include <dev/acpi/dsdt.h>
37 
38 #include <sys/sensors.h>
39 
40 int	acpicpu_match(struct device *, void *, void *);
41 void	acpicpu_attach(struct device *, struct device *, void *);
42 int	acpicpu_notify(struct aml_node *, int, void *);
43 void	acpicpu_setperf(int);
44 
45 #define ACPI_STATE_C0		0x00
46 #define ACPI_STATE_C1		0x01
47 #define ACPI_STATE_C2		0x02
48 #define ACPI_STATE_C3		0x03
49 
50 #define FLAGS_NO_C2		0x01
51 #define FLAGS_NO_C3		0x02
52 #define FLAGS_BMCHECK		0x04
53 #define FLAGS_NOTHROTTLE	0x08
54 #define FLAGS_NOPSS		0x10
55 #define FLAGS_NOPCT		0x20
56 
57 #define CPU_THT_EN		(1L << 4)
58 #define CPU_MAXSTATE(sc)	(1L << (sc)->sc_duty_wid)
59 #define CPU_STATE(sc,pct)	((pct * CPU_MAXSTATE(sc) / 100) << (sc)->sc_duty_off)
60 #define CPU_STATEMASK(sc)	((CPU_MAXSTATE(sc) - 1) << (sc)->sc_duty_off)
61 
62 #define ACPI_MAX_C2_LATENCY	100
63 #define ACPI_MAX_C3_LATENCY	1000
64 
65 /* Make sure throttling bits are valid,a=addr,o=offset,w=width */
66 #define valid_throttle(o,w,a)	(a && w && (o+w)<=31 && (o>4 || (o+w)<=4))
67 
68 struct acpi_cstate
69 {
70 	int	 type;
71 	int	 latency;
72 	int	 power;
73 	int	 address;
74 
75 	SLIST_ENTRY(acpi_cstate) link;
76 };
77 
78 struct acpicpu_softc {
79 	struct device		sc_dev;
80 	int			sc_cpu;
81 
82 	int			sc_duty_wid;
83 	int			sc_duty_off;
84 	int			sc_pblk_addr;
85 	int			sc_pblk_len;
86 	int			sc_flags;
87 
88 	SLIST_HEAD(,acpi_cstate) sc_cstates;
89 
90 	bus_space_tag_t		sc_iot;
91 	bus_space_handle_t	sc_ioh;
92 
93 	struct acpi_softc	*sc_acpi;
94 	struct aml_node		*sc_devnode;
95 
96 	int			sc_pss_len;
97 	int			sc_ppc;
98 	int			sc_level;
99 	struct acpicpu_pss	*sc_pss;
100 
101 	struct acpicpu_pct	sc_pct;
102 	/* save compensation for pct access for lying bios' */
103 	u_int32_t		sc_pct_stat_as;
104 	u_int32_t		sc_pct_ctrl_as;
105 	u_int32_t		sc_pct_stat_len;
106 	u_int32_t		sc_pct_ctrl_len;
107 	/*
108 	 * XXX: _PPC Change listener
109 	 * PPC changes can occur when for example a machine is disconnected
110 	 * from AC power and can no loger support the highest frequency or
111 	 * voltage when driven from the battery.
112 	 * Should probably be reimplemented as a list for now we assume only
113 	 * one listener
114 	 */
115 	void			(*sc_notify)(struct acpicpu_pss *, int);
116 };
117 
118 void    acpicpu_add_cstatepkg(struct aml_value *, void *);
119 int	acpicpu_getppc(struct acpicpu_softc *);
120 int	acpicpu_getpct(struct acpicpu_softc *);
121 int	acpicpu_getpss(struct acpicpu_softc *);
122 struct acpi_cstate *acpicpu_add_cstate(struct acpicpu_softc *, int, int, int,
123     int);
124 #if 0
125 void    acpicpu_set_throttle(struct acpicpu_softc *, int);
126 struct acpi_cstate *acpicpu_find_cstate(struct acpicpu_softc *, int);
127 #endif
128 
129 struct cfattach acpicpu_ca = {
130 	sizeof(struct acpicpu_softc), acpicpu_match, acpicpu_attach
131 };
132 
133 struct cfdriver acpicpu_cd = {
134 	NULL, "acpicpu", DV_DULL
135 };
136 
137 extern int setperf_prio;
138 
139 struct acpicpu_softc *acpicpu_sc[MAXCPUS];
140 
141 #if 0
142 void
143 acpicpu_set_throttle(struct acpicpu_softc *sc, int level)
144 {
145 	uint32_t pbval;
146 
147 	if (sc->sc_flags & FLAGS_NOTHROTTLE)
148 		return;
149 
150 	/* Disable throttling control */
151 	pbval = inl(sc->sc_pblk_addr);
152 	outl(sc->sc_pblk_addr, pbval & ~CPU_THT_EN);
153 	if (level < 100) {
154 		pbval &= ~CPU_STATEMASK(sc);
155 		pbval |= CPU_STATE(sc, level);
156 		outl(sc->sc_pblk_addr, pbval & ~CPU_THT_EN);
157 		outl(sc->sc_pblk_addr, pbval | CPU_THT_EN);
158 	}
159 }
160 
161 struct acpi_cstate *
162 acpicpu_find_cstate(struct acpicpu_softc *sc, int type)
163 {
164 	struct acpi_cstate	*cx;
165 
166 	SLIST_FOREACH(cx, &sc->sc_cstates, link)
167 		if (cx->type == type)
168 			return cx;
169 	return (NULL);
170 }
171 #endif
172 
173 struct acpi_cstate *
174 acpicpu_add_cstate(struct acpicpu_softc *sc, int type, int latency, int power,
175     int address)
176 {
177 	struct acpi_cstate	*cx;
178 
179 	dnprintf(10," C%d: latency:.%4x power:%.4x addr:%.8x\n",
180 	    type, latency, power, address);
181 
182 	switch (type) {
183 	case ACPI_STATE_C2:
184 		if (latency > ACPI_MAX_C2_LATENCY || !address ||
185 		    (sc->sc_flags & FLAGS_NO_C2))
186 			goto bad;
187 		break;
188 	case ACPI_STATE_C3:
189 		if (latency > ACPI_MAX_C3_LATENCY || !address ||
190 		    (sc->sc_flags & FLAGS_NO_C3))
191 			goto bad;
192 		break;
193 	}
194 
195 	cx = malloc(sizeof(*cx), M_DEVBUF, M_WAITOK | M_ZERO);
196 
197 	cx->type = type;
198 	cx->power = power;
199 	cx->latency = latency;
200 	cx->address = address;
201 
202 	SLIST_INSERT_HEAD(&sc->sc_cstates, cx, link);
203 
204 	return (cx);
205  bad:
206 	dprintf("acpicpu%d: C%d not supported", sc->sc_cpu, type);
207 	return (NULL);
208 }
209 
210 /* Found a _CST object, add new cstate for each entry */
211 void
212 acpicpu_add_cstatepkg(struct aml_value *val, void *arg)
213 {
214 	struct acpicpu_softc	*sc = arg;
215 
216 #if defined(ACPI_DEBUG) && !defined(SMALL_KERNEL)
217 	aml_showvalue(val, 0);
218 #endif
219 	if (val->type != AML_OBJTYPE_PACKAGE || val->length != 4)
220 		return;
221 
222 	acpicpu_add_cstate(sc, val->v_package[1]->v_integer,
223 	    val->v_package[2]->v_integer,
224 	    val->v_package[3]->v_integer, -1);
225 }
226 
227 
228 int
229 acpicpu_match(struct device *parent, void *match, void *aux)
230 {
231 	struct acpi_attach_args	*aa = aux;
232 	struct cfdata		*cf = match;
233 
234 	/* sanity */
235 	if (aa->aaa_name == NULL ||
236 	    strcmp(aa->aaa_name, cf->cf_driver->cd_name) != 0 ||
237 	    aa->aaa_table != NULL)
238 		return (0);
239 
240 	return (1);
241 }
242 
243 void
244 acpicpu_attach(struct device *parent, struct device *self, void *aux)
245 {
246 	struct acpicpu_softc	*sc = (struct acpicpu_softc *)self;
247 	struct acpi_attach_args *aa = aux;
248 	struct aml_value	res;
249 	int			i;
250 	struct acpi_cstate	*cx;
251 	u_int32_t		status = 0;
252 
253 	sc->sc_acpi = (struct acpi_softc *)parent;
254 	sc->sc_devnode = aa->aaa_node;
255 	acpicpu_sc[sc->sc_dev.dv_unit] = sc;
256 
257 	SLIST_INIT(&sc->sc_cstates);
258 
259 	sc->sc_pss = NULL;
260 
261 	if (aml_evalnode(sc->sc_acpi, sc->sc_devnode, 0, NULL, &res) == 0) {
262 		if (res.type == AML_OBJTYPE_PROCESSOR) {
263 			sc->sc_cpu = res.v_processor.proc_id;
264 			sc->sc_pblk_addr = res.v_processor.proc_addr;
265 			sc->sc_pblk_len = res.v_processor.proc_len;
266 		}
267 		aml_freevalue(&res);
268 	}
269 	sc->sc_duty_off = sc->sc_acpi->sc_fadt->duty_offset;
270 	sc->sc_duty_wid = sc->sc_acpi->sc_fadt->duty_width;
271 	if (!valid_throttle(sc->sc_duty_off, sc->sc_duty_wid, sc->sc_pblk_addr))
272 		sc->sc_flags |= FLAGS_NOTHROTTLE;
273 #ifdef ACPI_DEBUG
274 	printf(": %s: ", sc->sc_devnode->name);
275 	printf("\n: hdr:%x pblk:%x,%x duty:%x,%x pstate:%x (%d throttling states)\n",
276 		sc->sc_acpi->sc_fadt->hdr_revision,
277 		sc->sc_pblk_addr, sc->sc_pblk_len,
278 		sc->sc_duty_off, sc->sc_duty_wid,
279 		sc->sc_acpi->sc_fadt->pstate_cnt,
280 		CPU_MAXSTATE(sc));
281 #endif
282 
283 	/* Get C-States from _CST or FADT */
284 	if (!aml_evalname(sc->sc_acpi, sc->sc_devnode, "_CST", 0, NULL, &res)) {
285 		aml_foreachpkg(&res, 1, acpicpu_add_cstatepkg, sc);
286 		aml_freevalue(&res);
287 	}
288 	else {
289 		/* Some systems don't export a full PBLK reduce functionality */
290 		if (sc->sc_pblk_len < 5)
291 			sc->sc_flags |= FLAGS_NO_C2;
292 		if (sc->sc_pblk_len < 6)
293 			sc->sc_flags |= FLAGS_NO_C3;
294 		acpicpu_add_cstate(sc, ACPI_STATE_C2,
295 		    sc->sc_acpi->sc_fadt->p_lvl2_lat, -1,
296 		    sc->sc_pblk_addr + 4);
297 		acpicpu_add_cstate(sc, ACPI_STATE_C3,
298 		    sc->sc_acpi->sc_fadt->p_lvl3_lat, -1,
299 		    sc->sc_pblk_addr + 5);
300 	}
301 	if (acpicpu_getpss(sc)) {
302 		sc->sc_flags |= FLAGS_NOPSS;
303 	} else {
304 #ifdef ACPI_DEBUG
305 		for (i = 0; i < sc->sc_pss_len; i++) {
306 			dnprintf(20, "%d %d %d %d %d %d\n",
307 			    sc->sc_pss[i].pss_core_freq,
308 			    sc->sc_pss[i].pss_power,
309 			    sc->sc_pss[i].pss_trans_latency,
310 			    sc->sc_pss[i].pss_bus_latency,
311 			    sc->sc_pss[i].pss_ctrl,
312 			    sc->sc_pss[i].pss_status);
313 		}
314 		dnprintf(20, "\n");
315 #endif
316 		if (sc->sc_pss_len == 0) {
317 			/* this should never happen */
318 			printf("%s: invalid _PSS length\n", DEVNAME(sc));
319 			sc->sc_flags |= FLAGS_NOPSS;
320 		}
321 
322 		acpicpu_getppc(sc);
323 		if (acpicpu_getpct(sc))
324 			sc->sc_flags |= FLAGS_NOPCT;
325 		else {
326 
327 			/* Notify BIOS we are handing p-states */
328 			if (sc->sc_acpi->sc_fadt->pstate_cnt)
329 				acpi_write_pmreg(sc->sc_acpi, ACPIREG_SMICMD, 0,
330 				sc->sc_acpi->sc_fadt->pstate_cnt);
331 
332 			aml_register_notify(sc->sc_devnode, NULL,
333 			    acpicpu_notify, sc, ACPIDEV_NOPOLL);
334 
335 			acpi_gasio(sc->sc_acpi, ACPI_IOREAD,
336 			    sc->sc_pct.pct_status.grd_gas.address_space_id,
337 			    sc->sc_pct.pct_status.grd_gas.address,
338 			    sc->sc_pct_stat_as, sc->sc_pct_stat_as, &status);
339 			sc->sc_level = (100 / sc->sc_pss_len) *
340 			    (sc->sc_pss_len - status);
341 			dnprintf(20, "%s: cpu index %d, percentage %d\n",
342 			    DEVNAME(sc), status, sc->sc_level);
343 			if (setperf_prio < 30) {
344 				cpu_setperf = acpicpu_setperf;
345 				setperf_prio = 30;
346 				acpi_hasprocfvs = 1;
347 			}
348 		}
349 	}
350 
351 	/*
352 	 * Nicely enumerate what power management capabilities
353 	 * ACPI CPU provides.
354 	 */
355 	if (!SLIST_EMPTY(&sc->sc_cstates)) {
356 		printf(":");
357 
358 		i = 0;
359 		SLIST_FOREACH(cx, &sc->sc_cstates, link) {
360 			if (i++)
361 				printf(",");
362 			switch (cx->type) {
363 			case ACPI_STATE_C0:
364 				printf(" C0");
365 				break;
366 			case ACPI_STATE_C1:
367 				printf(" C1");
368 				break;
369 			case ACPI_STATE_C2:
370 				printf(" C2");
371 				break;
372 			case ACPI_STATE_C3:
373 				printf(" C3");
374 				break;
375 			}
376 		}
377 	}
378 
379 	if (!(sc->sc_flags & (FLAGS_NOPSS | FLAGS_NOPCT)) ||
380 	    !(sc->sc_flags & FLAGS_NOPSS)) {
381 		printf("%c ", SLIST_EMPTY(&sc->sc_cstates) ? ':' : ',');
382 
383 		/*
384 		 * If acpicpu is itself providing the capability to transition
385 		 * states, enumerate them in the fashion that est and powernow
386 		 * would.
387 		 */
388 		if (!(sc->sc_flags & (FLAGS_NOPSS | FLAGS_NOPCT))) {
389 			printf("FVS, ");
390 			for (i = 0; i < sc->sc_pss_len - 1; i++)
391 				printf("%d, ", sc->sc_pss[i].pss_core_freq);
392 			printf("%d MHz", sc->sc_pss[i].pss_core_freq);
393 		} else
394 			printf("PSS");
395 	}
396 
397 	printf("\n");
398 }
399 
400 int
401 acpicpu_getppc(struct acpicpu_softc *sc)
402 {
403 	struct aml_value	res;
404 
405 	sc->sc_ppc = 0;
406 
407 	if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "_PPC", 0, NULL, &res)) {
408 		dnprintf(10, "%s: no _PPC\n", DEVNAME(sc));
409 		return (1);
410 	}
411 
412 	sc->sc_ppc = aml_val2int(&res);
413 	dnprintf(10, "%s: _PPC: %d\n", DEVNAME(sc), sc->sc_ppc);
414 	aml_freevalue(&res);
415 
416 	return (0);
417 }
418 
419 int
420 acpicpu_getpct(struct acpicpu_softc *sc)
421 {
422 	struct aml_value	res;
423 	int			rv = 1;
424 
425 	if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "_PCT", 0, NULL, &res)) {
426 		dnprintf(20, "%s: no _PCT\n", DEVNAME(sc));
427 		return (1);
428 	}
429 
430 	if (res.length != 2) {
431 		dnprintf(20, "%s: %s: invalid _PCT length\n", DEVNAME(sc),
432 		    sc->sc_devnode->name);
433 		return (1);
434 	}
435 
436 	memcpy(&sc->sc_pct.pct_ctrl, res.v_package[0]->v_buffer,
437 	    sizeof sc->sc_pct.pct_ctrl);
438 	if (sc->sc_pct.pct_ctrl.grd_gas.address_space_id ==
439 	    GAS_FUNCTIONAL_FIXED) {
440 		dnprintf(20, "CTRL GASIO is functional fixed hardware.\n");
441 		goto ffh;
442 	}
443 
444 	memcpy(&sc->sc_pct.pct_status, res.v_package[1]->v_buffer,
445 	    sizeof sc->sc_pct.pct_status);
446 	if (sc->sc_pct.pct_status.grd_gas.address_space_id ==
447 	    GAS_FUNCTIONAL_FIXED) {
448 		dnprintf(20, "CTRL GASIO is functional fixed hardware.\n");
449 		goto ffh;
450 	}
451 
452 	dnprintf(10, "_PCT(ctrl)  : %02x %04x %02x %02x %02x %02x %016x\n",
453 	    sc->sc_pct.pct_ctrl.grd_descriptor,
454 	    sc->sc_pct.pct_ctrl.grd_length,
455 	    sc->sc_pct.pct_ctrl.grd_gas.address_space_id,
456 	    sc->sc_pct.pct_ctrl.grd_gas.register_bit_width,
457 	    sc->sc_pct.pct_ctrl.grd_gas.register_bit_offset,
458 	    sc->sc_pct.pct_ctrl.grd_gas.access_size,
459 	    sc->sc_pct.pct_ctrl.grd_gas.address);
460 
461 	dnprintf(10, "_PCT(status): %02x %04x %02x %02x %02x %02x %016x\n",
462 	    sc->sc_pct.pct_status.grd_descriptor,
463 	    sc->sc_pct.pct_status.grd_length,
464 	    sc->sc_pct.pct_status.grd_gas.address_space_id,
465 	    sc->sc_pct.pct_status.grd_gas.register_bit_width,
466 	    sc->sc_pct.pct_status.grd_gas.register_bit_offset,
467 	    sc->sc_pct.pct_status.grd_gas.access_size,
468 	    sc->sc_pct.pct_status.grd_gas.address);
469 
470 	/* if not set assume single 32 bit access */
471 	sc->sc_pct_stat_as = sc->sc_pct.pct_status.grd_gas.register_bit_width
472 	    / 8;
473 	if (sc->sc_pct_stat_as == 0)
474 		sc->sc_pct_stat_as = 4;
475 	sc->sc_pct_ctrl_as = sc->sc_pct.pct_ctrl.grd_gas.register_bit_width / 8;
476 	if (sc->sc_pct_ctrl_as == 0)
477 		sc->sc_pct_ctrl_as = 4;
478 	sc->sc_pct_stat_len = sc->sc_pct.pct_status.grd_gas.access_size;
479 	if (sc->sc_pct_stat_len == 0)
480 		sc->sc_pct_stat_len = sc->sc_pct_stat_as;
481 	sc->sc_pct_ctrl_len = sc->sc_pct.pct_ctrl.grd_gas.access_size;
482 	if (sc->sc_pct_ctrl_len == 0)
483 		sc->sc_pct_ctrl_len = sc->sc_pct_ctrl_as;
484 
485 	rv = 0;
486 ffh:
487 	aml_freevalue(&res);
488 	return (rv);
489 }
490 
491 int
492 acpicpu_getpss(struct acpicpu_softc *sc)
493 {
494 	struct aml_value	res;
495 	int			i;
496 
497 	if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "_PSS", 0, NULL, &res)) {
498 		dprintf("%s: no _PSS\n", DEVNAME(sc));
499 		return (1);
500 	}
501 
502 	if (sc->sc_pss)
503 		free(sc->sc_pss, M_DEVBUF);
504 
505 	sc->sc_pss = malloc(res.length * sizeof *sc->sc_pss, M_DEVBUF,
506 	    M_WAITOK | M_ZERO);
507 
508 	for (i = 0; i < res.length; i++) {
509 		sc->sc_pss[i].pss_core_freq = aml_val2int(
510 		    res.v_package[i]->v_package[0]);
511 		sc->sc_pss[i].pss_power = aml_val2int(
512 		    res.v_package[i]->v_package[1]);
513 		sc->sc_pss[i].pss_trans_latency = aml_val2int(
514 		    res.v_package[i]->v_package[2]);
515 		sc->sc_pss[i].pss_bus_latency = aml_val2int(
516 		    res.v_package[i]->v_package[3]);
517 		sc->sc_pss[i].pss_ctrl = aml_val2int(
518 		    res.v_package[i]->v_package[4]);
519 		sc->sc_pss[i].pss_status = aml_val2int(
520 		    res.v_package[i]->v_package[5]);
521 	}
522 	sc->sc_pss_len = res.length;
523 
524 	aml_freevalue(&res);
525 
526 	return (0);
527 }
528 
529 int
530 acpicpu_fetch_pss(struct acpicpu_pss **pss)
531 {
532 	struct acpicpu_softc	*sc;
533 
534 	/*
535 	 * XXX: According to the ACPI spec in an SMP system all processors
536 	 * are supposed to support the same states. For now we pray
537 	 * the bios ensures this...
538 	 * XXX part deux: this needs to account for _PPC as well
539 	 * when AC is removed the nr of _PSS entries can go down
540 	 */
541 
542 	sc = acpicpu_sc[0];
543 	if (!sc)
544 		return 0;
545 	*pss = sc->sc_pss;
546 
547 	return (sc->sc_pss_len);
548 }
549 
550 int
551 acpicpu_notify(struct aml_node *node, int notify_type, void *arg)
552 {
553 	struct acpicpu_softc	*sc = arg;
554 
555 	dnprintf(10, "acpicpu_notify: %.2x %s\n", notify_type,
556 	    sc->sc_devnode->name);
557 
558 	switch (notify_type) {
559 	case 0x80:	/* _PPC changed, retrieve new values */
560 		acpicpu_getppc(sc);
561 		acpicpu_getpss(sc);
562 		if (sc->sc_notify)
563 			sc->sc_notify(sc->sc_pss, sc->sc_pss_len);
564 
565 		/* reset performance to current percentage */
566 		/* XXX will fail for amd64 for now */
567 		cpu_setperf(sc->sc_level);
568 		break;
569 	default:
570 		printf("%s: unhandled cpu event %x\n", DEVNAME(sc),
571 		    notify_type);
572 		break;
573 	}
574 
575 	return (0);
576 }
577 
578 void
579 acpicpu_set_notify(void (*func)(struct acpicpu_pss *, int)) {
580 	struct acpicpu_softc    *sc;
581 
582 	sc = acpicpu_sc[0];
583 	if (sc != NULL)
584 		sc->sc_notify = func;
585 }
586 
587 void
588 acpicpu_setperf(int level)
589 {
590 	struct acpicpu_softc	*sc;
591 	struct acpicpu_pss	*pss = NULL;
592 	int			idx, len;
593 	u_int32_t		status = 0;
594 
595 	sc = acpicpu_sc[cpu_number()];
596 
597 	dnprintf(10, "%s: acpicpu setperf level %d\n",
598 	    sc->sc_devnode->name, level);
599 
600 	if (level < 0 || level > 100) {
601 		dnprintf(10, "%s: acpicpu setperf illegal percentage\n",
602 		    sc->sc_devnode->name);
603 		return;
604 	}
605 
606 	/*
607 	 * XXX this should be handled more gracefully and it needs to also do
608 	 * the duty cycle method instead of pss exclusively
609 	 */
610 	if (sc->sc_flags & FLAGS_NOPSS || sc->sc_flags & FLAGS_NOPCT) {
611 		dnprintf(10, "%s: acpicpu no _PSS or _PCT\n", sc->sc_devnode->name);
612 		return;
613 	}
614 
615 	if (sc->sc_ppc)
616 		len = sc->sc_ppc;
617 	else
618 		len = sc->sc_pss_len;
619 	idx = (len - 1) - (level / (100 / len));
620 	if (idx < 0)
621 		idx = 0;
622 
623 	if (sc->sc_ppc)
624 		idx += sc->sc_pss_len - sc->sc_ppc;
625 
626 	if (idx > sc->sc_pss_len)
627 		idx = sc->sc_pss_len - 1;
628 
629 	dnprintf(10, "%s: acpicpu setperf index %d pss_len %d ppc %d\n",
630 	    sc->sc_devnode->name, idx, sc->sc_pss_len, sc->sc_ppc);
631 
632 	pss = &sc->sc_pss[idx];
633 
634 #ifdef ACPI_DEBUG
635 	/* keep this for now since we will need this for debug in the field */
636 	printf("0 status: %x %llx %u %u ctrl: %x %llx %u %u\n",
637 	    sc->sc_pct.pct_status.grd_gas.address_space_id,
638 	    sc->sc_pct.pct_status.grd_gas.address,
639 	    sc->sc_pct_stat_as, sc->sc_pct_stat_len,
640 	    sc->sc_pct.pct_ctrl.grd_gas.address_space_id,
641 	    sc->sc_pct.pct_ctrl.grd_gas.address,
642 	    sc->sc_pct_ctrl_as, sc->sc_pct_ctrl_len);
643 #endif
644 	acpi_gasio(sc->sc_acpi, ACPI_IOREAD,
645 	    sc->sc_pct.pct_status.grd_gas.address_space_id,
646 	    sc->sc_pct.pct_status.grd_gas.address, sc->sc_pct_stat_as,
647 	    sc->sc_pct_stat_len, &status);
648 	dnprintf(20, "1 status: %u <- %u\n", status, pss->pss_status);
649 
650 	/* Are we already at the requested frequency? */
651 	if (status == pss->pss_status)
652 		return;
653 
654 	acpi_gasio(sc->sc_acpi, ACPI_IOWRITE,
655 	    sc->sc_pct.pct_ctrl.grd_gas.address_space_id,
656 	    sc->sc_pct.pct_ctrl.grd_gas.address, sc->sc_pct_ctrl_as,
657 	    sc->sc_pct_ctrl_len, &pss->pss_ctrl);
658 	dnprintf(20, "pss_ctrl: %x\n", pss->pss_ctrl);
659 
660 	acpi_gasio(sc->sc_acpi, ACPI_IOREAD,
661 	    sc->sc_pct.pct_status.grd_gas.address_space_id,
662 	    sc->sc_pct.pct_status.grd_gas.address, sc->sc_pct_stat_as,
663 	    sc->sc_pct_stat_as, &status);
664 	dnprintf(20, "2 status: %d\n", status);
665 
666 	/* Did the transition succeed? */
667 	 if (status == pss->pss_status) {
668 		cpuspeed = pss->pss_core_freq;
669 		sc->sc_level = level;
670 	} else
671 		printf("%s: acpicpu setperf failed to alter frequency\n",
672 		    sc->sc_devnode->name);
673 }
674