diff options
author | Bjorn Helgaas <bhelgaas@google.com> | 2013-01-09 13:37:16 -0500 |
---|---|---|
committer | Bjorn Helgaas <bhelgaas@google.com> | 2013-01-09 13:37:16 -0500 |
commit | e84813c0cba2af80f0910484f5ba4931375f57ba (patch) | |
tree | c400c9c06465f3c6888bbc91fe759c51056e5463 /arch/x86 | |
parent | e84d0dedd324379bd784be748314b251334a79b6 (diff) | |
parent | b3e65e1f9185a2eb034defe4270ba178ba70b9a9 (diff) |
Merge branch 'pci/yinghai-survey-resources' into next
* pci/yinghai-survey-resources:
x86/PCI: Implement pcibios_resource_survey_bus()
PCI/ACPI: Reserve firmware-allocated resources for hot-added root buses
x86/PCI: Keep resource allocation functions after boot
x86/PCI: Don't track firmware-assigned BAR values for hot-added devices
x86/PCI: Factor out pcibios_allocate_dev_rom_resource()
x86/PCI: Allocate resources on a per-bus basis for hot-adding root buses
x86/PCI: Factor out pcibios_allocate_dev_resources()
x86/PCI: Factor out pcibios_allocate_bridge_resources()
Diffstat (limited to 'arch/x86')
-rw-r--r-- | arch/x86/pci/i386.c | 185 |
1 files changed, 121 insertions, 64 deletions
diff --git a/arch/x86/pci/i386.c b/arch/x86/pci/i386.c index dd8ca6f7223b..94919e307f8e 100644 --- a/arch/x86/pci/i386.c +++ b/arch/x86/pci/i386.c | |||
@@ -51,6 +51,7 @@ struct pcibios_fwaddrmap { | |||
51 | 51 | ||
52 | static LIST_HEAD(pcibios_fwaddrmappings); | 52 | static LIST_HEAD(pcibios_fwaddrmappings); |
53 | static DEFINE_SPINLOCK(pcibios_fwaddrmap_lock); | 53 | static DEFINE_SPINLOCK(pcibios_fwaddrmap_lock); |
54 | static bool pcibios_fw_addr_done; | ||
54 | 55 | ||
55 | /* Must be called with 'pcibios_fwaddrmap_lock' lock held. */ | 56 | /* Must be called with 'pcibios_fwaddrmap_lock' lock held. */ |
56 | static struct pcibios_fwaddrmap *pcibios_fwaddrmap_lookup(struct pci_dev *dev) | 57 | static struct pcibios_fwaddrmap *pcibios_fwaddrmap_lookup(struct pci_dev *dev) |
@@ -72,6 +73,9 @@ pcibios_save_fw_addr(struct pci_dev *dev, int idx, resource_size_t fw_addr) | |||
72 | unsigned long flags; | 73 | unsigned long flags; |
73 | struct pcibios_fwaddrmap *map; | 74 | struct pcibios_fwaddrmap *map; |
74 | 75 | ||
76 | if (pcibios_fw_addr_done) | ||
77 | return; | ||
78 | |||
75 | spin_lock_irqsave(&pcibios_fwaddrmap_lock, flags); | 79 | spin_lock_irqsave(&pcibios_fwaddrmap_lock, flags); |
76 | map = pcibios_fwaddrmap_lookup(dev); | 80 | map = pcibios_fwaddrmap_lookup(dev); |
77 | if (!map) { | 81 | if (!map) { |
@@ -97,6 +101,9 @@ resource_size_t pcibios_retrieve_fw_addr(struct pci_dev *dev, int idx) | |||
97 | struct pcibios_fwaddrmap *map; | 101 | struct pcibios_fwaddrmap *map; |
98 | resource_size_t fw_addr = 0; | 102 | resource_size_t fw_addr = 0; |
99 | 103 | ||
104 | if (pcibios_fw_addr_done) | ||
105 | return 0; | ||
106 | |||
100 | spin_lock_irqsave(&pcibios_fwaddrmap_lock, flags); | 107 | spin_lock_irqsave(&pcibios_fwaddrmap_lock, flags); |
101 | map = pcibios_fwaddrmap_lookup(dev); | 108 | map = pcibios_fwaddrmap_lookup(dev); |
102 | if (map) | 109 | if (map) |
@@ -106,7 +113,7 @@ resource_size_t pcibios_retrieve_fw_addr(struct pci_dev *dev, int idx) | |||
106 | return fw_addr; | 113 | return fw_addr; |
107 | } | 114 | } |
108 | 115 | ||
109 | static void pcibios_fw_addr_list_del(void) | 116 | static void __init pcibios_fw_addr_list_del(void) |
110 | { | 117 | { |
111 | unsigned long flags; | 118 | unsigned long flags; |
112 | struct pcibios_fwaddrmap *entry, *next; | 119 | struct pcibios_fwaddrmap *entry, *next; |
@@ -118,6 +125,7 @@ static void pcibios_fw_addr_list_del(void) | |||
118 | kfree(entry); | 125 | kfree(entry); |
119 | } | 126 | } |
120 | spin_unlock_irqrestore(&pcibios_fwaddrmap_lock, flags); | 127 | spin_unlock_irqrestore(&pcibios_fwaddrmap_lock, flags); |
128 | pcibios_fw_addr_done = true; | ||
121 | } | 129 | } |
122 | 130 | ||
123 | static int | 131 | static int |
@@ -193,46 +201,46 @@ EXPORT_SYMBOL(pcibios_align_resource); | |||
193 | * as well. | 201 | * as well. |
194 | */ | 202 | */ |
195 | 203 | ||
196 | static void __init pcibios_allocate_bus_resources(struct list_head *bus_list) | 204 | static void pcibios_allocate_bridge_resources(struct pci_dev *dev) |
197 | { | 205 | { |
198 | struct pci_bus *bus; | ||
199 | struct pci_dev *dev; | ||
200 | int idx; | 206 | int idx; |
201 | struct resource *r; | 207 | struct resource *r; |
202 | 208 | ||
203 | /* Depth-First Search on bus tree */ | 209 | for (idx = PCI_BRIDGE_RESOURCES; idx < PCI_NUM_RESOURCES; idx++) { |
204 | list_for_each_entry(bus, bus_list, node) { | 210 | r = &dev->resource[idx]; |
205 | if ((dev = bus->self)) { | 211 | if (!r->flags) |
206 | for (idx = PCI_BRIDGE_RESOURCES; | 212 | continue; |
207 | idx < PCI_NUM_RESOURCES; idx++) { | 213 | if (!r->start || pci_claim_resource(dev, idx) < 0) { |
208 | r = &dev->resource[idx]; | 214 | /* |
209 | if (!r->flags) | 215 | * Something is wrong with the region. |
210 | continue; | 216 | * Invalidate the resource to prevent |
211 | if (!r->start || | 217 | * child resource allocations in this |
212 | pci_claim_resource(dev, idx) < 0) { | 218 | * range. |
213 | /* | 219 | */ |
214 | * Something is wrong with the region. | 220 | r->start = r->end = 0; |
215 | * Invalidate the resource to prevent | 221 | r->flags = 0; |
216 | * child resource allocations in this | ||
217 | * range. | ||
218 | */ | ||
219 | r->start = r->end = 0; | ||
220 | r->flags = 0; | ||
221 | } | ||
222 | } | ||
223 | } | 222 | } |
224 | pcibios_allocate_bus_resources(&bus->children); | ||
225 | } | 223 | } |
226 | } | 224 | } |
227 | 225 | ||
226 | static void pcibios_allocate_bus_resources(struct pci_bus *bus) | ||
227 | { | ||
228 | struct pci_bus *child; | ||
229 | |||
230 | /* Depth-First Search on bus tree */ | ||
231 | if (bus->self) | ||
232 | pcibios_allocate_bridge_resources(bus->self); | ||
233 | list_for_each_entry(child, &bus->children, node) | ||
234 | pcibios_allocate_bus_resources(child); | ||
235 | } | ||
236 | |||
228 | struct pci_check_idx_range { | 237 | struct pci_check_idx_range { |
229 | int start; | 238 | int start; |
230 | int end; | 239 | int end; |
231 | }; | 240 | }; |
232 | 241 | ||
233 | static void __init pcibios_allocate_resources(int pass) | 242 | static void pcibios_allocate_dev_resources(struct pci_dev *dev, int pass) |
234 | { | 243 | { |
235 | struct pci_dev *dev = NULL; | ||
236 | int idx, disabled, i; | 244 | int idx, disabled, i; |
237 | u16 command; | 245 | u16 command; |
238 | struct resource *r; | 246 | struct resource *r; |
@@ -244,14 +252,13 @@ static void __init pcibios_allocate_resources(int pass) | |||
244 | #endif | 252 | #endif |
245 | }; | 253 | }; |
246 | 254 | ||
247 | for_each_pci_dev(dev) { | 255 | pci_read_config_word(dev, PCI_COMMAND, &command); |
248 | pci_read_config_word(dev, PCI_COMMAND, &command); | 256 | for (i = 0; i < ARRAY_SIZE(idx_range); i++) |
249 | for (i = 0; i < ARRAY_SIZE(idx_range); i++) | ||
250 | for (idx = idx_range[i].start; idx <= idx_range[i].end; idx++) { | 257 | for (idx = idx_range[i].start; idx <= idx_range[i].end; idx++) { |
251 | r = &dev->resource[idx]; | 258 | r = &dev->resource[idx]; |
252 | if (r->parent) /* Already allocated */ | 259 | if (r->parent) /* Already allocated */ |
253 | continue; | 260 | continue; |
254 | if (!r->start) /* Address not assigned at all */ | 261 | if (!r->start) /* Address not assigned at all */ |
255 | continue; | 262 | continue; |
256 | if (r->flags & IORESOURCE_IO) | 263 | if (r->flags & IORESOURCE_IO) |
257 | disabled = !(command & PCI_COMMAND_IO); | 264 | disabled = !(command & PCI_COMMAND_IO); |
@@ -270,44 +277,74 @@ static void __init pcibios_allocate_resources(int pass) | |||
270 | } | 277 | } |
271 | } | 278 | } |
272 | } | 279 | } |
273 | if (!pass) { | 280 | if (!pass) { |
274 | r = &dev->resource[PCI_ROM_RESOURCE]; | 281 | r = &dev->resource[PCI_ROM_RESOURCE]; |
275 | if (r->flags & IORESOURCE_ROM_ENABLE) { | 282 | if (r->flags & IORESOURCE_ROM_ENABLE) { |
276 | /* Turn the ROM off, leave the resource region, | 283 | /* Turn the ROM off, leave the resource region, |
277 | * but keep it unregistered. */ | 284 | * but keep it unregistered. */ |
278 | u32 reg; | 285 | u32 reg; |
279 | dev_dbg(&dev->dev, "disabling ROM %pR\n", r); | 286 | dev_dbg(&dev->dev, "disabling ROM %pR\n", r); |
280 | r->flags &= ~IORESOURCE_ROM_ENABLE; | 287 | r->flags &= ~IORESOURCE_ROM_ENABLE; |
281 | pci_read_config_dword(dev, | 288 | pci_read_config_dword(dev, dev->rom_base_reg, ®); |
282 | dev->rom_base_reg, ®); | 289 | pci_write_config_dword(dev, dev->rom_base_reg, |
283 | pci_write_config_dword(dev, dev->rom_base_reg, | ||
284 | reg & ~PCI_ROM_ADDRESS_ENABLE); | 290 | reg & ~PCI_ROM_ADDRESS_ENABLE); |
285 | } | ||
286 | } | 291 | } |
287 | } | 292 | } |
288 | } | 293 | } |
289 | 294 | ||
290 | static int __init pcibios_assign_resources(void) | 295 | static void pcibios_allocate_resources(struct pci_bus *bus, int pass) |
296 | { | ||
297 | struct pci_dev *dev; | ||
298 | struct pci_bus *child; | ||
299 | |||
300 | list_for_each_entry(dev, &bus->devices, bus_list) { | ||
301 | pcibios_allocate_dev_resources(dev, pass); | ||
302 | |||
303 | child = dev->subordinate; | ||
304 | if (child) | ||
305 | pcibios_allocate_resources(child, pass); | ||
306 | } | ||
307 | } | ||
308 | |||
309 | static void pcibios_allocate_dev_rom_resource(struct pci_dev *dev) | ||
291 | { | 310 | { |
292 | struct pci_dev *dev = NULL; | ||
293 | struct resource *r; | 311 | struct resource *r; |
294 | 312 | ||
295 | if (!(pci_probe & PCI_ASSIGN_ROMS)) { | 313 | /* |
296 | /* | 314 | * Try to use BIOS settings for ROMs, otherwise let |
297 | * Try to use BIOS settings for ROMs, otherwise let | 315 | * pci_assign_unassigned_resources() allocate the new |
298 | * pci_assign_unassigned_resources() allocate the new | 316 | * addresses. |
299 | * addresses. | 317 | */ |
300 | */ | 318 | r = &dev->resource[PCI_ROM_RESOURCE]; |
301 | for_each_pci_dev(dev) { | 319 | if (!r->flags || !r->start) |
302 | r = &dev->resource[PCI_ROM_RESOURCE]; | 320 | return; |
303 | if (!r->flags || !r->start) | 321 | |
304 | continue; | 322 | if (pci_claim_resource(dev, PCI_ROM_RESOURCE) < 0) { |
305 | if (pci_claim_resource(dev, PCI_ROM_RESOURCE) < 0) { | 323 | r->end -= r->start; |
306 | r->end -= r->start; | 324 | r->start = 0; |
307 | r->start = 0; | ||
308 | } | ||
309 | } | ||
310 | } | 325 | } |
326 | } | ||
327 | static void pcibios_allocate_rom_resources(struct pci_bus *bus) | ||
328 | { | ||
329 | struct pci_dev *dev; | ||
330 | struct pci_bus *child; | ||
331 | |||
332 | list_for_each_entry(dev, &bus->devices, bus_list) { | ||
333 | pcibios_allocate_dev_rom_resource(dev); | ||
334 | |||
335 | child = dev->subordinate; | ||
336 | if (child) | ||
337 | pcibios_allocate_rom_resources(child); | ||
338 | } | ||
339 | } | ||
340 | |||
341 | static int __init pcibios_assign_resources(void) | ||
342 | { | ||
343 | struct pci_bus *bus; | ||
344 | |||
345 | if (!(pci_probe & PCI_ASSIGN_ROMS)) | ||
346 | list_for_each_entry(bus, &pci_root_buses, node) | ||
347 | pcibios_allocate_rom_resources(bus); | ||
311 | 348 | ||
312 | pci_assign_unassigned_resources(); | 349 | pci_assign_unassigned_resources(); |
313 | pcibios_fw_addr_list_del(); | 350 | pcibios_fw_addr_list_del(); |
@@ -315,12 +352,32 @@ static int __init pcibios_assign_resources(void) | |||
315 | return 0; | 352 | return 0; |
316 | } | 353 | } |
317 | 354 | ||
355 | void pcibios_resource_survey_bus(struct pci_bus *bus) | ||
356 | { | ||
357 | dev_printk(KERN_DEBUG, &bus->dev, "Allocating resources\n"); | ||
358 | |||
359 | pcibios_allocate_bus_resources(bus); | ||
360 | |||
361 | pcibios_allocate_resources(bus, 0); | ||
362 | pcibios_allocate_resources(bus, 1); | ||
363 | |||
364 | if (!(pci_probe & PCI_ASSIGN_ROMS)) | ||
365 | pcibios_allocate_rom_resources(bus); | ||
366 | } | ||
367 | |||
318 | void __init pcibios_resource_survey(void) | 368 | void __init pcibios_resource_survey(void) |
319 | { | 369 | { |
370 | struct pci_bus *bus; | ||
371 | |||
320 | DBG("PCI: Allocating resources\n"); | 372 | DBG("PCI: Allocating resources\n"); |
321 | pcibios_allocate_bus_resources(&pci_root_buses); | 373 | |
322 | pcibios_allocate_resources(0); | 374 | list_for_each_entry(bus, &pci_root_buses, node) |
323 | pcibios_allocate_resources(1); | 375 | pcibios_allocate_bus_resources(bus); |
376 | |||
377 | list_for_each_entry(bus, &pci_root_buses, node) | ||
378 | pcibios_allocate_resources(bus, 0); | ||
379 | list_for_each_entry(bus, &pci_root_buses, node) | ||
380 | pcibios_allocate_resources(bus, 1); | ||
324 | 381 | ||
325 | e820_reserve_resources_late(); | 382 | e820_reserve_resources_late(); |
326 | /* | 383 | /* |