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