diff options
Diffstat (limited to 'drivers/pci/bus.c')
| -rw-r--r-- | drivers/pci/bus.c | 53 |
1 files changed, 48 insertions, 5 deletions
diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c index 7f0af0e9b826..172bf26e0680 100644 --- a/drivers/pci/bus.c +++ b/drivers/pci/bus.c | |||
| @@ -64,6 +64,49 @@ void pci_bus_remove_resources(struct pci_bus *bus) | |||
| 64 | } | 64 | } |
| 65 | } | 65 | } |
| 66 | 66 | ||
| 67 | /* | ||
| 68 | * Find the highest-address bus resource below the cursor "res". If the | ||
| 69 | * cursor is NULL, return the highest resource. | ||
| 70 | */ | ||
| 71 | static struct resource *pci_bus_find_resource_prev(struct pci_bus *bus, | ||
| 72 | unsigned int type, | ||
| 73 | struct resource *res) | ||
| 74 | { | ||
| 75 | struct resource *r, *prev = NULL; | ||
| 76 | int i; | ||
| 77 | |||
| 78 | pci_bus_for_each_resource(bus, r, i) { | ||
| 79 | if (!r) | ||
| 80 | continue; | ||
| 81 | |||
| 82 | if ((r->flags & IORESOURCE_TYPE_BITS) != type) | ||
| 83 | continue; | ||
| 84 | |||
| 85 | /* If this resource is at or past the cursor, skip it */ | ||
| 86 | if (res) { | ||
| 87 | if (r == res) | ||
| 88 | continue; | ||
| 89 | if (r->end > res->end) | ||
| 90 | continue; | ||
| 91 | if (r->end == res->end && r->start > res->start) | ||
| 92 | continue; | ||
| 93 | } | ||
| 94 | |||
| 95 | if (!prev) | ||
| 96 | prev = r; | ||
| 97 | |||
| 98 | /* | ||
| 99 | * A small resource is higher than a large one that ends at | ||
| 100 | * the same address. | ||
| 101 | */ | ||
| 102 | if (r->end > prev->end || | ||
| 103 | (r->end == prev->end && r->start > prev->start)) | ||
| 104 | prev = r; | ||
| 105 | } | ||
| 106 | |||
| 107 | return prev; | ||
| 108 | } | ||
| 109 | |||
| 67 | /** | 110 | /** |
| 68 | * pci_bus_alloc_resource - allocate a resource from a parent bus | 111 | * pci_bus_alloc_resource - allocate a resource from a parent bus |
| 69 | * @bus: PCI bus | 112 | * @bus: PCI bus |
| @@ -89,9 +132,10 @@ pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res, | |||
| 89 | resource_size_t), | 132 | resource_size_t), |
| 90 | void *alignf_data) | 133 | void *alignf_data) |
| 91 | { | 134 | { |
| 92 | int i, ret = -ENOMEM; | 135 | int ret = -ENOMEM; |
| 93 | struct resource *r; | 136 | struct resource *r; |
| 94 | resource_size_t max = -1; | 137 | resource_size_t max = -1; |
| 138 | unsigned int type = res->flags & IORESOURCE_TYPE_BITS; | ||
| 95 | 139 | ||
| 96 | type_mask |= IORESOURCE_IO | IORESOURCE_MEM; | 140 | type_mask |= IORESOURCE_IO | IORESOURCE_MEM; |
| 97 | 141 | ||
| @@ -99,10 +143,9 @@ pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res, | |||
| 99 | if (!(res->flags & IORESOURCE_MEM_64)) | 143 | if (!(res->flags & IORESOURCE_MEM_64)) |
| 100 | max = PCIBIOS_MAX_MEM_32; | 144 | max = PCIBIOS_MAX_MEM_32; |
| 101 | 145 | ||
| 102 | pci_bus_for_each_resource(bus, r, i) { | 146 | /* Look for space at highest addresses first */ |
| 103 | if (!r) | 147 | r = pci_bus_find_resource_prev(bus, type, NULL); |
| 104 | continue; | 148 | for ( ; r; r = pci_bus_find_resource_prev(bus, type, r)) { |
| 105 | |||
| 106 | /* type_mask must match */ | 149 | /* type_mask must match */ |
| 107 | if ((res->flags ^ r->flags) & type_mask) | 150 | if ((res->flags ^ r->flags) & type_mask) |
| 108 | continue; | 151 | continue; |
