summaryrefslogtreecommitdiff
path: root/src/device
diff options
context:
space:
mode:
Diffstat (limited to 'src/device')
-rw-r--r--src/device/Kconfig44
-rw-r--r--src/device/pci_device.c10
-rw-r--r--src/device/pciexp_device.c59
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) */