git: dd9e49a10bc1 - main - intel/intelpmc: Add Intel PMC Core driver
Date: Sun, 13 Sep 2026 20:36:36 UTC
The branch main has been updated by seuros:
URL: https://cgit.FreeBSD.org/src/commit/?id=dd9e49a10bc14f7d876426d7d85fed0d1fe9699d
commit dd9e49a10bc14f7d876426d7d85fed0d1fe9699d
Author: Abdelkader Boudih <seuros@FreeBSD.org>
AuthorDate: 2026-09-13 20:36:16 +0000
Commit: Abdelkader Boudih <seuros@FreeBSD.org>
CommitDate: 2026-09-13 20:36:16 +0000
intel/intelpmc: Add Intel PMC Core driver
Add driver for Intel Power Management Controller (PMC) found on Sunrise Point PCH chipsets.
This device exposes S0ix sleep state residency counters and power management status.
Sysctls provided:
dev.intelpmc.0.slp_s0_residency_us - Time in deepest sleep (us)
dev.intelpmc.0.ltr_ignore - LTR ignore mask
dev.intelpmc.0.pm_cfg - PM configuration register
dev.intelpmc.0.pm_sts - PM status register
dev.intelpmc.0.access_denied - Firmware lock status
Supported devices for now:
- Sunrise Point-LP (0x9D21)
- Sunrise Point-H (0xA121)
Note: Later PCH generations (Cannon Lake, Tiger Lake, ect.) have different PMC register layouts according to the datasheet and would need per-generation tables.
I avoided adding untested hardware in case they have a quirk.
Reviewed by: olce, adrian
Differential Revision: https://reviews.freebsd.org/D54881
---
share/man/man4/Makefile | 1 +
share/man/man4/intel_pmc.4 | 83 ++++++++++++++++
sys/conf/files.x86 | 1 +
sys/dev/intel/intel_pmc.c | 219 +++++++++++++++++++++++++++++++++++++++++
sys/modules/Makefile | 2 +
sys/modules/intel_pmc/Makefile | 6 ++
6 files changed, 312 insertions(+)
diff --git a/share/man/man4/Makefile b/share/man/man4/Makefile
index 07307c59308c..7a5ef9f397eb 100644
--- a/share/man/man4/Makefile
+++ b/share/man/man4/Makefile
@@ -262,6 +262,7 @@ MAN= aac.4 \
${_imcsmb.4} \
inet.4 \
inet6.4 \
+ intel_pmc.4 \
intpm.4 \
intro.4 \
${_io.4} \
diff --git a/share/man/man4/intel_pmc.4 b/share/man/man4/intel_pmc.4
new file mode 100644
index 000000000000..1f12e8532675
--- /dev/null
+++ b/share/man/man4/intel_pmc.4
@@ -0,0 +1,83 @@
+.\"
+.\" SPDX-License-Identifier: BSD-2-Clause
+.\"
+.\" Copyright (c) 2026 Abdelkader Boudih <seuros@FreeBSD.org>
+.\"
+.Dd September 7, 2026
+.Dt INTEL_PMC 4
+.Os
+.Sh NAME
+.Nm intel_pmc
+.Nd Intel Performance Management Controller driver
+.Sh SYNOPSIS
+.Cd "device intel_pmc"
+.Sh DESCRIPTION
+The
+.Nm
+driver exposes information and controls from the Performance
+Management Controller (PMC) of Intel platforms.
+Presently, it supports the Intel Sunrise Point platform (Skylake CPU
+generation), providing access to debug information and controls
+related to S0ix (Modern Standby).
+.Pp
+The cumulative time spent in low power mode while in S0 is reported,
+and the administrator can force the platform to ignore IP blocks
+whose Latency Tolerance Reporting (LTR) prevents the platform from
+entering S0ix.
+.Sh HARDWARE
+The
+.Nm
+driver supports the PMC block found in the following Intel Platform
+Controller Hubs:
+.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.intel_pmc.%d.ltr_ignore Pq read-write
+Latency Tolerance Reporting (LTR) ignore mask.
+Each bit corresponds to one IP block (bits 0-16).
+Setting a bit tells the PMC to ignore that IP's LTR requirements,
+allowing S0ix entry even if the IP would otherwise block it.
+A clear bit means the IP's requirements are considered and can
+prevent S0ix.
+.Pp
+Sunrise Point bit assignments:
+SOUTHPORT_A, SOUTHPORT_B, SATA, GIGABIT_ETHERNET, XHCI, reserved,
+ME, EVA, SOUTHPORT_C, HD_AUDIO, LPSS, SOUTHPORT_D, SOUTHPORT_E,
+CAMERA, ESPI, SCC, ISH.
+.It Va dev.intel_pmc.%d.slp_s0_residency Pq read-only
+Cumulative time in microseconds that the SLP_S0 signal has been
+asserted (100\ us per counter step).
+Per the datasheet, SLP_S0 asserts when the PCH is idle and the
+processor is in C10 state.
+A value that never advances indicates S0ix transitions are not occurring.
+.Pp
+The counter is 32-bit and wraps after approximately 5 days of
+cumulative S0ix residency.
+.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.
+.Sh HISTORY
+The
+.Nm
+driver first appeared in
+.Fx 16.0 .
+.Sh AUTHORS
+.An Abdelkader Boudih Aq Mt seuros@FreeBSD.org
+.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
index bb52975598ab..d0c131bf0730 100644
--- a/sys/conf/files.x86
+++ b/sys/conf/files.x86
@@ -159,6 +159,7 @@ dev/ichwd/i6300esbwd.c optional ichwd
dev/ichwd/ichwd.c optional ichwd
dev/imcsmb/imcsmb.c optional imcsmb
dev/imcsmb/imcsmb_pci.c optional imcsmb pci
+dev/intel/intel_pmc.c optional intel_pmc 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/intel_pmc.c b/sys/dev/intel/intel_pmc.c
new file mode 100644
index 000000000000..ee437cc08b5d
--- /dev/null
+++ b/sys/dev/intel/intel_pmc.c
@@ -0,0 +1,219 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2026 Abdelkader Boudih <seuros@FreeBSD.org>
+ */
+
+/*
+ * intel_pmc -- Intel Performance Management Controller (PMC) driver
+ *
+ * Exposes information and controls from the PMC of Intel platforms.
+ * Only the Sunrise Point (SPT) platform, paired with Skylake
+ * generation processors, is currently supported; everything below
+ * describes that hardware only.
+ *
+ * The driver reports the cumulative time spent in low power mode
+ * (S0ix, Modern Standby) while in S0, and lets the administrator
+ * force the PMC to ignore IP blocks whose Latency Tolerance
+ * Reporting (LTR) prevents the platform from entering S0ix.
+ *
+ * The PMC register space (PWRM) is mapped via the PWRM_BASE register
+ * at PCI config offset 0x48, not through BAR0. See Intel 100 Series
+ * Chipset Family PCH Datasheet Vol 2, section 4.3.53.
+ *
+ * The SLP_S0_RESIDENCY counter increments (100 us per step) while the
+ * PCH SLP_S0 signal is asserted. Per the datasheet, SLP_S0 asserts
+ * when the PCH is idle and the processor is in C10 state; in general
+ * this also requires ModPhy lanes power-gated and PLLs idle (Linux
+ * commit b740d2e9233cb). A counter that never advances indicates
+ * S0ix transitions are not occurring. The counter is 32-bit and
+ * wraps after approximately 5 days of cumulative S0ix residency.
+ *
+ * LTR_IGNORE (PWRM+0x30C) has one bit per IP block. Setting a bit
+ * tells the PMC to ignore that IP's LTR requirements, allowing S0ix
+ * entry even if the IP would otherwise block it. A clear bit means
+ * the IP's LTR requirements are considered and can prevent S0ix.
+ *
+ * SPT bit assignments (0-16):
+ * 0=SOUTHPORT_A 1=SOUTHPORT_B 2=SATA
+ * 3=GIGABIT_ETHERNET 4=XHCI 5=reserved
+ * 6=ME 7=EVA 8=SOUTHPORT_C
+ * 9=HD_AUDIO 10=LPSS 11=SOUTHPORT_D
+ * 12=SOUTHPORT_E 13=CAMERA 14=ESPI
+ * 15=SCC 16=ISH
+ *
+ * Ref: Intel 100 Series Chipset Family PCH Datasheet Vol 2, section 4.3
+ * Ref: Linux commit 9c2ee19987ef (initial SPT PMC support)
+ * Ref: Linux commit 2eb150558bb7 (LTR IP block documentation)
+ */
+
+#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 <vm/vm.h>
+#include <vm/pmap.h>
+#include <machine/pmap.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.
+ * LTR_IGNORE is not documented there; the offset comes from Linux
+ * commit 9c2ee19987ef (initial SPT PMC support).
+ */
+#define SPT_PMC_ACTL_OFFSET 0x44 /* ACPI Control */
+#define SPT_PMC_ACTL_PWRM_EN (1u << 8) /* PWRM decode enabled */
+#define SPT_PMC_PWRM_BASE_OFFSET 0x48 /* PCI config offset for PWRM */
+#define SPT_PMC_PWRM_BASE_MASK 0xfffff000
+#define SPT_PMC_MMIO_SIZE 0x1000
+
+#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
+#define SPT_PMC_LTR_IGNORE_BITS 17 /* bits 0-16 are valid IP blocks */
+
+struct intel_pmc_softc {
+ bus_space_tag_t bst;
+ bus_space_handle_t bsh;
+};
+
+/*
+ * Only Sunrise Point - other generations have different layouts.
+ * 0xa121 is documented in the Intel 100 Series Chipset Family PCH
+ * Datasheet Vol 2. Sunrise Point-LP has no public datasheet; 0x9d21
+ * comes from the pci_vendors database, where Linux also sourced it.
+ */
+static const struct pci_device_table intel_pmc_devices[] = {
+ { PCI_DEV(0x8086, 0x9d21), PCI_DESCR("Sunrise Point-LP PMC")},
+ { PCI_DEV(0x8086, 0xa121), PCI_DESCR("Sunrise Point-H PMC")},
+};
+
+static inline uint32_t
+intel_pmc_read(struct intel_pmc_softc *sc, uint32_t offset)
+{
+
+ return (bus_space_read_4(sc->bst, sc->bsh, offset));
+}
+
+static inline void
+intel_pmc_write(struct intel_pmc_softc *sc, uint32_t offset, uint32_t val)
+{
+
+ bus_space_write_4(sc->bst, sc->bsh, offset, val);
+}
+
+static int
+intel_pmc_slp_s0_sysctl(SYSCTL_HANDLER_ARGS)
+{
+ struct intel_pmc_softc *sc = oidp->oid_arg1;
+ uint64_t val, usec;
+
+ val = intel_pmc_read(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
+intel_pmc_ltr_ignore_sysctl(SYSCTL_HANDLER_ARGS)
+{
+ struct intel_pmc_softc *sc = oidp->oid_arg1;
+ uint32_t val;
+ int error;
+
+ val = intel_pmc_read(sc, SPT_PMC_LTR_IGNORE_OFFSET);
+ error = sysctl_handle_32(oidp, &val, 0, req);
+ if (error != 0 || req->newptr == NULL)
+ return (error);
+ /* Only bits 0-16 are valid IP blocks */
+ val &= (1u << SPT_PMC_LTR_IGNORE_BITS) - 1;
+ intel_pmc_write(sc, SPT_PMC_LTR_IGNORE_OFFSET, val);
+ return (0);
+}
+
+static int
+intel_pmc_probe(device_t dev)
+{
+ const struct pci_device_table *tbl;
+
+ tbl = PCI_MATCH(dev, intel_pmc_devices);
+ if (tbl == NULL)
+ return (ENXIO);
+ device_set_desc(dev, tbl->descr);
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+intel_pmc_attach(device_t dev)
+{
+ struct intel_pmc_softc *sc = device_get_softc(dev);
+ struct sysctl_ctx_list *ctx;
+ struct sysctl_oid *tree;
+ vm_paddr_t pwrm_base;
+
+ if ((pci_read_config(dev, SPT_PMC_ACTL_OFFSET, 4) &
+ SPT_PMC_ACTL_PWRM_EN) == 0) {
+ device_printf(dev, "PWRM decode not enabled by firmware\n");
+ return (ENXIO);
+ }
+ pwrm_base = pci_read_config(dev, SPT_PMC_PWRM_BASE_OFFSET, 4) &
+ SPT_PMC_PWRM_BASE_MASK;
+
+ sc->bst = X86_BUS_SPACE_MEM;
+ sc->bsh = (bus_space_handle_t)pmap_mapdev(pwrm_base,
+ SPT_PMC_MMIO_SIZE);
+
+ ctx = device_get_sysctl_ctx(dev);
+ tree = device_get_sysctl_tree(dev);
+
+ SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+ "ltr_ignore",
+ CTLTYPE_U32 | CTLFLAG_RW | CTLFLAG_MPSAFE,
+ sc, 0, intel_pmc_ltr_ignore_sysctl, "IU",
+ "LTR ignore mask: set bit = ignore IP, allowing S0ix");
+
+ SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+ "slp_s0_residency",
+ CTLTYPE_U64 | CTLFLAG_RD | CTLFLAG_MPSAFE,
+ sc, 0, intel_pmc_slp_s0_sysctl, "QU",
+ "SLP_S0 residency time in microseconds");
+
+ return (0);
+}
+
+static int
+intel_pmc_detach(device_t dev)
+{
+ struct intel_pmc_softc *sc = device_get_softc(dev);
+
+ pmap_unmapdev((void *)sc->bsh, SPT_PMC_MMIO_SIZE);
+ return (0);
+}
+
+static device_method_t intel_pmc_methods[] = {
+ DEVMETHOD(device_probe, intel_pmc_probe),
+ DEVMETHOD(device_attach, intel_pmc_attach),
+ DEVMETHOD(device_detach, intel_pmc_detach),
+ DEVMETHOD_END
+};
+
+static driver_t intel_pmc_driver = {
+ "intel_pmc",
+ intel_pmc_methods,
+ sizeof(struct intel_pmc_softc)
+};
+
+DRIVER_MODULE(intel_pmc, pci, intel_pmc_driver, 0, 0);
+PCI_PNP_INFO(intel_pmc_devices);
+MODULE_VERSION(intel_pmc, 1);
+MODULE_DEPEND(intel_pmc, pci, 1, 1, 1);
diff --git a/sys/modules/Makefile b/sys/modules/Makefile
index 2c3553aea2f3..0b196b72c079 100644
--- a/sys/modules/Makefile
+++ b/sys/modules/Makefile
@@ -178,6 +178,7 @@ SUBDIR= \
${_igc} \
imgact_binmisc \
${_imx} \
+ ${_intel_pmc} \
${_intelspi} \
${_io} \
${_ioat} \
@@ -831,6 +832,7 @@ _hptrr= hptrr
_hyperv= hyperv
_ichwd= ichwd
_ida= ida
+_intel_pmc= intel_pmc
_intelspi= intelspi
_ips= ips
_isci= isci
diff --git a/sys/modules/intel_pmc/Makefile b/sys/modules/intel_pmc/Makefile
new file mode 100644
index 000000000000..5f68a19f4b3e
--- /dev/null
+++ b/sys/modules/intel_pmc/Makefile
@@ -0,0 +1,6 @@
+.PATH: ${SRCTOP}/sys/dev/intel
+KMOD= intel_pmc
+SRCS= intel_pmc.c
+SRCS+= device_if.h bus_if.h pci_if.h
+
+.include <bsd.kmod.mk>