diff options
Diffstat (limited to 'src/device')
-rw-r--r-- | src/device/Kconfig | 44 | ||||
-rw-r--r-- | src/device/pci_device.c | 10 | ||||
-rw-r--r-- | src/device/pciexp_device.c | 59 |
3 files changed, 112 insertions, 1 deletions
diff --git a/src/device/Kconfig b/src/device/Kconfig index 0bd9fe1d8b..a25bb911c9 100644 --- a/src/device/Kconfig +++ b/src/device/Kconfig @@ -555,6 +555,50 @@ 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 + +if PCIEXP_HOTPLUG + +config PCIEXP_HOTPLUG_BUSES + int "PCI Express Hotplug Buses" + default 32 + help + This is the number of buses allocated for hotplug PCI express + bridges, for use by hotplugged child devices. The default is 32 + buses. + +config PCIEXP_HOTPLUG_MEM + hex "PCI Express Hotplug Memory" + default 0x800000 + help + This is the amount of memory space, in bytes, to allocate to + hotplug PCI express bridges, for use by hotplugged child devices. + This size should be page-aligned. The default is 8 MiB. + +config PCIEXP_HOTPLUG_PREFETCH_MEM + hex "PCI Express Hotplug Prefetch Memory" + default 0x10000000 + help + This is the amount of pre-fetchable memory space, in bytes, to + allocate to hot-plug PCI express bridges, for use by hotplugged + child devices. This size should be page-aligned. The default is + 256 MiB. + +config PCIEXP_HOTPLUG_IO + hex "PCI Express Hotplug I/O Space" + default 0x2000 + help + This is the amount of I/O space to allocate to hot-plug PCI + express bridges, for use by hotplugged child devices. The default + is 8 KiB. + +endif # PCIEXP_HOTPLUG + endif # PCIEXP_PLUGIN_SUPPORT config EARLY_PCI_BRIDGE diff --git a/src/device/pci_device.c b/src/device/pci_device.c index 36b7c82d2a..47c0e9f2d2 100644 --- a/src/device/pci_device.c +++ b/src/device/pci_device.c @@ -878,6 +878,14 @@ static struct device_operations *get_pci_bridge_ops(struct device *dev) case PCI_EXP_TYPE_DOWNSTREAM: printk(BIOS_DEBUG, "%s subordinate bus PCI Express\n", dev_path(dev)); +#if CONFIG(PCIEXP_HOTPLUG) + u16 sltcap; + sltcap = pci_read_config16(dev, pciexpos + PCI_EXP_SLTCAP); + if (sltcap & PCI_EXP_SLTCAP_HPC) { + printk(BIOS_DEBUG, "%s hot-plug capable\n", dev_path(dev)); + return &default_pciexp_hotplug_ops_bus; + } +#endif /* CONFIG(PCIEXP_HOTPLUG) */ return &default_pciexp_ops_bus; case PCI_EXP_TYPE_PCI_BRIDGE: printk(BIOS_DEBUG, "%s subordinate PCI\n", @@ -1259,7 +1267,7 @@ static void pci_bridge_route(struct bus *link, scan_state state) if (state == PCI_ROUTE_SCAN) { link->secondary = parent->subordinate + 1; - link->subordinate = link->secondary; + link->subordinate = link->secondary + dev->hotplug_buses; } if (state == PCI_ROUTE_CLOSE) { diff --git a/src/device/pciexp_device.c b/src/device/pciexp_device.c index 479891c5d6..b0ad1450e0 100644 --- a/src/device/pciexp_device.c +++ b/src/device/pciexp_device.c @@ -518,3 +518,62 @@ struct device_operations default_pciexp_ops_bus = { .reset_bus = pci_bus_reset, .ops_pci = &pciexp_bus_ops_pci, }; + +#if CONFIG(PCIEXP_HOTPLUG) + +static void pciexp_hotplug_dummy_read_resources(struct device *dev) +{ + struct resource *resource; + + // Add extra memory space + resource = new_resource(dev, 0x10); + resource->size = CONFIG_PCIEXP_HOTPLUG_MEM; + resource->align = 12; + resource->gran = 12; + resource->limit = 0xffffffff; + resource->flags |= IORESOURCE_MEM; + + // Add extra prefetchable memory space + resource = new_resource(dev, 0x14); + resource->size = CONFIG_PCIEXP_HOTPLUG_PREFETCH_MEM; + resource->align = 12; + resource->gran = 12; + resource->limit = 0xffffffffffffffff; + resource->flags |= IORESOURCE_MEM | IORESOURCE_PREFETCH; + + // Add extra I/O space + resource = new_resource(dev, 0x18); + resource->size = CONFIG_PCIEXP_HOTPLUG_IO; + resource->align = 12; + resource->gran = 12; + resource->limit = 0xffff; + resource->flags |= IORESOURCE_IO; +} + +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 = CONFIG_PCIEXP_HOTPLUG_BUSES; + + /* Normal PCIe Scan */ + pciexp_scan_bridge(dev); + + /* Add dummy slot to preserve resources, must happen after bus scan */ + 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) */ |