From: kettenis Date: Sun, 4 Aug 2024 11:05:18 +0000 (+0000) Subject: Add intelpmc(4), a driver for the power management controller found on X-Git-Url: http://artulab.com/gitweb/?a=commitdiff_plain;h=6635b7e6ef328800dfff4b5e8a9c341d2daf9739;p=openbsd Add intelpmc(4), a driver for the power management controller found on various Intel SoCs. The driver takes care of calling the AML methods needed to enter low power idle states during suspend-to-idle (S0i). The driver also implements some debug code that prints the residency of various power states in dmesg. Based on some earlier code by jcs@ ok jcs@ --- diff --git a/sys/arch/amd64/conf/GENERIC b/sys/arch/amd64/conf/GENERIC index 86407b9310c..1263cc52c61 100644 --- a/sys/arch/amd64/conf/GENERIC +++ b/sys/arch/amd64/conf/GENERIC @@ -1,4 +1,4 @@ -# $OpenBSD: GENERIC,v 1.523 2024/05/09 17:05:22 mglocker Exp $ +# $OpenBSD: GENERIC,v 1.524 2024/08/04 11:05:18 kettenis Exp $ # # For further information on compiling OpenBSD kernels, see the config(8) # man page. @@ -85,6 +85,7 @@ acpihid* at acpi? ipmi0 at acpi? disable ccpmic* at iic? tipmic* at iic? +intelpmc* at acpi? efi0 at bios0 mpbios0 at bios0 diff --git a/sys/arch/amd64/include/specialreg.h b/sys/arch/amd64/include/specialreg.h index a8a7e492a17..4208e2e13a1 100644 --- a/sys/arch/amd64/include/specialreg.h +++ b/sys/arch/amd64/include/specialreg.h @@ -1,4 +1,4 @@ -/* $OpenBSD: specialreg.h,v 1.115 2024/07/21 19:41:31 bluhm Exp $ */ +/* $OpenBSD: specialreg.h,v 1.116 2024/08/04 11:05:18 kettenis Exp $ */ /* $NetBSD: specialreg.h,v 1.1 2003/04/26 18:39:48 fvdl Exp $ */ /* $NetBSD: x86/specialreg.h,v 1.2 2003/04/25 21:54:30 fvdl Exp $ */ @@ -624,6 +624,12 @@ #define MSR_PERF_GLOBAL_CTRL 0x38f #define MSR_PERF_GLOBAL_CTR1_EN (1ULL << 33) #define MSR_PERF_GLOBAL_CTR2_EN (1ULL << 34) +#define MSR_PKG_C3_RESIDENCY 0x3f8 +#define MSR_PKG_C6_RESIDENCY 0x3f9 +#define MSR_PKG_C7_RESIDENCY 0x3fa +#define MSR_CORE_C3_RESIDENCY 0x3fc +#define MSR_CORE_C6_RESIDENCY 0x3fd +#define MSR_CORE_C7_RESIDENCY 0x3fe #define MSR_MC0_CTL 0x400 #define MSR_MC0_STATUS 0x401 #define MSR_MC0_ADDR 0x402 @@ -644,6 +650,10 @@ #define MSR_MC3_STATUS 0x411 #define MSR_MC3_ADDR 0x412 #define MSR_MC3_MISC 0x413 +#define MSR_PKG_C2_RESIDENCY 0x60d +#define MSR_PKG_C8_RESIDENCY 0x630 +#define MSR_PKG_C9_RESIDENCY 0x631 +#define MSR_PKG_C10_RESIDENCY 0x632 #define MSR_U_CET 0x6a0 #define MSR_CET_ENDBR_EN (1 << 2) #define MSR_CET_NO_TRACK_EN (1 << 4) diff --git a/sys/dev/acpi/acpi_x86.c b/sys/dev/acpi/acpi_x86.c index 243afdd95b8..5b8ff4365e0 100644 --- a/sys/dev/acpi/acpi_x86.c +++ b/sys/dev/acpi/acpi_x86.c @@ -1,4 +1,4 @@ -/* $OpenBSD: acpi_x86.c,v 1.22 2024/06/25 11:57:10 kettenis Exp $ */ +/* $OpenBSD: acpi_x86.c,v 1.23 2024/08/04 11:05:18 kettenis Exp $ */ /* * Copyright (c) 2005 Thorsten Lockert * Copyright (c) 2005 Jordan Hargrave @@ -104,9 +104,15 @@ gosleep(void *v) acpi_disable_allgpes(sc); acpi_enable_wakegpes(sc, sc->sc_state); + if (sc->sc_pmc_suspend) + sc->sc_pmc_suspend(sc->sc_pmc_cookie); + ret = acpi_sleep_cpu(sc, sc->sc_state); acpi_resume_cpu(sc, sc->sc_state); + if (sc->sc_pmc_resume) + sc->sc_pmc_resume(sc->sc_pmc_cookie); + return ret; } diff --git a/sys/dev/acpi/acpivar.h b/sys/dev/acpi/acpivar.h index 46c2abf5748..d1af26fa0b0 100644 --- a/sys/dev/acpi/acpivar.h +++ b/sys/dev/acpi/acpivar.h @@ -1,4 +1,4 @@ -/* $OpenBSD: acpivar.h,v 1.131 2024/06/30 00:29:36 jsg Exp $ */ +/* $OpenBSD: acpivar.h,v 1.132 2024/08/04 11:05:18 kettenis Exp $ */ /* * Copyright (c) 2005 Thorsten Lockert * @@ -282,6 +282,10 @@ struct acpi_softc { int sc_flags; int sc_skip_processor; + + void (*sc_pmc_suspend)(void *); + void (*sc_pmc_resume)(void *); + void *sc_pmc_cookie; }; extern struct acpi_softc *acpi_softc; diff --git a/sys/dev/acpi/files.acpi b/sys/dev/acpi/files.acpi index f664ac350da..840e374a59a 100644 --- a/sys/dev/acpi/files.acpi +++ b/sys/dev/acpi/files.acpi @@ -1,4 +1,4 @@ -# $OpenBSD: files.acpi,v 1.70 2024/07/30 19:47:06 mglocker Exp $ +# $OpenBSD: files.acpi,v 1.71 2024/08/04 11:05:18 kettenis Exp $ # # Config file and device description for machine-independent ACPI code. # Included by ports that need it. @@ -284,3 +284,8 @@ file dev/acpi/ufshci_acpi.c ufshci_acpi # Intel OnChip System Fabric attach iosf at acpi with iosf_acpi file dev/acpi/iosf_acpi.c iosf_acpi + +# Intel Power Management Controller +device intelpmc +attach intelpmc at acpi +file dev/acpi/intelpmc.c intelpmc diff --git a/sys/dev/acpi/intelpmc.c b/sys/dev/acpi/intelpmc.c new file mode 100644 index 00000000000..7e06bc5f461 --- /dev/null +++ b/sys/dev/acpi/intelpmc.c @@ -0,0 +1,220 @@ +/* $OpenBSD: intelpmc.c,v 1.1 2024/08/04 11:05:18 kettenis Exp $ */ +/* + * Copyright (c) 2024 Mark Kettenis + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#define INTELPMC_DEBUG + +/* Low Power S0 Idle DSM methods */ +#define ACPI_LPS0_ENUM_FUNCTIONS 0 +#define ACPI_LPS0_GET_CONSTRAINTS 1 +#define ACPI_LPS0_SCREEN_OFF 3 +#define ACPI_LPS0_SCREEN_ON 4 +#define ACPI_LPS0_ENTRY 5 +#define ACPI_LPS0_EXIT 6 + +struct intelpmc_softc { + struct device sc_dev; + bus_space_tag_t sc_iot; + bus_space_handle_t sc_ioh; + + struct acpi_softc *sc_acpi; + struct aml_node *sc_node; + +#ifdef INTELPMC_DEBUG + uint64_t sc_c3[2]; + uint64_t sc_c6[2]; + uint64_t sc_c7[2]; + uint64_t sc_pc2[2]; + uint64_t sc_pc3[2]; + uint64_t sc_pc6[2]; + uint64_t sc_pc7[2]; + uint64_t sc_pc8[2]; + uint64_t sc_pc9[2]; + uint64_t sc_pc10[2]; +#endif +}; + +int intelpmc_match(struct device *, void *, void *); +void intelpmc_attach(struct device *, struct device *, void *); +int intelpmc_activate(struct device *, int); + +const struct cfattach intelpmc_ca = { + sizeof (struct intelpmc_softc), intelpmc_match, intelpmc_attach, + NULL, intelpmc_activate +}; + +struct cfdriver intelpmc_cd = { + NULL, "intelpmc", DV_DULL +}; + +const char *intelpmc_hids[] = { + "INT33A1", + NULL +}; + +void intelpmc_suspend(void *); +void intelpmc_resume(void *); + +int +intelpmc_match(struct device *parent, void *match, void *aux) +{ + struct acpi_attach_args *aaa = aux; + struct cfdata *cf = match; + + return acpi_matchhids(aaa, intelpmc_hids, cf->cf_driver->cd_name); +} + +void +intelpmc_attach(struct device *parent, struct device *self, void *aux) +{ + struct intelpmc_softc *sc = (struct intelpmc_softc *)self; + struct acpi_attach_args *aaa = aux; + + sc->sc_acpi = (struct acpi_softc *)parent; + sc->sc_node = aaa->aaa_node; + + printf(": %s\n", aaa->aaa_node->name); + + sc->sc_acpi->sc_pmc_suspend = intelpmc_suspend; + sc->sc_acpi->sc_pmc_resume = intelpmc_resume; + sc->sc_acpi->sc_pmc_cookie = sc; +} + +int +intelpmc_activate(struct device *self, int act) +{ +#ifdef INTELPMC_DEBUG + struct intelpmc_softc *sc = (struct intelpmc_softc *)self; + + switch (act) { + case DVACT_RESUME: + printf("C3: %lld -> %lld\n", sc->sc_c3[0], sc->sc_c3[1]); + printf("C6: %lld -> %lld\n", sc->sc_c6[0], sc->sc_c6[1]); + printf("C7: %lld -> %lld\n", sc->sc_c7[0], sc->sc_c7[1]); + printf("PC2: %lld -> %lld\n", sc->sc_pc2[0], sc->sc_pc2[1]); + printf("PC3: %lld -> %lld\n", sc->sc_pc3[0], sc->sc_pc3[1]); + printf("PC6: %lld -> %lld\n", sc->sc_pc6[0], sc->sc_pc6[1]); + printf("PC7: %lld -> %lld\n", sc->sc_pc7[0], sc->sc_pc7[1]); + printf("PC8: %lld -> %lld\n", sc->sc_pc8[0], sc->sc_pc8[1]); + printf("PC9: %lld -> %lld\n", sc->sc_pc9[0], sc->sc_pc9[1]); + printf("PC10: %lld -> %lld\n", sc->sc_pc10[0], sc->sc_pc10[1]); + break; + } +#endif + + return 0; +} + +int +intelpmc_dsm(struct acpi_softc *sc, struct aml_node *node, int func) +{ + struct aml_value cmd[4]; + struct aml_value res; + + /* c4eb40a0-6cd2-11e2-bcfd-0800200c9a66 */ + static uint8_t lps0_dsm_guid[] = { + 0xA0, 0x40, 0xEB, 0xC4, 0xD2, 0x6C, 0xE2, 0x11, + 0xBC, 0xFD, 0x08, 0x00, 0x20, 0x0C, 0x9A, 0x66, + }; + + bzero(&cmd, sizeof(cmd)); + cmd[0].type = AML_OBJTYPE_BUFFER; + cmd[0].v_buffer = (uint8_t *)&lps0_dsm_guid; + cmd[0].length = sizeof(lps0_dsm_guid); + /* rev */ + cmd[1].type = AML_OBJTYPE_INTEGER; + cmd[1].v_integer = 0; + cmd[1].length = 1; + /* func */ + cmd[2].type = AML_OBJTYPE_INTEGER; + cmd[2].v_integer = func; + cmd[2].length = 1; + /* not used */ + cmd[3].type = AML_OBJTYPE_PACKAGE; + cmd[3].length = 0; + + if (aml_evalname(sc, node, "_DSM", 4, cmd, &res)) { + printf("%s: eval of _DSM at %s failed\n", + sc->sc_dev.dv_xname, aml_nodename(node)); + return 1; + } + aml_freevalue(&res); + + return 0; +} + +void +intelpmc_suspend(void *cookie) +{ + struct intelpmc_softc *sc = cookie; + + if (sc->sc_acpi->sc_state != ACPI_STATE_S0) + return; + +#ifdef INTELPMC_DEBUG + rdmsr_safe(MSR_CORE_C3_RESIDENCY, &sc->sc_c3[0]); + rdmsr_safe(MSR_CORE_C6_RESIDENCY, &sc->sc_c6[0]); + rdmsr_safe(MSR_CORE_C7_RESIDENCY, &sc->sc_c7[0]); + rdmsr_safe(MSR_PKG_C2_RESIDENCY, &sc->sc_pc2[0]); + rdmsr_safe(MSR_PKG_C3_RESIDENCY, &sc->sc_pc3[0]); + rdmsr_safe(MSR_PKG_C6_RESIDENCY, &sc->sc_pc6[0]); + rdmsr_safe(MSR_PKG_C7_RESIDENCY, &sc->sc_pc7[0]); + rdmsr_safe(MSR_PKG_C8_RESIDENCY, &sc->sc_pc8[0]); + rdmsr_safe(MSR_PKG_C9_RESIDENCY, &sc->sc_pc9[0]); + rdmsr_safe(MSR_PKG_C10_RESIDENCY, &sc->sc_pc10[0]); +#endif + + intelpmc_dsm(sc->sc_acpi, sc->sc_node, ACPI_LPS0_SCREEN_OFF); + intelpmc_dsm(sc->sc_acpi, sc->sc_node, ACPI_LPS0_ENTRY); +} + +void +intelpmc_resume(void *cookie) +{ + struct intelpmc_softc *sc = cookie; + + if (sc->sc_acpi->sc_state != ACPI_STATE_S0) + return; + + intelpmc_dsm(sc->sc_acpi, sc->sc_node, ACPI_LPS0_EXIT); + intelpmc_dsm(sc->sc_acpi, sc->sc_node, ACPI_LPS0_SCREEN_ON); + +#ifdef INTELPMC_DEBUG + rdmsr_safe(MSR_CORE_C3_RESIDENCY, &sc->sc_c3[1]); + rdmsr_safe(MSR_CORE_C6_RESIDENCY, &sc->sc_c6[1]); + rdmsr_safe(MSR_CORE_C7_RESIDENCY, &sc->sc_c7[1]); + rdmsr_safe(MSR_PKG_C2_RESIDENCY, &sc->sc_pc2[1]); + rdmsr_safe(MSR_PKG_C3_RESIDENCY, &sc->sc_pc3[1]); + rdmsr_safe(MSR_PKG_C6_RESIDENCY, &sc->sc_pc6[1]); + rdmsr_safe(MSR_PKG_C7_RESIDENCY, &sc->sc_pc7[1]); + rdmsr_safe(MSR_PKG_C8_RESIDENCY, &sc->sc_pc8[1]); + rdmsr_safe(MSR_PKG_C9_RESIDENCY, &sc->sc_pc9[1]); + rdmsr_safe(MSR_PKG_C10_RESIDENCY, &sc->sc_pc10[1]); +#endif +}