xref: /openbsd-src/sys/dev/acpi/acpicpu.c (revision fabcfecb28af5579f8c0c1cb806278199e88da42)
1*fabcfecbSjsg /* $OpenBSD: acpicpu.c,v 1.95 2024/10/22 21:50:02 jsg Exp $ */
295f8e6e8Smarco /*
395f8e6e8Smarco  * Copyright (c) 2005 Marco Peereboom <marco@openbsd.org>
424c8c90cSguenther  * Copyright (c) 2015 Philip Guenther <guenther@openbsd.org>
595f8e6e8Smarco  *
695f8e6e8Smarco  * Permission to use, copy, modify, and distribute this software for any
795f8e6e8Smarco  * purpose with or without fee is hereby granted, provided that the above
895f8e6e8Smarco  * copyright notice and this permission notice appear in all copies.
995f8e6e8Smarco  *
1095f8e6e8Smarco  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1195f8e6e8Smarco  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1295f8e6e8Smarco  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1395f8e6e8Smarco  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1495f8e6e8Smarco  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1595f8e6e8Smarco  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1695f8e6e8Smarco  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1795f8e6e8Smarco  */
1895f8e6e8Smarco 
1995f8e6e8Smarco #include <sys/param.h>
2024c8c90cSguenther #include <sys/kernel.h>		/* for tick */
2195f8e6e8Smarco #include <sys/signalvar.h>
2279fd4ddcSgwk #include <sys/sysctl.h>
2395f8e6e8Smarco #include <sys/systm.h>
2495f8e6e8Smarco #include <sys/device.h>
2595f8e6e8Smarco #include <sys/malloc.h>
26226a7f6dSjordan #include <sys/queue.h>
2724c8c90cSguenther #include <sys/atomic.h>
2895f8e6e8Smarco 
2995f8e6e8Smarco #include <machine/bus.h>
3010f4946eSmarco #include <machine/cpu.h>
3124c8c90cSguenther #include <machine/cpufunc.h>
3210f4946eSmarco #include <machine/specialreg.h>
3395f8e6e8Smarco 
3495f8e6e8Smarco #include <dev/acpi/acpireg.h>
3595f8e6e8Smarco #include <dev/acpi/acpivar.h>
3695f8e6e8Smarco #include <dev/acpi/acpidev.h>
3795f8e6e8Smarco #include <dev/acpi/amltypes.h>
3895f8e6e8Smarco #include <dev/acpi/dsdt.h>
3995f8e6e8Smarco 
4095f8e6e8Smarco #include <sys/sensors.h>
4195f8e6e8Smarco 
4295f8e6e8Smarco int	acpicpu_match(struct device *, void *, void *);
4395f8e6e8Smarco void	acpicpu_attach(struct device *, struct device *, void *);
4495f8e6e8Smarco int	acpicpu_notify(struct aml_node *, int, void *);
4579fd4ddcSgwk void	acpicpu_setperf(int);
46f6f14d38Sgwk void	acpicpu_setperf_ppc_change(struct acpicpu_pss *, int);
4795f8e6e8Smarco 
48226a7f6dSjordan #define ACPI_STATE_C0		0x00
49226a7f6dSjordan #define ACPI_STATE_C1		0x01
50226a7f6dSjordan #define ACPI_STATE_C2		0x02
51226a7f6dSjordan #define ACPI_STATE_C3		0x03
52226a7f6dSjordan 
53f6f14d38Sgwk #define ACPI_PDC_REVID		0x1
54f6f14d38Sgwk #define ACPI_PDC_SMP		0xa
55f6f14d38Sgwk #define ACPI_PDC_MSR		0x1
56f6f14d38Sgwk 
5724c8c90cSguenther /* _PDC/_OSC Intel capabilities flags */
58f6f14d38Sgwk #define ACPI_PDC_P_FFH		0x0001
59f6f14d38Sgwk #define ACPI_PDC_C_C1_HALT	0x0002
60f6f14d38Sgwk #define ACPI_PDC_T_FFH		0x0004
61f6f14d38Sgwk #define ACPI_PDC_SMP_C1PT	0x0008
62f6f14d38Sgwk #define ACPI_PDC_SMP_C2C3	0x0010
63f6f14d38Sgwk #define ACPI_PDC_SMP_P_SWCOORD	0x0020
64f6f14d38Sgwk #define ACPI_PDC_SMP_C_SWCOORD	0x0040
65f6f14d38Sgwk #define ACPI_PDC_SMP_T_SWCOORD	0x0080
66f6f14d38Sgwk #define ACPI_PDC_C_C1_FFH	0x0100
67f6f14d38Sgwk #define ACPI_PDC_C_C2C3_FFH	0x0200
6824c8c90cSguenther /* reserved			0x0400 */
6924c8c90cSguenther #define ACPI_PDC_P_HWCOORD	0x0800
7024c8c90cSguenther #define ACPI_PDC_PPC_NOTIFY	0x1000
71f6f14d38Sgwk 
7224c8c90cSguenther #define CST_METH_HALT		0
7324c8c90cSguenther #define CST_METH_IO_HALT	1
7424c8c90cSguenther #define CST_METH_MWAIT		2
7524c8c90cSguenther #define CST_METH_GAS_IO		3
7624c8c90cSguenther 
7724c8c90cSguenther /* flags on Intel's FFH mwait method */
7824c8c90cSguenther #define CST_FLAG_MWAIT_HW_COORD		0x1
7924c8c90cSguenther #define CST_FLAG_MWAIT_BM_AVOIDANCE	0x2
809f8ad71dSguenther #define CST_FLAG_FALLBACK		0x4000	/* fallback for broken _CST */
8124c8c90cSguenther #define CST_FLAG_SKIP			0x8000	/* state is worse choice */
8224c8c90cSguenther 
8324c8c90cSguenther #define FLAGS_MWAIT_ONLY	0x02
84226a7f6dSjordan #define FLAGS_BMCHECK		0x04
85226a7f6dSjordan #define FLAGS_NOTHROTTLE	0x08
86226a7f6dSjordan #define FLAGS_NOPSS		0x10
873cd93248Sgwk #define FLAGS_NOPCT		0x20
88226a7f6dSjordan 
89226a7f6dSjordan #define CPU_THT_EN		(1L << 4)
90226a7f6dSjordan #define CPU_MAXSTATE(sc)	(1L << (sc)->sc_duty_wid)
91226a7f6dSjordan #define CPU_STATE(sc,pct)	((pct * CPU_MAXSTATE(sc) / 100) << (sc)->sc_duty_off)
92226a7f6dSjordan #define CPU_STATEMASK(sc)	((CPU_MAXSTATE(sc) - 1) << (sc)->sc_duty_off)
93226a7f6dSjordan 
94226a7f6dSjordan #define ACPI_MAX_C2_LATENCY	100
95226a7f6dSjordan #define ACPI_MAX_C3_LATENCY	1000
96226a7f6dSjordan 
97c1d253c2Sguenther #define CSD_COORD_SW_ALL	0xFC
98c1d253c2Sguenther #define CSD_COORD_SW_ANY	0xFD
99c1d253c2Sguenther #define CSD_COORD_HW_ALL	0xFE
100c1d253c2Sguenther 
101226a7f6dSjordan /* Make sure throttling bits are valid,a=addr,o=offset,w=width */
102226a7f6dSjordan #define valid_throttle(o,w,a)	(a && w && (o+w)<=31 && (o>4 || (o+w)<=4))
103226a7f6dSjordan 
104*fabcfecbSjsg struct acpi_cstate {
105226a7f6dSjordan 	SLIST_ENTRY(acpi_cstate) link;
10624c8c90cSguenther 
10724c8c90cSguenther 	u_short		state;
10824c8c90cSguenther 	short		method;		/* CST_METH_* */
10924c8c90cSguenther 	u_short		flags;		/* CST_FLAG_* */
11024c8c90cSguenther 	u_short		latency;
11124c8c90cSguenther 	int		power;
112d2eaebe9Skettenis 	uint64_t	address;	/* or mwait hint */
113226a7f6dSjordan };
114226a7f6dSjordan 
11524c8c90cSguenther unsigned long cst_stats[4] = { 0 };
11624c8c90cSguenther 
11795f8e6e8Smarco struct acpicpu_softc {
11895f8e6e8Smarco 	struct device		sc_dev;
119226a7f6dSjordan 	int			sc_cpu;
120226a7f6dSjordan 
121226a7f6dSjordan 	int			sc_duty_wid;
122226a7f6dSjordan 	int			sc_duty_off;
123d2eaebe9Skettenis 	uint32_t		sc_pblk_addr;
124226a7f6dSjordan 	int			sc_pblk_len;
125226a7f6dSjordan 	int			sc_flags;
12624c8c90cSguenther 	unsigned long		sc_prev_sleep;
12724c8c90cSguenther 	unsigned long		sc_last_itime;
128226a7f6dSjordan 
12924c8c90cSguenther 	struct cpu_info		*sc_ci;
130226a7f6dSjordan 	SLIST_HEAD(,acpi_cstate) sc_cstates;
13195f8e6e8Smarco 
13295f8e6e8Smarco 	bus_space_tag_t		sc_iot;
13395f8e6e8Smarco 	bus_space_handle_t	sc_ioh;
13495f8e6e8Smarco 
13595f8e6e8Smarco 	struct acpi_softc	*sc_acpi;
13695f8e6e8Smarco 	struct aml_node		*sc_devnode;
137ae89b26eSmarco 
1386a1ea70fSderaadt 	int			sc_pss_len;	/* XXX */
139cc42ddb4Smarco 	int			sc_ppc;
140cc42ddb4Smarco 	int			sc_level;
141ae89b26eSmarco 	struct acpicpu_pss	*sc_pss;
1426a1ea70fSderaadt 	size_t			sc_pssfulllen;
14356138cddSmarco 
14456138cddSmarco 	struct acpicpu_pct	sc_pct;
145cc42ddb4Smarco 	/* save compensation for pct access for lying bios' */
146d2eaebe9Skettenis 	uint32_t		sc_pct_stat_as;
147d2eaebe9Skettenis 	uint32_t		sc_pct_ctrl_as;
148d2eaebe9Skettenis 	uint32_t		sc_pct_stat_len;
149d2eaebe9Skettenis 	uint32_t		sc_pct_ctrl_len;
15063f04804Smarco 	/*
15163f04804Smarco 	 * XXX: _PPC Change listener
1523cd93248Sgwk 	 * PPC changes can occur when for example a machine is disconnected
1534b1a56afSjsg 	 * from AC power and can no longer support the highest frequency or
1543cd93248Sgwk 	 * voltage when driven from the battery.
1553cd93248Sgwk 	 * Should probably be reimplemented as a list for now we assume only
15663f04804Smarco 	 * one listener
15763f04804Smarco 	 */
1583cd93248Sgwk 	void			(*sc_notify)(struct acpicpu_pss *, int);
15995f8e6e8Smarco };
16095f8e6e8Smarco 
161226a7f6dSjordan void	acpicpu_add_cstatepkg(struct aml_value *, void *);
16224c8c90cSguenther void	acpicpu_add_cdeppkg(struct aml_value *, void *);
163cc42ddb4Smarco int	acpicpu_getppc(struct acpicpu_softc *);
16456138cddSmarco int	acpicpu_getpct(struct acpicpu_softc *);
165ae89b26eSmarco int	acpicpu_getpss(struct acpicpu_softc *);
16624c8c90cSguenther int	acpicpu_getcst(struct acpicpu_softc *);
16724c8c90cSguenther void	acpicpu_getcst_from_fadt(struct acpicpu_softc *);
16824c8c90cSguenther void	acpicpu_print_one_cst(struct acpi_cstate *_cx);
16924c8c90cSguenther void	acpicpu_print_cst(struct acpicpu_softc *_sc);
17024c8c90cSguenther void	acpicpu_add_cstate(struct acpicpu_softc *_sc, int _state, int _method,
171d2eaebe9Skettenis 	    int _flags, int _latency, int _power, uint64_t _address);
172f6f14d38Sgwk void	acpicpu_set_pdc(struct acpicpu_softc *);
17324c8c90cSguenther void	acpicpu_idle(void);
1746c195505Skettenis void	acpicpu_suspend(void);
175f6f14d38Sgwk 
1763b455a03Smarco #if 0
1773b455a03Smarco void    acpicpu_set_throttle(struct acpicpu_softc *, int);
178226a7f6dSjordan struct acpi_cstate *acpicpu_find_cstate(struct acpicpu_softc *, int);
1793b455a03Smarco #endif
18095f8e6e8Smarco 
181471aeecfSnaddy const struct cfattach acpicpu_ca = {
18295f8e6e8Smarco 	sizeof(struct acpicpu_softc), acpicpu_match, acpicpu_attach
18395f8e6e8Smarco };
18495f8e6e8Smarco 
18595f8e6e8Smarco struct cfdriver acpicpu_cd = {
18695f8e6e8Smarco 	NULL, "acpicpu", DV_DULL
18795f8e6e8Smarco };
18895f8e6e8Smarco 
189c950c8a4Skettenis const char *acpicpu_hids[] = {
190c950c8a4Skettenis 	"ACPI0007",
191c950c8a4Skettenis 	NULL
192c950c8a4Skettenis };
193c950c8a4Skettenis 
19479fd4ddcSgwk extern int setperf_prio;
19579fd4ddcSgwk 
1963b455a03Smarco #if 0
197226a7f6dSjordan void
198226a7f6dSjordan acpicpu_set_throttle(struct acpicpu_softc *sc, int level)
199226a7f6dSjordan {
200226a7f6dSjordan 	uint32_t pbval;
201226a7f6dSjordan 
202226a7f6dSjordan 	if (sc->sc_flags & FLAGS_NOTHROTTLE)
203226a7f6dSjordan 		return;
204226a7f6dSjordan 
205226a7f6dSjordan 	/* Disable throttling control */
206226a7f6dSjordan 	pbval = inl(sc->sc_pblk_addr);
207226a7f6dSjordan 	outl(sc->sc_pblk_addr, pbval & ~CPU_THT_EN);
208226a7f6dSjordan 	if (level < 100) {
209226a7f6dSjordan 		pbval &= ~CPU_STATEMASK(sc);
210226a7f6dSjordan 		pbval |= CPU_STATE(sc, level);
211226a7f6dSjordan 		outl(sc->sc_pblk_addr, pbval & ~CPU_THT_EN);
212226a7f6dSjordan 		outl(sc->sc_pblk_addr, pbval | CPU_THT_EN);
213226a7f6dSjordan 	}
214226a7f6dSjordan }
215226a7f6dSjordan 
216226a7f6dSjordan struct acpi_cstate *
21724c8c90cSguenther acpicpu_find_cstate(struct acpicpu_softc *sc, int state)
218226a7f6dSjordan {
219226a7f6dSjordan 	struct acpi_cstate	*cx;
220226a7f6dSjordan 
221226a7f6dSjordan 	SLIST_FOREACH(cx, &sc->sc_cstates, link)
22224c8c90cSguenther 		if (cx->state == state)
223226a7f6dSjordan 			return cx;
22463f04804Smarco 	return (NULL);
225226a7f6dSjordan }
2263b455a03Smarco #endif
227226a7f6dSjordan 
228f6f14d38Sgwk 
229f6f14d38Sgwk void
230f6f14d38Sgwk acpicpu_set_pdc(struct acpicpu_softc *sc)
231f6f14d38Sgwk {
232f6f14d38Sgwk 	struct aml_value cmd, osc_cmd[4];
233f6f14d38Sgwk 	struct aml_value res;
2341ac622a5Skettenis 	uint32_t cap;
235f6f14d38Sgwk 	uint32_t buf[3];
236f6f14d38Sgwk 
237f6f14d38Sgwk 	/* 4077A616-290C-47BE-9EBD-D87058713953 */
238f6f14d38Sgwk 	static uint8_t cpu_oscuuid[16] = { 0x16, 0xA6, 0x77, 0x40, 0x0C, 0x29,
239f6f14d38Sgwk 					   0xBE, 0x47, 0x9E, 0xBD, 0xD8, 0x70,
240f6f14d38Sgwk 					   0x58, 0x71, 0x39, 0x53 };
2411ac622a5Skettenis 	cap = ACPI_PDC_C_C1_HALT | ACPI_PDC_P_FFH | ACPI_PDC_C_C1_FFH
242f6f14d38Sgwk 	    | ACPI_PDC_C_C2C3_FFH | ACPI_PDC_SMP_P_SWCOORD | ACPI_PDC_SMP_C2C3
243f6f14d38Sgwk 	    | ACPI_PDC_SMP_C1PT;
244f6f14d38Sgwk 
2451ac622a5Skettenis 	if (aml_searchname(sc->sc_devnode, "_OSC")) {
2461ac622a5Skettenis 		/* Query _OSC */
2473efad746Sguenther 		memset(&osc_cmd, 0, sizeof(osc_cmd));
2481ac622a5Skettenis 		osc_cmd[0].type = AML_OBJTYPE_BUFFER;
2491ac622a5Skettenis 		osc_cmd[0].v_buffer = (uint8_t *)&cpu_oscuuid;
2501ac622a5Skettenis 		osc_cmd[0].length = sizeof(cpu_oscuuid);
2511ac622a5Skettenis 
2521ac622a5Skettenis 		osc_cmd[1].type = AML_OBJTYPE_INTEGER;
2531ac622a5Skettenis 		osc_cmd[1].v_integer = 1;
2541ac622a5Skettenis 		osc_cmd[1].length = 1;
2551ac622a5Skettenis 
2561ac622a5Skettenis 		osc_cmd[2].type = AML_OBJTYPE_INTEGER;
2571ac622a5Skettenis 		osc_cmd[2].v_integer = 2;
2581ac622a5Skettenis 		osc_cmd[2].length = 1;
2591ac622a5Skettenis 
2601ac622a5Skettenis 		buf[0] = 1;
2611ac622a5Skettenis 		buf[1] = cap;
2621ac622a5Skettenis 		osc_cmd[3].type = AML_OBJTYPE_BUFFER;
2631ac622a5Skettenis 		osc_cmd[3].v_buffer = (int8_t *)&buf;
2641ac622a5Skettenis 		osc_cmd[3].length = sizeof(buf);
2651ac622a5Skettenis 
2661ac622a5Skettenis 		aml_evalname(sc->sc_acpi, sc->sc_devnode, "_OSC",
2671ac622a5Skettenis 		    4, osc_cmd, &res);
2681ac622a5Skettenis 
2691ac622a5Skettenis 		if (res.type != AML_OBJTYPE_BUFFER || res.length < 8) {
2701ac622a5Skettenis 			printf(": unable to query capabilities\n");
271d05ffcd6Sjcs 			aml_freevalue(&res);
2721ac622a5Skettenis 			return;
2731ac622a5Skettenis 		}
274f6f14d38Sgwk 
275d56fb76dSmiod 		/* Evaluate _OSC */
2763efad746Sguenther 		memset(&osc_cmd, 0, sizeof(osc_cmd));
277f6f14d38Sgwk 		osc_cmd[0].type = AML_OBJTYPE_BUFFER;
278f6f14d38Sgwk 		osc_cmd[0].v_buffer = (uint8_t *)&cpu_oscuuid;
279f6f14d38Sgwk 		osc_cmd[0].length = sizeof(cpu_oscuuid);
280f6f14d38Sgwk 
281f6f14d38Sgwk 		osc_cmd[1].type = AML_OBJTYPE_INTEGER;
282f6f14d38Sgwk 		osc_cmd[1].v_integer = 1;
283f6f14d38Sgwk 		osc_cmd[1].length = 1;
284f6f14d38Sgwk 
285f6f14d38Sgwk 		osc_cmd[2].type = AML_OBJTYPE_INTEGER;
2861ac622a5Skettenis 		osc_cmd[2].v_integer = 2;
287f6f14d38Sgwk 		osc_cmd[2].length = 1;
288f6f14d38Sgwk 
289f6f14d38Sgwk 		buf[0] = 0;
2901ac622a5Skettenis 		buf[1] = (*(uint32_t *)&res.v_buffer[4]) & cap;
291f6f14d38Sgwk 		osc_cmd[3].type = AML_OBJTYPE_BUFFER;
292f6f14d38Sgwk 		osc_cmd[3].v_buffer = (int8_t *)&buf;
293f6f14d38Sgwk 		osc_cmd[3].length = sizeof(buf);
294f6f14d38Sgwk 
295d05ffcd6Sjcs 		aml_freevalue(&res);
296d05ffcd6Sjcs 
2971ac622a5Skettenis 		aml_evalname(sc->sc_acpi, sc->sc_devnode, "_OSC",
298d05ffcd6Sjcs 		    4, osc_cmd, NULL);
2991ac622a5Skettenis 	} else {
3001ac622a5Skettenis 		/* Evaluate _PDC */
3011ac622a5Skettenis 		memset(&cmd, 0, sizeof(cmd));
3021ac622a5Skettenis 		cmd.type = AML_OBJTYPE_BUFFER;
3031ac622a5Skettenis 		cmd.v_buffer = (uint8_t *)&buf;
3041ac622a5Skettenis 		cmd.length = sizeof(buf);
3051ac622a5Skettenis 
3061ac622a5Skettenis 		buf[0] = ACPI_PDC_REVID;
3071ac622a5Skettenis 		buf[1] = 1;
3081ac622a5Skettenis 		buf[2] = cap;
3091ac622a5Skettenis 
3101ac622a5Skettenis 		aml_evalname(sc->sc_acpi, sc->sc_devnode, "_PDC",
311d05ffcd6Sjcs 		    1, &cmd, NULL);
3121ac622a5Skettenis 	}
313f6f14d38Sgwk }
314f6f14d38Sgwk 
31524c8c90cSguenther /*
31624c8c90cSguenther  * sanity check mwait hints against what cpuid told us
31765ef1213Sguenther  * ...but because intel screwed up, just check whether cpuid says
31865ef1213Sguenther  * the given state has _any_ substates.
31924c8c90cSguenther  */
32024c8c90cSguenther static int
32124c8c90cSguenther check_mwait_hints(int state, int hints)
32224c8c90cSguenther {
32324c8c90cSguenther 	int cstate;
32424c8c90cSguenther 	int num_substates;
325f6f14d38Sgwk 
32624c8c90cSguenther 	if (cpu_mwait_size == 0)
32724c8c90cSguenther 		return (0);
32824c8c90cSguenther 	cstate = ((hints >> 4) & 0xf) + 1;
32924c8c90cSguenther 	if (cstate == 16)
33024c8c90cSguenther 		cstate = 0;
33124c8c90cSguenther 	else if (cstate > 7) {
33224c8c90cSguenther 		/* out of range of test against CPUID; just trust'em */
33324c8c90cSguenther 		return (1);
33424c8c90cSguenther 	}
33524c8c90cSguenther 	num_substates = (cpu_mwait_states >> (4 * cstate)) & 0xf;
33665ef1213Sguenther 	if (num_substates == 0) {
33765ef1213Sguenther 		printf(": C%d bad (state %d has no substates)", state, cstate);
33824c8c90cSguenther 		return (0);
33924c8c90cSguenther 	}
34024c8c90cSguenther 	return (1);
34124c8c90cSguenther }
34224c8c90cSguenther 
34324c8c90cSguenther void
34424c8c90cSguenther acpicpu_add_cstate(struct acpicpu_softc *sc, int state, int method,
345d2eaebe9Skettenis     int flags, int latency, int power, uint64_t address)
346226a7f6dSjordan {
347226a7f6dSjordan 	struct acpi_cstate	*cx;
348226a7f6dSjordan 
34924c8c90cSguenther 	dnprintf(10," C%d: latency:.%4x power:%.4x addr:%.16llx\n",
35024c8c90cSguenther 	    state, latency, power, address);
351226a7f6dSjordan 
3529f8ad71dSguenther 	/* add a new state, or overwrite the fallback C1 state? */
3539f8ad71dSguenther 	if (state != ACPI_STATE_C1 ||
3549f8ad71dSguenther 	    (cx = SLIST_FIRST(&sc->sc_cstates)) == NULL ||
3559f8ad71dSguenther 	    (cx->flags & CST_FLAG_FALLBACK) == 0) {
35624c8c90cSguenther 		cx = malloc(sizeof(*cx), M_DEVBUF, M_WAITOK);
3579f8ad71dSguenther 		SLIST_INSERT_HEAD(&sc->sc_cstates, cx, link);
3589f8ad71dSguenther 	}
359226a7f6dSjordan 
36024c8c90cSguenther 	cx->state = state;
36124c8c90cSguenther 	cx->method = method;
36224c8c90cSguenther 	cx->flags = flags;
363226a7f6dSjordan 	cx->latency = latency;
36424c8c90cSguenther 	cx->power = power;
365226a7f6dSjordan 	cx->address = address;
366226a7f6dSjordan }
367226a7f6dSjordan 
368226a7f6dSjordan /* Found a _CST object, add new cstate for each entry */
369226a7f6dSjordan void
370226a7f6dSjordan acpicpu_add_cstatepkg(struct aml_value *val, void *arg)
371226a7f6dSjordan {
372226a7f6dSjordan 	struct acpicpu_softc	*sc = arg;
373d2eaebe9Skettenis 	uint64_t addr;
37424c8c90cSguenther 	struct acpi_grd *grd;
37524c8c90cSguenther 	int state, method, flags;
376226a7f6dSjordan 
377d275d9fcSpvalchev #if defined(ACPI_DEBUG) && !defined(SMALL_KERNEL)
3780e505e73Spirofti 	aml_showvalue(val);
379226a7f6dSjordan #endif
380226a7f6dSjordan 	if (val->type != AML_OBJTYPE_PACKAGE || val->length != 4)
381226a7f6dSjordan 		return;
38263f04804Smarco 
38324c8c90cSguenther 	/* range and sanity checks */
38424c8c90cSguenther 	state = val->v_package[1]->v_integer;
38524c8c90cSguenther 	if (state < 0 || state > 4)
38624c8c90cSguenther 		return;
38724c8c90cSguenther 	if (val->v_package[0]->type != AML_OBJTYPE_BUFFER) {
38865ef1213Sguenther 		printf(": C%d (unexpected ACPI object type %d)",
38924c8c90cSguenther 		    state, val->v_package[0]->type);
39024c8c90cSguenther 		return;
39124c8c90cSguenther 	}
39224c8c90cSguenther 	grd = (struct acpi_grd *)val->v_package[0]->v_buffer;
39324c8c90cSguenther 	if (val->v_package[0]->length != sizeof(*grd) + 2 ||
39424c8c90cSguenther 	    grd->grd_descriptor != LR_GENREGISTER ||
39524c8c90cSguenther 	    grd->grd_length != sizeof(grd->grd_gas) ||
3961774e241Sguenther 	    val->v_package[0]->v_buffer[sizeof(*grd)] != SRT_ENDTAG) {
39765ef1213Sguenther 		printf(": C%d (bogo buffer)", state);
39824c8c90cSguenther 		return;
39924c8c90cSguenther 	}
40024c8c90cSguenther 
40124c8c90cSguenther 	flags = 0;
40224c8c90cSguenther 	switch (grd->grd_gas.address_space_id) {
40324c8c90cSguenther 	case GAS_FUNCTIONAL_FIXED:
40424c8c90cSguenther 		if (grd->grd_gas.register_bit_width == 0) {
40524c8c90cSguenther 			method = CST_METH_HALT;
40624c8c90cSguenther 			addr = 0;
407ac7bf6a1Sguenther 		} else {
408ccde6567Sguenther 			/*
409ac7bf6a1Sguenther 			 * In theory we should only do this for
410ac7bf6a1Sguenther 			 * vendor 1 == Intel but other values crop up,
411ac7bf6a1Sguenther 			 * presumably due to the normal ACPI spec confusion.
412ccde6567Sguenther 			 */
41324c8c90cSguenther 			switch (grd->grd_gas.register_bit_offset) {
41424c8c90cSguenther 			case 0x1:
41524c8c90cSguenther 				method = CST_METH_IO_HALT;
41624c8c90cSguenther 				addr = grd->grd_gas.address;
41724c8c90cSguenther 
41824c8c90cSguenther 				/* i386 and amd64 I/O space is 16bits */
41924c8c90cSguenther 				if (addr > 0xffff) {
42065ef1213Sguenther 					printf(": C%d (bogo I/O addr %llx)",
42124c8c90cSguenther 					    state, addr);
42224c8c90cSguenther 					return;
42324c8c90cSguenther 				}
42424c8c90cSguenther 				break;
42524c8c90cSguenther 			case 0x2:
42624c8c90cSguenther 				addr = grd->grd_gas.address;
42724c8c90cSguenther 				if (!check_mwait_hints(state, addr))
42824c8c90cSguenther 					return;
42924c8c90cSguenther 				method = CST_METH_MWAIT;
43024c8c90cSguenther 				flags = grd->grd_gas.access_size;
43124c8c90cSguenther 				break;
43224c8c90cSguenther 			default:
43365ef1213Sguenther 				printf(": C%d (unknown FFH class %d)",
43424c8c90cSguenther 				    state, grd->grd_gas.register_bit_offset);
43524c8c90cSguenther 				return;
43624c8c90cSguenther 			}
43724c8c90cSguenther 		}
43824c8c90cSguenther 		break;
43924c8c90cSguenther 
44024c8c90cSguenther 	case GAS_SYSTEM_IOSPACE:
44124c8c90cSguenther 		addr = grd->grd_gas.address;
44224c8c90cSguenther 		if (grd->grd_gas.register_bit_width != 8 ||
44324c8c90cSguenther 		    grd->grd_gas.register_bit_offset != 0) {
44465ef1213Sguenther 			printf(": C%d (unhandled %s spec: %d/%d)", state,
44524c8c90cSguenther 			    "I/O", grd->grd_gas.register_bit_width,
44624c8c90cSguenther 			    grd->grd_gas.register_bit_offset);
44724c8c90cSguenther 			return;
44824c8c90cSguenther 		}
44924c8c90cSguenther 		method = CST_METH_GAS_IO;
45024c8c90cSguenther 		break;
45124c8c90cSguenther 
45224c8c90cSguenther 	default:
45324c8c90cSguenther 		/* dump the GAS for analysis */
45424c8c90cSguenther 		{
45524c8c90cSguenther 			int i;
45665ef1213Sguenther 			printf(": C%d (unhandled GAS:", state);
45724c8c90cSguenther 			for (i = 0; i < sizeof(grd->grd_gas); i++)
45824c8c90cSguenther 				printf(" %#x", ((u_char *)&grd->grd_gas)[i]);
45965ef1213Sguenther 			printf(")");
46024c8c90cSguenther 
46124c8c90cSguenther 		}
46224c8c90cSguenther 		return;
46324c8c90cSguenther 	}
46424c8c90cSguenther 
46524c8c90cSguenther 	acpicpu_add_cstate(sc, state, method, flags,
46624c8c90cSguenther 	    val->v_package[2]->v_integer, val->v_package[3]->v_integer, addr);
46724c8c90cSguenther }
46824c8c90cSguenther 
46924c8c90cSguenther 
47024c8c90cSguenther /* Found a _CSD object, print the dependency  */
47124c8c90cSguenther void
47224c8c90cSguenther acpicpu_add_cdeppkg(struct aml_value *val, void *arg)
47324c8c90cSguenther {
474c1d253c2Sguenther 	int64_t	num_proc, coord_type, domain, cindex;
475c1d253c2Sguenther 
476c1d253c2Sguenther 	/*
477c1d253c2Sguenther 	 * errors: unexpected object type, bad length, mismatched length,
478c1d253c2Sguenther 	 * and bad CSD revision
479c1d253c2Sguenther 	 */
480c1d253c2Sguenther 	if (val->type != AML_OBJTYPE_PACKAGE || val->length < 6 ||
481c1d253c2Sguenther 	    val->length != val->v_package[0]->v_integer ||
482c1d253c2Sguenther 	    val->v_package[1]->v_integer != 0) {
48324c8c90cSguenther #if 1 || defined(ACPI_DEBUG) && !defined(SMALL_KERNEL)
4840e505e73Spirofti 		aml_showvalue(val);
48524c8c90cSguenther #endif
48624c8c90cSguenther 		printf("bogus CSD\n");
48724c8c90cSguenther 		return;
48824c8c90cSguenther 	}
48924c8c90cSguenther 
490c1d253c2Sguenther 	/* coordinating 'among' one CPU is trivial, ignore */
491c1d253c2Sguenther 	num_proc = val->v_package[4]->v_integer;
492c1d253c2Sguenther 	if (num_proc == 1)
493c1d253c2Sguenther 		return;
494c1d253c2Sguenther 
495c1d253c2Sguenther 	/* we practically assume the hardware will coordinate, so ignore */
496c1d253c2Sguenther 	coord_type = val->v_package[3]->v_integer;
497c1d253c2Sguenther 	if (coord_type == CSD_COORD_HW_ALL)
498c1d253c2Sguenther 		return;
499c1d253c2Sguenther 
500c1d253c2Sguenther 	domain = val->v_package[2]->v_integer;
501c1d253c2Sguenther 	cindex = val->v_package[5]->v_integer;
50265ef1213Sguenther 	printf(": CSD (c=%#llx d=%lld n=%lld i=%lli)",
503c1d253c2Sguenther 	    coord_type, domain, num_proc, cindex);
50424c8c90cSguenther }
50524c8c90cSguenther 
50624c8c90cSguenther int
50724c8c90cSguenther acpicpu_getcst(struct acpicpu_softc *sc)
50824c8c90cSguenther {
50924c8c90cSguenther 	struct aml_value	res;
51024c8c90cSguenther 	struct acpi_cstate	*cx, *next_cx;
51124c8c90cSguenther 	int			use_nonmwait;
51224c8c90cSguenther 
51324c8c90cSguenther 	/* delete the existing list */
51424c8c90cSguenther 	while ((cx = SLIST_FIRST(&sc->sc_cstates)) != NULL) {
51524c8c90cSguenther 		SLIST_REMOVE_HEAD(&sc->sc_cstates, link);
51624c8c90cSguenther 		free(cx, M_DEVBUF, sizeof(*cx));
51724c8c90cSguenther 	}
51824c8c90cSguenther 
5199f8ad71dSguenther 	/* provide a fallback C1-via-halt in case _CST's C1 is bogus */
5209f8ad71dSguenther 	acpicpu_add_cstate(sc, ACPI_STATE_C1, CST_METH_HALT,
5219f8ad71dSguenther 	    CST_FLAG_FALLBACK, 1, -1, 0);
5229f8ad71dSguenther 
523813bea30Sguenther 	if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "_CST", 0, NULL, &res))
524813bea30Sguenther 		return (1);
525813bea30Sguenther 
52624c8c90cSguenther 	aml_foreachpkg(&res, 1, acpicpu_add_cstatepkg, sc);
52724c8c90cSguenther 	aml_freevalue(&res);
52824c8c90cSguenther 
5299f8ad71dSguenther 	/* only have fallback state?  then no _CST objects were understood */
5309f8ad71dSguenther 	cx = SLIST_FIRST(&sc->sc_cstates);
5319f8ad71dSguenther 	if (cx->flags & CST_FLAG_FALLBACK)
5329f8ad71dSguenther 		return (1);
5339f8ad71dSguenther 
53424c8c90cSguenther 	/*
53557f11685Sguenther 	 * Skip states >= C2 if the CPU's LAPIC timer stops in deep
53624c8c90cSguenther 	 * states (i.e., it doesn't have the 'ARAT' bit set).
53724c8c90cSguenther 	 * Also keep track if all the states we'll use use mwait.
53824c8c90cSguenther 	 */
53924c8c90cSguenther 	use_nonmwait = 0;
54024c8c90cSguenther 	while ((next_cx = SLIST_NEXT(cx, link)) != NULL) {
54157f11685Sguenther 		if (cx->state > 1 &&
54257f11685Sguenther 		    (sc->sc_ci->ci_feature_tpmflags & TPM_ARAT) == 0)
54324c8c90cSguenther 			cx->flags |= CST_FLAG_SKIP;
54424c8c90cSguenther 		else if (cx->method != CST_METH_MWAIT)
54524c8c90cSguenther 			use_nonmwait = 1;
54624c8c90cSguenther 		cx = next_cx;
54724c8c90cSguenther 	}
54824c8c90cSguenther 	if (use_nonmwait)
54924c8c90cSguenther 		sc->sc_flags &= ~FLAGS_MWAIT_ONLY;
55024c8c90cSguenther 	else
55124c8c90cSguenther 		sc->sc_flags |= FLAGS_MWAIT_ONLY;
55224c8c90cSguenther 
55324c8c90cSguenther 	if (!aml_evalname(sc->sc_acpi, sc->sc_devnode, "_CSD", 0, NULL, &res)) {
55424c8c90cSguenther 		aml_foreachpkg(&res, 1, acpicpu_add_cdeppkg, sc);
55524c8c90cSguenther 		aml_freevalue(&res);
55624c8c90cSguenther 	}
55724c8c90cSguenther 
55824c8c90cSguenther 	return (0);
55924c8c90cSguenther }
56024c8c90cSguenther 
56124c8c90cSguenther /*
56224c8c90cSguenther  * old-style fixed C-state info in the FADT.
56324c8c90cSguenther  * Note that this has extra restrictions on values and flags.
56424c8c90cSguenther  */
56524c8c90cSguenther void
56624c8c90cSguenther acpicpu_getcst_from_fadt(struct acpicpu_softc *sc)
56724c8c90cSguenther {
56824c8c90cSguenther 	struct acpi_fadt	*fadt = sc->sc_acpi->sc_fadt;
569136fb34aSguenther 	int flags;
57024c8c90cSguenther 
57124c8c90cSguenther 	/* FADT has to set flag to do C2 and higher on MP */
57224c8c90cSguenther 	if ((fadt->flags & FADT_P_LVL2_UP) == 0 && ncpus > 1)
57324c8c90cSguenther 		return;
57424c8c90cSguenther 
575136fb34aSguenther 	/* skip these C2 and C3 states if the CPU doesn't have ARAT */
576136fb34aSguenther 	flags = (sc->sc_ci->ci_feature_tpmflags & TPM_ARAT)
577136fb34aSguenther 	    ? 0 : CST_FLAG_SKIP;
578136fb34aSguenther 
57924c8c90cSguenther 	/* Some systems don't export a full PBLK; reduce functionality */
58024c8c90cSguenther 	if (sc->sc_pblk_len >= 5 && fadt->p_lvl2_lat <= ACPI_MAX_C2_LATENCY) {
581136fb34aSguenther 		acpicpu_add_cstate(sc, ACPI_STATE_C2, CST_METH_GAS_IO, flags,
58224c8c90cSguenther 		    fadt->p_lvl2_lat, -1, sc->sc_pblk_addr + 4);
58324c8c90cSguenther 	}
58424c8c90cSguenther 	if (sc->sc_pblk_len >= 6 && fadt->p_lvl3_lat <= ACPI_MAX_C3_LATENCY)
585136fb34aSguenther 		acpicpu_add_cstate(sc, ACPI_STATE_C3, CST_METH_GAS_IO, flags,
58624c8c90cSguenther 		    fadt->p_lvl3_lat, -1, sc->sc_pblk_addr + 5);
58724c8c90cSguenther }
58824c8c90cSguenther 
58924c8c90cSguenther 
59024c8c90cSguenther void
59124c8c90cSguenther acpicpu_print_one_cst(struct acpi_cstate *cx)
59224c8c90cSguenther {
59324c8c90cSguenther 	const char *meth = "";
59424c8c90cSguenther 	int show_addr = 0;
59524c8c90cSguenther 
59624c8c90cSguenther 	switch (cx->method) {
59724c8c90cSguenther 	case CST_METH_IO_HALT:
59824c8c90cSguenther 		show_addr = 1;
59924c8c90cSguenther 		/* fallthrough */
60024c8c90cSguenther 	case CST_METH_HALT:
60124c8c90cSguenther 		meth = " halt";
60224c8c90cSguenther 		break;
60324c8c90cSguenther 
60424c8c90cSguenther 	case CST_METH_MWAIT:
60524c8c90cSguenther 		meth = " mwait";
60624c8c90cSguenther 		show_addr = cx->address != 0;
60724c8c90cSguenther 		break;
60824c8c90cSguenther 
60924c8c90cSguenther 	case CST_METH_GAS_IO:
61024c8c90cSguenther 		meth = " io";
61124c8c90cSguenther 		show_addr = 1;
61224c8c90cSguenther 		break;
61324c8c90cSguenther 
61424c8c90cSguenther 	}
61524c8c90cSguenther 
61624c8c90cSguenther 	printf(" %sC%d(", (cx->flags & CST_FLAG_SKIP ? "!" : ""), cx->state);
61724c8c90cSguenther 	if (cx->power != -1)
61824c8c90cSguenther 		printf("%d", cx->power);
61924c8c90cSguenther 	printf("@%d%s", cx->latency, meth);
6209f8ad71dSguenther 	if (cx->flags & ~CST_FLAG_SKIP) {
6219f8ad71dSguenther 		if (cx->flags & CST_FLAG_FALLBACK)
6229f8ad71dSguenther 			printf("!");
6239f8ad71dSguenther 		else
62424c8c90cSguenther 			printf(".%x", (cx->flags & ~CST_FLAG_SKIP));
6259f8ad71dSguenther 	}
62624c8c90cSguenther 	if (show_addr)
62724c8c90cSguenther 		printf("@0x%llx", cx->address);
62824c8c90cSguenther 	printf(")");
62924c8c90cSguenther }
63024c8c90cSguenther 
63124c8c90cSguenther void
63224c8c90cSguenther acpicpu_print_cst(struct acpicpu_softc *sc)
63324c8c90cSguenther {
63424c8c90cSguenther 	struct acpi_cstate	*cx;
63524c8c90cSguenther 	int i;
63624c8c90cSguenther 
63724c8c90cSguenther 	if (!SLIST_EMPTY(&sc->sc_cstates)) {
63824c8c90cSguenther 		printf(":");
63924c8c90cSguenther 
64024c8c90cSguenther 		i = 0;
64124c8c90cSguenther 		SLIST_FOREACH(cx, &sc->sc_cstates, link) {
64224c8c90cSguenther 			if (i++)
64324c8c90cSguenther 				printf(",");
64424c8c90cSguenther 			acpicpu_print_one_cst(cx);
64524c8c90cSguenther 		}
64624c8c90cSguenther 	}
647226a7f6dSjordan }
648226a7f6dSjordan 
649226a7f6dSjordan 
65095f8e6e8Smarco int
65195f8e6e8Smarco acpicpu_match(struct device *parent, void *match, void *aux)
65295f8e6e8Smarco {
65395f8e6e8Smarco 	struct acpi_attach_args	*aa = aux;
65495f8e6e8Smarco 	struct cfdata		*cf = match;
65564fc6eebSjmatthew 	struct acpi_softc	*acpi = (struct acpi_softc *)parent;
656a580e941Sjmatthew 	CPU_INFO_ITERATOR	cii;
657a580e941Sjmatthew 	struct cpu_info		*ci;
658a580e941Sjmatthew 	int64_t			uid;
65995f8e6e8Smarco 
66042aac20cSkettenis 	if (acpi_matchhids(aa, acpicpu_hids, cf->cf_driver->cd_name) &&
66142aac20cSkettenis 	    aa->aaa_node && aa->aaa_node->value &&
66242aac20cSkettenis 	    aa->aaa_node->value->type == AML_OBJTYPE_DEVICE) {
66364fc6eebSjmatthew 		/*
66464fc6eebSjmatthew 		 * Record that we've seen a Device() CPU object,
66564fc6eebSjmatthew 		 * so we won't attach any Processor() nodes.
66664fc6eebSjmatthew 		 */
66764fc6eebSjmatthew 		acpi->sc_skip_processor = 1;
668a580e941Sjmatthew 
669a580e941Sjmatthew 		/* Only match if we can find a CPU with the right ID */
670a580e941Sjmatthew 		if (aml_evalinteger(acpi, aa->aaa_node, "_UID", 0,
671a580e941Sjmatthew 		    NULL, &uid) == 0)
672a580e941Sjmatthew 			CPU_INFO_FOREACH(cii, ci)
673a580e941Sjmatthew 				if (ci->ci_acpi_proc_id == uid)
674c950c8a4Skettenis 					return (1);
675a580e941Sjmatthew 
676a580e941Sjmatthew 		return (0);
67764fc6eebSjmatthew 	}
678c950c8a4Skettenis 
67995f8e6e8Smarco 	/* sanity */
68095f8e6e8Smarco 	if (aa->aaa_name == NULL ||
68195f8e6e8Smarco 	    strcmp(aa->aaa_name, cf->cf_driver->cd_name) != 0 ||
68295f8e6e8Smarco 	    aa->aaa_table != NULL)
68395f8e6e8Smarco 		return (0);
68495f8e6e8Smarco 
68595f8e6e8Smarco 	return (1);
68695f8e6e8Smarco }
68795f8e6e8Smarco 
68895f8e6e8Smarco void
68995f8e6e8Smarco acpicpu_attach(struct device *parent, struct device *self, void *aux)
69095f8e6e8Smarco {
69195f8e6e8Smarco 	struct acpicpu_softc	*sc = (struct acpicpu_softc *)self;
69295f8e6e8Smarco 	struct acpi_attach_args *aa = aux;
693226a7f6dSjordan 	struct aml_value	res;
694c950c8a4Skettenis 	int64_t			uid;
695ae89b26eSmarco 	int			i;
696d2eaebe9Skettenis 	uint32_t		status = 0;
69724c8c90cSguenther 	CPU_INFO_ITERATOR	cii;
69824c8c90cSguenther 	struct cpu_info		*ci;
69995f8e6e8Smarco 
70095f8e6e8Smarco 	sc->sc_acpi = (struct acpi_softc *)parent;
701226a7f6dSjordan 	sc->sc_devnode = aa->aaa_node;
702226a7f6dSjordan 
703226a7f6dSjordan 	SLIST_INIT(&sc->sc_cstates);
70495f8e6e8Smarco 
705c950c8a4Skettenis 	if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode,
706c950c8a4Skettenis 	    "_UID", 0, NULL, &uid) == 0)
707c950c8a4Skettenis 		sc->sc_cpu = uid;
708c950c8a4Skettenis 
709226a7f6dSjordan 	if (aml_evalnode(sc->sc_acpi, sc->sc_devnode, 0, NULL, &res) == 0) {
710226a7f6dSjordan 		if (res.type == AML_OBJTYPE_PROCESSOR) {
711226a7f6dSjordan 			sc->sc_cpu = res.v_processor.proc_id;
712226a7f6dSjordan 			sc->sc_pblk_addr = res.v_processor.proc_addr;
713226a7f6dSjordan 			sc->sc_pblk_len = res.v_processor.proc_len;
714226a7f6dSjordan 		}
715226a7f6dSjordan 		aml_freevalue(&res);
716226a7f6dSjordan 	}
717226a7f6dSjordan 	sc->sc_duty_off = sc->sc_acpi->sc_fadt->duty_offset;
718226a7f6dSjordan 	sc->sc_duty_wid = sc->sc_acpi->sc_fadt->duty_width;
719f6f14d38Sgwk 
72024c8c90cSguenther 	/* link in the matching cpu_info */
72124c8c90cSguenther 	CPU_INFO_FOREACH(cii, ci)
722a2e7ce2eSkettenis 		if (ci->ci_acpi_proc_id == sc->sc_cpu) {
72324c8c90cSguenther 			ci->ci_acpicpudev = self;
72424c8c90cSguenther 			sc->sc_ci = ci;
72524c8c90cSguenther 			break;
72624c8c90cSguenther 		}
727909c5087Sguenther 	if (ci == NULL) {
728909c5087Sguenther 		printf(": no cpu matching ACPI ID %d\n", sc->sc_cpu);
729909c5087Sguenther 		return;
730909c5087Sguenther 	}
731909c5087Sguenther 
73224c8c90cSguenther 	sc->sc_prev_sleep = 1000000;
73324c8c90cSguenther 
734f6f14d38Sgwk 	acpicpu_set_pdc(sc);
735f6f14d38Sgwk 
736226a7f6dSjordan 	if (!valid_throttle(sc->sc_duty_off, sc->sc_duty_wid, sc->sc_pblk_addr))
737226a7f6dSjordan 		sc->sc_flags |= FLAGS_NOTHROTTLE;
738226a7f6dSjordan #ifdef ACPI_DEBUG
739226a7f6dSjordan 	printf(": %s: ", sc->sc_devnode->name);
740f6f14d38Sgwk 	printf("\n: hdr:%x pblk:%x,%x duty:%x,%x pstate:%x "
7417a27271eSmlarkin 	       "(%ld throttling states)\n", sc->sc_acpi->sc_fadt->hdr_revision,
742f6f14d38Sgwk 		sc->sc_pblk_addr, sc->sc_pblk_len, sc->sc_duty_off,
743f6f14d38Sgwk 		sc->sc_duty_wid, sc->sc_acpi->sc_fadt->pstate_cnt,
744226a7f6dSjordan 		CPU_MAXSTATE(sc));
745226a7f6dSjordan #endif
746226a7f6dSjordan 
747226a7f6dSjordan 	/* Get C-States from _CST or FADT */
74824c8c90cSguenther 	if (acpicpu_getcst(sc) || SLIST_EMPTY(&sc->sc_cstates))
74924c8c90cSguenther 		acpicpu_getcst_from_fadt(sc);
750226a7f6dSjordan 	else {
75124c8c90cSguenther 		/* Notify BIOS we use _CST objects */
75224c8c90cSguenther 		if (sc->sc_acpi->sc_fadt->cst_cnt) {
75324c8c90cSguenther 			acpi_write_pmreg(sc->sc_acpi, ACPIREG_SMICMD, 0,
75424c8c90cSguenther 			    sc->sc_acpi->sc_fadt->cst_cnt);
755226a7f6dSjordan 		}
75624c8c90cSguenther 	}
75724c8c90cSguenther 	if (!SLIST_EMPTY(&sc->sc_cstates)) {
758d2eaebe9Skettenis 		extern uint32_t acpi_force_bm;
759b9e1a775Sderaadt 
76024c8c90cSguenther 		cpu_idle_cycle_fcn = &acpicpu_idle;
7616c195505Skettenis 		cpu_suspend_cycle_fcn = &acpicpu_suspend;
76224c8c90cSguenther 
76324c8c90cSguenther 		/*
76424c8c90cSguenther 		 * C3 (and maybe C2?) needs BM_RLD to be set to
76524c8c90cSguenther 		 * wake the system
76624c8c90cSguenther 		 */
767b9e1a775Sderaadt 		if (SLIST_FIRST(&sc->sc_cstates)->state > 1 && acpi_force_bm == 0) {
76824c8c90cSguenther 			uint16_t en = acpi_read_pmreg(sc->sc_acpi,
76924c8c90cSguenther 			    ACPIREG_PM1_CNT, 0);
77024c8c90cSguenther 			if ((en & ACPI_PM1_BM_RLD) == 0) {
77124c8c90cSguenther 				acpi_write_pmreg(sc->sc_acpi, ACPIREG_PM1_CNT,
77224c8c90cSguenther 				    0, en | ACPI_PM1_BM_RLD);
773b9e1a775Sderaadt 				acpi_force_bm = ACPI_PM1_BM_RLD;
77424c8c90cSguenther 			}
77524c8c90cSguenther 		}
77624c8c90cSguenther 	}
77724c8c90cSguenther 
778ae89b26eSmarco 	if (acpicpu_getpss(sc)) {
779226a7f6dSjordan 		sc->sc_flags |= FLAGS_NOPSS;
7803cd93248Sgwk 	} else {
78110f4946eSmarco #ifdef ACPI_DEBUG
78210f4946eSmarco 		for (i = 0; i < sc->sc_pss_len; i++) {
78310f4946eSmarco 			dnprintf(20, "%d %d %d %d %d %d\n",
78410f4946eSmarco 			    sc->sc_pss[i].pss_core_freq,
78510f4946eSmarco 			    sc->sc_pss[i].pss_power,
78610f4946eSmarco 			    sc->sc_pss[i].pss_trans_latency,
78710f4946eSmarco 			    sc->sc_pss[i].pss_bus_latency,
78810f4946eSmarco 			    sc->sc_pss[i].pss_ctrl,
78910f4946eSmarco 			    sc->sc_pss[i].pss_status);
79010f4946eSmarco 		}
79110f4946eSmarco 		dnprintf(20, "\n");
79210f4946eSmarco #endif
793cc42ddb4Smarco 		if (sc->sc_pss_len == 0) {
794cc42ddb4Smarco 			/* this should never happen */
795421a338cSmiod 			printf("%s: invalid _PSS length\n", DEVNAME(sc));
796cc42ddb4Smarco 			sc->sc_flags |= FLAGS_NOPSS;
797cc42ddb4Smarco 		}
798cc42ddb4Smarco 
799cc42ddb4Smarco 		acpicpu_getppc(sc);
800fdc7f605Smarco 		if (acpicpu_getpct(sc))
8013cd93248Sgwk 			sc->sc_flags |= FLAGS_NOPCT;
8023ca2fd94Sgwk 		else if (sc->sc_pss_len > 0) {
80324c8c90cSguenther 			/* Notify BIOS we are handling p-states */
80424c8c90cSguenther 			if (sc->sc_acpi->sc_fadt->pstate_cnt) {
80524c8c90cSguenther 				acpi_write_pmreg(sc->sc_acpi, ACPIREG_SMICMD,
80624c8c90cSguenther 				    0, sc->sc_acpi->sc_fadt->pstate_cnt);
80724c8c90cSguenther 			}
808226a7f6dSjordan 
809226a7f6dSjordan 			aml_register_notify(sc->sc_devnode, NULL,
81025ae5cf1Smarco 			    acpicpu_notify, sc, ACPIDEV_NOPOLL);
81179fd4ddcSgwk 
812cc42ddb4Smarco 			acpi_gasio(sc->sc_acpi, ACPI_IOREAD,
813cc42ddb4Smarco 			    sc->sc_pct.pct_status.grd_gas.address_space_id,
814cc42ddb4Smarco 			    sc->sc_pct.pct_status.grd_gas.address,
815cc42ddb4Smarco 			    sc->sc_pct_stat_as, sc->sc_pct_stat_as, &status);
816cc42ddb4Smarco 			sc->sc_level = (100 / sc->sc_pss_len) *
817cc42ddb4Smarco 			    (sc->sc_pss_len - status);
818cc42ddb4Smarco 			dnprintf(20, "%s: cpu index %d, percentage %d\n",
819cc42ddb4Smarco 			    DEVNAME(sc), status, sc->sc_level);
82079fd4ddcSgwk 			if (setperf_prio < 30) {
82179fd4ddcSgwk 				cpu_setperf = acpicpu_setperf;
822f6f14d38Sgwk 				acpicpu_set_notify(acpicpu_setperf_ppc_change);
82379fd4ddcSgwk 				setperf_prio = 30;
8248b9a27e5Sgwk 				acpi_hasprocfvs = 1;
82579fd4ddcSgwk 			}
8263cd93248Sgwk 		}
8273cd93248Sgwk 	}
828eb9a39afSmarco 
8293cd93248Sgwk 	/*
8303cd93248Sgwk 	 * Nicely enumerate what power management capabilities
8313cd93248Sgwk 	 * ACPI CPU provides.
832593ed30fSderaadt 	 */
83324c8c90cSguenther 	acpicpu_print_cst(sc);
834e734ee1dSmikeb 	if (!(sc->sc_flags & (FLAGS_NOPSS | FLAGS_NOPCT)) ||
835e734ee1dSmikeb 	    !(sc->sc_flags & FLAGS_NOPSS)) {
83648cc2e7fSmikeb 		printf("%c ", SLIST_EMPTY(&sc->sc_cstates) ? ':' : ',');
8373cd93248Sgwk 
8383cd93248Sgwk 		/*
8393cd93248Sgwk 		 * If acpicpu is itself providing the capability to transition
8403cd93248Sgwk 		 * states, enumerate them in the fashion that est and powernow
8413cd93248Sgwk 		 * would.
8423cd93248Sgwk 		 */
843e734ee1dSmikeb 		if (!(sc->sc_flags & (FLAGS_NOPSS | FLAGS_NOPCT))) {
84448cc2e7fSmikeb 			printf("FVS, ");
84548cc2e7fSmikeb 			for (i = 0; i < sc->sc_pss_len - 1; i++)
84648cc2e7fSmikeb 				printf("%d, ", sc->sc_pss[i].pss_core_freq);
84748cc2e7fSmikeb 			printf("%d MHz", sc->sc_pss[i].pss_core_freq);
848e734ee1dSmikeb 		} else
84948cc2e7fSmikeb 			printf("PSS");
850e734ee1dSmikeb 	}
85148cc2e7fSmikeb 
85248cc2e7fSmikeb 	printf("\n");
85395f8e6e8Smarco }
85495f8e6e8Smarco 
85595f8e6e8Smarco int
856cc42ddb4Smarco acpicpu_getppc(struct acpicpu_softc *sc)
85756138cddSmarco {
8586ab98c39Sjordan 	struct aml_value	res;
859cc42ddb4Smarco 
860cc42ddb4Smarco 	sc->sc_ppc = 0;
86156138cddSmarco 
862fdc7f605Smarco 	if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "_PPC", 0, NULL, &res)) {
8633cd93248Sgwk 		dnprintf(10, "%s: no _PPC\n", DEVNAME(sc));
86456138cddSmarco 		return (1);
86556138cddSmarco 	}
86656138cddSmarco 
867cc42ddb4Smarco 	sc->sc_ppc = aml_val2int(&res);
868cc42ddb4Smarco 	dnprintf(10, "%s: _PPC: %d\n", DEVNAME(sc), sc->sc_ppc);
8696ab98c39Sjordan 	aml_freevalue(&res);
87056138cddSmarco 
871cc42ddb4Smarco 	return (0);
872cc42ddb4Smarco }
873cc42ddb4Smarco 
874cc42ddb4Smarco int
875cc42ddb4Smarco acpicpu_getpct(struct acpicpu_softc *sc)
876cc42ddb4Smarco {
877cc42ddb4Smarco 	struct aml_value	res;
878cc42ddb4Smarco 	int			rv = 1;
879cc42ddb4Smarco 
880fdc7f605Smarco 	if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "_PCT", 0, NULL, &res)) {
8813cd93248Sgwk 		dnprintf(20, "%s: no _PCT\n", DEVNAME(sc));
88256138cddSmarco 		return (1);
88356138cddSmarco 	}
88456138cddSmarco 
88556138cddSmarco 	if (res.length != 2) {
8863cd93248Sgwk 		dnprintf(20, "%s: %s: invalid _PCT length\n", DEVNAME(sc),
887226a7f6dSjordan 		    sc->sc_devnode->name);
88856138cddSmarco 		return (1);
88956138cddSmarco 	}
89056138cddSmarco 
89156138cddSmarco 	memcpy(&sc->sc_pct.pct_ctrl, res.v_package[0]->v_buffer,
89256138cddSmarco 	    sizeof sc->sc_pct.pct_ctrl);
893fdc7f605Smarco 	if (sc->sc_pct.pct_ctrl.grd_gas.address_space_id ==
894fdc7f605Smarco 	    GAS_FUNCTIONAL_FIXED) {
8953cd93248Sgwk 		dnprintf(20, "CTRL GASIO is functional fixed hardware.\n");
8963cd93248Sgwk 		goto ffh;
897fdc7f605Smarco 	}
898fdc7f605Smarco 
89956138cddSmarco 	memcpy(&sc->sc_pct.pct_status, res.v_package[1]->v_buffer,
90056138cddSmarco 	    sizeof sc->sc_pct.pct_status);
901fdc7f605Smarco 	if (sc->sc_pct.pct_status.grd_gas.address_space_id ==
902fdc7f605Smarco 	    GAS_FUNCTIONAL_FIXED) {
9033cd93248Sgwk 		dnprintf(20, "CTRL GASIO is functional fixed hardware.\n");
9043cd93248Sgwk 		goto ffh;
905fdc7f605Smarco 	}
90656138cddSmarco 
9077a27271eSmlarkin 	dnprintf(10, "_PCT(ctrl)  : %02x %04x %02x %02x %02x %02x %016llx\n",
90856138cddSmarco 	    sc->sc_pct.pct_ctrl.grd_descriptor,
90956138cddSmarco 	    sc->sc_pct.pct_ctrl.grd_length,
9103027e9c6Smarco 	    sc->sc_pct.pct_ctrl.grd_gas.address_space_id,
9113027e9c6Smarco 	    sc->sc_pct.pct_ctrl.grd_gas.register_bit_width,
9123027e9c6Smarco 	    sc->sc_pct.pct_ctrl.grd_gas.register_bit_offset,
9133027e9c6Smarco 	    sc->sc_pct.pct_ctrl.grd_gas.access_size,
9143027e9c6Smarco 	    sc->sc_pct.pct_ctrl.grd_gas.address);
91556138cddSmarco 
9167a27271eSmlarkin 	dnprintf(10, "_PCT(status): %02x %04x %02x %02x %02x %02x %016llx\n",
91756138cddSmarco 	    sc->sc_pct.pct_status.grd_descriptor,
91856138cddSmarco 	    sc->sc_pct.pct_status.grd_length,
9193027e9c6Smarco 	    sc->sc_pct.pct_status.grd_gas.address_space_id,
9203027e9c6Smarco 	    sc->sc_pct.pct_status.grd_gas.register_bit_width,
9213027e9c6Smarco 	    sc->sc_pct.pct_status.grd_gas.register_bit_offset,
9223027e9c6Smarco 	    sc->sc_pct.pct_status.grd_gas.access_size,
9233027e9c6Smarco 	    sc->sc_pct.pct_status.grd_gas.address);
9243027e9c6Smarco 
925cc42ddb4Smarco 	/* if not set assume single 32 bit access */
926cc42ddb4Smarco 	sc->sc_pct_stat_as = sc->sc_pct.pct_status.grd_gas.register_bit_width
927cc42ddb4Smarco 	    / 8;
928cc42ddb4Smarco 	if (sc->sc_pct_stat_as == 0)
929cc42ddb4Smarco 		sc->sc_pct_stat_as = 4;
930cc42ddb4Smarco 	sc->sc_pct_ctrl_as = sc->sc_pct.pct_ctrl.grd_gas.register_bit_width / 8;
931cc42ddb4Smarco 	if (sc->sc_pct_ctrl_as == 0)
932cc42ddb4Smarco 		sc->sc_pct_ctrl_as = 4;
933cc42ddb4Smarco 	sc->sc_pct_stat_len = sc->sc_pct.pct_status.grd_gas.access_size;
934cc42ddb4Smarco 	if (sc->sc_pct_stat_len == 0)
935cc42ddb4Smarco 		sc->sc_pct_stat_len = sc->sc_pct_stat_as;
936cc42ddb4Smarco 	sc->sc_pct_ctrl_len = sc->sc_pct.pct_ctrl.grd_gas.access_size;
937cc42ddb4Smarco 	if (sc->sc_pct_ctrl_len == 0)
938cc42ddb4Smarco 		sc->sc_pct_ctrl_len = sc->sc_pct_ctrl_as;
939cc42ddb4Smarco 
940fdc7f605Smarco 	rv = 0;
9413cd93248Sgwk ffh:
942fdc7f605Smarco 	aml_freevalue(&res);
943fdc7f605Smarco 	return (rv);
94456138cddSmarco }
94556138cddSmarco 
94656138cddSmarco int
947ae89b26eSmarco acpicpu_getpss(struct acpicpu_softc *sc)
94895f8e6e8Smarco {
9496ab98c39Sjordan 	struct aml_value	res;
950f6f14d38Sgwk 	int			i, c, cf;
95195f8e6e8Smarco 
952fdc7f605Smarco 	if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "_PSS", 0, NULL, &res)) {
9533cd93248Sgwk 		dprintf("%s: no _PSS\n", DEVNAME(sc));
954ae89b26eSmarco 		return (1);
95595f8e6e8Smarco 	}
95695f8e6e8Smarco 
9576a1ea70fSderaadt 	free(sc->sc_pss, M_DEVBUF, sc->sc_pssfulllen);
958249d9c8fSjordan 
9595714159aSdoug 	sc->sc_pss = mallocarray(res.length, sizeof(*sc->sc_pss), M_DEVBUF,
9608b13d1b4Skrw 	    M_WAITOK | M_ZERO);
9616a1ea70fSderaadt 	sc->sc_pssfulllen = res.length * sizeof(*sc->sc_pss);
962ae89b26eSmarco 
963f6f14d38Sgwk 	c = 0;
964ae89b26eSmarco 	for (i = 0; i < res.length; i++) {
965f6f14d38Sgwk 		cf = aml_val2int(res.v_package[i]->v_package[0]);
966f6f14d38Sgwk 
967f6f14d38Sgwk 		/* This heuristic comes from FreeBSDs
968f6f14d38Sgwk 		 * dev/acpica/acpi_perf.c to weed out invalid PSS entries.
969f6f14d38Sgwk 		 */
970f6f14d38Sgwk 		if (cf == sc->sc_pss[c].pss_core_freq) {
971f6f14d38Sgwk 			printf("%s: struck PSS entry, core frequency equals "
972f6f14d38Sgwk 			    " last\n", sc->sc_dev.dv_xname);
973f6f14d38Sgwk 			continue;
974ae89b26eSmarco 		}
975f6f14d38Sgwk 
976f6f14d38Sgwk 		if (cf == 0xFFFF || cf == 0x9999 || cf == 99999 || cf == 0) {
977f6f14d38Sgwk 			printf("%s: struck PSS entry, inappropriate core "
978f6f14d38Sgwk 			    "frequency value\n", sc->sc_dev.dv_xname);
979f6f14d38Sgwk 			continue;
980f6f14d38Sgwk 		}
981f6f14d38Sgwk 
982f6f14d38Sgwk 		sc->sc_pss[c].pss_core_freq = cf;
983f6f14d38Sgwk 		sc->sc_pss[c].pss_power = aml_val2int(
984f6f14d38Sgwk 		    res.v_package[i]->v_package[1]);
985f6f14d38Sgwk 		sc->sc_pss[c].pss_trans_latency = aml_val2int(
986f6f14d38Sgwk 		    res.v_package[i]->v_package[2]);
987f6f14d38Sgwk 		sc->sc_pss[c].pss_bus_latency = aml_val2int(
988f6f14d38Sgwk 		    res.v_package[i]->v_package[3]);
989f6f14d38Sgwk 		sc->sc_pss[c].pss_ctrl = aml_val2int(
990f6f14d38Sgwk 		    res.v_package[i]->v_package[4]);
991f6f14d38Sgwk 		sc->sc_pss[c].pss_status = aml_val2int(
992f6f14d38Sgwk 		    res.v_package[i]->v_package[5]);
993f6f14d38Sgwk 		c++;
994f6f14d38Sgwk 	}
995f6f14d38Sgwk 	sc->sc_pss_len = c;
996ae89b26eSmarco 
99746af9f1aSfgsch 	aml_freevalue(&res);
99846af9f1aSfgsch 
99995f8e6e8Smarco 	return (0);
100095f8e6e8Smarco }
100195f8e6e8Smarco 
100295f8e6e8Smarco int
100363f04804Smarco acpicpu_fetch_pss(struct acpicpu_pss **pss)
100463f04804Smarco {
10053cd93248Sgwk 	struct acpicpu_softc	*sc;
10063cd93248Sgwk 
100763f04804Smarco 	/*
100863f04804Smarco 	 * XXX: According to the ACPI spec in an SMP system all processors
10094171d639Skrw 	 * are supposed to support the same states. For now we pray
101063f04804Smarco 	 * the bios ensures this...
101163f04804Smarco 	 */
101263f04804Smarco 
1013c03004a5Sjmatthew 	sc = (struct acpicpu_softc *)cpu_info_primary.ci_acpicpudev;
101463f04804Smarco 	if (!sc)
10153cd93248Sgwk 		return 0;
10163cd93248Sgwk 	*pss = sc->sc_pss;
10173cd93248Sgwk 
101863f04804Smarco 	return (sc->sc_pss_len);
10193cd93248Sgwk }
10203cd93248Sgwk 
10213cd93248Sgwk int
102295f8e6e8Smarco acpicpu_notify(struct aml_node *node, int notify_type, void *arg)
102395f8e6e8Smarco {
102495f8e6e8Smarco 	struct acpicpu_softc	*sc = arg;
102595f8e6e8Smarco 
102695f8e6e8Smarco 	dnprintf(10, "acpicpu_notify: %.2x %s\n", notify_type,
1027226a7f6dSjordan 	    sc->sc_devnode->name);
102895f8e6e8Smarco 
10290c1f1f40Smarco 	switch (notify_type) {
10300c1f1f40Smarco 	case 0x80:	/* _PPC changed, retrieve new values */
1031cc42ddb4Smarco 		acpicpu_getppc(sc);
1032eb9a39afSmarco 		acpicpu_getpss(sc);
1033eb9a39afSmarco 		if (sc->sc_notify)
1034eb9a39afSmarco 			sc->sc_notify(sc->sc_pss, sc->sc_pss_len);
10350c1f1f40Smarco 		break;
103624c8c90cSguenther 
103724c8c90cSguenther 	case 0x81:	/* _CST changed, retrieve new values */
103824c8c90cSguenther 		acpicpu_getcst(sc);
103924c8c90cSguenther 		printf("%s: notify", DEVNAME(sc));
104024c8c90cSguenther 		acpicpu_print_cst(sc);
104124c8c90cSguenther 		printf("\n");
104224c8c90cSguenther 		break;
104324c8c90cSguenther 
10440c1f1f40Smarco 	default:
10450c1f1f40Smarco 		printf("%s: unhandled cpu event %x\n", DEVNAME(sc),
10460c1f1f40Smarco 		    notify_type);
10470c1f1f40Smarco 		break;
10480c1f1f40Smarco 	}
10490c1f1f40Smarco 
105095f8e6e8Smarco 	return (0);
105195f8e6e8Smarco }
105279fd4ddcSgwk 
105379fd4ddcSgwk void
1054f6f14d38Sgwk acpicpu_set_notify(void (*func)(struct acpicpu_pss *, int))
1055f6f14d38Sgwk {
10563cd93248Sgwk 	struct acpicpu_softc    *sc;
10573cd93248Sgwk 
1058c03004a5Sjmatthew 	sc = (struct acpicpu_softc *)cpu_info_primary.ci_acpicpudev;
10593cd93248Sgwk 	if (sc != NULL)
10603cd93248Sgwk 		sc->sc_notify = func;
10613cd93248Sgwk }
10623cd93248Sgwk 
10633cd93248Sgwk void
1064f6f14d38Sgwk acpicpu_setperf_ppc_change(struct acpicpu_pss *pss, int npss)
1065f6f14d38Sgwk {
1066f6f14d38Sgwk 	struct acpicpu_softc    *sc;
1067f6f14d38Sgwk 
1068c03004a5Sjmatthew 	sc = (struct acpicpu_softc *)cpu_info_primary.ci_acpicpudev;
1069f6f14d38Sgwk 
1070f6f14d38Sgwk 	if (sc != NULL)
1071f6f14d38Sgwk 		cpu_setperf(sc->sc_level);
1072f6f14d38Sgwk }
1073f6f14d38Sgwk 
1074f6f14d38Sgwk void
107563f04804Smarco acpicpu_setperf(int level)
107663f04804Smarco {
107779fd4ddcSgwk 	struct acpicpu_softc	*sc;
107879fd4ddcSgwk 	struct acpicpu_pss	*pss = NULL;
1079cc42ddb4Smarco 	int			idx, len;
1080d2eaebe9Skettenis 	uint32_t		status = 0;
108179fd4ddcSgwk 
1082c03004a5Sjmatthew 	sc = (struct acpicpu_softc *)curcpu()->ci_acpicpudev;
108379fd4ddcSgwk 
1084620d531aSmarco 	dnprintf(10, "%s: acpicpu setperf level %d\n",
1085dd33bd45Srobert 	    sc->sc_devnode->name, level);
108679fd4ddcSgwk 
1087620d531aSmarco 	if (level < 0 || level > 100) {
1088620d531aSmarco 		dnprintf(10, "%s: acpicpu setperf illegal percentage\n",
1089226a7f6dSjordan 		    sc->sc_devnode->name);
109079fd4ddcSgwk 		return;
1091620d531aSmarco 	}
109279fd4ddcSgwk 
10930d56030dSmarco 	/*
10940d56030dSmarco 	 * XXX this should be handled more gracefully and it needs to also do
10950d56030dSmarco 	 * the duty cycle method instead of pss exclusively
10960d56030dSmarco 	 */
1097cc42ddb4Smarco 	if (sc->sc_flags & FLAGS_NOPSS || sc->sc_flags & FLAGS_NOPCT) {
1098f6f14d38Sgwk 		dnprintf(10, "%s: acpicpu no _PSS or _PCT\n",
1099f6f14d38Sgwk 		    sc->sc_devnode->name);
11000d56030dSmarco 		return;
11010d56030dSmarco 	}
11020d56030dSmarco 
1103cc42ddb4Smarco 	if (sc->sc_ppc)
1104cc42ddb4Smarco 		len = sc->sc_ppc;
1105cc42ddb4Smarco 	else
1106cc42ddb4Smarco 		len = sc->sc_pss_len;
1107cc42ddb4Smarco 	idx = (len - 1) - (level / (100 / len));
1108620d531aSmarco 	if (idx < 0)
1109cc42ddb4Smarco 		idx = 0;
1110620d531aSmarco 
1111cc42ddb4Smarco 	if (sc->sc_ppc)
1112cc42ddb4Smarco 		idx += sc->sc_pss_len - sc->sc_ppc;
1113cc42ddb4Smarco 
1114cc42ddb4Smarco 	if (idx > sc->sc_pss_len)
1115cc42ddb4Smarco 		idx = sc->sc_pss_len - 1;
1116cc42ddb4Smarco 
1117cc42ddb4Smarco 	dnprintf(10, "%s: acpicpu setperf index %d pss_len %d ppc %d\n",
1118cc42ddb4Smarco 	    sc->sc_devnode->name, idx, sc->sc_pss_len, sc->sc_ppc);
1119620d531aSmarco 
1120620d531aSmarco 	pss = &sc->sc_pss[idx];
1121620d531aSmarco 
1122620d531aSmarco #ifdef ACPI_DEBUG
1123620d531aSmarco 	/* keep this for now since we will need this for debug in the field */
1124620d531aSmarco 	printf("0 status: %x %llx %u %u ctrl: %x %llx %u %u\n",
1125620d531aSmarco 	    sc->sc_pct.pct_status.grd_gas.address_space_id,
1126620d531aSmarco 	    sc->sc_pct.pct_status.grd_gas.address,
1127cc42ddb4Smarco 	    sc->sc_pct_stat_as, sc->sc_pct_stat_len,
1128620d531aSmarco 	    sc->sc_pct.pct_ctrl.grd_gas.address_space_id,
1129620d531aSmarco 	    sc->sc_pct.pct_ctrl.grd_gas.address,
1130cc42ddb4Smarco 	    sc->sc_pct_ctrl_as, sc->sc_pct_ctrl_len);
1131620d531aSmarco #endif
113279fd4ddcSgwk 	acpi_gasio(sc->sc_acpi, ACPI_IOREAD,
113379fd4ddcSgwk 	    sc->sc_pct.pct_status.grd_gas.address_space_id,
1134cc42ddb4Smarco 	    sc->sc_pct.pct_status.grd_gas.address, sc->sc_pct_stat_as,
1135cc42ddb4Smarco 	    sc->sc_pct_stat_len, &status);
113663f04804Smarco 	dnprintf(20, "1 status: %u <- %u\n", status, pss->pss_status);
113779fd4ddcSgwk 
113879fd4ddcSgwk 	/* Are we already at the requested frequency? */
113979fd4ddcSgwk 	if (status == pss->pss_status)
114079fd4ddcSgwk 		return;
114179fd4ddcSgwk 
114279fd4ddcSgwk 	acpi_gasio(sc->sc_acpi, ACPI_IOWRITE,
114379fd4ddcSgwk 	    sc->sc_pct.pct_ctrl.grd_gas.address_space_id,
1144cc42ddb4Smarco 	    sc->sc_pct.pct_ctrl.grd_gas.address, sc->sc_pct_ctrl_as,
1145cc42ddb4Smarco 	    sc->sc_pct_ctrl_len, &pss->pss_ctrl);
1146620d531aSmarco 	dnprintf(20, "pss_ctrl: %x\n", pss->pss_ctrl);
114779fd4ddcSgwk 
114879fd4ddcSgwk 	acpi_gasio(sc->sc_acpi, ACPI_IOREAD,
114979fd4ddcSgwk 	    sc->sc_pct.pct_status.grd_gas.address_space_id,
1150cc42ddb4Smarco 	    sc->sc_pct.pct_status.grd_gas.address, sc->sc_pct_stat_as,
1151cc42ddb4Smarco 	    sc->sc_pct_stat_as, &status);
115263f04804Smarco 	dnprintf(20, "2 status: %d\n", status);
115379fd4ddcSgwk 
115479fd4ddcSgwk 	/* Did the transition succeed? */
1155cc42ddb4Smarco 	if (status == pss->pss_status) {
115679fd4ddcSgwk 		cpuspeed = pss->pss_core_freq;
1157cc42ddb4Smarco 		sc->sc_level = level;
1158cc42ddb4Smarco 	} else
1159620d531aSmarco 		printf("%s: acpicpu setperf failed to alter frequency\n",
1160226a7f6dSjordan 		    sc->sc_devnode->name);
116179fd4ddcSgwk }
116224c8c90cSguenther 
116324c8c90cSguenther void
116424c8c90cSguenther acpicpu_idle(void)
116524c8c90cSguenther {
116624c8c90cSguenther 	struct cpu_info *ci = curcpu();
116724c8c90cSguenther 	struct acpicpu_softc *sc = (struct acpicpu_softc *)ci->ci_acpicpudev;
116824c8c90cSguenther 	struct acpi_cstate *best, *cx;
116924c8c90cSguenther 	unsigned long itime;
117024c8c90cSguenther 
117124c8c90cSguenther 	if (sc == NULL) {
117224c8c90cSguenther 		__asm volatile("sti");
117324c8c90cSguenther 		panic("null acpicpu");
117424c8c90cSguenther 	}
117524c8c90cSguenther 
117624c8c90cSguenther 	/* possibly update the MWAIT_ONLY flag in cpu_info */
117724c8c90cSguenther 	if (sc->sc_flags & FLAGS_MWAIT_ONLY) {
117824c8c90cSguenther 		if ((ci->ci_mwait & MWAIT_ONLY) == 0)
117924c8c90cSguenther 			atomic_setbits_int(&ci->ci_mwait, MWAIT_ONLY);
118024c8c90cSguenther 	} else if (ci->ci_mwait & MWAIT_ONLY)
118124c8c90cSguenther 		atomic_clearbits_int(&ci->ci_mwait, MWAIT_ONLY);
118224c8c90cSguenther 
118324c8c90cSguenther 	/*
118424c8c90cSguenther 	 * Find the first state with a latency we'll accept, ignoring
118524c8c90cSguenther 	 * states marked skippable
118624c8c90cSguenther 	 */
118724c8c90cSguenther 	best = cx = SLIST_FIRST(&sc->sc_cstates);
118824c8c90cSguenther 	while ((cx->flags & CST_FLAG_SKIP) ||
118924c8c90cSguenther 	    cx->latency * 3 > sc->sc_prev_sleep) {
119024c8c90cSguenther 		if ((cx = SLIST_NEXT(cx, link)) == NULL)
119124c8c90cSguenther 			break;
119224c8c90cSguenther 		best = cx;
119324c8c90cSguenther 	}
119424c8c90cSguenther 
119524c8c90cSguenther 	if (best->state >= 3 &&
119624c8c90cSguenther 	    (best->flags & CST_FLAG_MWAIT_BM_AVOIDANCE) &&
119724c8c90cSguenther 	    acpi_read_pmreg(acpi_softc, ACPIREG_PM1_STS, 0) & ACPI_PM1_BM_STS) {
119824c8c90cSguenther 		/* clear it and back off */
119924c8c90cSguenther 		acpi_write_pmreg(acpi_softc, ACPIREG_PM1_STS, 0,
120024c8c90cSguenther 		    ACPI_PM1_BM_STS);
120124c8c90cSguenther 		while ((cx = SLIST_NEXT(cx, link)) != NULL) {
120224c8c90cSguenther 			if (cx->flags & CST_FLAG_SKIP)
120324c8c90cSguenther 				continue;
120424c8c90cSguenther 			if (cx->state < 3 ||
120524c8c90cSguenther 			    (cx->flags & CST_FLAG_MWAIT_BM_AVOIDANCE) == 0)
120624c8c90cSguenther 				break;
120724c8c90cSguenther 		}
120824c8c90cSguenther 		best = cx;
120924c8c90cSguenther 	}
121024c8c90cSguenther 
121124c8c90cSguenther 
121224c8c90cSguenther 	atomic_inc_long(&cst_stats[best->state]);
121324c8c90cSguenther 
121424c8c90cSguenther 	itime = tick / 2;
121524c8c90cSguenther 	switch (best->method) {
121624c8c90cSguenther 	default:
121724c8c90cSguenther 	case CST_METH_HALT:
121824c8c90cSguenther 		__asm volatile("sti; hlt");
121924c8c90cSguenther 		break;
122024c8c90cSguenther 
122124c8c90cSguenther 	case CST_METH_IO_HALT:
122224c8c90cSguenther 		inb((u_short)best->address);
122324c8c90cSguenther 		__asm volatile("sti; hlt");
122424c8c90cSguenther 		break;
122524c8c90cSguenther 
122624c8c90cSguenther 	case CST_METH_MWAIT:
122724c8c90cSguenther 		{
122824c8c90cSguenther 		struct timeval start, stop;
122924c8c90cSguenther 		unsigned int hints;
123024c8c90cSguenther 
123124c8c90cSguenther #ifdef __LP64__
123224c8c90cSguenther 		if ((read_rflags() & PSL_I) == 0)
123324c8c90cSguenther 			panic("idle with interrupts blocked!");
123424c8c90cSguenther #else
123524c8c90cSguenther 		if ((read_eflags() & PSL_I) == 0)
123624c8c90cSguenther 			panic("idle with interrupts blocked!");
123724c8c90cSguenther #endif
123824c8c90cSguenther 
123924c8c90cSguenther 		/* something already queued? */
1240b2602131Smpi 		if (!cpu_is_idle(ci))
124124c8c90cSguenther 			return;
124224c8c90cSguenther 
124324c8c90cSguenther 		/*
124424c8c90cSguenther 		 * About to idle; setting the MWAIT_IN_IDLE bit tells
124524c8c90cSguenther 		 * cpu_unidle() that it can't be a no-op and tells cpu_kick()
124624c8c90cSguenther 		 * that it doesn't need to use an IPI.  We also set the
124724c8c90cSguenther 		 * MWAIT_KEEP_IDLING bit: those routines clear it to stop
124824c8c90cSguenther 		 * the mwait.  Once they're set, we do a final check of the
124924c8c90cSguenther 		 * queue, in case another cpu called setrunqueue() and added
125024c8c90cSguenther 		 * something to the queue and called cpu_unidle() between
125124c8c90cSguenther 		 * the check in sched_idle() and here.
125224c8c90cSguenther 		 */
125324c8c90cSguenther 		hints = (unsigned)best->address;
125424c8c90cSguenther 		microuptime(&start);
125524c8c90cSguenther 		atomic_setbits_int(&ci->ci_mwait, MWAIT_IDLING);
1256b2602131Smpi 		if (cpu_is_idle(ci)) {
125724c8c90cSguenther 			/* intel errata AAI65: cflush before monitor */
12589914efbfSjsg 			if (ci->ci_cflushsz != 0 &&
12599914efbfSjsg 			    strcmp(cpu_vendor, "GenuineIntel") == 0) {
126024c8c90cSguenther 				membar_sync();
126124c8c90cSguenther 				clflush((unsigned long)&ci->ci_mwait);
126224c8c90cSguenther 				membar_sync();
126324c8c90cSguenther 			}
126424c8c90cSguenther 
126524c8c90cSguenther 			monitor(&ci->ci_mwait, 0, 0);
126624c8c90cSguenther 			if ((ci->ci_mwait & MWAIT_IDLING) == MWAIT_IDLING)
126724c8c90cSguenther 				mwait(0, hints);
126824c8c90cSguenther 		}
126924c8c90cSguenther 
127024c8c90cSguenther 		microuptime(&stop);
127124c8c90cSguenther 		timersub(&stop, &start, &stop);
127224c8c90cSguenther 		itime = stop.tv_sec * 1000000 + stop.tv_usec;
127324c8c90cSguenther 
127424c8c90cSguenther 		/* done idling; let cpu_kick() know that an IPI is required */
127524c8c90cSguenther 		atomic_clearbits_int(&ci->ci_mwait, MWAIT_IDLING);
127624c8c90cSguenther 		break;
127724c8c90cSguenther 		}
127824c8c90cSguenther 
127924c8c90cSguenther 	case CST_METH_GAS_IO:
128024c8c90cSguenther 		inb((u_short)best->address);
128124c8c90cSguenther 		/* something harmless to give system time to change state */
128224c8c90cSguenther 		acpi_read_pmreg(acpi_softc, ACPIREG_PM1_STS, 0);
128324c8c90cSguenther 		break;
128424c8c90cSguenther 
128524c8c90cSguenther 	}
128624c8c90cSguenther 
128724c8c90cSguenther 	sc->sc_last_itime = itime;
128824c8c90cSguenther 	itime >>= 1;
128924c8c90cSguenther 	sc->sc_prev_sleep = (sc->sc_prev_sleep + (sc->sc_prev_sleep >> 1)
129024c8c90cSguenther 	    + itime) >> 1;
129124c8c90cSguenther }
12926c195505Skettenis 
12936c195505Skettenis void
12946c195505Skettenis acpicpu_suspend(void)
12956c195505Skettenis {
12966c195505Skettenis 	extern int cpu_suspended;
12976c195505Skettenis 	struct cpu_info *ci = curcpu();
12986c195505Skettenis 	struct acpicpu_softc *sc = (struct acpicpu_softc *)ci->ci_acpicpudev;
12996c195505Skettenis 	struct acpi_cstate *best, *cx;
13006c195505Skettenis 
13016c195505Skettenis 	if (sc == NULL) {
13026c195505Skettenis 		__asm volatile("sti");
13036c195505Skettenis 		panic("null acpicpu");
13046c195505Skettenis 	}
13056c195505Skettenis 
13066c195505Skettenis 	/*
13076c195505Skettenis 	 * Find the lowest usable state.
13086c195505Skettenis 	 */
13096c195505Skettenis 	best = cx = SLIST_FIRST(&sc->sc_cstates);
13106c195505Skettenis 	while ((cx->flags & CST_FLAG_SKIP)) {
13116c195505Skettenis 		if ((cx = SLIST_NEXT(cx, link)) == NULL)
13126c195505Skettenis 			break;
13136c195505Skettenis 		best = cx;
13146c195505Skettenis 	}
13156c195505Skettenis 
13166c195505Skettenis 	switch (best->method) {
13176c195505Skettenis 	default:
13186c195505Skettenis 	case CST_METH_HALT:
13196c195505Skettenis 		__asm volatile("sti; hlt");
13206c195505Skettenis 		break;
13216c195505Skettenis 
13226c195505Skettenis 	case CST_METH_IO_HALT:
13236c195505Skettenis 		inb((u_short)best->address);
13246c195505Skettenis 		__asm volatile("sti; hlt");
13256c195505Skettenis 		break;
13266c195505Skettenis 
13276c195505Skettenis 	case CST_METH_MWAIT:
13286c195505Skettenis 		{
13296c195505Skettenis 		unsigned int hints;
13306c195505Skettenis 
13316c195505Skettenis 		hints = (unsigned)best->address;
13326c195505Skettenis 		/* intel errata AAI65: cflush before monitor */
13336c195505Skettenis 		if (ci->ci_cflushsz != 0 &&
13346c195505Skettenis 		    strcmp(cpu_vendor, "GenuineIntel") == 0) {
13356c195505Skettenis 			membar_sync();
13366c195505Skettenis 			clflush((unsigned long)&cpu_suspended);
13376c195505Skettenis 			membar_sync();
13386c195505Skettenis 		}
13396c195505Skettenis 
13406c195505Skettenis 		monitor(&cpu_suspended, 0, 0);
13416c195505Skettenis 		if (cpu_suspended || !CPU_IS_PRIMARY(ci))
13426c195505Skettenis 			mwait(0, hints);
13436c195505Skettenis 
13446c195505Skettenis 		break;
13456c195505Skettenis 		}
13466c195505Skettenis 
13476c195505Skettenis 	case CST_METH_GAS_IO:
13486c195505Skettenis 		inb((u_short)best->address);
13496c195505Skettenis 		/* something harmless to give system time to change state */
13506c195505Skettenis 		acpi_read_pmreg(acpi_softc, ACPIREG_PM1_STS, 0);
13516c195505Skettenis 		break;
13526c195505Skettenis 
13536c195505Skettenis 	}
13546c195505Skettenis }
1355