Page MenuHomeFreeBSD

D54881.diff
No OneTemporary

D54881.diff

diff --git a/share/man/man4/Makefile b/share/man/man4/Makefile
--- a/share/man/man4/Makefile
+++ b/share/man/man4/Makefile
@@ -252,6 +252,7 @@
${_imcsmb.4} \
inet.4 \
inet6.4 \
+ intelpmc.4 \
intpm.4 \
intro.4 \
${_io.4} \
diff --git a/share/man/man4/intelpmc.4 b/share/man/man4/intelpmc.4
new file mode 100644
--- /dev/null
+++ b/share/man/man4/intelpmc.4
@@ -0,0 +1,81 @@
+.\"
+.\" SPDX-License-Identifier: BSD-2-Clause
+.\"
+.\" Copyright (c) 2026 Abdelkader Boudih <freebsd@seuros.com>
+.\"
+.Dd February 23, 2026
+.Dt INTELPMC 4
+.Os
+.Sh NAME
+.Nm intelpmc
+.Nd Intel PCH Power Management Controller driver
+.Sh SYNOPSIS
+.Cd "device intelpmc"
+.Sh DESCRIPTION
+The
+.Nm
+driver exposes sleep state residency counters and power gating status
+for Intel Sunrise Point PCH (Platform Controller Hub) via
+.Xr sysctl 8 .
+.Pp
+The driver supports debugging S0ix (Modern Standby) power states by
+providing access to the cumulative time the system has spent in any
+S0ix sub-state (S0i1, S0i2, S0i3).
+.Pp
+If firmware has locked access to the PMC registers, the driver detects
+this condition at attach time and reports it via the
+.Va access_denied
+sysctl.
+All register reads return zero when access is denied.
+.Sh HARDWARE
+The
+.Nm
+driver supports the following devices:
+.Pp
+.Bl -bullet -compact
+.It
+Intel Sunrise Point-LP PMC (6th/7th Gen Core mobile, PCI ID 0x8086:0x9d21)
+.It
+Intel Sunrise Point-H PMC (6th/7th Gen Core desktop/server,
+PCI ID 0x8086:0xa121)
+.El
+.Sh SYSCTLS
+The following
+.Xr sysctl 8
+variables are available:
+.Bl -tag -width indent
+.It Va dev.intelpmc.%d.access_denied
+Set to 1 if firmware has denied access to PMC registers.
+.It Va dev.intelpmc.%d.ltr_ignore
+Latency Tolerance Reporting (LTR) ignore mask.
+.It Va dev.intelpmc.%d.pm_cfg
+Power Management configuration register (PM_CFG).
+.It Va dev.intelpmc.%d.pm_sts
+Power Management status register (PM_STS).
+.It Va dev.intelpmc.%d.slp_s0_residency
+Cumulative time the PCH SLP_S0 signal was asserted, in microseconds
+(100\ us per step).
+SLP_S0 is asserted only when all of the following conditions are
+met simultaneously: CPU in Package C10, PCH in low power mode,
+ModPhy lanes power-gated, and PLLs idle.
+This represents the deepest achievable S0ix platform state.
+A value that never advances indicates S0ix transitions are not
+occurring.
+.El
+.Sh SEE ALSO
+.Xr acpi 4 ,
+.Xr pci 4 ,
+.Xr sysctl 8
+.Pp
+Intel 100 Series Chipset Family PCH Datasheet, Volume 2, Section 4.3.53.
+.Sh HISTORY
+The
+.Nm
+driver first appeared in
+.Fx 16.0 .
+.Sh AUTHORS
+.An Abdelkader Boudih Aq Mt freebsd@seuros.com
+.Sh CAVEATS
+Only Sunrise Point PCH is supported.
+Other PCH generations (Cannon Point, Tiger Lake, etc.) have different
+register layouts and are not handled by this driver.
diff --git a/sys/conf/files.x86 b/sys/conf/files.x86
--- a/sys/conf/files.x86
+++ b/sys/conf/files.x86
@@ -150,6 +150,7 @@
dev/ichwd/ichwd.c optional ichwd
dev/imcsmb/imcsmb.c optional imcsmb
dev/imcsmb/imcsmb_pci.c optional imcsmb pci
+dev/intel/intelpmc.c optional intelpmc pci
dev/intel/pchtherm.c optional pchtherm
dev/intel/spi.c optional intelspi
dev/intel/spi_pci.c optional intelspi pci
diff --git a/sys/dev/intel/intelpmc.c b/sys/dev/intel/intelpmc.c
new file mode 100644
--- /dev/null
+++ b/sys/dev/intel/intelpmc.c
@@ -0,0 +1,213 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2026 Abdelkader Boudih <freebsd@seuros.com>
+ */
+
+/*
+ * intelpmc — Intel PCH Power Management Controller driver
+ *
+ * Exposes sleep state residency counters and power gating status
+ * for debugging S0ix (Modern Standby) power states.
+ *
+ * Register offsets from Intel 100 Series Chipset Family PCH Datasheet Vol 2
+ * Section 4.3.53 describes the SLP_S0_RESIDENCY_COUNTER (offset 0x13C).
+ *
+ * The SLP_S0_RESIDENCY counter increments (100 us per step) while the PCH
+ * SLP_S0 signal is asserted. SLP_S0 is asserted only when all of the
+ * following conditions are met simultaneously:
+ * - CPU in Package C10 (deepest package C-state)
+ * - PCH in low power mode
+ * - ModPhy lanes power-gated
+ * - PLLs idle
+ * This represents the deepest achievable S0ix platform state. A counter
+ * that never advances indicates S0ix transitions are not occurring, which
+ * is useful for diagnosing Modern Standby power regressions.
+ *
+ * Ref: https://patchwork.kernel.org/project/platform-driver-x86/patch/\
+ * 1462891545-24060-1-git-send-email-rajneesh.bhardwaj@intel.com/
+ * Ref: Intel 100 Series Chipset Family PCH Datasheet Vol 2, §4.3.53
+ *
+ * NOTE: This driver only supports Sunrise Point (different PCH generations
+ * have different register layouts). Some firmware/BIOS implementations
+ * lock PMC access - if registers read as 0xFFFFFFFF, access is denied.
+ */
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/errno.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/rman.h>
+#include <sys/sysctl.h>
+#include <sys/systm.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcivar.h>
+
+/*
+ * Sunrise Point (SPT) PMC Register Definitions
+ * Reference: Intel 100 Series Chipset Family PCH Datasheet Vol 2
+ */
+#define SPT_PMC_PM_CFG_OFFSET 0x18
+#define SPT_PMC_PM_STS_OFFSET 0x1C
+#define SPT_PMC_SLP_S0_RES_OFFSET 0x13C
+#define SPT_PMC_SLP_S0_RES_STEP 100 /* 100us per datasheet */
+#define SPT_PMC_LTR_IGNORE_OFFSET 0x30C
+
+struct intelpmc_softc {
+ int rid;
+ struct resource *res;
+ bus_space_tag_t bst;
+ bus_space_handle_t bsh;
+ int access_denied;
+};
+
+static const struct pci_device_table intelpmc_devices[] = {
+ /* Only Sunrise Point - other generations have different layouts */
+ { PCI_DEV(0x8086, 0x9d21), PCI_DESCR("Sunrise Point-LP PMC")},
+ { PCI_DEV(0x8086, 0xa121), PCI_DESCR("Sunrise Point-H PMC")},
+};
+
+static inline uint32_t
+intelpmc_read(struct intelpmc_softc *sc, uint32_t offset)
+{
+ return (bus_space_read_4(sc->bst, sc->bsh, offset));
+}
+
+static inline uint32_t
+intelpmc_read_safe(struct intelpmc_softc *sc, uint32_t offset)
+{
+ if (sc->access_denied)
+ return (0);
+ return (intelpmc_read(sc, offset));
+}
+
+static int
+intelpmc_slp_s0_sysctl(SYSCTL_HANDLER_ARGS)
+{
+ struct intelpmc_softc *sc = oidp->oid_arg1;
+ uint64_t val, usec;
+
+ val = intelpmc_read_safe(sc, SPT_PMC_SLP_S0_RES_OFFSET);
+ usec = val * SPT_PMC_SLP_S0_RES_STEP;
+ return (sysctl_handle_64(oidp, &usec, 0, req));
+}
+
+static int
+intelpmc_reg32_sysctl(SYSCTL_HANDLER_ARGS)
+{
+ struct intelpmc_softc *sc = oidp->oid_arg1;
+ uint32_t val;
+
+ val = intelpmc_read_safe(sc, oidp->oid_arg2);
+ return (sysctl_handle_32(oidp, &val, 0, req));
+}
+
+static int
+intelpmc_probe(device_t dev)
+{
+ const struct pci_device_table *tbl;
+
+ tbl = PCI_MATCH(dev, intelpmc_devices);
+ if (tbl == NULL)
+ return (ENXIO);
+ device_set_desc(dev, tbl->descr);
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+intelpmc_attach(device_t dev)
+{
+ struct intelpmc_softc *sc = device_get_softc(dev);
+ struct sysctl_ctx_list *ctx;
+ struct sysctl_oid *tree;
+ uint32_t pm_cfg, pm_sts, ltr_ign, slp_s0;
+
+ sc->rid = PCIR_BAR(0);
+ sc->res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
+ &sc->rid, RF_ACTIVE);
+ if (sc->res == NULL) {
+ device_printf(dev, "cannot allocate PMC BAR0\n");
+ return (ENOMEM);
+ }
+
+ sc->bst = rman_get_bustag(sc->res);
+ sc->bsh = rman_get_bushandle(sc->res);
+
+ /*
+ * Check multiple registers to detect firmware lock.
+ * All reading 0xFFFFFFFF indicates access denied.
+ */
+ pm_cfg = intelpmc_read(sc, SPT_PMC_PM_CFG_OFFSET);
+ pm_sts = intelpmc_read(sc, SPT_PMC_PM_STS_OFFSET);
+ ltr_ign = intelpmc_read(sc, SPT_PMC_LTR_IGNORE_OFFSET);
+ slp_s0 = intelpmc_read(sc, SPT_PMC_SLP_S0_RES_OFFSET);
+
+ if (pm_cfg == 0xFFFFFFFF && pm_sts == 0xFFFFFFFF &&
+ ltr_ign == 0xFFFFFFFF && slp_s0 == 0xFFFFFFFF) {
+ sc->access_denied = 1;
+ device_printf(dev,
+ "PMC access denied by firmware (S0ix not supported)\n");
+ } else
+ sc->access_denied = 0;
+
+ ctx = device_get_sysctl_ctx(dev);
+ tree = device_get_sysctl_tree(dev);
+
+ SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+ "access_denied", CTLFLAG_RD | CTLFLAG_MPSAFE, &sc->access_denied, 0,
+ "Firmware denied access to PMC registers");
+
+ SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+ "slp_s0_residency", CTLTYPE_U64 | CTLFLAG_RD | CTLFLAG_MPSAFE,
+ sc, 0, intelpmc_slp_s0_sysctl, "QU",
+ "Time PCH SLP_S0 signal asserted (PC10+PCH low power+ModPhy gated+PLLs idle), in microseconds");
+
+ SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+ "ltr_ignore", CTLTYPE_U32 | CTLFLAG_RD | CTLFLAG_MPSAFE,
+ sc, SPT_PMC_LTR_IGNORE_OFFSET, intelpmc_reg32_sysctl, "IU",
+ "LTR ignore mask");
+
+ SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+ "pm_cfg", CTLTYPE_U32 | CTLFLAG_RD | CTLFLAG_MPSAFE,
+ sc, SPT_PMC_PM_CFG_OFFSET, intelpmc_reg32_sysctl, "IU",
+ "Power Management configuration");
+
+ SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+ "pm_sts", CTLTYPE_U32 | CTLFLAG_RD | CTLFLAG_MPSAFE,
+ sc, SPT_PMC_PM_STS_OFFSET, intelpmc_reg32_sysctl, "IU",
+ "Power Management status");
+
+ return (0);
+}
+
+static int
+intelpmc_detach(device_t dev)
+{
+ struct intelpmc_softc *sc = device_get_softc(dev);
+
+ bus_release_resource(dev, SYS_RES_MEMORY, sc->rid, sc->res);
+ return (0);
+}
+
+static device_method_t intelpmc_methods[] = {
+ DEVMETHOD(device_probe, intelpmc_probe),
+ DEVMETHOD(device_attach, intelpmc_attach),
+ DEVMETHOD(device_detach, intelpmc_detach),
+ DEVMETHOD_END
+};
+
+static driver_t intelpmc_driver = {
+ "intelpmc",
+ intelpmc_methods,
+ sizeof(struct intelpmc_softc)
+};
+
+DRIVER_MODULE(intelpmc, pci, intelpmc_driver, 0, 0);
+PCI_PNP_INFO(intelpmc_devices);
+MODULE_VERSION(intelpmc, 1);
+MODULE_DEPEND(intelpmc, pci, 1, 1, 1);
diff --git a/sys/modules/Makefile b/sys/modules/Makefile
--- a/sys/modules/Makefile
+++ b/sys/modules/Makefile
@@ -174,6 +174,7 @@
${_igc} \
imgact_binmisc \
${_imx} \
+ ${_intelpmc} \
${_intelspi} \
${_io} \
${_ioat} \
@@ -821,6 +822,7 @@
_hyperv= hyperv
_ichwd= ichwd
_ida= ida
+_intelpmc= intelpmc
_intelspi= intelspi
_ips= ips
_isci= isci
diff --git a/sys/modules/intelpmc/Makefile b/sys/modules/intelpmc/Makefile
new file mode 100644
--- /dev/null
+++ b/sys/modules/intelpmc/Makefile
@@ -0,0 +1,6 @@
+.PATH: ${SRCTOP}/sys/dev/intel
+KMOD= intelpmc
+SRCS= intelpmc.c
+SRCS+= device_if.h bus_if.h pci_if.h
+
+.include <bsd.kmod.mk>

File Metadata

Mime Type
text/plain
Expires
Fri, Apr 17, 7:00 PM (18 h, 43 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
31675399
Default Alt Text
D54881.diff (10 KB)

Event Timeline