diff options
Diffstat (limited to 'drivers/pci')
-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; |