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
@@ -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

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)

Event Timeline