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 <matt.devillier@gmail.com>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/90913
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Sean Rhodes <sean@starlabs.systems>
This commit is contained in:
Matt DeVillier 2026-01-26 09:41:42 -06:00
commit a2a868f199

View file

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