Add intelpmc(4), a driver for the power management controller found on
authorkettenis <kettenis@openbsd.org>
Sun, 4 Aug 2024 11:05:18 +0000 (11:05 +0000)
committerkettenis <kettenis@openbsd.org>
Sun, 4 Aug 2024 11:05:18 +0000 (11:05 +0000)
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@

sys/arch/amd64/conf/GENERIC
sys/arch/amd64/include/specialreg.h
sys/dev/acpi/acpi_x86.c
sys/dev/acpi/acpivar.h
sys/dev/acpi/files.acpi
sys/dev/acpi/intelpmc.c [new file with mode: 0644]

index 86407b9..1263cc5 100644 (file)
@@ -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
index a8a7e49..4208e2e 100644 (file)
@@ -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 $  */
 
 #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
 #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)
index 243afdd..5b8ff43 100644 (file)
@@ -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 <tholo@sigmasoft.com>
  * Copyright (c) 2005 Jordan Hargrave <jordan@openbsd.org>
@@ -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;
 }
 
index 46c2abf..d1af26f 100644 (file)
@@ -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 <tholo@sigmasoft.com>
  *
@@ -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;
index f664ac3..840e374 100644 (file)
@@ -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 (file)
index 0000000..7e06bc5
--- /dev/null
@@ -0,0 +1,220 @@
+/*     $OpenBSD: intelpmc.c,v 1.1 2024/08/04 11:05:18 kettenis Exp $   */
+/*
+ * Copyright (c) 2024 Mark Kettenis <kettenis@openbsd.org>
+ *
+ * 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 <sys/param.h>
+#include <sys/systm.h>
+#include <sys/device.h>
+
+#include <machine/intr.h>
+#include <machine/bus.h>
+
+#include <dev/acpi/acpireg.h>
+#include <dev/acpi/acpivar.h>
+#include <dev/acpi/acpidev.h>
+#include <dev/acpi/amltypes.h>
+#include <dev/acpi/dsdt.h>
+
+#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
+}