From a2a868f199c53f96f71a0cc85f4667cad4186f33 Mon Sep 17 00:00:00 2001 From: Matt DeVillier Date: Mon, 26 Jan 2026 09:41:42 -0600 Subject: [PATCH] device/pciexp: Enable ASPM on root ports without endpoints Program ASPM on PCIe root ports when no endpoint device is connected at boot. This ensures proper power management for TBT ports that often do not have devices connected at boot. Add pciexp_enable_aspm_root_port_only() to program ASPM based on the root port's Link Capabilities, and call it from pciexp_scan_bus() when no children are detected on a root port. TEST=build/boot Starlabs Starfighter MTL, verify ASPM enabled on TBT ports even when no devices attached via lspci: LnkCtrl: ASPM L1 Enabled Change-Id: I1da6d36afcbe18411c01ceabf8b903c4ae13cd73 Signed-off-by: Matt DeVillier Reviewed-on: https://review.coreboot.org/c/coreboot/+/90913 Tested-by: build bot (Jenkins) Reviewed-by: Sean Rhodes --- src/device/pciexp_device.c | 49 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/src/device/pciexp_device.c b/src/device/pciexp_device.c index ec90a22b74..3c2f06e013 100644 --- a/src/device/pciexp_device.c +++ b/src/device/pciexp_device.c @@ -563,6 +563,42 @@ static int pciexp_aspm_latency(struct device *root, unsigned int root_cap, return (endp_lat > root_lat) ? endp_lat : root_lat; } +/* + * Enable ASPM on PCIe root port when no endpoint is connected. + * This programs ASPM based on the root port's own capabilities. + */ +static void pciexp_enable_aspm_root_port_only(struct device *root, unsigned int root_cap) +{ + static const char * const aspm_type_str[] = { "None", "L0s", "L1", "L0s and L1" }; + enum aspm_type apmc = PCIE_ASPM_NONE; + u16 lnkctl; + u32 lnkcap; + + /* Read root port's ASPM support from Link Capabilities */ + lnkcap = pci_read_config32(root, root_cap + PCI_EXP_LNKCAP); + + /* Extract ASPM support bits (11:10) */ + u32 aspm_support = (lnkcap & PCI_EXP_LNKCAP_ASPMS) >> 10; + + /* Map ASPM support to control value: + * 00 = No ASPM -> apmc = 0 (NONE) + * 01 = L0s supported -> apmc = 1 (L0S) + * 10 = L1 supported -> apmc = 2 (L1) + * 11 = L0s and L1 supported -> apmc = 3 (BOTH) + */ + apmc = (enum aspm_type)aspm_support; + + if (apmc != PCIE_ASPM_NONE) { + /* Set ASPM control in root port's Link Control register */ + lnkctl = pci_read_config16(root, root_cap + PCI_EXP_LNKCTL); + /* Clear existing ASPM bits (0:1) and set new value */ + lnkctl = (lnkctl & ~0x3) | apmc; + pci_write_config16(root, root_cap + PCI_EXP_LNKCTL, lnkctl); + printk(BIOS_INFO, "%s: ASPM enabled %s (no endpoint)\n", + dev_path(root), aspm_type_str[apmc]); + } +} + /* * Enable ASPM on PCIe root port and endpoint. */ @@ -843,6 +879,8 @@ void pciexp_scan_bus(struct bus *bus, unsigned int min_devfn, { struct device *child; unsigned int max_payload; + bool has_children = false; + unsigned int root_cap; pciexp_enable_ltr(bus->dev); @@ -862,9 +900,20 @@ void pciexp_scan_bus(struct bus *bus, unsigned int min_devfn, (child->path.pci.devfn > max_devfn)) { continue; } + has_children = true; pciexp_tune_dev(child); } + /* + * If no endpoint is connected and this is a root port, program ASPM + * based on the root port's own capabilities. + */ + if (!has_children && pcie_is_root_port(bus->dev) && CONFIG(PCIEXP_ASPM)) { + root_cap = pci_find_capability(bus->dev, PCI_CAP_ID_PCIE); + if (root_cap) + pciexp_enable_aspm_root_port_only(bus->dev, root_cap); + } + /* * Now the root port's Max Payload Size should be set to the highest * possible value supported by all devices under a given root port.