diff --git a/src/device/Kconfig b/src/device/Kconfig index 97ed997aee..59a43754f7 100644 --- a/src/device/Kconfig +++ b/src/device/Kconfig @@ -533,6 +533,13 @@ config PCIEXP_L1_SUB_STATE help Detect and enable ASPM on PCIe links. +config PCIEXP_HOTPLUG + prompt "Enable PCIe Hotplug Support" + bool + default n + help + Allocate resources for PCIe hotplug bridges + endif # PCIEXP_PLUGIN_SUPPORT config EARLY_PCI_BRIDGE diff --git a/src/device/pci_device.c b/src/device/pci_device.c index 1796d818ff..f1b52856de 100644 --- a/src/device/pci_device.c +++ b/src/device/pci_device.c @@ -828,14 +828,22 @@ static struct device_operations *get_pci_bridge_ops(struct device *dev) unsigned int pciexpos; pciexpos = pci_find_capability(dev, PCI_CAP_ID_PCIE); if (pciexpos) { - u16 flags; + u16 flags, sltcap; flags = pci_read_config16(dev, pciexpos + PCI_EXP_FLAGS); + sltcap = pci_read_config16(dev, pciexpos + PCI_EXP_SLTCAP); + printk(BIOS_DEBUG, "%s sltcap %x\n", dev_path(dev), sltcap); switch ((flags & PCI_EXP_FLAGS_TYPE) >> 4) { case PCI_EXP_TYPE_ROOT_PORT: case PCI_EXP_TYPE_UPSTREAM: case PCI_EXP_TYPE_DOWNSTREAM: printk(BIOS_DEBUG, "%s subordinate bus PCI Express\n", dev_path(dev)); +#if CONFIG(PCIEXP_HOTPLUG) + if (sltcap & PCI_EXP_SLTCAP_HPC) { + printk(BIOS_DEBUG, "%s hot-plug capable\n", dev_path(dev)); + return &default_pciexp_hotplug_ops_bus; + } else +#endif /* CONFIG(PCIEXP_HOTPLUG) */ return &default_pciexp_ops_bus; case PCI_EXP_TYPE_PCI_BRIDGE: printk(BIOS_DEBUG, "%s subordinate PCI\n", diff --git a/src/device/pciexp_device.c b/src/device/pciexp_device.c index 9a45be87a9..26c2c338f4 100644 --- a/src/device/pciexp_device.c +++ b/src/device/pciexp_device.c @@ -475,3 +475,79 @@ struct device_operations default_pciexp_ops_bus = { .reset_bus = pci_bus_reset, .ops_pci = &pciexp_bus_ops_pci, }; + +#if CONFIG(PCIEXP_HOTPLUG) +#define PCIEXP_HOTPLUG_BUSES 32 +#define PCIEXP_HOTPLUG_MEM (256 * 1024 * 1024) +#define PCIEXP_HOTPLUG_IO (8 * 1024) + +static void pciexp_hotplug_dummy_read_resources(struct device *dev) +{ + struct resource *resource; + + // Add extra memory space + resource = new_resource(dev, 0x10); + resource->size = PCIEXP_HOTPLUG_MEM; + resource->align = 22; + resource->gran = 22; + resource->limit = 0xffffffff; + resource->flags |= IORESOURCE_MEM; + printk(BIOS_DEBUG, "%s: add 0x%llx of memory space\n", __func__, resource->size); + + // Add extra prefetchable memory space + resource = new_resource(dev, 0x14); + resource->size = PCIEXP_HOTPLUG_MEM; + resource->align = 22; + resource->gran = 22; + resource->limit = 0xffffffff; + resource->flags |= IORESOURCE_MEM | IORESOURCE_PREFETCH; + printk(BIOS_DEBUG, "%s: add 0x%llx of prefetch memory space\n", __func__, resource->size); + + // Add extra I/O space + resource = new_resource(dev, 0x18); + resource->size = PCIEXP_HOTPLUG_IO; + resource->align = 12; + resource->gran = 12; + resource->limit = 0xffff; + resource->flags |= IORESOURCE_IO; + printk(BIOS_DEBUG, "%s: add 0x%llx of I/O space\n", __func__, resource->size); +} + +static struct device_operations pciexp_hotplug_dummy_ops = { + .read_resources = pciexp_hotplug_dummy_read_resources, +}; + +void pciexp_hotplug_scan_bridge(struct device *dev) +{ + dev->hotplug_buses = PCIEXP_HOTPLUG_BUSES; + printk( + BIOS_DEBUG, + "%s set hotplug_buses to %d\n", + dev_path(dev), + dev->hotplug_buses + ); + + /* Normal PCIe Scan */ + pciexp_scan_bridge(dev); + + /* Add dummy slot to preserve resources, must happen after bus scan */ + printk( + BIOS_DEBUG, + "%s: add hotplug dummy device\n", + dev_path(dev) + ); + struct device *dummy; + struct device_path dummy_path = { .type = DEVICE_PATH_NONE }; + dummy = alloc_dev(dev->link_list, &dummy_path); + dummy->ops = &pciexp_hotplug_dummy_ops; +} + +struct device_operations default_pciexp_hotplug_ops_bus = { + .read_resources = pci_bus_read_resources, + .set_resources = pci_dev_set_resources, + .enable_resources = pci_bus_enable_resources, + .scan_bus = pciexp_hotplug_scan_bridge, + .reset_bus = pci_bus_reset, + .ops_pci = &pciexp_bus_ops_pci, +}; +#endif /* CONFIG(PCIEXP_HOTPLUG) */ diff --git a/src/include/device/pci_def.h b/src/include/device/pci_def.h index bc5bc79e28..c9c9575b01 100644 --- a/src/include/device/pci_def.h +++ b/src/include/device/pci_def.h @@ -433,6 +433,7 @@ #define PCI_EXP_LNKSTA_LT 0x800 /* Link Training */ #define PCI_EXP_LNKSTA_SLC 0x1000 /* Slot Clock Configuration */ #define PCI_EXP_SLTCAP 20 /* Slot Capabilities */ +#define PCI_EXP_SLTCAP_HPC 0x0040 /* Hot-Plug Capable */ #define PCI_EXP_SLTCTL 24 /* Slot Control */ #define PCI_EXP_SLTSTA 26 /* Slot Status */ #define PCI_EXP_RTCTL 28 /* Root Control */ diff --git a/src/include/device/pciexp.h b/src/include/device/pciexp.h index 3a9825d871..44914063f6 100644 --- a/src/include/device/pciexp.h +++ b/src/include/device/pciexp.h @@ -26,5 +26,11 @@ void pciexp_scan_bridge(struct device *dev); extern struct device_operations default_pciexp_ops_bus; +#if CONFIG(PCIEXP_HOTPLUG) +void pciexp_hotplug_scan_bridge(struct device *dev); + +extern struct device_operations default_pciexp_hotplug_ops_bus; +#endif /* CONFIG(PCIEXP_HOTPLUG) */ + unsigned int pciexp_find_extended_cap(struct device *dev, unsigned int cap); #endif /* DEVICE_PCIEXP_H */ diff --git a/src/mainboard/system76/cml-u/Kconfig b/src/mainboard/system76/cml-u/Kconfig index 58adab8c4b..49cf486aab 100644 --- a/src/mainboard/system76/cml-u/Kconfig +++ b/src/mainboard/system76/cml-u/Kconfig @@ -16,6 +16,7 @@ config BOARD_SPECIFIC_OPTIONS # select MAINBOARD_HAS_TPM2 select NO_UART_ON_SUPERIO select PCIE_DEBUG_INFO + select PCIEXP_HOTPLUG select SOC_INTEL_COMETLAKE select SOC_INTEL_COMMON_ACPI_EC_PTS_WAK select SOC_INTEL_COMMON_BLOCK_HDA