@@ -1882,6 +1882,7 @@ static void pci_bus_distribute_available_resources(struct pci_bus *bus,
resource_size_t available_mmio, resource_size_t available_mmio_pref)
{
resource_size_t remaining_io, remaining_mmio, remaining_mmio_pref;
+ resource_size_t io_start, mmio_start, mmio_pref_start;
unsigned int normal_bridges = 0, hotplug_bridges = 0;
struct resource *io_res, *mmio_res, *mmio_pref_res;
struct pci_dev *dev, *bridge = bus->self;
@@ -1946,11 +1947,16 @@ static void pci_bus_distribute_available_resources(struct pci_bus *bus,
remaining_mmio_pref -= resource_size(res);
}
+ io_start = io_res->start;
+ mmio_start = mmio_res->start;
+ mmio_pref_start = mmio_pref_res->start;
+
/*
* Go over devices on this bus and distribute the remaining
* resource space between hotplug bridges.
*/
for_each_pci_bridge(dev, bus) {
+ resource_size_t align;
struct pci_bus *b;
b = dev->subordinate;
@@ -1968,7 +1974,7 @@ static void pci_bus_distribute_available_resources(struct pci_bus *bus,
available_io, available_mmio,
available_mmio_pref);
} else if (dev->is_hotplug_bridge) {
- resource_size_t align, io, mmio, mmio_pref;
+ resource_size_t io, mmio, mmio_pref;
/*
* Distribute available extra resources equally
@@ -1981,11 +1987,13 @@ static void pci_bus_distribute_available_resources(struct pci_bus *bus,
io = div64_ul(available_io, hotplug_bridges);
io = min(ALIGN(io, align), remaining_io);
remaining_io -= io;
+ io_start += io;
align = pci_resource_alignment(bridge, mmio_res);
mmio = div64_ul(available_mmio, hotplug_bridges);
mmio = min(ALIGN(mmio, align), remaining_mmio);
remaining_mmio -= mmio;
+ mmio_start += mmio;
align = pci_resource_alignment(bridge, mmio_pref_res);
mmio_pref = div64_ul(available_mmio_pref,
@@ -1993,9 +2001,40 @@ static void pci_bus_distribute_available_resources(struct pci_bus *bus,
mmio_pref = min(ALIGN(mmio_pref, align),
remaining_mmio_pref);
remaining_mmio_pref -= mmio_pref;
+ mmio_pref_start += mmio_pref;
pci_bus_distribute_available_resources(b, add_list, io,
mmio, mmio_pref);
+ } else {
+ /*
+ * For normal bridges, track start of the parent
+ * bridge window to make sure we align the
+ * remaining space which is distributed to the
+ * hotplug bridges properly.
+ */
+ resource_size_t aligned;
+ struct resource *res;
+
+ res = &dev->resource[PCI_BRIDGE_RESOURCES + 0];
+ io_start += resource_size(res);
+ aligned = ALIGN(io_start,
+ pci_resource_alignment(dev, res));
+ if (aligned > io_start)
+ remaining_io -= aligned - io_start;
+
+ res = &dev->resource[PCI_BRIDGE_RESOURCES + 1];
+ mmio_start += resource_size(res);
+ aligned = ALIGN(mmio_start,
+ pci_resource_alignment(dev, res));
+ if (aligned > mmio_start)
+ remaining_mmio -= aligned - mmio_start;
+
+ res = &dev->resource[PCI_BRIDGE_RESOURCES + 2];
+ mmio_pref_start += resource_size(res);
+ aligned = ALIGN(mmio_pref_start,
+ pci_resource_alignment(dev, res));
+ if (aligned > mmio_pref_start)
+ remaining_mmio_pref -= aligned - mmio_pref_start;
}
}
}