Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F157948452
D54881.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
11 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
@@ -254,6 +254,7 @@
${_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
--- /dev/null
+++ b/share/man/man4/intel_pmc.4
@@ -0,0 +1,94 @@
+.\"
+.\" SPDX-License-Identifier: BSD-2-Clause
+.\"
+.\" Copyright (c) 2026 Abdelkader Boudih <freebsd@seuros.com>
+.\"
+.Dd May 21, 2026
+.Dt INTEL_PMC 4
+.Os
+.Sh NAME
+.Nm intel_pmc
+.Nd Intel PCH Power Management Controller driver
+.Sh SYNOPSIS
+.Cd "device intel_pmc"
+.Pp
+Alternatively, to load the driver as a
+module at boot time, place the following line in
+.Xr loader.conf 5 :
+.Bd -literal -offset indent
+intel_pmc_load="YES"
+.Ed
+.Sh DESCRIPTION
+The
+.Nm
+driver exposes sleep state residency counters and LTR block 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 the cumulative time the system has spent in S0ix and
+identifying which IP blocks are preventing deeper sleep states.
+.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 following Intel Platform Controller Hubs are supported by the
+.Nm
+driver:
+.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.access_denied
+Set to 1 if firmware has denied access to PMC registers.
+.It Va dev.intel_pmc.%d.ltr_ignore
+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
+Cumulative time in microseconds that the PCH has been in the deepest
+S0ix state (100\ us per counter step).
+The PCH enters this state when the CPU is in Package C10, PCH is in
+low power mode, ModPhy lanes are power-gated, and PLLs are idle.
+A value that never advances indicates S0ix transitions are not occurring.
+.Pp
+The counter is 32-bit and wraps after approximately 4.97 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 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/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
--- /dev/null
+++ b/sys/dev/intel/intel_pmc.c
@@ -0,0 +1,230 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2026 Abdelkader Boudih <freebsd@seuros.com>
+ */
+
+/*
+ * intel_pmc -- Intel PCH Power Management Controller driver
+ *
+ * Exposes sleep state residency counters and LTR block status
+ * for debugging S0ix (Modern Standby) power states.
+ *
+ * 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. 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
+ * A counter that never advances indicates S0ix transitions are not
+ * occurring. The counter is 32-bit and wraps after approximately
+ * 4.97 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
+ */
+#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;
+ int access_denied;
+};
+
+static const struct pci_device_table intel_pmc_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
+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;
+
+ if (sc->access_denied)
+ return (EACCES);
+ 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;
+
+ if (sc->access_denied)
+ return (EACCES);
+ 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;
+ uint32_t slp_s0, ltr_ign;
+
+ pwrm_base = pci_read_config(dev, SPT_PMC_PWRM_BASE_OFFSET, 4) &
+ SPT_PMC_PWRM_BASE_MASK;
+ if (pwrm_base == 0) {
+ device_printf(dev, "PWRM base not configured by firmware\n");
+ return (ENXIO);
+ }
+
+ sc->bst = X86_BUS_SPACE_MEM;
+ sc->bsh = (bus_space_handle_t)pmap_mapdev(pwrm_base,
+ SPT_PMC_MMIO_SIZE);
+
+ /*
+ * Detect firmware access lock: both key registers returning
+ * 0xFFFFFFFF indicates PCIe Unsupported Request completion,
+ * meaning the PMC MMIO BAR is inaccessible.
+ */
+ slp_s0 = intel_pmc_read(sc, SPT_PMC_SLP_S0_RES_OFFSET);
+ ltr_ign = intel_pmc_read(sc, SPT_PMC_LTR_IGNORE_OFFSET);
+
+ if (slp_s0 == 0xFFFFFFFF && ltr_ign == 0xFFFFFFFF) {
+ sc->access_denied = 1;
+ device_printf(dev, "PMC registers inaccessible "
+ "(locked by firmware)\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 locked PMC registers");
+
+ 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
--- a/sys/modules/Makefile
+++ b/sys/modules/Makefile
@@ -175,6 +175,7 @@
${_igc} \
imgact_binmisc \
${_imx} \
+ ${_intel_pmc} \
${_intelspi} \
${_io} \
${_ioat} \
@@ -822,6 +823,7 @@
_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
--- /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>
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Wed, May 27, 7:46 PM (5 h, 1 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
33563829
Default Alt Text
D54881.diff (11 KB)
Attached To
Mode
D54881: intel/intelpmc: Add Intel PMC Core driver
Attached
Detach File
Event Timeline
Log In to Comment