PCI: Add support for root bus sizing

In certain cases we should be able to enumerate IO and MEM ranges of all
PCI devices installed in the system, and then set respective host bridge
apertures basing on calculated size and alignment.  Particularly when
firmware is broken and fails to assign bridge windows properly, like on
Alpha UP1500 platform.

Actually, almost everything is already in place, and required changes are
minimal:

- add "size_windows" flag to struct pci_host_bridge: when set, it
  instructs __pci_bus_size_bridges() to continue with the root bus;
- in the __pci_bus_size_bridges() path: add checks for bus->self,
  as it can legitimately be null for the root bus.

Link: https://lore.kernel.org/r/20200314194355.GA12510@mail.rc.ru
Tested-by: Matt Turner <mattst88@gmail.com>
Signed-off-by: Ivan Kokshaysky <ink@jurassic.park.msu.ru>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
This commit is contained in:
Ivan Kokshaysky 2020-03-14 19:43:55 +00:00 committed by Bjorn Helgaas
parent 72e0ef0e5f
commit 2c8d5a2dc1
2 changed files with 23 additions and 12 deletions

View File

@ -846,7 +846,7 @@ static resource_size_t window_alignment(struct pci_bus *bus, unsigned long type)
* Per spec, I/O windows are 4K-aligned, but some bridges have * Per spec, I/O windows are 4K-aligned, but some bridges have
* an extension to support 1K alignment. * an extension to support 1K alignment.
*/ */
if (bus->self->io_window_1k) if (bus->self && bus->self->io_window_1k)
align = PCI_P2P_DEFAULT_IO_ALIGN_1K; align = PCI_P2P_DEFAULT_IO_ALIGN_1K;
else else
align = PCI_P2P_DEFAULT_IO_ALIGN; align = PCI_P2P_DEFAULT_IO_ALIGN;
@ -920,7 +920,7 @@ static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size,
calculate_iosize(size, min_size, size1, add_size, children_add_size, calculate_iosize(size, min_size, size1, add_size, children_add_size,
resource_size(b_res), min_align); resource_size(b_res), min_align);
if (!size0 && !size1) { if (!size0 && !size1) {
if (b_res->start || b_res->end) if (bus->self && (b_res->start || b_res->end))
pci_info(bus->self, "disabling bridge window %pR to %pR (unused)\n", pci_info(bus->self, "disabling bridge window %pR to %pR (unused)\n",
b_res, &bus->busn_res); b_res, &bus->busn_res);
b_res->flags = 0; b_res->flags = 0;
@ -930,7 +930,7 @@ static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size,
b_res->start = min_align; b_res->start = min_align;
b_res->end = b_res->start + size0 - 1; b_res->end = b_res->start + size0 - 1;
b_res->flags |= IORESOURCE_STARTALIGN; b_res->flags |= IORESOURCE_STARTALIGN;
if (size1 > size0 && realloc_head) { if (bus->self && size1 > size0 && realloc_head) {
add_to_list(realloc_head, bus->self, b_res, size1-size0, add_to_list(realloc_head, bus->self, b_res, size1-size0,
min_align); min_align);
pci_info(bus->self, "bridge window %pR to %pR add_size %llx\n", pci_info(bus->self, "bridge window %pR to %pR add_size %llx\n",
@ -1073,7 +1073,7 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask,
calculate_memsize(size, min_size, add_size, children_add_size, calculate_memsize(size, min_size, add_size, children_add_size,
resource_size(b_res), add_align); resource_size(b_res), add_align);
if (!size0 && !size1) { if (!size0 && !size1) {
if (b_res->start || b_res->end) if (bus->self && (b_res->start || b_res->end))
pci_info(bus->self, "disabling bridge window %pR to %pR (unused)\n", pci_info(bus->self, "disabling bridge window %pR to %pR (unused)\n",
b_res, &bus->busn_res); b_res, &bus->busn_res);
b_res->flags = 0; b_res->flags = 0;
@ -1082,7 +1082,7 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask,
b_res->start = min_align; b_res->start = min_align;
b_res->end = size0 + min_align - 1; b_res->end = size0 + min_align - 1;
b_res->flags |= IORESOURCE_STARTALIGN; b_res->flags |= IORESOURCE_STARTALIGN;
if (size1 > size0 && realloc_head) { if (bus->self && size1 > size0 && realloc_head) {
add_to_list(realloc_head, bus->self, b_res, size1-size0, add_align); add_to_list(realloc_head, bus->self, b_res, size1-size0, add_align);
pci_info(bus->self, "bridge window %pR to %pR add_size %llx add_align %llx\n", pci_info(bus->self, "bridge window %pR to %pR add_size %llx add_align %llx\n",
b_res, &bus->busn_res, b_res, &bus->busn_res,
@ -1196,8 +1196,9 @@ void __pci_bus_size_bridges(struct pci_bus *bus, struct list_head *realloc_head)
unsigned long mask, prefmask, type2 = 0, type3 = 0; unsigned long mask, prefmask, type2 = 0, type3 = 0;
resource_size_t additional_io_size = 0, additional_mmio_size = 0, resource_size_t additional_io_size = 0, additional_mmio_size = 0,
additional_mmio_pref_size = 0; additional_mmio_pref_size = 0;
struct resource *b_res; struct resource *pref;
int ret; struct pci_host_bridge *host;
int hdr_type, i, ret;
list_for_each_entry(dev, &bus->devices, bus_list) { list_for_each_entry(dev, &bus->devices, bus_list) {
struct pci_bus *b = dev->subordinate; struct pci_bus *b = dev->subordinate;
@ -1217,10 +1218,20 @@ void __pci_bus_size_bridges(struct pci_bus *bus, struct list_head *realloc_head)
} }
/* The root bus? */ /* The root bus? */
if (pci_is_root_bus(bus)) if (pci_is_root_bus(bus)) {
return; host = to_pci_host_bridge(bus->bridge);
if (!host->size_windows)
return;
pci_bus_for_each_resource(bus, pref, i)
if (pref && (pref->flags & IORESOURCE_PREFETCH))
break;
hdr_type = -1; /* Intentionally invalid - not a PCI device. */
} else {
pref = &bus->self->resource[PCI_BRIDGE_RESOURCES + 2];
hdr_type = bus->self->hdr_type;
}
switch (bus->self->hdr_type) { switch (hdr_type) {
case PCI_HEADER_TYPE_CARDBUS: case PCI_HEADER_TYPE_CARDBUS:
/* Don't size CardBuses yet */ /* Don't size CardBuses yet */
break; break;
@ -1242,10 +1253,9 @@ void __pci_bus_size_bridges(struct pci_bus *bus, struct list_head *realloc_head)
* the size required to put all 64-bit prefetchable * the size required to put all 64-bit prefetchable
* resources in it. * resources in it.
*/ */
b_res = &bus->self->resource[PCI_BRIDGE_RESOURCES];
mask = IORESOURCE_MEM; mask = IORESOURCE_MEM;
prefmask = IORESOURCE_MEM | IORESOURCE_PREFETCH; prefmask = IORESOURCE_MEM | IORESOURCE_PREFETCH;
if (b_res[2].flags & IORESOURCE_MEM_64) { if (pref && (pref->flags & IORESOURCE_MEM_64)) {
prefmask |= IORESOURCE_MEM_64; prefmask |= IORESOURCE_MEM_64;
ret = pbus_size_mem(bus, prefmask, prefmask, ret = pbus_size_mem(bus, prefmask, prefmask,
prefmask, prefmask, prefmask, prefmask,

View File

@ -511,6 +511,7 @@ struct pci_host_bridge {
unsigned int native_pme:1; /* OS may use PCIe PME */ unsigned int native_pme:1; /* OS may use PCIe PME */
unsigned int native_ltr:1; /* OS may use PCIe LTR */ unsigned int native_ltr:1; /* OS may use PCIe LTR */
unsigned int preserve_config:1; /* Preserve FW resource setup */ unsigned int preserve_config:1; /* Preserve FW resource setup */
unsigned int size_windows:1; /* Enable root bus sizing */
/* Resource alignment requirements */ /* Resource alignment requirements */
resource_size_t (*align_resource)(struct pci_dev *dev, resource_size_t (*align_resource)(struct pci_dev *dev,