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