xref: /netbsd-src/sys/dev/acpi/acpi_cpu_pstate.c (revision 30d28f20005de11b20499fed000071fb0a455337)
1*30d28f20Sjmcneill /* $NetBSD: acpi_cpu_pstate.c,v 1.54 2020/12/07 10:57:41 jmcneill Exp $ */
253e8f6c9Sjruoho 
353e8f6c9Sjruoho /*-
497b3ad9cSjruoho  * Copyright (c) 2010, 2011 Jukka Ruohonen <jruohonen@iki.fi>
553e8f6c9Sjruoho  * All rights reserved.
653e8f6c9Sjruoho  *
753e8f6c9Sjruoho  * Redistribution and use in source and binary forms, with or without
853e8f6c9Sjruoho  * modification, are permitted provided that the following conditions
953e8f6c9Sjruoho  * are met:
1053e8f6c9Sjruoho  *
1153e8f6c9Sjruoho  * 1. Redistributions of source code must retain the above copyright
1253e8f6c9Sjruoho  *    notice, this list of conditions and the following disclaimer.
1353e8f6c9Sjruoho  * 2. Redistributions in binary form must reproduce the above copyright
1453e8f6c9Sjruoho  *    notice, this list of conditions and the following disclaimer in the
1553e8f6c9Sjruoho  *    documentation and/or other materials provided with the distribution.
1653e8f6c9Sjruoho  *
1753e8f6c9Sjruoho  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1853e8f6c9Sjruoho  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1953e8f6c9Sjruoho  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2053e8f6c9Sjruoho  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2153e8f6c9Sjruoho  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2253e8f6c9Sjruoho  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2353e8f6c9Sjruoho  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2453e8f6c9Sjruoho  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2553e8f6c9Sjruoho  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2653e8f6c9Sjruoho  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2753e8f6c9Sjruoho  * SUCH DAMAGE.
2853e8f6c9Sjruoho  */
2953e8f6c9Sjruoho #include <sys/cdefs.h>
30*30d28f20Sjmcneill __KERNEL_RCSID(0, "$NetBSD: acpi_cpu_pstate.c,v 1.54 2020/12/07 10:57:41 jmcneill Exp $");
3153e8f6c9Sjruoho 
3253e8f6c9Sjruoho #include <sys/param.h>
33804fdee3Sjruoho #include <sys/cpufreq.h>
34*30d28f20Sjmcneill #include <sys/cpu.h>
3553e8f6c9Sjruoho #include <sys/kmem.h>
3653e8f6c9Sjruoho 
3753e8f6c9Sjruoho #include <dev/acpi/acpireg.h>
3853e8f6c9Sjruoho #include <dev/acpi/acpivar.h>
3953e8f6c9Sjruoho #include <dev/acpi/acpi_cpu.h>
4053e8f6c9Sjruoho 
4153e8f6c9Sjruoho #define _COMPONENT	 ACPI_BUS_COMPONENT
4253e8f6c9Sjruoho ACPI_MODULE_NAME	 ("acpi_cpu_pstate")
4353e8f6c9Sjruoho 
446c06255cSjruoho static ACPI_STATUS	 acpicpu_pstate_pss(struct acpicpu_softc *);
4553e8f6c9Sjruoho static ACPI_STATUS	 acpicpu_pstate_pss_add(struct acpicpu_pstate *,
4653e8f6c9Sjruoho 						ACPI_OBJECT *);
476c06255cSjruoho static ACPI_STATUS	 acpicpu_pstate_xpss(struct acpicpu_softc *);
486c06255cSjruoho static ACPI_STATUS	 acpicpu_pstate_xpss_add(struct acpicpu_pstate *,
496c06255cSjruoho 						 ACPI_OBJECT *);
5053e8f6c9Sjruoho static ACPI_STATUS	 acpicpu_pstate_pct(struct acpicpu_softc *);
51a080ec14Sjruoho static ACPI_STATUS	 acpicpu_pstate_dep(struct acpicpu_softc *);
5253e8f6c9Sjruoho static int		 acpicpu_pstate_max(struct acpicpu_softc *);
53e70b5284Sjruoho static int		 acpicpu_pstate_min(struct acpicpu_softc *);
5453e8f6c9Sjruoho static void		 acpicpu_pstate_change(struct acpicpu_softc *);
5599f8ada8Sjruoho static void		 acpicpu_pstate_reset(struct acpicpu_softc *);
5653e8f6c9Sjruoho static void		 acpicpu_pstate_bios(void);
5753e8f6c9Sjruoho 
58c9111546Sjruoho extern struct acpicpu_softc **acpicpu_sc;
59c1f0324cSjruoho 
6053e8f6c9Sjruoho void
acpicpu_pstate_attach(device_t self)6153e8f6c9Sjruoho acpicpu_pstate_attach(device_t self)
6253e8f6c9Sjruoho {
6353e8f6c9Sjruoho 	struct acpicpu_softc *sc = device_private(self);
645520795aSjruoho 	const char *str;
65e70b5284Sjruoho 	ACPI_HANDLE tmp;
6653e8f6c9Sjruoho 	ACPI_STATUS rv;
6753e8f6c9Sjruoho 
6853e8f6c9Sjruoho 	rv = acpicpu_pstate_pss(sc);
6953e8f6c9Sjruoho 
705520795aSjruoho 	if (ACPI_FAILURE(rv)) {
715520795aSjruoho 		str = "_PSS";
725520795aSjruoho 		goto fail;
735520795aSjruoho 	}
7453e8f6c9Sjruoho 
756c06255cSjruoho 	/*
76512cae94Sjruoho 	 * Append additional information from the extended _PSS,
77512cae94Sjruoho 	 * if available. Note that XPSS can not be used on Intel
78512cae94Sjruoho 	 * systems that use either _PDC or _OSC. From the XPSS
79512cae94Sjruoho 	 * method specification:
80512cae94Sjruoho 	 *
81512cae94Sjruoho 	 *   "The platform must not require the use of the
82512cae94Sjruoho 	 *    optional _PDC or _OSC methods to coordinate
83512cae94Sjruoho 	 *    between the operating system and firmware for
84512cae94Sjruoho 	 *    the purposes of enabling specific processor
85512cae94Sjruoho 	 *    power management features or implementations."
866c06255cSjruoho 	 */
876c06255cSjruoho 	if (sc->sc_cap == 0) {
88a4915ab4Sjruoho 
896c06255cSjruoho 		rv = acpicpu_pstate_xpss(sc);
906c06255cSjruoho 
916c06255cSjruoho 		if (ACPI_SUCCESS(rv))
926c06255cSjruoho 			sc->sc_flags |= ACPICPU_FLAG_P_XPSS;
936c06255cSjruoho 	}
946c06255cSjruoho 
9553e8f6c9Sjruoho 	rv = acpicpu_pstate_pct(sc);
9653e8f6c9Sjruoho 
975520795aSjruoho 	if (ACPI_FAILURE(rv)) {
985520795aSjruoho 		str = "_PCT";
995520795aSjruoho 		goto fail;
1005520795aSjruoho 	}
10153e8f6c9Sjruoho 
1022510a0e2Sjruoho 	/*
1032510a0e2Sjruoho 	 * The ACPI 3.0 and 4.0 specifications mandate three
1042510a0e2Sjruoho 	 * objects for P-states: _PSS, _PCT, and _PPC. A less
1052510a0e2Sjruoho 	 * strict wording is however used in the earlier 2.0
1062510a0e2Sjruoho 	 * standard, and some systems conforming to ACPI 2.0
1072510a0e2Sjruoho 	 * do not have _PPC, the method for dynamic maximum.
1082510a0e2Sjruoho 	 */
109e70b5284Sjruoho 	rv = AcpiGetHandle(sc->sc_node->ad_handle, "_PPC", &tmp);
110e70b5284Sjruoho 
111e70b5284Sjruoho 	if (ACPI_FAILURE(rv))
112e70b5284Sjruoho 		aprint_debug_dev(self, "_PPC missing\n");
113e70b5284Sjruoho 
1148116cea3Sjruoho 	/*
1156315c7d6Sjruoho 	 * Carry out MD initialization.
1168116cea3Sjruoho 	 */
1176315c7d6Sjruoho 	rv = acpicpu_md_pstate_init(sc);
1188116cea3Sjruoho 
1198116cea3Sjruoho 	if (rv != 0) {
1208116cea3Sjruoho 		rv = AE_SUPPORT;
1218116cea3Sjruoho 		goto fail;
1228116cea3Sjruoho 	}
1238116cea3Sjruoho 
124a080ec14Sjruoho 	/*
125a080ec14Sjruoho 	 * Query the optional _PSD.
126a080ec14Sjruoho 	 */
127a080ec14Sjruoho 	rv = acpicpu_pstate_dep(sc);
128a080ec14Sjruoho 
129a080ec14Sjruoho 	if (ACPI_SUCCESS(rv))
130a080ec14Sjruoho 		sc->sc_flags |= ACPICPU_FLAG_P_DEP;
131a080ec14Sjruoho 
132804fdee3Sjruoho 	sc->sc_pstate_current = 0;
13353e8f6c9Sjruoho 	sc->sc_flags |= ACPICPU_FLAG_P;
13453e8f6c9Sjruoho 
13553e8f6c9Sjruoho 	acpicpu_pstate_bios();
13699f8ada8Sjruoho 	acpicpu_pstate_reset(sc);
1375520795aSjruoho 
1385520795aSjruoho 	return;
1395520795aSjruoho 
1405520795aSjruoho fail:
1416b9ff107Sjruoho 	switch (rv) {
1426b9ff107Sjruoho 
1436b9ff107Sjruoho 	case AE_NOT_FOUND:
1446b9ff107Sjruoho 		return;
1456b9ff107Sjruoho 
1466b9ff107Sjruoho 	case AE_SUPPORT:
147512cae94Sjruoho 		aprint_verbose_dev(self, "P-states not supported\n");
1486b9ff107Sjruoho 		return;
1496b9ff107Sjruoho 
1506b9ff107Sjruoho 	default:
151512cae94Sjruoho 		aprint_error_dev(self, "failed to evaluate "
1525520795aSjruoho 		    "%s: %s\n", str, AcpiFormatException(rv));
15353e8f6c9Sjruoho 	}
1546b9ff107Sjruoho }
15553e8f6c9Sjruoho 
1564fb79b4eSjruoho void
acpicpu_pstate_detach(device_t self)15753e8f6c9Sjruoho acpicpu_pstate_detach(device_t self)
15853e8f6c9Sjruoho {
15953e8f6c9Sjruoho 	struct acpicpu_softc *sc = device_private(self);
16053e8f6c9Sjruoho 	size_t size;
16153e8f6c9Sjruoho 
16253e8f6c9Sjruoho 	if ((sc->sc_flags & ACPICPU_FLAG_P) == 0)
1634fb79b4eSjruoho 		return;
16453e8f6c9Sjruoho 
1654fb79b4eSjruoho 	(void)acpicpu_md_pstate_stop();
16653e8f6c9Sjruoho 
16753e8f6c9Sjruoho 	size = sc->sc_pstate_count * sizeof(*sc->sc_pstate);
16853e8f6c9Sjruoho 
16953e8f6c9Sjruoho 	if (sc->sc_pstate != NULL)
17053e8f6c9Sjruoho 		kmem_free(sc->sc_pstate, size);
17153e8f6c9Sjruoho 
17253e8f6c9Sjruoho 	sc->sc_flags &= ~ACPICPU_FLAG_P;
17353e8f6c9Sjruoho }
17453e8f6c9Sjruoho 
1752510a0e2Sjruoho void
acpicpu_pstate_start(device_t self)17653e8f6c9Sjruoho acpicpu_pstate_start(device_t self)
17753e8f6c9Sjruoho {
17853e8f6c9Sjruoho 	struct acpicpu_softc *sc = device_private(self);
17953e8f6c9Sjruoho 
180804fdee3Sjruoho 	if (acpicpu_md_pstate_start(sc) == 0)
1812510a0e2Sjruoho 		return;
1822510a0e2Sjruoho 
1832510a0e2Sjruoho 	sc->sc_flags &= ~ACPICPU_FLAG_P;
184804fdee3Sjruoho 	aprint_error_dev(self, "failed to start P-states\n");
18553e8f6c9Sjruoho }
18653e8f6c9Sjruoho 
1878e61d414Sjruoho void
acpicpu_pstate_suspend(void * aux)1888e61d414Sjruoho acpicpu_pstate_suspend(void *aux)
18953e8f6c9Sjruoho {
1908e61d414Sjruoho 	struct acpicpu_softc *sc;
1918e61d414Sjruoho 	device_t self = aux;
1928e61d414Sjruoho 
1939567c7d5Sjruoho 	/*
1949567c7d5Sjruoho 	 * Reset any dynamic limits.
1959567c7d5Sjruoho 	 */
196804fdee3Sjruoho 	sc = device_private(self);
1976846d4aaSjruoho 	mutex_enter(&sc->sc_mtx);
19899f8ada8Sjruoho 	acpicpu_pstate_reset(sc);
1999567c7d5Sjruoho 	mutex_exit(&sc->sc_mtx);
20053e8f6c9Sjruoho }
20153e8f6c9Sjruoho 
2028e61d414Sjruoho void
acpicpu_pstate_resume(void * aux)2038e61d414Sjruoho acpicpu_pstate_resume(void *aux)
20453e8f6c9Sjruoho {
205804fdee3Sjruoho 	/* Nothing. */
20653e8f6c9Sjruoho }
20753e8f6c9Sjruoho 
20853e8f6c9Sjruoho void
acpicpu_pstate_callback(void * aux)20953e8f6c9Sjruoho acpicpu_pstate_callback(void *aux)
21053e8f6c9Sjruoho {
21153e8f6c9Sjruoho 	struct acpicpu_softc *sc;
21253e8f6c9Sjruoho 	device_t self = aux;
21378ce31faSjruoho 	uint32_t freq;
21453e8f6c9Sjruoho 
21553e8f6c9Sjruoho 	sc = device_private(self);
21653e8f6c9Sjruoho 	mutex_enter(&sc->sc_mtx);
21753e8f6c9Sjruoho 	acpicpu_pstate_change(sc);
218a5a73b2aSjruoho 
21978ce31faSjruoho 	freq = sc->sc_pstate[sc->sc_pstate_max].ps_freq;
22078ce31faSjruoho 
22178ce31faSjruoho 	if (sc->sc_pstate_saved == 0)
22278ce31faSjruoho 		sc->sc_pstate_saved = sc->sc_pstate_current;
22378ce31faSjruoho 
22478ce31faSjruoho 	if (sc->sc_pstate_saved <= freq) {
22578ce31faSjruoho 		freq = sc->sc_pstate_saved;
22678ce31faSjruoho 		sc->sc_pstate_saved = 0;
227a5a73b2aSjruoho 	}
228a5a73b2aSjruoho 
22953e8f6c9Sjruoho 	mutex_exit(&sc->sc_mtx);
230804fdee3Sjruoho 	cpufreq_set(sc->sc_ci, freq);
231c4e35077Sjruoho }
23253e8f6c9Sjruoho 
233804fdee3Sjruoho static ACPI_STATUS
acpicpu_pstate_pss(struct acpicpu_softc * sc)23453e8f6c9Sjruoho acpicpu_pstate_pss(struct acpicpu_softc *sc)
23553e8f6c9Sjruoho {
23653e8f6c9Sjruoho 	struct acpicpu_pstate *ps;
23753e8f6c9Sjruoho 	ACPI_OBJECT *obj;
23853e8f6c9Sjruoho 	ACPI_BUFFER buf;
23953e8f6c9Sjruoho 	ACPI_STATUS rv;
24053e8f6c9Sjruoho 	uint32_t count;
24153e8f6c9Sjruoho 	uint32_t i, j;
24253e8f6c9Sjruoho 
24353e8f6c9Sjruoho 	rv = acpi_eval_struct(sc->sc_node->ad_handle, "_PSS", &buf);
24453e8f6c9Sjruoho 
24553e8f6c9Sjruoho 	if (ACPI_FAILURE(rv))
24653e8f6c9Sjruoho 		return rv;
24753e8f6c9Sjruoho 
24853e8f6c9Sjruoho 	obj = buf.Pointer;
24953e8f6c9Sjruoho 
25053e8f6c9Sjruoho 	if (obj->Type != ACPI_TYPE_PACKAGE) {
25153e8f6c9Sjruoho 		rv = AE_TYPE;
25253e8f6c9Sjruoho 		goto out;
25353e8f6c9Sjruoho 	}
25453e8f6c9Sjruoho 
25553e8f6c9Sjruoho 	sc->sc_pstate_count = obj->Package.Count;
25653e8f6c9Sjruoho 
25753e8f6c9Sjruoho 	if (sc->sc_pstate_count == 0) {
25853e8f6c9Sjruoho 		rv = AE_NOT_EXIST;
25953e8f6c9Sjruoho 		goto out;
26053e8f6c9Sjruoho 	}
26153e8f6c9Sjruoho 
2620963b3f6Sjruoho 	if (sc->sc_pstate_count > ACPICPU_P_STATE_MAX) {
26353e8f6c9Sjruoho 		rv = AE_LIMIT;
26453e8f6c9Sjruoho 		goto out;
26553e8f6c9Sjruoho 	}
26653e8f6c9Sjruoho 
26753e8f6c9Sjruoho 	sc->sc_pstate = kmem_zalloc(sc->sc_pstate_count *
26853e8f6c9Sjruoho 	    sizeof(struct acpicpu_pstate), KM_SLEEP);
26953e8f6c9Sjruoho 
27053e8f6c9Sjruoho 	if (sc->sc_pstate == NULL) {
27153e8f6c9Sjruoho 		rv = AE_NO_MEMORY;
27253e8f6c9Sjruoho 		goto out;
27353e8f6c9Sjruoho 	}
27453e8f6c9Sjruoho 
27553e8f6c9Sjruoho 	for (count = i = 0; i < sc->sc_pstate_count; i++) {
27653e8f6c9Sjruoho 
27753e8f6c9Sjruoho 		ps = &sc->sc_pstate[i];
27853e8f6c9Sjruoho 		rv = acpicpu_pstate_pss_add(ps, &obj->Package.Elements[i]);
27953e8f6c9Sjruoho 
280aa6fdddcSjruoho 		if (ACPI_FAILURE(rv)) {
281512cae94Sjruoho 			aprint_error_dev(sc->sc_dev, "failed to add "
282512cae94Sjruoho 			    "P-state: %s\n", AcpiFormatException(rv));
283aa6fdddcSjruoho 			ps->ps_freq = 0;
28453e8f6c9Sjruoho 			continue;
285aa6fdddcSjruoho 		}
28653e8f6c9Sjruoho 
28753e8f6c9Sjruoho 		for (j = 0; j < i; j++) {
28853e8f6c9Sjruoho 
28953e8f6c9Sjruoho 			if (ps->ps_freq >= sc->sc_pstate[j].ps_freq) {
29053e8f6c9Sjruoho 				ps->ps_freq = 0;
29153e8f6c9Sjruoho 				break;
29253e8f6c9Sjruoho 			}
29353e8f6c9Sjruoho 		}
29453e8f6c9Sjruoho 
29553e8f6c9Sjruoho 		if (ps->ps_freq != 0)
29653e8f6c9Sjruoho 			count++;
29753e8f6c9Sjruoho 	}
29853e8f6c9Sjruoho 
29953e8f6c9Sjruoho 	rv = (count != 0) ? AE_OK : AE_NOT_EXIST;
30053e8f6c9Sjruoho 
30153e8f6c9Sjruoho out:
30253e8f6c9Sjruoho 	if (buf.Pointer != NULL)
30353e8f6c9Sjruoho 		ACPI_FREE(buf.Pointer);
30453e8f6c9Sjruoho 
30553e8f6c9Sjruoho 	return rv;
30653e8f6c9Sjruoho }
30753e8f6c9Sjruoho 
30853e8f6c9Sjruoho static ACPI_STATUS
acpicpu_pstate_pss_add(struct acpicpu_pstate * ps,ACPI_OBJECT * obj)30953e8f6c9Sjruoho acpicpu_pstate_pss_add(struct acpicpu_pstate *ps, ACPI_OBJECT *obj)
31053e8f6c9Sjruoho {
31153e8f6c9Sjruoho 	ACPI_OBJECT *elm;
31253e8f6c9Sjruoho 	int i;
31353e8f6c9Sjruoho 
31453e8f6c9Sjruoho 	if (obj->Type != ACPI_TYPE_PACKAGE)
31553e8f6c9Sjruoho 		return AE_TYPE;
31653e8f6c9Sjruoho 
31753e8f6c9Sjruoho 	if (obj->Package.Count != 6)
31853e8f6c9Sjruoho 		return AE_BAD_DATA;
31953e8f6c9Sjruoho 
32053e8f6c9Sjruoho 	elm = obj->Package.Elements;
32153e8f6c9Sjruoho 
32253e8f6c9Sjruoho 	for (i = 0; i < 6; i++) {
32353e8f6c9Sjruoho 
32453e8f6c9Sjruoho 		if (elm[i].Type != ACPI_TYPE_INTEGER)
32553e8f6c9Sjruoho 			return AE_TYPE;
32653e8f6c9Sjruoho 
32753e8f6c9Sjruoho 		if (elm[i].Integer.Value > UINT32_MAX)
32853e8f6c9Sjruoho 			return AE_AML_NUMERIC_OVERFLOW;
32953e8f6c9Sjruoho 	}
33053e8f6c9Sjruoho 
3316c06255cSjruoho 	ps->ps_freq       = elm[0].Integer.Value;
3326c06255cSjruoho 	ps->ps_power      = elm[1].Integer.Value;
3336c06255cSjruoho 	ps->ps_latency    = elm[2].Integer.Value;
3346c06255cSjruoho 	ps->ps_latency_bm = elm[3].Integer.Value;
3356c06255cSjruoho 	ps->ps_control    = elm[4].Integer.Value;
3366c06255cSjruoho 	ps->ps_status     = elm[5].Integer.Value;
33753e8f6c9Sjruoho 
338aa6fdddcSjruoho 	if (ps->ps_freq == 0 || ps->ps_freq > 9999)
339aa6fdddcSjruoho 		return AE_BAD_DECIMAL_CONSTANT;
340aa6fdddcSjruoho 
341f7eabaaeSjruoho 	/*
342f7eabaaeSjruoho 	 * Sanity check also the latency levels. Some systems may
343f7eabaaeSjruoho 	 * report a value zero, but we keep one microsecond as the
344f7eabaaeSjruoho 	 * lower bound; see for instance AMD family 12h,
345f7eabaaeSjruoho 	 *
346f7eabaaeSjruoho 	 *	Advanced Micro Devices: BIOS and Kernel Developer's
347f7eabaaeSjruoho 	 *	Guide (BKDG) for AMD Family 12h Processors. Section
348f7eabaaeSjruoho 	 *	2.5.3.1.9.2, Revision 3.02, October, 2011.
349f7eabaaeSjruoho 	 */
350bf04997fSjruoho 	if (ps->ps_latency == 0 || ps->ps_latency > 1000)
351bf04997fSjruoho 		ps->ps_latency = 1;
35253e8f6c9Sjruoho 
35353e8f6c9Sjruoho 	return AE_OK;
35453e8f6c9Sjruoho }
35553e8f6c9Sjruoho 
3566c06255cSjruoho static ACPI_STATUS
acpicpu_pstate_xpss(struct acpicpu_softc * sc)3576c06255cSjruoho acpicpu_pstate_xpss(struct acpicpu_softc *sc)
3586c06255cSjruoho {
359a4915ab4Sjruoho 	struct acpicpu_pstate *ps;
3606c06255cSjruoho 	ACPI_OBJECT *obj;
3616c06255cSjruoho 	ACPI_BUFFER buf;
3626c06255cSjruoho 	ACPI_STATUS rv;
363a4915ab4Sjruoho 	uint32_t i = 0;
3646c06255cSjruoho 
3656c06255cSjruoho 	rv = acpi_eval_struct(sc->sc_node->ad_handle, "XPSS", &buf);
3666c06255cSjruoho 
3676c06255cSjruoho 	if (ACPI_FAILURE(rv))
368512cae94Sjruoho 		goto out;
3696c06255cSjruoho 
3706c06255cSjruoho 	obj = buf.Pointer;
3716c06255cSjruoho 
3726c06255cSjruoho 	if (obj->Type != ACPI_TYPE_PACKAGE) {
3736c06255cSjruoho 		rv = AE_TYPE;
3746c06255cSjruoho 		goto out;
3756c06255cSjruoho 	}
3766c06255cSjruoho 
377a4915ab4Sjruoho 	if (obj->Package.Count != sc->sc_pstate_count) {
3786c06255cSjruoho 		rv = AE_LIMIT;
3796c06255cSjruoho 		goto out;
3806c06255cSjruoho 	}
3816c06255cSjruoho 
382a4915ab4Sjruoho 	while (i < sc->sc_pstate_count) {
3836c06255cSjruoho 
384a4915ab4Sjruoho 		ps = &sc->sc_pstate[i];
385a4915ab4Sjruoho 		acpicpu_pstate_xpss_add(ps, &obj->Package.Elements[i]);
3866c06255cSjruoho 
387a4915ab4Sjruoho 		i++;
3880b3fb1feSjmcneill 	}
3896c06255cSjruoho 
3906c06255cSjruoho out:
391512cae94Sjruoho 	if (ACPI_FAILURE(rv) && rv != AE_NOT_FOUND)
392512cae94Sjruoho 		aprint_error_dev(sc->sc_dev, "failed to evaluate "
393512cae94Sjruoho 		    "XPSS: %s\n", AcpiFormatException(rv));
394512cae94Sjruoho 
3956c06255cSjruoho 	if (buf.Pointer != NULL)
3966c06255cSjruoho 		ACPI_FREE(buf.Pointer);
3976c06255cSjruoho 
3986c06255cSjruoho 	return rv;
3996c06255cSjruoho }
4006c06255cSjruoho 
4016c06255cSjruoho static ACPI_STATUS
acpicpu_pstate_xpss_add(struct acpicpu_pstate * ps,ACPI_OBJECT * obj)4026c06255cSjruoho acpicpu_pstate_xpss_add(struct acpicpu_pstate *ps, ACPI_OBJECT *obj)
4036c06255cSjruoho {
4046c06255cSjruoho 	ACPI_OBJECT *elm;
4056c06255cSjruoho 	int i;
4066c06255cSjruoho 
4076c06255cSjruoho 	if (obj->Type != ACPI_TYPE_PACKAGE)
4086c06255cSjruoho 		return AE_TYPE;
4096c06255cSjruoho 
4106c06255cSjruoho 	if (obj->Package.Count != 8)
4116c06255cSjruoho 		return AE_BAD_DATA;
4126c06255cSjruoho 
4136c06255cSjruoho 	elm = obj->Package.Elements;
4146c06255cSjruoho 
4156c06255cSjruoho 	for (i = 0; i < 4; i++) {
4166c06255cSjruoho 
4176c06255cSjruoho 		if (elm[i].Type != ACPI_TYPE_INTEGER)
4186c06255cSjruoho 			return AE_TYPE;
4196c06255cSjruoho 
4206c06255cSjruoho 		if (elm[i].Integer.Value > UINT32_MAX)
4216c06255cSjruoho 			return AE_AML_NUMERIC_OVERFLOW;
4226c06255cSjruoho 	}
4236c06255cSjruoho 
4246c06255cSjruoho 	for (; i < 8; i++) {
4256c06255cSjruoho 
4266c06255cSjruoho 		if (elm[i].Type != ACPI_TYPE_BUFFER)
4276c06255cSjruoho 			return AE_TYPE;
4286c06255cSjruoho 
4290b3fb1feSjmcneill 		if (elm[i].Buffer.Length != 8)
4306c06255cSjruoho 			return AE_LIMIT;
4316c06255cSjruoho 	}
4326c06255cSjruoho 
433a4915ab4Sjruoho 	/*
434a4915ab4Sjruoho 	 * Only overwrite the elements that were
435a4915ab4Sjruoho 	 * not available from the conventional _PSS.
436a4915ab4Sjruoho 	 */
437a4915ab4Sjruoho 	if (ps->ps_freq == 0)
4386c06255cSjruoho 		ps->ps_freq = elm[0].Integer.Value;
439a4915ab4Sjruoho 
440a4915ab4Sjruoho 	if (ps->ps_power == 0)
4416c06255cSjruoho 		ps->ps_power = elm[1].Integer.Value;
442a4915ab4Sjruoho 
443a4915ab4Sjruoho 	if (ps->ps_latency == 0)
4446c06255cSjruoho 		ps->ps_latency = elm[2].Integer.Value;
445a4915ab4Sjruoho 
446a4915ab4Sjruoho 	if (ps->ps_latency_bm == 0)
4476c06255cSjruoho 		ps->ps_latency_bm = elm[3].Integer.Value;
4486c06255cSjruoho 
449a4915ab4Sjruoho 	if (ps->ps_control == 0)
4500b3fb1feSjmcneill 		ps->ps_control = ACPI_GET64(elm[4].Buffer.Pointer);
451a4915ab4Sjruoho 
452a4915ab4Sjruoho 	if (ps->ps_status == 0)
4530b3fb1feSjmcneill 		ps->ps_status = ACPI_GET64(elm[5].Buffer.Pointer);
454a4915ab4Sjruoho 
455a4915ab4Sjruoho 	if (ps->ps_control_mask == 0)
4560b3fb1feSjmcneill 		ps->ps_control_mask = ACPI_GET64(elm[6].Buffer.Pointer);
457a4915ab4Sjruoho 
458a4915ab4Sjruoho 	if (ps->ps_status_mask == 0)
4590b3fb1feSjmcneill 		ps->ps_status_mask = ACPI_GET64(elm[7].Buffer.Pointer);
4606c06255cSjruoho 
4616c06255cSjruoho 	ps->ps_flags |= ACPICPU_FLAG_P_XPSS;
4626c06255cSjruoho 
463bf04997fSjruoho 	if (ps->ps_freq == 0 || ps->ps_freq > 9999)
464a4915ab4Sjruoho 		return AE_BAD_DECIMAL_CONSTANT;
465a4915ab4Sjruoho 
466bf04997fSjruoho 	if (ps->ps_latency == 0 || ps->ps_latency > 1000)
467bf04997fSjruoho 		ps->ps_latency = 1;
468bf04997fSjruoho 
4696c06255cSjruoho 	return AE_OK;
4706c06255cSjruoho }
4716c06255cSjruoho 
472804fdee3Sjruoho static ACPI_STATUS
acpicpu_pstate_pct(struct acpicpu_softc * sc)47353e8f6c9Sjruoho acpicpu_pstate_pct(struct acpicpu_softc *sc)
47453e8f6c9Sjruoho {
47553e8f6c9Sjruoho 	static const size_t size = sizeof(struct acpicpu_reg);
47653e8f6c9Sjruoho 	struct acpicpu_reg *reg[2];
4776c06255cSjruoho 	struct acpicpu_pstate *ps;
47853e8f6c9Sjruoho 	ACPI_OBJECT *elm, *obj;
47953e8f6c9Sjruoho 	ACPI_BUFFER buf;
48053e8f6c9Sjruoho 	ACPI_STATUS rv;
48153e8f6c9Sjruoho 	uint8_t width;
4826c06255cSjruoho 	uint32_t i;
48353e8f6c9Sjruoho 
48453e8f6c9Sjruoho 	rv = acpi_eval_struct(sc->sc_node->ad_handle, "_PCT", &buf);
48553e8f6c9Sjruoho 
48653e8f6c9Sjruoho 	if (ACPI_FAILURE(rv))
48753e8f6c9Sjruoho 		return rv;
48853e8f6c9Sjruoho 
48953e8f6c9Sjruoho 	obj = buf.Pointer;
49053e8f6c9Sjruoho 
49153e8f6c9Sjruoho 	if (obj->Type != ACPI_TYPE_PACKAGE) {
49253e8f6c9Sjruoho 		rv = AE_TYPE;
49353e8f6c9Sjruoho 		goto out;
49453e8f6c9Sjruoho 	}
49553e8f6c9Sjruoho 
49653e8f6c9Sjruoho 	if (obj->Package.Count != 2) {
49753e8f6c9Sjruoho 		rv = AE_LIMIT;
49853e8f6c9Sjruoho 		goto out;
49953e8f6c9Sjruoho 	}
50053e8f6c9Sjruoho 
50153e8f6c9Sjruoho 	for (i = 0; i < 2; i++) {
50253e8f6c9Sjruoho 
50353e8f6c9Sjruoho 		elm = &obj->Package.Elements[i];
50453e8f6c9Sjruoho 
50553e8f6c9Sjruoho 		if (elm->Type != ACPI_TYPE_BUFFER) {
50653e8f6c9Sjruoho 			rv = AE_TYPE;
50753e8f6c9Sjruoho 			goto out;
50853e8f6c9Sjruoho 		}
50953e8f6c9Sjruoho 
51053e8f6c9Sjruoho 		if (size > elm->Buffer.Length) {
51153e8f6c9Sjruoho 			rv = AE_AML_BAD_RESOURCE_LENGTH;
51253e8f6c9Sjruoho 			goto out;
51353e8f6c9Sjruoho 		}
51453e8f6c9Sjruoho 
51553e8f6c9Sjruoho 		reg[i] = (struct acpicpu_reg *)elm->Buffer.Pointer;
51653e8f6c9Sjruoho 
51753e8f6c9Sjruoho 		switch (reg[i]->reg_spaceid) {
51853e8f6c9Sjruoho 
519*30d28f20Sjmcneill 		case ACPI_ADR_SPACE_SYSTEM_MEMORY:
52053e8f6c9Sjruoho 		case ACPI_ADR_SPACE_SYSTEM_IO:
52153e8f6c9Sjruoho 
52253e8f6c9Sjruoho 			if (reg[i]->reg_addr == 0) {
52353e8f6c9Sjruoho 				rv = AE_AML_ILLEGAL_ADDRESS;
52453e8f6c9Sjruoho 				goto out;
52553e8f6c9Sjruoho 			}
52653e8f6c9Sjruoho 
52753e8f6c9Sjruoho 			width = reg[i]->reg_bitwidth;
52853e8f6c9Sjruoho 
52933733d61Sjruoho 			if (width + reg[i]->reg_bitoffset > 32) {
53033733d61Sjruoho 				rv = AE_AML_BAD_RESOURCE_VALUE;
53133733d61Sjruoho 				goto out;
53233733d61Sjruoho 			}
53333733d61Sjruoho 
53453e8f6c9Sjruoho 			if (width != 8 && width != 16 && width != 32) {
535337dd649Sjruoho 				rv = AE_AML_BAD_RESOURCE_VALUE;
53653e8f6c9Sjruoho 				goto out;
53753e8f6c9Sjruoho 			}
53853e8f6c9Sjruoho 
53953e8f6c9Sjruoho 			break;
54053e8f6c9Sjruoho 
54153e8f6c9Sjruoho 		case ACPI_ADR_SPACE_FIXED_HARDWARE:
54253e8f6c9Sjruoho 
5436c06255cSjruoho 			if ((sc->sc_flags & ACPICPU_FLAG_P_XPSS) != 0) {
5446c06255cSjruoho 
5456c06255cSjruoho 				if (reg[i]->reg_bitwidth != 64) {
5466c06255cSjruoho 					rv = AE_AML_BAD_RESOURCE_VALUE;
5476c06255cSjruoho 					goto out;
5486c06255cSjruoho 				}
5496c06255cSjruoho 
5506c06255cSjruoho 				if (reg[i]->reg_bitoffset != 0) {
5516c06255cSjruoho 					rv = AE_AML_BAD_RESOURCE_VALUE;
5526c06255cSjruoho 					goto out;
5536c06255cSjruoho 				}
5546c06255cSjruoho 
5556c06255cSjruoho 				break;
5566c06255cSjruoho 			}
5576c06255cSjruoho 
55853e8f6c9Sjruoho 			if ((sc->sc_flags & ACPICPU_FLAG_P_FFH) == 0) {
559337dd649Sjruoho 				rv = AE_SUPPORT;
56053e8f6c9Sjruoho 				goto out;
56153e8f6c9Sjruoho 			}
56253e8f6c9Sjruoho 
56353e8f6c9Sjruoho 			break;
56453e8f6c9Sjruoho 
56553e8f6c9Sjruoho 		default:
56653e8f6c9Sjruoho 			rv = AE_AML_INVALID_SPACE_ID;
56753e8f6c9Sjruoho 			goto out;
56853e8f6c9Sjruoho 		}
56953e8f6c9Sjruoho 	}
57053e8f6c9Sjruoho 
57153e8f6c9Sjruoho 	if (reg[0]->reg_spaceid != reg[1]->reg_spaceid) {
57253e8f6c9Sjruoho 		rv = AE_AML_INVALID_SPACE_ID;
57353e8f6c9Sjruoho 		goto out;
57453e8f6c9Sjruoho 	}
57553e8f6c9Sjruoho 
5766b9ff107Sjruoho 	(void)memcpy(&sc->sc_pstate_control, reg[0], size);
5776b9ff107Sjruoho 	(void)memcpy(&sc->sc_pstate_status,  reg[1], size);
57853e8f6c9Sjruoho 
579804fdee3Sjruoho 	if ((sc->sc_flags & ACPICPU_FLAG_P_XPSS) != 0) {
580c4a7ea78Sjruoho 
581c4a7ea78Sjruoho 		/*
5826315c7d6Sjruoho 		 * At the very least, mandate that
5836315c7d6Sjruoho 		 * XPSS supplies the control address.
584c4a7ea78Sjruoho 		 */
585c4a7ea78Sjruoho 		if (sc->sc_pstate_control.reg_addr == 0) {
58681c69bbcSjmcneill 			rv = AE_AML_BAD_RESOURCE_LENGTH;
587c4a7ea78Sjruoho 			goto out;
588c4a7ea78Sjruoho 		}
589c4a7ea78Sjruoho 
5906c06255cSjruoho 		/*
591804fdee3Sjruoho 		 * If XPSS is present, copy the supplied
592804fdee3Sjruoho 		 * MSR addresses to the P-state structures.
5936c06255cSjruoho 		 */
5946c06255cSjruoho 		for (i = 0; i < sc->sc_pstate_count; i++) {
5956c06255cSjruoho 
5966c06255cSjruoho 			ps = &sc->sc_pstate[i];
5976c06255cSjruoho 
5986c06255cSjruoho 			if (ps->ps_freq == 0)
5996c06255cSjruoho 				continue;
6006c06255cSjruoho 
6016c06255cSjruoho 			ps->ps_status_addr  = sc->sc_pstate_status.reg_addr;
6026c06255cSjruoho 			ps->ps_control_addr = sc->sc_pstate_control.reg_addr;
6036c06255cSjruoho 		}
604804fdee3Sjruoho 	}
6056c06255cSjruoho 
60653e8f6c9Sjruoho out:
60753e8f6c9Sjruoho 	if (buf.Pointer != NULL)
60853e8f6c9Sjruoho 		ACPI_FREE(buf.Pointer);
60953e8f6c9Sjruoho 
61053e8f6c9Sjruoho 	return rv;
61153e8f6c9Sjruoho }
61253e8f6c9Sjruoho 
613a080ec14Sjruoho static ACPI_STATUS
acpicpu_pstate_dep(struct acpicpu_softc * sc)614a080ec14Sjruoho acpicpu_pstate_dep(struct acpicpu_softc *sc)
615a080ec14Sjruoho {
616a080ec14Sjruoho 	ACPI_OBJECT *elm, *obj;
617a080ec14Sjruoho 	ACPI_BUFFER buf;
618a080ec14Sjruoho 	ACPI_STATUS rv;
619a080ec14Sjruoho 	uint32_t val;
620a080ec14Sjruoho 	uint8_t i, n;
621a080ec14Sjruoho 
622a080ec14Sjruoho 	rv = acpi_eval_struct(sc->sc_node->ad_handle, "_PSD", &buf);
623a080ec14Sjruoho 
624a080ec14Sjruoho 	if (ACPI_FAILURE(rv))
625a080ec14Sjruoho 		goto out;
626a080ec14Sjruoho 
627a080ec14Sjruoho 	obj = buf.Pointer;
628a080ec14Sjruoho 
629a080ec14Sjruoho 	if (obj->Type != ACPI_TYPE_PACKAGE) {
630a080ec14Sjruoho 		rv = AE_TYPE;
631a080ec14Sjruoho 		goto out;
632a080ec14Sjruoho 	}
633a080ec14Sjruoho 
634a080ec14Sjruoho 	if (obj->Package.Count != 1) {
635a080ec14Sjruoho 		rv = AE_LIMIT;
636a080ec14Sjruoho 		goto out;
637a080ec14Sjruoho 	}
638a080ec14Sjruoho 
639a080ec14Sjruoho 	elm = &obj->Package.Elements[0];
640a080ec14Sjruoho 
641a080ec14Sjruoho 	if (obj->Type != ACPI_TYPE_PACKAGE) {
642a080ec14Sjruoho 		rv = AE_TYPE;
643a080ec14Sjruoho 		goto out;
644a080ec14Sjruoho 	}
645a080ec14Sjruoho 
646a080ec14Sjruoho 	n = elm->Package.Count;
647a080ec14Sjruoho 
648a080ec14Sjruoho 	if (n != 5) {
649a080ec14Sjruoho 		rv = AE_LIMIT;
650a080ec14Sjruoho 		goto out;
651a080ec14Sjruoho 	}
652a080ec14Sjruoho 
653a080ec14Sjruoho 	elm = elm->Package.Elements;
654a080ec14Sjruoho 
655a080ec14Sjruoho 	for (i = 0; i < n; i++) {
656a080ec14Sjruoho 
657a080ec14Sjruoho 		if (elm[i].Type != ACPI_TYPE_INTEGER) {
658a080ec14Sjruoho 			rv = AE_TYPE;
659a080ec14Sjruoho 			goto out;
660a080ec14Sjruoho 		}
661a080ec14Sjruoho 
662a080ec14Sjruoho 		if (elm[i].Integer.Value > UINT32_MAX) {
663a080ec14Sjruoho 			rv = AE_AML_NUMERIC_OVERFLOW;
664a080ec14Sjruoho 			goto out;
665a080ec14Sjruoho 		}
666a080ec14Sjruoho 	}
667a080ec14Sjruoho 
668a080ec14Sjruoho 	val = elm[1].Integer.Value;
669a080ec14Sjruoho 
670a080ec14Sjruoho 	if (val != 0)
671a080ec14Sjruoho 		aprint_debug_dev(sc->sc_dev, "invalid revision in _PSD\n");
672a080ec14Sjruoho 
673a080ec14Sjruoho 	val = elm[3].Integer.Value;
674a080ec14Sjruoho 
675a080ec14Sjruoho 	if (val < ACPICPU_DEP_SW_ALL || val > ACPICPU_DEP_HW_ALL) {
676a080ec14Sjruoho 		rv = AE_AML_BAD_RESOURCE_VALUE;
677a080ec14Sjruoho 		goto out;
678a080ec14Sjruoho 	}
679a080ec14Sjruoho 
680a080ec14Sjruoho 	val = elm[4].Integer.Value;
681a080ec14Sjruoho 
682a080ec14Sjruoho 	if (val > sc->sc_ncpus) {
683a080ec14Sjruoho 		rv = AE_BAD_VALUE;
684a080ec14Sjruoho 		goto out;
685a080ec14Sjruoho 	}
686a080ec14Sjruoho 
687a080ec14Sjruoho 	sc->sc_pstate_dep.dep_domain = elm[2].Integer.Value;
688a080ec14Sjruoho 	sc->sc_pstate_dep.dep_type   = elm[3].Integer.Value;
689a080ec14Sjruoho 	sc->sc_pstate_dep.dep_ncpus  = elm[4].Integer.Value;
690a080ec14Sjruoho 
691a080ec14Sjruoho out:
692a080ec14Sjruoho 	if (ACPI_FAILURE(rv) && rv != AE_NOT_FOUND)
693a080ec14Sjruoho 		aprint_debug_dev(sc->sc_dev, "failed to evaluate "
694a080ec14Sjruoho 		    "_PSD: %s\n", AcpiFormatException(rv));
695a080ec14Sjruoho 
696a080ec14Sjruoho 	if (buf.Pointer != NULL)
697a080ec14Sjruoho 		ACPI_FREE(buf.Pointer);
698a080ec14Sjruoho 
699a080ec14Sjruoho 	return rv;
700a080ec14Sjruoho }
701a080ec14Sjruoho 
70253e8f6c9Sjruoho static int
acpicpu_pstate_max(struct acpicpu_softc * sc)70353e8f6c9Sjruoho acpicpu_pstate_max(struct acpicpu_softc *sc)
70453e8f6c9Sjruoho {
70553e8f6c9Sjruoho 	ACPI_INTEGER val;
70653e8f6c9Sjruoho 	ACPI_STATUS rv;
70753e8f6c9Sjruoho 
70853e8f6c9Sjruoho 	/*
70953e8f6c9Sjruoho 	 * Evaluate the currently highest P-state that can be used.
71053e8f6c9Sjruoho 	 * If available, we can use either this state or any lower
71153e8f6c9Sjruoho 	 * power (i.e. higher numbered) state from the _PSS object.
712e70b5284Sjruoho 	 * Note that the return value must match the _OST parameter.
71353e8f6c9Sjruoho 	 */
71453e8f6c9Sjruoho 	rv = acpi_eval_integer(sc->sc_node->ad_handle, "_PPC", &val);
71553e8f6c9Sjruoho 
716e70b5284Sjruoho 	if (ACPI_SUCCESS(rv) && val < sc->sc_pstate_count) {
71753e8f6c9Sjruoho 
718e70b5284Sjruoho 		if (sc->sc_pstate[val].ps_freq != 0) {
719e70b5284Sjruoho 			sc->sc_pstate_max = val;
720e70b5284Sjruoho 			return 0;
721e70b5284Sjruoho 		}
722e70b5284Sjruoho 	}
72353e8f6c9Sjruoho 
72453e8f6c9Sjruoho 	return 1;
725e70b5284Sjruoho }
726e70b5284Sjruoho 
727e70b5284Sjruoho static int
acpicpu_pstate_min(struct acpicpu_softc * sc)728e70b5284Sjruoho acpicpu_pstate_min(struct acpicpu_softc *sc)
729e70b5284Sjruoho {
730e70b5284Sjruoho 	ACPI_INTEGER val;
731e70b5284Sjruoho 	ACPI_STATUS rv;
732e70b5284Sjruoho 
733e70b5284Sjruoho 	/*
734e70b5284Sjruoho 	 * The _PDL object defines the minimum when passive cooling
735e70b5284Sjruoho 	 * is being performed. If available, we can use the returned
736e70b5284Sjruoho 	 * state or any higher power (i.e. lower numbered) state.
737e70b5284Sjruoho 	 */
738e70b5284Sjruoho 	rv = acpi_eval_integer(sc->sc_node->ad_handle, "_PDL", &val);
739e70b5284Sjruoho 
740e70b5284Sjruoho 	if (ACPI_SUCCESS(rv) && val < sc->sc_pstate_count) {
74153e8f6c9Sjruoho 
74253e8f6c9Sjruoho 		if (sc->sc_pstate[val].ps_freq == 0)
74353e8f6c9Sjruoho 			return 1;
74453e8f6c9Sjruoho 
745e70b5284Sjruoho 		if (val >= sc->sc_pstate_max) {
746e70b5284Sjruoho 			sc->sc_pstate_min = val;
74753e8f6c9Sjruoho 			return 0;
74853e8f6c9Sjruoho 		}
749e70b5284Sjruoho 	}
750e70b5284Sjruoho 
751e70b5284Sjruoho 	return 1;
752e70b5284Sjruoho }
75353e8f6c9Sjruoho 
75453e8f6c9Sjruoho static void
acpicpu_pstate_change(struct acpicpu_softc * sc)75553e8f6c9Sjruoho acpicpu_pstate_change(struct acpicpu_softc *sc)
75653e8f6c9Sjruoho {
757e70b5284Sjruoho 	static ACPI_STATUS rv = AE_OK;
75853e8f6c9Sjruoho 	ACPI_OBJECT_LIST arg;
75953e8f6c9Sjruoho 	ACPI_OBJECT obj[2];
760a5a73b2aSjruoho 	static int val = 0;
76153e8f6c9Sjruoho 
76299f8ada8Sjruoho 	acpicpu_pstate_reset(sc);
763e70b5284Sjruoho 
764a5a73b2aSjruoho 	/*
765a5a73b2aSjruoho 	 * Cache the checks as the optional
766a5a73b2aSjruoho 	 * _PDL and _OST are rarely present.
767a5a73b2aSjruoho 	 */
768a5a73b2aSjruoho 	if (val == 0)
769a5a73b2aSjruoho 		val = acpicpu_pstate_min(sc);
770a5a73b2aSjruoho 
77153e8f6c9Sjruoho 	arg.Count = 2;
77253e8f6c9Sjruoho 	arg.Pointer = obj;
77353e8f6c9Sjruoho 
77453e8f6c9Sjruoho 	obj[0].Type = ACPI_TYPE_INTEGER;
77553e8f6c9Sjruoho 	obj[1].Type = ACPI_TYPE_INTEGER;
77653e8f6c9Sjruoho 
77753e8f6c9Sjruoho 	obj[0].Integer.Value = ACPICPU_P_NOTIFY;
77853e8f6c9Sjruoho 	obj[1].Integer.Value = acpicpu_pstate_max(sc);
77953e8f6c9Sjruoho 
780e70b5284Sjruoho 	if (ACPI_FAILURE(rv))
781e70b5284Sjruoho 		return;
782e70b5284Sjruoho 
783e70b5284Sjruoho 	rv = AcpiEvaluateObject(sc->sc_node->ad_handle, "_OST", &arg, NULL);
78453e8f6c9Sjruoho }
78553e8f6c9Sjruoho 
78653e8f6c9Sjruoho static void
acpicpu_pstate_reset(struct acpicpu_softc * sc)78799f8ada8Sjruoho acpicpu_pstate_reset(struct acpicpu_softc *sc)
78899f8ada8Sjruoho {
78999f8ada8Sjruoho 
79099f8ada8Sjruoho 	sc->sc_pstate_max = 0;
79199f8ada8Sjruoho 	sc->sc_pstate_min = sc->sc_pstate_count - 1;
79299f8ada8Sjruoho 
79399f8ada8Sjruoho }
79499f8ada8Sjruoho 
79599f8ada8Sjruoho static void
acpicpu_pstate_bios(void)79653e8f6c9Sjruoho acpicpu_pstate_bios(void)
79753e8f6c9Sjruoho {
79853e8f6c9Sjruoho 	const uint8_t val = AcpiGbl_FADT.PstateControl;
79953e8f6c9Sjruoho 	const uint32_t addr = AcpiGbl_FADT.SmiCommand;
80053e8f6c9Sjruoho 
801e04e5594Sjruoho 	if (addr == 0 || val == 0)
80253e8f6c9Sjruoho 		return;
80353e8f6c9Sjruoho 
80453e8f6c9Sjruoho 	(void)AcpiOsWritePort(addr, val, 8);
80553e8f6c9Sjruoho }
80653e8f6c9Sjruoho 
807804fdee3Sjruoho void
acpicpu_pstate_get(void * aux,void * cpu_freq)808804fdee3Sjruoho acpicpu_pstate_get(void *aux, void *cpu_freq)
80953e8f6c9Sjruoho {
81053e8f6c9Sjruoho 	struct acpicpu_pstate *ps = NULL;
811804fdee3Sjruoho 	struct cpu_info *ci = curcpu();
812c9111546Sjruoho 	struct acpicpu_softc *sc;
813804fdee3Sjruoho 	uint32_t freq, i, val = 0;
81453e8f6c9Sjruoho 	int rv;
81553e8f6c9Sjruoho 
816c9111546Sjruoho 	sc = acpicpu_sc[ci->ci_acpiid];
817c9111546Sjruoho 
818c9111546Sjruoho 	if (__predict_false(sc == NULL)) {
819c9111546Sjruoho 		rv = ENXIO;
820c9111546Sjruoho 		goto fail;
821c9111546Sjruoho 	}
822c9111546Sjruoho 
8238e53b1ceSjruoho 	if (__predict_false((sc->sc_flags & ACPICPU_FLAG_P) == 0)) {
82453e8f6c9Sjruoho 		rv = ENODEV;
82553e8f6c9Sjruoho 		goto fail;
82653e8f6c9Sjruoho 	}
82753e8f6c9Sjruoho 
828c4e35077Sjruoho 	mutex_enter(&sc->sc_mtx);
829c4e35077Sjruoho 
8308e53b1ceSjruoho 	/*
8318e53b1ceSjruoho 	 * Use the cached value, if available.
8328e53b1ceSjruoho 	 */
833804fdee3Sjruoho 	if (sc->sc_pstate_current != 0) {
834804fdee3Sjruoho 		*(uint32_t *)cpu_freq = sc->sc_pstate_current;
835c4e35077Sjruoho 		mutex_exit(&sc->sc_mtx);
836804fdee3Sjruoho 		return;
83753e8f6c9Sjruoho 	}
83853e8f6c9Sjruoho 
839c4e35077Sjruoho 	mutex_exit(&sc->sc_mtx);
840c4e35077Sjruoho 
841c9111546Sjruoho 	switch (sc->sc_pstate_status.reg_spaceid) {
84253e8f6c9Sjruoho 
84353e8f6c9Sjruoho 	case ACPI_ADR_SPACE_FIXED_HARDWARE:
84453e8f6c9Sjruoho 
845804fdee3Sjruoho 		rv = acpicpu_md_pstate_get(sc, &freq);
84653e8f6c9Sjruoho 
8478e53b1ceSjruoho 		if (__predict_false(rv != 0))
84853e8f6c9Sjruoho 			goto fail;
84953e8f6c9Sjruoho 
85053e8f6c9Sjruoho 		break;
85153e8f6c9Sjruoho 
852*30d28f20Sjmcneill 	case ACPI_ADR_SPACE_SYSTEM_MEMORY:
85353e8f6c9Sjruoho 	case ACPI_ADR_SPACE_SYSTEM_IO:
85453e8f6c9Sjruoho 
855*30d28f20Sjmcneill 		val = acpicpu_readreg(&sc->sc_pstate_status);
85653e8f6c9Sjruoho 
85753e8f6c9Sjruoho 		if (val == 0) {
85853e8f6c9Sjruoho 			rv = EIO;
85953e8f6c9Sjruoho 			goto fail;
86053e8f6c9Sjruoho 		}
86153e8f6c9Sjruoho 
862c72ee204Sjruoho 		for (i = 0; i < sc->sc_pstate_count; i++) {
86353e8f6c9Sjruoho 
86453e8f6c9Sjruoho 			if (sc->sc_pstate[i].ps_freq == 0)
86553e8f6c9Sjruoho 				continue;
86653e8f6c9Sjruoho 
86753e8f6c9Sjruoho 			if (val == sc->sc_pstate[i].ps_status) {
86853e8f6c9Sjruoho 				ps = &sc->sc_pstate[i];
86953e8f6c9Sjruoho 				break;
87053e8f6c9Sjruoho 			}
87153e8f6c9Sjruoho 		}
87253e8f6c9Sjruoho 
8738e53b1ceSjruoho 		if (ps == NULL) {
87453e8f6c9Sjruoho 			rv = EIO;
87553e8f6c9Sjruoho 			goto fail;
87653e8f6c9Sjruoho 		}
87753e8f6c9Sjruoho 
878804fdee3Sjruoho 		freq = ps->ps_freq;
87953e8f6c9Sjruoho 		break;
88053e8f6c9Sjruoho 
88153e8f6c9Sjruoho 	default:
88253e8f6c9Sjruoho 		rv = ENOTTY;
88353e8f6c9Sjruoho 		goto fail;
88453e8f6c9Sjruoho 	}
88553e8f6c9Sjruoho 
886c4e35077Sjruoho 	mutex_enter(&sc->sc_mtx);
887804fdee3Sjruoho 	sc->sc_pstate_current = freq;
888804fdee3Sjruoho 	*(uint32_t *)cpu_freq = freq;
889c4e35077Sjruoho 	mutex_exit(&sc->sc_mtx);
89053e8f6c9Sjruoho 
891804fdee3Sjruoho 	return;
89253e8f6c9Sjruoho 
89353e8f6c9Sjruoho fail:
894796e6c82Sjruoho 	aprint_error_dev(sc->sc_dev, "failed "
89553e8f6c9Sjruoho 	    "to get frequency (err %d)\n", rv);
89653e8f6c9Sjruoho 
897c4e35077Sjruoho 	mutex_enter(&sc->sc_mtx);
898804fdee3Sjruoho 	sc->sc_pstate_current = 0;
899804fdee3Sjruoho 	*(uint32_t *)cpu_freq = 0;
900c4e35077Sjruoho 	mutex_exit(&sc->sc_mtx);
90153e8f6c9Sjruoho }
90253e8f6c9Sjruoho 
903c9111546Sjruoho void
acpicpu_pstate_set(void * aux,void * cpu_freq)904804fdee3Sjruoho acpicpu_pstate_set(void *aux, void *cpu_freq)
905c9111546Sjruoho {
90653e8f6c9Sjruoho 	struct acpicpu_pstate *ps = NULL;
907c9111546Sjruoho 	struct cpu_info *ci = curcpu();
908c9111546Sjruoho 	struct acpicpu_softc *sc;
909c9111546Sjruoho 	uint32_t freq, i, val;
91053e8f6c9Sjruoho 	int rv;
91153e8f6c9Sjruoho 
912804fdee3Sjruoho 	freq = *(uint32_t *)cpu_freq;
913c9111546Sjruoho 	sc = acpicpu_sc[ci->ci_acpiid];
914c9111546Sjruoho 
915c9111546Sjruoho 	if (__predict_false(sc == NULL)) {
916c9111546Sjruoho 		rv = ENXIO;
917c9111546Sjruoho 		goto fail;
918c9111546Sjruoho 	}
919c9111546Sjruoho 
9208e53b1ceSjruoho 	if (__predict_false((sc->sc_flags & ACPICPU_FLAG_P) == 0)) {
92153e8f6c9Sjruoho 		rv = ENODEV;
92253e8f6c9Sjruoho 		goto fail;
92353e8f6c9Sjruoho 	}
92453e8f6c9Sjruoho 
92553e8f6c9Sjruoho 	mutex_enter(&sc->sc_mtx);
92653e8f6c9Sjruoho 
927ce692d69Sjruoho 	if (sc->sc_pstate_current == freq) {
928ce692d69Sjruoho 		mutex_exit(&sc->sc_mtx);
929c9111546Sjruoho 		return;
930ce692d69Sjruoho 	}
931ce692d69Sjruoho 
9328e53b1ceSjruoho 	/*
9338e53b1ceSjruoho 	 * Verify that the requested frequency is available.
9348e53b1ceSjruoho 	 *
9358e53b1ceSjruoho 	 * The access needs to be protected since the currently
9368e53b1ceSjruoho 	 * available maximum and minimum may change dynamically.
9378e53b1ceSjruoho 	 */
938e70b5284Sjruoho 	for (i = sc->sc_pstate_max; i <= sc->sc_pstate_min; i++) {
93953e8f6c9Sjruoho 
9408e53b1ceSjruoho 		if (__predict_false(sc->sc_pstate[i].ps_freq == 0))
94153e8f6c9Sjruoho 			continue;
94253e8f6c9Sjruoho 
94353e8f6c9Sjruoho 		if (sc->sc_pstate[i].ps_freq == freq) {
94453e8f6c9Sjruoho 			ps = &sc->sc_pstate[i];
94553e8f6c9Sjruoho 			break;
94653e8f6c9Sjruoho 		}
94753e8f6c9Sjruoho 	}
94853e8f6c9Sjruoho 
94953e8f6c9Sjruoho 	mutex_exit(&sc->sc_mtx);
95053e8f6c9Sjruoho 
9516b9ff107Sjruoho 	if (__predict_false(ps == NULL)) {
95253e8f6c9Sjruoho 		rv = EINVAL;
95353e8f6c9Sjruoho 		goto fail;
95453e8f6c9Sjruoho 	}
95553e8f6c9Sjruoho 
956c9111546Sjruoho 	switch (sc->sc_pstate_control.reg_spaceid) {
95753e8f6c9Sjruoho 
95853e8f6c9Sjruoho 	case ACPI_ADR_SPACE_FIXED_HARDWARE:
95953e8f6c9Sjruoho 
96053e8f6c9Sjruoho 		rv = acpicpu_md_pstate_set(ps);
96153e8f6c9Sjruoho 
9628e53b1ceSjruoho 		if (__predict_false(rv != 0))
96353e8f6c9Sjruoho 			goto fail;
96453e8f6c9Sjruoho 
96553e8f6c9Sjruoho 		break;
96653e8f6c9Sjruoho 
967*30d28f20Sjmcneill 	case ACPI_ADR_SPACE_SYSTEM_MEMORY:
96853e8f6c9Sjruoho 	case ACPI_ADR_SPACE_SYSTEM_IO:
96953e8f6c9Sjruoho 
970*30d28f20Sjmcneill 		acpicpu_writereg(&sc->sc_pstate_control, ps->ps_control);
97153e8f6c9Sjruoho 
97253e8f6c9Sjruoho 		/*
97353e8f6c9Sjruoho 		 * Some systems take longer to respond
97453e8f6c9Sjruoho 		 * than the reported worst-case latency.
97553e8f6c9Sjruoho 		 */
97653e8f6c9Sjruoho 		for (i = val = 0; i < ACPICPU_P_STATE_RETRY; i++) {
97753e8f6c9Sjruoho 
978*30d28f20Sjmcneill 			val = acpicpu_readreg(&sc->sc_pstate_status);
97953e8f6c9Sjruoho 
98053e8f6c9Sjruoho 			if (val == ps->ps_status)
98153e8f6c9Sjruoho 				break;
98253e8f6c9Sjruoho 
98353e8f6c9Sjruoho 			DELAY(ps->ps_latency);
98453e8f6c9Sjruoho 		}
98553e8f6c9Sjruoho 
98653e8f6c9Sjruoho 		if (i == ACPICPU_P_STATE_RETRY) {
98753e8f6c9Sjruoho 			rv = EAGAIN;
98853e8f6c9Sjruoho 			goto fail;
98953e8f6c9Sjruoho 		}
99053e8f6c9Sjruoho 
99153e8f6c9Sjruoho 		break;
99253e8f6c9Sjruoho 
99353e8f6c9Sjruoho 	default:
99453e8f6c9Sjruoho 		rv = ENOTTY;
99553e8f6c9Sjruoho 		goto fail;
99653e8f6c9Sjruoho 	}
99753e8f6c9Sjruoho 
998c4e35077Sjruoho 	mutex_enter(&sc->sc_mtx);
9993d52b10fSjruoho 	ps->ps_evcnt.ev_count++;
100053e8f6c9Sjruoho 	sc->sc_pstate_current = freq;
1001c4e35077Sjruoho 	mutex_exit(&sc->sc_mtx);
100253e8f6c9Sjruoho 
1003c9111546Sjruoho 	return;
100453e8f6c9Sjruoho 
100553e8f6c9Sjruoho fail:
1006796e6c82Sjruoho 	if (rv != EINVAL)
1007796e6c82Sjruoho 		aprint_error_dev(sc->sc_dev, "failed to set "
100853e8f6c9Sjruoho 		    "frequency to %u (err %d)\n", freq, rv);
100953e8f6c9Sjruoho 
1010c4e35077Sjruoho 	mutex_enter(&sc->sc_mtx);
1011804fdee3Sjruoho 	sc->sc_pstate_current = 0;
1012c4e35077Sjruoho 	mutex_exit(&sc->sc_mtx);
101353e8f6c9Sjruoho }
1014