xref: /openbsd-src/sys/dev/acpi/acpicpu.c (revision 50b7afb2c2c0993b0894d4e34bf857cb13ed9c80)
1 /* $OpenBSD: acpicpu.c,v 1.60 2014/07/12 18:48:17 tedu 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 void	acpicpu_setperf_ppc_change(struct acpicpu_pss *, int);
45 
46 #define ACPI_STATE_C0		0x00
47 #define ACPI_STATE_C1		0x01
48 #define ACPI_STATE_C2		0x02
49 #define ACPI_STATE_C3		0x03
50 
51 #define ACPI_PDC_REVID		0x1
52 #define ACPI_PDC_SMP		0xa
53 #define ACPI_PDC_MSR		0x1
54 
55 /* _PDC Intel capabilities flags from linux */
56 #define ACPI_PDC_P_FFH		0x0001
57 #define ACPI_PDC_C_C1_HALT	0x0002
58 #define ACPI_PDC_T_FFH		0x0004
59 #define ACPI_PDC_SMP_C1PT	0x0008
60 #define ACPI_PDC_SMP_C2C3	0x0010
61 #define ACPI_PDC_SMP_P_SWCOORD	0x0020
62 #define ACPI_PDC_SMP_C_SWCOORD	0x0040
63 #define ACPI_PDC_SMP_T_SWCOORD	0x0080
64 #define ACPI_PDC_C_C1_FFH	0x0100
65 #define ACPI_PDC_C_C2C3_FFH	0x0200
66 
67 #define FLAGS_NO_C2		0x01
68 #define FLAGS_NO_C3		0x02
69 #define FLAGS_BMCHECK		0x04
70 #define FLAGS_NOTHROTTLE	0x08
71 #define FLAGS_NOPSS		0x10
72 #define FLAGS_NOPCT		0x20
73 
74 #define CPU_THT_EN		(1L << 4)
75 #define CPU_MAXSTATE(sc)	(1L << (sc)->sc_duty_wid)
76 #define CPU_STATE(sc,pct)	((pct * CPU_MAXSTATE(sc) / 100) << (sc)->sc_duty_off)
77 #define CPU_STATEMASK(sc)	((CPU_MAXSTATE(sc) - 1) << (sc)->sc_duty_off)
78 
79 #define ACPI_MAX_C2_LATENCY	100
80 #define ACPI_MAX_C3_LATENCY	1000
81 
82 /* Make sure throttling bits are valid,a=addr,o=offset,w=width */
83 #define valid_throttle(o,w,a)	(a && w && (o+w)<=31 && (o>4 || (o+w)<=4))
84 
85 struct acpi_cstate
86 {
87 	int	 type;
88 	int	 latency;
89 	int	 power;
90 	int	 address;
91 
92 	SLIST_ENTRY(acpi_cstate) link;
93 };
94 
95 struct acpicpu_softc {
96 	struct device		sc_dev;
97 	int			sc_cpu;
98 
99 	int			sc_duty_wid;
100 	int			sc_duty_off;
101 	int			sc_pblk_addr;
102 	int			sc_pblk_len;
103 	int			sc_flags;
104 
105 	SLIST_HEAD(,acpi_cstate) sc_cstates;
106 
107 	bus_space_tag_t		sc_iot;
108 	bus_space_handle_t	sc_ioh;
109 
110 	struct acpi_softc	*sc_acpi;
111 	struct aml_node		*sc_devnode;
112 
113 	int			sc_pss_len;
114 	int			sc_ppc;
115 	int			sc_level;
116 	struct acpicpu_pss	*sc_pss;
117 
118 	struct acpicpu_pct	sc_pct;
119 	/* save compensation for pct access for lying bios' */
120 	u_int32_t		sc_pct_stat_as;
121 	u_int32_t		sc_pct_ctrl_as;
122 	u_int32_t		sc_pct_stat_len;
123 	u_int32_t		sc_pct_ctrl_len;
124 	/*
125 	 * XXX: _PPC Change listener
126 	 * PPC changes can occur when for example a machine is disconnected
127 	 * from AC power and can no loger support the highest frequency or
128 	 * voltage when driven from the battery.
129 	 * Should probably be reimplemented as a list for now we assume only
130 	 * one listener
131 	 */
132 	void			(*sc_notify)(struct acpicpu_pss *, int);
133 };
134 
135 void    acpicpu_add_cstatepkg(struct aml_value *, void *);
136 int	acpicpu_getppc(struct acpicpu_softc *);
137 int	acpicpu_getpct(struct acpicpu_softc *);
138 int	acpicpu_getpss(struct acpicpu_softc *);
139 struct acpi_cstate *acpicpu_add_cstate(struct acpicpu_softc *, int, int, int,
140     int);
141 void	acpicpu_set_pdc(struct acpicpu_softc *);
142 
143 #if 0
144 void    acpicpu_set_throttle(struct acpicpu_softc *, int);
145 struct acpi_cstate *acpicpu_find_cstate(struct acpicpu_softc *, int);
146 #endif
147 
148 struct cfattach acpicpu_ca = {
149 	sizeof(struct acpicpu_softc), acpicpu_match, acpicpu_attach
150 };
151 
152 struct cfdriver acpicpu_cd = {
153 	NULL, "acpicpu", DV_DULL
154 };
155 
156 extern int setperf_prio;
157 
158 struct acpicpu_softc *acpicpu_sc[MAXCPUS];
159 
160 #if 0
161 void
162 acpicpu_set_throttle(struct acpicpu_softc *sc, int level)
163 {
164 	uint32_t pbval;
165 
166 	if (sc->sc_flags & FLAGS_NOTHROTTLE)
167 		return;
168 
169 	/* Disable throttling control */
170 	pbval = inl(sc->sc_pblk_addr);
171 	outl(sc->sc_pblk_addr, pbval & ~CPU_THT_EN);
172 	if (level < 100) {
173 		pbval &= ~CPU_STATEMASK(sc);
174 		pbval |= CPU_STATE(sc, level);
175 		outl(sc->sc_pblk_addr, pbval & ~CPU_THT_EN);
176 		outl(sc->sc_pblk_addr, pbval | CPU_THT_EN);
177 	}
178 }
179 
180 struct acpi_cstate *
181 acpicpu_find_cstate(struct acpicpu_softc *sc, int type)
182 {
183 	struct acpi_cstate	*cx;
184 
185 	SLIST_FOREACH(cx, &sc->sc_cstates, link)
186 		if (cx->type == type)
187 			return cx;
188 	return (NULL);
189 }
190 #endif
191 
192 
193 void
194 acpicpu_set_pdc(struct acpicpu_softc *sc)
195 {
196 	struct aml_value cmd, osc_cmd[4];
197 	struct aml_value res;
198 	uint32_t cap;
199 	uint32_t buf[3];
200 
201 	/* 4077A616-290C-47BE-9EBD-D87058713953 */
202 	static uint8_t cpu_oscuuid[16] = { 0x16, 0xA6, 0x77, 0x40, 0x0C, 0x29,
203 					   0xBE, 0x47, 0x9E, 0xBD, 0xD8, 0x70,
204 					   0x58, 0x71, 0x39, 0x53 };
205 	cap = ACPI_PDC_C_C1_HALT | ACPI_PDC_P_FFH | ACPI_PDC_C_C1_FFH
206 	    | ACPI_PDC_C_C2C3_FFH | ACPI_PDC_SMP_P_SWCOORD | ACPI_PDC_SMP_C2C3
207 	    | ACPI_PDC_SMP_C1PT;
208 
209 	if (aml_searchname(sc->sc_devnode, "_OSC")) {
210 		/* Query _OSC */
211 		memset(&osc_cmd, 0, sizeof(cmd) * 4);
212 		osc_cmd[0].type = AML_OBJTYPE_BUFFER;
213 		osc_cmd[0].v_buffer = (uint8_t *)&cpu_oscuuid;
214 		osc_cmd[0].length = sizeof(cpu_oscuuid);
215 
216 		osc_cmd[1].type = AML_OBJTYPE_INTEGER;
217 		osc_cmd[1].v_integer = 1;
218 		osc_cmd[1].length = 1;
219 
220 		osc_cmd[2].type = AML_OBJTYPE_INTEGER;
221 		osc_cmd[2].v_integer = 2;
222 		osc_cmd[2].length = 1;
223 
224 		buf[0] = 1;
225 		buf[1] = cap;
226 		osc_cmd[3].type = AML_OBJTYPE_BUFFER;
227 		osc_cmd[3].v_buffer = (int8_t *)&buf;
228 		osc_cmd[3].length = sizeof(buf);
229 
230 		aml_evalname(sc->sc_acpi, sc->sc_devnode, "_OSC",
231 		    4, osc_cmd, &res);
232 
233 		if (res.type != AML_OBJTYPE_BUFFER || res.length < 8) {
234 			printf(": unable to query capabilities\n");
235 			return;
236 		}
237 
238 		/* Evaluate _OSC */
239 		memset(&osc_cmd, 0, sizeof(cmd) * 4);
240 		osc_cmd[0].type = AML_OBJTYPE_BUFFER;
241 		osc_cmd[0].v_buffer = (uint8_t *)&cpu_oscuuid;
242 		osc_cmd[0].length = sizeof(cpu_oscuuid);
243 
244 		osc_cmd[1].type = AML_OBJTYPE_INTEGER;
245 		osc_cmd[1].v_integer = 1;
246 		osc_cmd[1].length = 1;
247 
248 		osc_cmd[2].type = AML_OBJTYPE_INTEGER;
249 		osc_cmd[2].v_integer = 2;
250 		osc_cmd[2].length = 1;
251 
252 		buf[0] = 0;
253 		buf[1] = (*(uint32_t *)&res.v_buffer[4]) & cap;
254 		osc_cmd[3].type = AML_OBJTYPE_BUFFER;
255 		osc_cmd[3].v_buffer = (int8_t *)&buf;
256 		osc_cmd[3].length = sizeof(buf);
257 
258 		aml_evalname(sc->sc_acpi, sc->sc_devnode, "_OSC",
259 		    4, osc_cmd, &res);
260 	} else {
261 		/* Evaluate _PDC */
262 		memset(&cmd, 0, sizeof(cmd));
263 		cmd.type = AML_OBJTYPE_BUFFER;
264 		cmd.v_buffer = (uint8_t *)&buf;
265 		cmd.length = sizeof(buf);
266 
267 		buf[0] = ACPI_PDC_REVID;
268 		buf[1] = 1;
269 		buf[2] = cap;
270 
271 		aml_evalname(sc->sc_acpi, sc->sc_devnode, "_PDC",
272 		    1, &cmd, &res);
273 	}
274 }
275 
276 
277 struct acpi_cstate *
278 acpicpu_add_cstate(struct acpicpu_softc *sc, int type, int latency, int power,
279     int address)
280 {
281 	struct acpi_cstate	*cx;
282 
283 	dnprintf(10," C%d: latency:.%4x power:%.4x addr:%.8x\n",
284 	    type, latency, power, address);
285 
286 	switch (type) {
287 	case ACPI_STATE_C2:
288 		if (latency > ACPI_MAX_C2_LATENCY || !address ||
289 		    (sc->sc_flags & FLAGS_NO_C2))
290 			goto bad;
291 		break;
292 	case ACPI_STATE_C3:
293 		if (latency > ACPI_MAX_C3_LATENCY || !address ||
294 		    (sc->sc_flags & FLAGS_NO_C3))
295 			goto bad;
296 		break;
297 	}
298 
299 	cx = malloc(sizeof(*cx), M_DEVBUF, M_WAITOK | M_ZERO);
300 
301 	cx->type = type;
302 	cx->power = power;
303 	cx->latency = latency;
304 	cx->address = address;
305 
306 	SLIST_INSERT_HEAD(&sc->sc_cstates, cx, link);
307 
308 	return (cx);
309  bad:
310 	dprintf("acpicpu%d: C%d not supported", sc->sc_cpu, type);
311 	return (NULL);
312 }
313 
314 /* Found a _CST object, add new cstate for each entry */
315 void
316 acpicpu_add_cstatepkg(struct aml_value *val, void *arg)
317 {
318 	struct acpicpu_softc	*sc = arg;
319 
320 #if defined(ACPI_DEBUG) && !defined(SMALL_KERNEL)
321 	aml_showvalue(val, 0);
322 #endif
323 	if (val->type != AML_OBJTYPE_PACKAGE || val->length != 4)
324 		return;
325 
326 	acpicpu_add_cstate(sc, val->v_package[1]->v_integer,
327 	    val->v_package[2]->v_integer,
328 	    val->v_package[3]->v_integer, -1);
329 }
330 
331 
332 int
333 acpicpu_match(struct device *parent, void *match, void *aux)
334 {
335 	struct acpi_attach_args	*aa = aux;
336 	struct cfdata		*cf = match;
337 
338 	/* sanity */
339 	if (aa->aaa_name == NULL ||
340 	    strcmp(aa->aaa_name, cf->cf_driver->cd_name) != 0 ||
341 	    aa->aaa_table != NULL)
342 		return (0);
343 
344 	return (1);
345 }
346 
347 void
348 acpicpu_attach(struct device *parent, struct device *self, void *aux)
349 {
350 	struct acpicpu_softc	*sc = (struct acpicpu_softc *)self;
351 	struct acpi_attach_args *aa = aux;
352 	struct aml_value	res;
353 	int			i;
354 	struct acpi_cstate	*cx;
355 	u_int32_t		status = 0;
356 
357 	sc->sc_acpi = (struct acpi_softc *)parent;
358 	sc->sc_devnode = aa->aaa_node;
359 	acpicpu_sc[sc->sc_dev.dv_unit] = sc;
360 
361 	SLIST_INIT(&sc->sc_cstates);
362 
363 	sc->sc_pss = NULL;
364 
365 	if (aml_evalnode(sc->sc_acpi, sc->sc_devnode, 0, NULL, &res) == 0) {
366 		if (res.type == AML_OBJTYPE_PROCESSOR) {
367 			sc->sc_cpu = res.v_processor.proc_id;
368 			sc->sc_pblk_addr = res.v_processor.proc_addr;
369 			sc->sc_pblk_len = res.v_processor.proc_len;
370 		}
371 		aml_freevalue(&res);
372 	}
373 	sc->sc_duty_off = sc->sc_acpi->sc_fadt->duty_offset;
374 	sc->sc_duty_wid = sc->sc_acpi->sc_fadt->duty_width;
375 
376 	acpicpu_set_pdc(sc);
377 
378 	if (!valid_throttle(sc->sc_duty_off, sc->sc_duty_wid, sc->sc_pblk_addr))
379 		sc->sc_flags |= FLAGS_NOTHROTTLE;
380 #ifdef ACPI_DEBUG
381 	printf(": %s: ", sc->sc_devnode->name);
382 	printf("\n: hdr:%x pblk:%x,%x duty:%x,%x pstate:%x "
383 	       "(%ld throttling states)\n", sc->sc_acpi->sc_fadt->hdr_revision,
384 		sc->sc_pblk_addr, sc->sc_pblk_len, sc->sc_duty_off,
385 		sc->sc_duty_wid, sc->sc_acpi->sc_fadt->pstate_cnt,
386 		CPU_MAXSTATE(sc));
387 #endif
388 
389 	/* Get C-States from _CST or FADT */
390 	if (!aml_evalname(sc->sc_acpi, sc->sc_devnode, "_CST", 0, NULL, &res)) {
391 		aml_foreachpkg(&res, 1, acpicpu_add_cstatepkg, sc);
392 		aml_freevalue(&res);
393 	}
394 	else {
395 		/* Some systems don't export a full PBLK reduce functionality */
396 		if (sc->sc_pblk_len < 5)
397 			sc->sc_flags |= FLAGS_NO_C2;
398 		if (sc->sc_pblk_len < 6)
399 			sc->sc_flags |= FLAGS_NO_C3;
400 		acpicpu_add_cstate(sc, ACPI_STATE_C2,
401 		    sc->sc_acpi->sc_fadt->p_lvl2_lat, -1,
402 		    sc->sc_pblk_addr + 4);
403 		acpicpu_add_cstate(sc, ACPI_STATE_C3,
404 		    sc->sc_acpi->sc_fadt->p_lvl3_lat, -1,
405 		    sc->sc_pblk_addr + 5);
406 	}
407 	if (acpicpu_getpss(sc)) {
408 		sc->sc_flags |= FLAGS_NOPSS;
409 	} else {
410 #ifdef ACPI_DEBUG
411 		for (i = 0; i < sc->sc_pss_len; i++) {
412 			dnprintf(20, "%d %d %d %d %d %d\n",
413 			    sc->sc_pss[i].pss_core_freq,
414 			    sc->sc_pss[i].pss_power,
415 			    sc->sc_pss[i].pss_trans_latency,
416 			    sc->sc_pss[i].pss_bus_latency,
417 			    sc->sc_pss[i].pss_ctrl,
418 			    sc->sc_pss[i].pss_status);
419 		}
420 		dnprintf(20, "\n");
421 #endif
422 		if (sc->sc_pss_len == 0) {
423 			/* this should never happen */
424 			printf("%s: invalid _PSS length\n", DEVNAME(sc));
425 			sc->sc_flags |= FLAGS_NOPSS;
426 		}
427 
428 		acpicpu_getppc(sc);
429 		if (acpicpu_getpct(sc))
430 			sc->sc_flags |= FLAGS_NOPCT;
431 		else if (sc->sc_pss_len > 0) {
432 			/* Notify BIOS we are handing p-states */
433 			if (sc->sc_acpi->sc_fadt->pstate_cnt)
434 				acpi_write_pmreg(sc->sc_acpi, ACPIREG_SMICMD, 0,
435 				sc->sc_acpi->sc_fadt->pstate_cnt);
436 
437 			aml_register_notify(sc->sc_devnode, NULL,
438 			    acpicpu_notify, sc, ACPIDEV_NOPOLL);
439 
440 			acpi_gasio(sc->sc_acpi, ACPI_IOREAD,
441 			    sc->sc_pct.pct_status.grd_gas.address_space_id,
442 			    sc->sc_pct.pct_status.grd_gas.address,
443 			    sc->sc_pct_stat_as, sc->sc_pct_stat_as, &status);
444 			sc->sc_level = (100 / sc->sc_pss_len) *
445 			    (sc->sc_pss_len - status);
446 			dnprintf(20, "%s: cpu index %d, percentage %d\n",
447 			    DEVNAME(sc), status, sc->sc_level);
448 			if (setperf_prio < 30) {
449 				cpu_setperf = acpicpu_setperf;
450 				acpicpu_set_notify(acpicpu_setperf_ppc_change);
451 				setperf_prio = 30;
452 				acpi_hasprocfvs = 1;
453 			}
454 		}
455 	}
456 
457 	/*
458 	 * Nicely enumerate what power management capabilities
459 	 * ACPI CPU provides.
460 	 */
461 	if (!SLIST_EMPTY(&sc->sc_cstates)) {
462 		printf(":");
463 
464 		i = 0;
465 		SLIST_FOREACH(cx, &sc->sc_cstates, link) {
466 			if (i++)
467 				printf(",");
468 			switch (cx->type) {
469 			case ACPI_STATE_C0:
470 				printf(" C0");
471 				break;
472 			case ACPI_STATE_C1:
473 				printf(" C1");
474 				break;
475 			case ACPI_STATE_C2:
476 				printf(" C2");
477 				break;
478 			case ACPI_STATE_C3:
479 				printf(" C3");
480 				break;
481 			}
482 		}
483 	}
484 
485 	if (!(sc->sc_flags & (FLAGS_NOPSS | FLAGS_NOPCT)) ||
486 	    !(sc->sc_flags & FLAGS_NOPSS)) {
487 		printf("%c ", SLIST_EMPTY(&sc->sc_cstates) ? ':' : ',');
488 
489 		/*
490 		 * If acpicpu is itself providing the capability to transition
491 		 * states, enumerate them in the fashion that est and powernow
492 		 * would.
493 		 */
494 		if (!(sc->sc_flags & (FLAGS_NOPSS | FLAGS_NOPCT))) {
495 			printf("FVS, ");
496 			for (i = 0; i < sc->sc_pss_len - 1; i++)
497 				printf("%d, ", sc->sc_pss[i].pss_core_freq);
498 			printf("%d MHz", sc->sc_pss[i].pss_core_freq);
499 		} else
500 			printf("PSS");
501 	}
502 
503 	printf("\n");
504 }
505 
506 int
507 acpicpu_getppc(struct acpicpu_softc *sc)
508 {
509 	struct aml_value	res;
510 
511 	sc->sc_ppc = 0;
512 
513 	if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "_PPC", 0, NULL, &res)) {
514 		dnprintf(10, "%s: no _PPC\n", DEVNAME(sc));
515 		return (1);
516 	}
517 
518 	sc->sc_ppc = aml_val2int(&res);
519 	dnprintf(10, "%s: _PPC: %d\n", DEVNAME(sc), sc->sc_ppc);
520 	aml_freevalue(&res);
521 
522 	return (0);
523 }
524 
525 int
526 acpicpu_getpct(struct acpicpu_softc *sc)
527 {
528 	struct aml_value	res;
529 	int			rv = 1;
530 
531 	if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "_PCT", 0, NULL, &res)) {
532 		dnprintf(20, "%s: no _PCT\n", DEVNAME(sc));
533 		return (1);
534 	}
535 
536 	if (res.length != 2) {
537 		dnprintf(20, "%s: %s: invalid _PCT length\n", DEVNAME(sc),
538 		    sc->sc_devnode->name);
539 		return (1);
540 	}
541 
542 	memcpy(&sc->sc_pct.pct_ctrl, res.v_package[0]->v_buffer,
543 	    sizeof sc->sc_pct.pct_ctrl);
544 	if (sc->sc_pct.pct_ctrl.grd_gas.address_space_id ==
545 	    GAS_FUNCTIONAL_FIXED) {
546 		dnprintf(20, "CTRL GASIO is functional fixed hardware.\n");
547 		goto ffh;
548 	}
549 
550 	memcpy(&sc->sc_pct.pct_status, res.v_package[1]->v_buffer,
551 	    sizeof sc->sc_pct.pct_status);
552 	if (sc->sc_pct.pct_status.grd_gas.address_space_id ==
553 	    GAS_FUNCTIONAL_FIXED) {
554 		dnprintf(20, "CTRL GASIO is functional fixed hardware.\n");
555 		goto ffh;
556 	}
557 
558 	dnprintf(10, "_PCT(ctrl)  : %02x %04x %02x %02x %02x %02x %016llx\n",
559 	    sc->sc_pct.pct_ctrl.grd_descriptor,
560 	    sc->sc_pct.pct_ctrl.grd_length,
561 	    sc->sc_pct.pct_ctrl.grd_gas.address_space_id,
562 	    sc->sc_pct.pct_ctrl.grd_gas.register_bit_width,
563 	    sc->sc_pct.pct_ctrl.grd_gas.register_bit_offset,
564 	    sc->sc_pct.pct_ctrl.grd_gas.access_size,
565 	    sc->sc_pct.pct_ctrl.grd_gas.address);
566 
567 	dnprintf(10, "_PCT(status): %02x %04x %02x %02x %02x %02x %016llx\n",
568 	    sc->sc_pct.pct_status.grd_descriptor,
569 	    sc->sc_pct.pct_status.grd_length,
570 	    sc->sc_pct.pct_status.grd_gas.address_space_id,
571 	    sc->sc_pct.pct_status.grd_gas.register_bit_width,
572 	    sc->sc_pct.pct_status.grd_gas.register_bit_offset,
573 	    sc->sc_pct.pct_status.grd_gas.access_size,
574 	    sc->sc_pct.pct_status.grd_gas.address);
575 
576 	/* if not set assume single 32 bit access */
577 	sc->sc_pct_stat_as = sc->sc_pct.pct_status.grd_gas.register_bit_width
578 	    / 8;
579 	if (sc->sc_pct_stat_as == 0)
580 		sc->sc_pct_stat_as = 4;
581 	sc->sc_pct_ctrl_as = sc->sc_pct.pct_ctrl.grd_gas.register_bit_width / 8;
582 	if (sc->sc_pct_ctrl_as == 0)
583 		sc->sc_pct_ctrl_as = 4;
584 	sc->sc_pct_stat_len = sc->sc_pct.pct_status.grd_gas.access_size;
585 	if (sc->sc_pct_stat_len == 0)
586 		sc->sc_pct_stat_len = sc->sc_pct_stat_as;
587 	sc->sc_pct_ctrl_len = sc->sc_pct.pct_ctrl.grd_gas.access_size;
588 	if (sc->sc_pct_ctrl_len == 0)
589 		sc->sc_pct_ctrl_len = sc->sc_pct_ctrl_as;
590 
591 	rv = 0;
592 ffh:
593 	aml_freevalue(&res);
594 	return (rv);
595 }
596 
597 int
598 acpicpu_getpss(struct acpicpu_softc *sc)
599 {
600 	struct aml_value	res;
601 	int			i, c, cf;
602 
603 	if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "_PSS", 0, NULL, &res)) {
604 		dprintf("%s: no _PSS\n", DEVNAME(sc));
605 		return (1);
606 	}
607 
608 	if (sc->sc_pss)
609 		free(sc->sc_pss, M_DEVBUF, 0);
610 
611 	sc->sc_pss = malloc(res.length * sizeof *sc->sc_pss, M_DEVBUF,
612 	    M_WAITOK | M_ZERO);
613 
614 	c = 0;
615 	for (i = 0; i < res.length; i++) {
616 		cf = aml_val2int(res.v_package[i]->v_package[0]);
617 
618 		/* This heuristic comes from FreeBSDs
619 		 * dev/acpica/acpi_perf.c to weed out invalid PSS entries.
620 		 */
621 		if (cf == sc->sc_pss[c].pss_core_freq) {
622 			printf("%s: struck PSS entry, core frequency equals "
623 			    " last\n", sc->sc_dev.dv_xname);
624 			continue;
625 		}
626 
627 		if (cf == 0xFFFF || cf == 0x9999 || cf == 99999 || cf == 0) {
628 			printf("%s: struck PSS entry, inappropriate core "
629 			    "frequency value\n", sc->sc_dev.dv_xname);
630 			continue;
631 		}
632 
633 		sc->sc_pss[c].pss_core_freq = cf;
634 		sc->sc_pss[c].pss_power = aml_val2int(
635 		    res.v_package[i]->v_package[1]);
636 		sc->sc_pss[c].pss_trans_latency = aml_val2int(
637 		    res.v_package[i]->v_package[2]);
638 		sc->sc_pss[c].pss_bus_latency = aml_val2int(
639 		    res.v_package[i]->v_package[3]);
640 		sc->sc_pss[c].pss_ctrl = aml_val2int(
641 		    res.v_package[i]->v_package[4]);
642 		sc->sc_pss[c].pss_status = aml_val2int(
643 		    res.v_package[i]->v_package[5]);
644 		c++;
645 	}
646 	sc->sc_pss_len = c;
647 
648 	aml_freevalue(&res);
649 
650 	return (0);
651 }
652 
653 int
654 acpicpu_fetch_pss(struct acpicpu_pss **pss)
655 {
656 	struct acpicpu_softc	*sc;
657 
658 	/*
659 	 * XXX: According to the ACPI spec in an SMP system all processors
660 	 * are supposed to support the same states. For now we pray
661 	 * the bios ensures this...
662 	 */
663 
664 	sc = acpicpu_sc[0];
665 	if (!sc)
666 		return 0;
667 	*pss = sc->sc_pss;
668 
669 	return (sc->sc_pss_len);
670 }
671 
672 int
673 acpicpu_notify(struct aml_node *node, int notify_type, void *arg)
674 {
675 	struct acpicpu_softc	*sc = arg;
676 
677 	dnprintf(10, "acpicpu_notify: %.2x %s\n", notify_type,
678 	    sc->sc_devnode->name);
679 
680 	switch (notify_type) {
681 	case 0x80:	/* _PPC changed, retrieve new values */
682 		acpicpu_getppc(sc);
683 		acpicpu_getpss(sc);
684 		if (sc->sc_notify)
685 			sc->sc_notify(sc->sc_pss, sc->sc_pss_len);
686 
687 		break;
688 	default:
689 		printf("%s: unhandled cpu event %x\n", DEVNAME(sc),
690 		    notify_type);
691 		break;
692 	}
693 
694 	return (0);
695 }
696 
697 void
698 acpicpu_set_notify(void (*func)(struct acpicpu_pss *, int))
699 {
700 	struct acpicpu_softc    *sc;
701 
702 	sc = acpicpu_sc[0];
703 	if (sc != NULL)
704 		sc->sc_notify = func;
705 }
706 
707 void
708 acpicpu_setperf_ppc_change(struct acpicpu_pss *pss, int npss)
709 {
710 	struct acpicpu_softc    *sc;
711 
712 	sc = acpicpu_sc[0];
713 
714 	if (sc != NULL)
715 		cpu_setperf(sc->sc_level);
716 }
717 
718 void
719 acpicpu_setperf(int level)
720 {
721 	struct acpicpu_softc	*sc;
722 	struct acpicpu_pss	*pss = NULL;
723 	int			idx, len;
724 	u_int32_t		status = 0;
725 
726 	sc = acpicpu_sc[cpu_number()];
727 
728 	dnprintf(10, "%s: acpicpu setperf level %d\n",
729 	    sc->sc_devnode->name, level);
730 
731 	if (level < 0 || level > 100) {
732 		dnprintf(10, "%s: acpicpu setperf illegal percentage\n",
733 		    sc->sc_devnode->name);
734 		return;
735 	}
736 
737 	/*
738 	 * XXX this should be handled more gracefully and it needs to also do
739 	 * the duty cycle method instead of pss exclusively
740 	 */
741 	if (sc->sc_flags & FLAGS_NOPSS || sc->sc_flags & FLAGS_NOPCT) {
742 		dnprintf(10, "%s: acpicpu no _PSS or _PCT\n",
743 		    sc->sc_devnode->name);
744 		return;
745 	}
746 
747 	if (sc->sc_ppc)
748 		len = sc->sc_ppc;
749 	else
750 		len = sc->sc_pss_len;
751 	idx = (len - 1) - (level / (100 / len));
752 	if (idx < 0)
753 		idx = 0;
754 
755 	if (sc->sc_ppc)
756 		idx += sc->sc_pss_len - sc->sc_ppc;
757 
758 	if (idx > sc->sc_pss_len)
759 		idx = sc->sc_pss_len - 1;
760 
761 	dnprintf(10, "%s: acpicpu setperf index %d pss_len %d ppc %d\n",
762 	    sc->sc_devnode->name, idx, sc->sc_pss_len, sc->sc_ppc);
763 
764 	pss = &sc->sc_pss[idx];
765 
766 #ifdef ACPI_DEBUG
767 	/* keep this for now since we will need this for debug in the field */
768 	printf("0 status: %x %llx %u %u ctrl: %x %llx %u %u\n",
769 	    sc->sc_pct.pct_status.grd_gas.address_space_id,
770 	    sc->sc_pct.pct_status.grd_gas.address,
771 	    sc->sc_pct_stat_as, sc->sc_pct_stat_len,
772 	    sc->sc_pct.pct_ctrl.grd_gas.address_space_id,
773 	    sc->sc_pct.pct_ctrl.grd_gas.address,
774 	    sc->sc_pct_ctrl_as, sc->sc_pct_ctrl_len);
775 #endif
776 	acpi_gasio(sc->sc_acpi, ACPI_IOREAD,
777 	    sc->sc_pct.pct_status.grd_gas.address_space_id,
778 	    sc->sc_pct.pct_status.grd_gas.address, sc->sc_pct_stat_as,
779 	    sc->sc_pct_stat_len, &status);
780 	dnprintf(20, "1 status: %u <- %u\n", status, pss->pss_status);
781 
782 	/* Are we already at the requested frequency? */
783 	if (status == pss->pss_status)
784 		return;
785 
786 	acpi_gasio(sc->sc_acpi, ACPI_IOWRITE,
787 	    sc->sc_pct.pct_ctrl.grd_gas.address_space_id,
788 	    sc->sc_pct.pct_ctrl.grd_gas.address, sc->sc_pct_ctrl_as,
789 	    sc->sc_pct_ctrl_len, &pss->pss_ctrl);
790 	dnprintf(20, "pss_ctrl: %x\n", pss->pss_ctrl);
791 
792 	acpi_gasio(sc->sc_acpi, ACPI_IOREAD,
793 	    sc->sc_pct.pct_status.grd_gas.address_space_id,
794 	    sc->sc_pct.pct_status.grd_gas.address, sc->sc_pct_stat_as,
795 	    sc->sc_pct_stat_as, &status);
796 	dnprintf(20, "2 status: %d\n", status);
797 
798 	/* Did the transition succeed? */
799 	 if (status == pss->pss_status) {
800 		cpuspeed = pss->pss_core_freq;
801 		sc->sc_level = level;
802 	} else
803 		printf("%s: acpicpu setperf failed to alter frequency\n",
804 		    sc->sc_devnode->name);
805 }
806