@@ -99,6 +99,64 @@ void pci_bus_remove_resources(struct pci_bus *bus)
}
/**
+ * pci_bus_to_resource
+ *
+ * Much like pcibios_bus_to_resource() except it takes a single address
+ * and returns an approximate one if target address is not included
+ * in the bridge window. The approximate address is smaller than required
+ * one is 'bound' is 1, larger than required one if 'bound' is 0.
+ */
+static resource_size_t pci_bus_to_resource(struct pci_bus *bus, int flags,
+ int mask, resource_size_t addr, int bound)
+{
+ struct pci_host_bridge *bridge;
+ struct pci_host_bridge_window *window, *match = NULL;
+ resource_size_t max = 0, min = -1;
+ resource_size_t offset = -1, start, end;
+
+ while (bus->parent)
+ bus = bus->parent;
+
+ bridge = to_pci_host_bridge(bus->bridge);
+
+ list_for_each_entry(window, &bridge->windows, list) {
+ if ((flags ^ window->res->flags) & mask)
+ continue;
+
+ start = window->res->start - window->offset;
+ end = window->res->end - window->offset;
+
+ if (addr >= start && addr <= end) {
+ offset = window->offset;
+ break;
+ }
+
+ if (bound && addr > end && end > max) {
+ max = end;
+ match = window;
+ } else if (!bound && addr < start && start < min) {
+ min = start;
+ match = window;
+ }
+ }
+
+ if (offset == -1) {
+ /*
+ * Not even found the matched type. This may happen,
+ * for example, if try to translate IO address in a HB
+ * without IO window. Just return the original address,
+ * it will fail later anyway.
+ */
+ if (match == NULL)
+ return addr;
+
+ return (bound ? max : min) + match->offset;
+ }
+
+ return addr + offset;
+}
+
+/**
* pci_bus_alloc_resource - allocate a resource from a parent bus
* @bus: PCI bus
* @res: resource to allocate
@@ -129,9 +187,12 @@ pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res,
type_mask |= IORESOURCE_IO | IORESOURCE_MEM;
- /* don't allocate too high if the pref mem doesn't support 64bit*/
+ /* don't allocate too high if the pref mem doesn't support 64bit */
if (!(res->flags & IORESOURCE_MEM_64))
- max = PCIBIOS_MAX_MEM_32;
+ max = pci_bus_to_resource(bus, res->flags, type_mask,
+ PCIBIOS_MAX_MEM_32, 1);
+
+ min = pci_bus_to_resource(bus, res->flags, type_mask, min, 0);
pci_bus_for_each_resource(bus, r, i) {
if (!r)
In resource assignment code, limits are exerted to restrict allocated resource range. However these limits are PCI address but compared to CPU address in the end. Translated them before comparing. We can't just use pcibios_bus_to_resource because the limits may not included in the host bridge window. Introduce a help function to do this translation, if address missed, return an approximate one. Signed-off-by: Guo Chao <yan@linux.vnet.ibm.com> --- drivers/pci/bus.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 63 insertions(+), 2 deletions(-)