Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F152733924
D54881.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
10 KB
Referenced Files
None
Subscribers
None
D54881.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D54881: intel/intelpmc: Add Intel PMC Core driver
Attached
Detach File
Event Timeline
Log In to Comment