1 /* 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2026 Abdelkader Boudih <seuros@FreeBSD.org> 5 */ 6 7 /* 8 * intel_pmc -- Intel Performance Management Controller (PMC) driver 9 * 10 * Exposes information and controls from the PMC of Intel platforms. 11 * Only the Sunrise Point (SPT) platform, paired with Skylake 12 * generation processors, is currently supported; everything below 13 * describes that hardware only. 14 * 15 * The driver reports the cumulative time spent in low power mode 16 * (S0ix, Modern Standby) while in S0, and lets the administrator 17 * force the PMC to ignore IP blocks whose Latency Tolerance 18 * Reporting (LTR) prevents the platform from entering S0ix. 19 * 20 * The PMC register space (PWRM) is mapped via the PWRM_BASE register 21 * at PCI config offset 0x48, not through BAR0. See Intel 100 Series 22 * Chipset Family PCH Datasheet Vol 2, section 4.3.53. 23 * 24 * The SLP_S0_RESIDENCY counter increments (100 us per step) while the 25 * PCH SLP_S0 signal is asserted. Per the datasheet, SLP_S0 asserts 26 * when the PCH is idle and the processor is in C10 state; in general 27 * this also requires ModPhy lanes power-gated and PLLs idle (Linux 28 * commit b740d2e9233cb). A counter that never advances indicates 29 * S0ix transitions are not occurring. The counter is 32-bit and 30 * wraps after approximately 5 days of cumulative S0ix residency. 31 * 32 * LTR_IGNORE (PWRM+0x30C) has one bit per IP block. Setting a bit 33 * tells the PMC to ignore that IP's LTR requirements, allowing S0ix 34 * entry even if the IP would otherwise block it. A clear bit means 35 * the IP's LTR requirements are considered and can prevent S0ix. 36 * 37 * SPT bit assignments (0-16): 38 * 0=SOUTHPORT_A 1=SOUTHPORT_B 2=SATA 39 * 3=GIGABIT_ETHERNET 4=XHCI 5=reserved 40 * 6=ME 7=EVA 8=SOUTHPORT_C 41 * 9=HD_AUDIO 10=LPSS 11=SOUTHPORT_D 42 * 12=SOUTHPORT_E 13=CAMERA 14=ESPI 43 * 15=SCC 16=ISH 44 * 45 * Ref: Intel 100 Series Chipset Family PCH Datasheet Vol 2, section 4.3 46 * Ref: Linux commit 9c2ee19987ef (initial SPT PMC support) 47 * Ref: Linux commit 2eb150558bb7 (LTR IP block documentation) 48 */ 49 50 #include <sys/param.h> 51 #include <sys/bus.h> 52 #include <sys/errno.h> 53 #include <sys/kernel.h> 54 #include <sys/module.h> 55 #include <sys/rman.h> 56 #include <sys/sysctl.h> 57 #include <sys/systm.h> 58 59 #include <machine/bus.h> 60 #include <machine/resource.h> 61 62 #include <vm/vm.h> 63 #include <vm/pmap.h> 64 #include <machine/pmap.h> 65 66 #include <dev/pci/pcireg.h> 67 #include <dev/pci/pcivar.h> 68 69 /* 70 * Sunrise Point (SPT) PMC Register Definitions 71 * Reference: Intel 100 Series Chipset Family PCH Datasheet Vol 2. 72 * LTR_IGNORE is not documented there; the offset comes from Linux 73 * commit 9c2ee19987ef (initial SPT PMC support). 74 */ 75 #define SPT_PMC_ACTL_OFFSET 0x44 /* ACPI Control */ 76 #define SPT_PMC_ACTL_PWRM_EN (1u << 8) /* PWRM decode enabled */ 77 #define SPT_PMC_PWRM_BASE_OFFSET 0x48 /* PCI config offset for PWRM */ 78 #define SPT_PMC_PWRM_BASE_MASK 0xfffff000 79 #define SPT_PMC_MMIO_SIZE 0x1000 80 81 #define SPT_PMC_SLP_S0_RES_OFFSET 0x13C 82 #define SPT_PMC_SLP_S0_RES_STEP 100 /* 100us per datasheet */ 83 #define SPT_PMC_LTR_IGNORE_OFFSET 0x30C 84 #define SPT_PMC_LTR_IGNORE_BITS 17 /* bits 0-16 are valid IP blocks */ 85 86 struct intel_pmc_softc { 87 bus_space_tag_t bst; 88 bus_space_handle_t bsh; 89 }; 90 91 /* 92 * Only Sunrise Point - other generations have different layouts. 93 * 0xa121 is documented in the Intel 100 Series Chipset Family PCH 94 * Datasheet Vol 2. Sunrise Point-LP has no public datasheet; 0x9d21 95 * comes from the pci_vendors database, where Linux also sourced it. 96 */ 97 static const struct pci_device_table intel_pmc_devices[] = { 98 { PCI_DEV(0x8086, 0x9d21), PCI_DESCR("Sunrise Point-LP PMC")}, 99 { PCI_DEV(0x8086, 0xa121), PCI_DESCR("Sunrise Point-H PMC")}, 100 }; 101 102 static inline uint32_t 103 intel_pmc_read(struct intel_pmc_softc *sc, uint32_t offset) 104 { 105 106 return (bus_space_read_4(sc->bst, sc->bsh, offset)); 107 } 108 109 static inline void 110 intel_pmc_write(struct intel_pmc_softc *sc, uint32_t offset, uint32_t val) 111 { 112 113 bus_space_write_4(sc->bst, sc->bsh, offset, val); 114 } 115 116 static int 117 intel_pmc_slp_s0_sysctl(SYSCTL_HANDLER_ARGS) 118 { 119 struct intel_pmc_softc *sc = oidp->oid_arg1; 120 uint64_t val, usec; 121 122 val = intel_pmc_read(sc, SPT_PMC_SLP_S0_RES_OFFSET); 123 usec = val * SPT_PMC_SLP_S0_RES_STEP; 124 return (sysctl_handle_64(oidp, &usec, 0, req)); 125 } 126 127 static int 128 intel_pmc_ltr_ignore_sysctl(SYSCTL_HANDLER_ARGS) 129 { 130 struct intel_pmc_softc *sc = oidp->oid_arg1; 131 uint32_t val; 132 int error; 133 134 val = intel_pmc_read(sc, SPT_PMC_LTR_IGNORE_OFFSET); 135 error = sysctl_handle_32(oidp, &val, 0, req); 136 if (error != 0 || req->newptr == NULL) 137 return (error); 138 /* Only bits 0-16 are valid IP blocks */ 139 val &= (1u << SPT_PMC_LTR_IGNORE_BITS) - 1; 140 intel_pmc_write(sc, SPT_PMC_LTR_IGNORE_OFFSET, val); 141 return (0); 142 } 143 144 static int 145 intel_pmc_probe(device_t dev) 146 { 147 const struct pci_device_table *tbl; 148 149 tbl = PCI_MATCH(dev, intel_pmc_devices); 150 if (tbl == NULL) 151 return (ENXIO); 152 device_set_desc(dev, tbl->descr); 153 return (BUS_PROBE_DEFAULT); 154 } 155 156 static int 157 intel_pmc_attach(device_t dev) 158 { 159 struct intel_pmc_softc *sc = device_get_softc(dev); 160 struct sysctl_ctx_list *ctx; 161 struct sysctl_oid *tree; 162 vm_paddr_t pwrm_base; 163 164 if ((pci_read_config(dev, SPT_PMC_ACTL_OFFSET, 4) & 165 SPT_PMC_ACTL_PWRM_EN) == 0) { 166 device_printf(dev, "PWRM decode not enabled by firmware\n"); 167 return (ENXIO); 168 } 169 pwrm_base = pci_read_config(dev, SPT_PMC_PWRM_BASE_OFFSET, 4) & 170 SPT_PMC_PWRM_BASE_MASK; 171 172 sc->bst = X86_BUS_SPACE_MEM; 173 sc->bsh = (bus_space_handle_t)pmap_mapdev(pwrm_base, 174 SPT_PMC_MMIO_SIZE); 175 176 ctx = device_get_sysctl_ctx(dev); 177 tree = device_get_sysctl_tree(dev); 178 179 SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 180 "ltr_ignore", 181 CTLTYPE_U32 | CTLFLAG_RW | CTLFLAG_MPSAFE, 182 sc, 0, intel_pmc_ltr_ignore_sysctl, "IU", 183 "LTR ignore mask: set bit = ignore IP, allowing S0ix"); 184 185 SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 186 "slp_s0_residency", 187 CTLTYPE_U64 | CTLFLAG_RD | CTLFLAG_MPSAFE, 188 sc, 0, intel_pmc_slp_s0_sysctl, "QU", 189 "SLP_S0 residency time in microseconds"); 190 191 return (0); 192 } 193 194 static int 195 intel_pmc_detach(device_t dev) 196 { 197 struct intel_pmc_softc *sc = device_get_softc(dev); 198 199 pmap_unmapdev((void *)sc->bsh, SPT_PMC_MMIO_SIZE); 200 return (0); 201 } 202 203 static device_method_t intel_pmc_methods[] = { 204 DEVMETHOD(device_probe, intel_pmc_probe), 205 DEVMETHOD(device_attach, intel_pmc_attach), 206 DEVMETHOD(device_detach, intel_pmc_detach), 207 DEVMETHOD_END 208 }; 209 210 static driver_t intel_pmc_driver = { 211 "intel_pmc", 212 intel_pmc_methods, 213 sizeof(struct intel_pmc_softc) 214 }; 215 216 DRIVER_MODULE(intel_pmc, pci, intel_pmc_driver, 0, 0); 217 PCI_PNP_INFO(intel_pmc_devices); 218 MODULE_VERSION(intel_pmc, 1); 219 MODULE_DEPEND(intel_pmc, pci, 1, 1, 1); 220