diff options
author | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2007-12-19 22:54:53 -0500 |
---|---|---|
committer | Paul Mackerras <paulus@samba.org> | 2007-12-20 00:18:09 -0500 |
commit | 3fd94c6b1a1158d3e0e505b0a00c3a707b5fcd40 (patch) | |
tree | ad542ce91f44a5aeacf095d2100a831ab6ebf23e /arch/powerpc/kernel/pci_32.c | |
parent | bf5e2ba28f24f82a64524ef4772c9ebe12e2cd2a (diff) |
[POWERPC] Merge PCI resource allocation & assignment
The 32 bits PCI code now uses the generic code for assigning unassigned
resources and an algorithm similar to x86 for claiming existing ones.
This works far better than the 64 bits code which basically can only
claim existing ones (pci_probe_only=1) or would fall apart completely.
This merges them so that the new 32 bits implementation is used for both.
64 bits now gets the new PCI flags for controlling the behaviour, though
the old pci_probe_only global is still there for now to be cleared if you
want to.
I kept a pcibios_claim_one_bus() function mostly based on the old 64
bits code for use by the DLPAR hotplug. This will have to be cleaned
up, thought I hope it will work in the meantime.
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Paul Mackerras <paulus@samba.org>
Diffstat (limited to 'arch/powerpc/kernel/pci_32.c')
-rw-r--r-- | arch/powerpc/kernel/pci_32.c | 246 |
1 files changed, 3 insertions, 243 deletions
diff --git a/arch/powerpc/kernel/pci_32.c b/arch/powerpc/kernel/pci_32.c index 717c554d4658..ce7c20c8191f 100644 --- a/arch/powerpc/kernel/pci_32.c +++ b/arch/powerpc/kernel/pci_32.c | |||
@@ -35,13 +35,9 @@ unsigned long isa_io_base = 0; | |||
35 | unsigned long pci_dram_offset = 0; | 35 | unsigned long pci_dram_offset = 0; |
36 | int pcibios_assign_bus_offset = 1; | 36 | int pcibios_assign_bus_offset = 1; |
37 | 37 | ||
38 | /* Default PCI flags is 0 */ | ||
39 | unsigned int ppc_pci_flags; | ||
40 | |||
41 | void pcibios_make_OF_bus_map(void); | 38 | void pcibios_make_OF_bus_map(void); |
42 | 39 | ||
43 | static void fixup_broken_pcnet32(struct pci_dev* dev); | 40 | static void fixup_broken_pcnet32(struct pci_dev* dev); |
44 | static int reparent_resources(struct resource *parent, struct resource *res); | ||
45 | static void fixup_cpc710_pci64(struct pci_dev* dev); | 41 | static void fixup_cpc710_pci64(struct pci_dev* dev); |
46 | #ifdef CONFIG_PPC_OF | 42 | #ifdef CONFIG_PPC_OF |
47 | static u8* pci_to_OF_bus_map; | 43 | static u8* pci_to_OF_bus_map; |
@@ -97,170 +93,6 @@ fixup_cpc710_pci64(struct pci_dev* dev) | |||
97 | } | 93 | } |
98 | DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CPC710_PCI64, fixup_cpc710_pci64); | 94 | DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CPC710_PCI64, fixup_cpc710_pci64); |
99 | 95 | ||
100 | static int skip_isa_ioresource_align(struct pci_dev *dev) | ||
101 | { | ||
102 | if ((ppc_pci_flags & PPC_PCI_CAN_SKIP_ISA_ALIGN) && | ||
103 | !(dev->bus->bridge_ctl & PCI_BRIDGE_CTL_ISA)) | ||
104 | return 1; | ||
105 | return 0; | ||
106 | } | ||
107 | |||
108 | /* | ||
109 | * We need to avoid collisions with `mirrored' VGA ports | ||
110 | * and other strange ISA hardware, so we always want the | ||
111 | * addresses to be allocated in the 0x000-0x0ff region | ||
112 | * modulo 0x400. | ||
113 | * | ||
114 | * Why? Because some silly external IO cards only decode | ||
115 | * the low 10 bits of the IO address. The 0x00-0xff region | ||
116 | * is reserved for motherboard devices that decode all 16 | ||
117 | * bits, so it's ok to allocate at, say, 0x2800-0x28ff, | ||
118 | * but we want to try to avoid allocating at 0x2900-0x2bff | ||
119 | * which might have be mirrored at 0x0100-0x03ff.. | ||
120 | */ | ||
121 | void pcibios_align_resource(void *data, struct resource *res, | ||
122 | resource_size_t size, resource_size_t align) | ||
123 | { | ||
124 | struct pci_dev *dev = data; | ||
125 | |||
126 | if (res->flags & IORESOURCE_IO) { | ||
127 | resource_size_t start = res->start; | ||
128 | |||
129 | if (skip_isa_ioresource_align(dev)) | ||
130 | return; | ||
131 | if (start & 0x300) { | ||
132 | start = (start + 0x3ff) & ~0x3ff; | ||
133 | res->start = start; | ||
134 | } | ||
135 | } | ||
136 | } | ||
137 | EXPORT_SYMBOL(pcibios_align_resource); | ||
138 | |||
139 | /* | ||
140 | * Handle resources of PCI devices. If the world were perfect, we could | ||
141 | * just allocate all the resource regions and do nothing more. It isn't. | ||
142 | * On the other hand, we cannot just re-allocate all devices, as it would | ||
143 | * require us to know lots of host bridge internals. So we attempt to | ||
144 | * keep as much of the original configuration as possible, but tweak it | ||
145 | * when it's found to be wrong. | ||
146 | * | ||
147 | * Known BIOS problems we have to work around: | ||
148 | * - I/O or memory regions not configured | ||
149 | * - regions configured, but not enabled in the command register | ||
150 | * - bogus I/O addresses above 64K used | ||
151 | * - expansion ROMs left enabled (this may sound harmless, but given | ||
152 | * the fact the PCI specs explicitly allow address decoders to be | ||
153 | * shared between expansion ROMs and other resource regions, it's | ||
154 | * at least dangerous) | ||
155 | * | ||
156 | * Our solution: | ||
157 | * (1) Allocate resources for all buses behind PCI-to-PCI bridges. | ||
158 | * This gives us fixed barriers on where we can allocate. | ||
159 | * (2) Allocate resources for all enabled devices. If there is | ||
160 | * a collision, just mark the resource as unallocated. Also | ||
161 | * disable expansion ROMs during this step. | ||
162 | * (3) Try to allocate resources for disabled devices. If the | ||
163 | * resources were assigned correctly, everything goes well, | ||
164 | * if they weren't, they won't disturb allocation of other | ||
165 | * resources. | ||
166 | * (4) Assign new addresses to resources which were either | ||
167 | * not configured at all or misconfigured. If explicitly | ||
168 | * requested by the user, configure expansion ROM address | ||
169 | * as well. | ||
170 | */ | ||
171 | |||
172 | static void __init | ||
173 | pcibios_allocate_bus_resources(struct list_head *bus_list) | ||
174 | { | ||
175 | struct pci_bus *bus; | ||
176 | int i; | ||
177 | struct resource *res, *pr; | ||
178 | |||
179 | /* Depth-First Search on bus tree */ | ||
180 | list_for_each_entry(bus, bus_list, node) { | ||
181 | for (i = 0; i < 4; ++i) { | ||
182 | if ((res = bus->resource[i]) == NULL || !res->flags | ||
183 | || res->start > res->end) | ||
184 | continue; | ||
185 | if (bus->parent == NULL) | ||
186 | pr = (res->flags & IORESOURCE_IO)? | ||
187 | &ioport_resource : &iomem_resource; | ||
188 | else { | ||
189 | /* Don't bother with non-root busses when | ||
190 | * re-assigning all resources. | ||
191 | */ | ||
192 | if (ppc_pci_flags & PPC_PCI_REASSIGN_ALL_RSRC) | ||
193 | continue; | ||
194 | pr = pci_find_parent_resource(bus->self, res); | ||
195 | if (pr == res) { | ||
196 | /* this happens when the generic PCI | ||
197 | * code (wrongly) decides that this | ||
198 | * bridge is transparent -- paulus | ||
199 | */ | ||
200 | continue; | ||
201 | } | ||
202 | } | ||
203 | |||
204 | DBG("PCI: dev %s (bus 0x%02x) bridge rsrc %d: %016llx..%016llx " | ||
205 | "(f:0x%08lx), parent %p\n", | ||
206 | bus->self ? pci_name(bus->self) : "PHB", bus->number, i, | ||
207 | (u64)res->start, (u64)res->end, res->flags, pr); | ||
208 | |||
209 | if (pr && !(pr->flags & IORESOURCE_UNSET)) { | ||
210 | if (request_resource(pr, res) == 0) | ||
211 | continue; | ||
212 | /* | ||
213 | * Must be a conflict with an existing entry. | ||
214 | * Move that entry (or entries) under the | ||
215 | * bridge resource and try again. | ||
216 | */ | ||
217 | if (reparent_resources(pr, res) == 0) | ||
218 | continue; | ||
219 | } | ||
220 | printk(KERN_WARNING | ||
221 | "PCI: Cannot allocate resource region " | ||
222 | "%d of PCI bridge %d, will remap\n", | ||
223 | i, bus->number); | ||
224 | res->flags |= IORESOURCE_UNSET; | ||
225 | } | ||
226 | pcibios_allocate_bus_resources(&bus->children); | ||
227 | } | ||
228 | } | ||
229 | |||
230 | /* | ||
231 | * Reparent resource children of pr that conflict with res | ||
232 | * under res, and make res replace those children. | ||
233 | */ | ||
234 | static int __init | ||
235 | reparent_resources(struct resource *parent, struct resource *res) | ||
236 | { | ||
237 | struct resource *p, **pp; | ||
238 | struct resource **firstpp = NULL; | ||
239 | |||
240 | for (pp = &parent->child; (p = *pp) != NULL; pp = &p->sibling) { | ||
241 | if (p->end < res->start) | ||
242 | continue; | ||
243 | if (res->end < p->start) | ||
244 | break; | ||
245 | if (p->start < res->start || p->end > res->end) | ||
246 | return -1; /* not completely contained */ | ||
247 | if (firstpp == NULL) | ||
248 | firstpp = pp; | ||
249 | } | ||
250 | if (firstpp == NULL) | ||
251 | return -1; /* didn't find any conflicting entries? */ | ||
252 | res->parent = parent; | ||
253 | res->child = *firstpp; | ||
254 | res->sibling = *pp; | ||
255 | *firstpp = res; | ||
256 | *pp = NULL; | ||
257 | for (p = res->child; p != NULL; p = p->sibling) { | ||
258 | p->parent = res; | ||
259 | DBG(KERN_INFO "PCI: reparented %s [%llx..%llx] under %s\n", | ||
260 | p->name, (u64)p->start, (u64)p->end, res->name); | ||
261 | } | ||
262 | return 0; | ||
263 | } | ||
264 | 96 | ||
265 | void __init | 97 | void __init |
266 | update_bridge_resource(struct pci_dev *dev, struct resource *res) | 98 | update_bridge_resource(struct pci_dev *dev, struct resource *res) |
@@ -318,63 +150,6 @@ update_bridge_resource(struct pci_dev *dev, struct resource *res) | |||
318 | pci_write_config_word(dev, PCI_COMMAND, cmd); | 150 | pci_write_config_word(dev, PCI_COMMAND, cmd); |
319 | } | 151 | } |
320 | 152 | ||
321 | static inline void alloc_resource(struct pci_dev *dev, int idx) | ||
322 | { | ||
323 | struct resource *pr, *r = &dev->resource[idx]; | ||
324 | |||
325 | DBG("PCI: Allocating %s: Resource %d: %016llx..%016llx (f=%lx)\n", | ||
326 | pci_name(dev), idx, (u64)r->start, (u64)r->end, r->flags); | ||
327 | pr = pci_find_parent_resource(dev, r); | ||
328 | if (!pr || (pr->flags & IORESOURCE_UNSET) || request_resource(pr, r) < 0) { | ||
329 | printk(KERN_WARNING "PCI: Cannot allocate resource region %d" | ||
330 | " of device %s, will remap\n", idx, pci_name(dev)); | ||
331 | if (pr) | ||
332 | DBG("PCI: parent is %p: %016llx-%016llx (f=%lx)\n", | ||
333 | pr, (u64)pr->start, (u64)pr->end, pr->flags); | ||
334 | /* We'll assign a new address later */ | ||
335 | r->flags |= IORESOURCE_UNSET; | ||
336 | r->end -= r->start; | ||
337 | r->start = 0; | ||
338 | } | ||
339 | } | ||
340 | |||
341 | static void __init | ||
342 | pcibios_allocate_resources(int pass) | ||
343 | { | ||
344 | struct pci_dev *dev = NULL; | ||
345 | int idx, disabled; | ||
346 | u16 command; | ||
347 | struct resource *r; | ||
348 | |||
349 | for_each_pci_dev(dev) { | ||
350 | pci_read_config_word(dev, PCI_COMMAND, &command); | ||
351 | for (idx = 0; idx < 6; idx++) { | ||
352 | r = &dev->resource[idx]; | ||
353 | if (r->parent) /* Already allocated */ | ||
354 | continue; | ||
355 | if (!r->flags || (r->flags & IORESOURCE_UNSET)) | ||
356 | continue; /* Not assigned at all */ | ||
357 | if (r->flags & IORESOURCE_IO) | ||
358 | disabled = !(command & PCI_COMMAND_IO); | ||
359 | else | ||
360 | disabled = !(command & PCI_COMMAND_MEMORY); | ||
361 | if (pass == disabled) | ||
362 | alloc_resource(dev, idx); | ||
363 | } | ||
364 | if (pass) | ||
365 | continue; | ||
366 | r = &dev->resource[PCI_ROM_RESOURCE]; | ||
367 | if (r->flags & IORESOURCE_ROM_ENABLE) { | ||
368 | /* Turn the ROM off, leave the resource region, but keep it unregistered. */ | ||
369 | u32 reg; | ||
370 | DBG("PCI: Switching off ROM of %s\n", pci_name(dev)); | ||
371 | r->flags &= ~IORESOURCE_ROM_ENABLE; | ||
372 | pci_read_config_dword(dev, dev->rom_base_reg, ®); | ||
373 | pci_write_config_dword(dev, dev->rom_base_reg, | ||
374 | reg & ~PCI_ROM_ADDRESS_ENABLE); | ||
375 | } | ||
376 | } | ||
377 | } | ||
378 | 153 | ||
379 | #ifdef CONFIG_PPC_OF | 154 | #ifdef CONFIG_PPC_OF |
380 | /* | 155 | /* |
@@ -649,8 +424,7 @@ void pcibios_make_OF_bus_map(void) | |||
649 | } | 424 | } |
650 | #endif /* CONFIG_PPC_OF */ | 425 | #endif /* CONFIG_PPC_OF */ |
651 | 426 | ||
652 | static int __init | 427 | static int __init pcibios_init(void) |
653 | pcibios_init(void) | ||
654 | { | 428 | { |
655 | struct pci_controller *hose, *tmp; | 429 | struct pci_controller *hose, *tmp; |
656 | struct pci_bus *bus; | 430 | struct pci_bus *bus; |
@@ -683,22 +457,8 @@ pcibios_init(void) | |||
683 | if (pci_assign_all_buses && have_of) | 457 | if (pci_assign_all_buses && have_of) |
684 | pcibios_make_OF_bus_map(); | 458 | pcibios_make_OF_bus_map(); |
685 | 459 | ||
686 | /* Call machine dependent fixup */ | 460 | /* Call common code to handle resource allocation */ |
687 | if (ppc_md.pcibios_fixup) | 461 | pcibios_resource_survey(); |
688 | ppc_md.pcibios_fixup(); | ||
689 | |||
690 | /* Allocate and assign resources. If we re-assign everything, then | ||
691 | * we skip the allocate phase | ||
692 | */ | ||
693 | pcibios_allocate_bus_resources(&pci_root_buses); | ||
694 | if (!(ppc_pci_flags & PPC_PCI_REASSIGN_ALL_RSRC)) { | ||
695 | pcibios_allocate_resources(0); | ||
696 | pcibios_allocate_resources(1); | ||
697 | } | ||
698 | if (!(ppc_pci_flags & PPC_PCI_PROBE_ONLY)) { | ||
699 | DBG("PCI: Assigning unassigned resouces...\n"); | ||
700 | pci_assign_unassigned_resources(); | ||
701 | } | ||
702 | 462 | ||
703 | /* Call machine dependent post-init code */ | 463 | /* Call machine dependent post-init code */ |
704 | if (ppc_md.pcibios_after_init) | 464 | if (ppc_md.pcibios_after_init) |