diff options
author | Yinghai Lu <yinghai@kernel.org> | 2009-04-23 23:48:32 -0400 |
---|---|---|
committer | Jesse Barnes <jbarnes@virtuousgeek.org> | 2009-06-11 15:04:06 -0400 |
commit | 1f82de10d6b1d845155363c895c552e61b36b51a (patch) | |
tree | 3e93b9d1c97ae48509133fbbec9c81b4823816a5 /drivers/pci/setup-bus.c | |
parent | 67b5db6502ddd27d65dea43bf036abbd82d0dfc9 (diff) |
PCI/x86: don't assume prefetchable ranges are 64bit
We should not assign 64bit ranges to PCI devices that only take 32bit
prefetchable addresses.
Try to set IORESOURCE_MEM_64 in 64bit resource of pci_device/pci_bridge
and make the bus resource only have that bit set when all devices under
it support 64bit prefetchable memory. Use that flag to allocate
resources from that range.
Reported-by: Yannick <yannick.roehlly@free.fr>
Reviewed-by: Ivan Kokshaysky <ink@jurassic.park.msu.ru>
Signed-off-by: Yinghai Lu <yinghai@kernel.org>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
Diffstat (limited to 'drivers/pci/setup-bus.c')
-rw-r--r-- | drivers/pci/setup-bus.c | 52 |
1 files changed, 42 insertions, 10 deletions
diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index a00f85471b6e..e1c360a5b0db 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c | |||
@@ -143,6 +143,7 @@ static void pci_setup_bridge(struct pci_bus *bus) | |||
143 | struct pci_dev *bridge = bus->self; | 143 | struct pci_dev *bridge = bus->self; |
144 | struct pci_bus_region region; | 144 | struct pci_bus_region region; |
145 | u32 l, bu, lu, io_upper16; | 145 | u32 l, bu, lu, io_upper16; |
146 | int pref_mem64; | ||
146 | 147 | ||
147 | if (pci_is_enabled(bridge)) | 148 | if (pci_is_enabled(bridge)) |
148 | return; | 149 | return; |
@@ -198,16 +199,22 @@ static void pci_setup_bridge(struct pci_bus *bus) | |||
198 | pci_write_config_dword(bridge, PCI_PREF_LIMIT_UPPER32, 0); | 199 | pci_write_config_dword(bridge, PCI_PREF_LIMIT_UPPER32, 0); |
199 | 200 | ||
200 | /* Set up PREF base/limit. */ | 201 | /* Set up PREF base/limit. */ |
202 | pref_mem64 = 0; | ||
201 | bu = lu = 0; | 203 | bu = lu = 0; |
202 | pcibios_resource_to_bus(bridge, ®ion, bus->resource[2]); | 204 | pcibios_resource_to_bus(bridge, ®ion, bus->resource[2]); |
203 | if (bus->resource[2]->flags & IORESOURCE_PREFETCH) { | 205 | if (bus->resource[2]->flags & IORESOURCE_PREFETCH) { |
206 | int width = 8; | ||
204 | l = (region.start >> 16) & 0xfff0; | 207 | l = (region.start >> 16) & 0xfff0; |
205 | l |= region.end & 0xfff00000; | 208 | l |= region.end & 0xfff00000; |
206 | bu = upper_32_bits(region.start); | 209 | if (bus->resource[2]->flags & IORESOURCE_MEM_64) { |
207 | lu = upper_32_bits(region.end); | 210 | pref_mem64 = 1; |
208 | dev_info(&bridge->dev, " PREFETCH window: %#016llx-%#016llx\n", | 211 | bu = upper_32_bits(region.start); |
209 | (unsigned long long)region.start, | 212 | lu = upper_32_bits(region.end); |
210 | (unsigned long long)region.end); | 213 | width = 16; |
214 | } | ||
215 | dev_info(&bridge->dev, " PREFETCH window: %#0*llx-%#0*llx\n", | ||
216 | width, (unsigned long long)region.start, | ||
217 | width, (unsigned long long)region.end); | ||
211 | } | 218 | } |
212 | else { | 219 | else { |
213 | l = 0x0000fff0; | 220 | l = 0x0000fff0; |
@@ -215,9 +222,11 @@ static void pci_setup_bridge(struct pci_bus *bus) | |||
215 | } | 222 | } |
216 | pci_write_config_dword(bridge, PCI_PREF_MEMORY_BASE, l); | 223 | pci_write_config_dword(bridge, PCI_PREF_MEMORY_BASE, l); |
217 | 224 | ||
218 | /* Set the upper 32 bits of PREF base & limit. */ | 225 | if (pref_mem64) { |
219 | pci_write_config_dword(bridge, PCI_PREF_BASE_UPPER32, bu); | 226 | /* Set the upper 32 bits of PREF base & limit. */ |
220 | pci_write_config_dword(bridge, PCI_PREF_LIMIT_UPPER32, lu); | 227 | pci_write_config_dword(bridge, PCI_PREF_BASE_UPPER32, bu); |
228 | pci_write_config_dword(bridge, PCI_PREF_LIMIT_UPPER32, lu); | ||
229 | } | ||
221 | 230 | ||
222 | pci_write_config_word(bridge, PCI_BRIDGE_CONTROL, bus->bridge_ctl); | 231 | pci_write_config_word(bridge, PCI_BRIDGE_CONTROL, bus->bridge_ctl); |
223 | } | 232 | } |
@@ -255,8 +264,25 @@ static void pci_bridge_check_ranges(struct pci_bus *bus) | |||
255 | pci_read_config_dword(bridge, PCI_PREF_MEMORY_BASE, &pmem); | 264 | pci_read_config_dword(bridge, PCI_PREF_MEMORY_BASE, &pmem); |
256 | pci_write_config_dword(bridge, PCI_PREF_MEMORY_BASE, 0x0); | 265 | pci_write_config_dword(bridge, PCI_PREF_MEMORY_BASE, 0x0); |
257 | } | 266 | } |
258 | if (pmem) | 267 | if (pmem) { |
259 | b_res[2].flags |= IORESOURCE_MEM | IORESOURCE_PREFETCH; | 268 | b_res[2].flags |= IORESOURCE_MEM | IORESOURCE_PREFETCH; |
269 | if ((pmem & PCI_PREF_RANGE_TYPE_MASK) == PCI_PREF_RANGE_TYPE_64) | ||
270 | b_res[2].flags |= IORESOURCE_MEM_64; | ||
271 | } | ||
272 | |||
273 | /* double check if bridge does support 64 bit pref */ | ||
274 | if (b_res[2].flags & IORESOURCE_MEM_64) { | ||
275 | u32 mem_base_hi, tmp; | ||
276 | pci_read_config_dword(bridge, PCI_PREF_BASE_UPPER32, | ||
277 | &mem_base_hi); | ||
278 | pci_write_config_dword(bridge, PCI_PREF_BASE_UPPER32, | ||
279 | 0xffffffff); | ||
280 | pci_read_config_dword(bridge, PCI_PREF_BASE_UPPER32, &tmp); | ||
281 | if (!tmp) | ||
282 | b_res[2].flags &= ~IORESOURCE_MEM_64; | ||
283 | pci_write_config_dword(bridge, PCI_PREF_BASE_UPPER32, | ||
284 | mem_base_hi); | ||
285 | } | ||
260 | } | 286 | } |
261 | 287 | ||
262 | /* Helper function for sizing routines: find first available | 288 | /* Helper function for sizing routines: find first available |
@@ -336,6 +362,7 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, unsigned long | |||
336 | resource_size_t aligns[12]; /* Alignments from 1Mb to 2Gb */ | 362 | resource_size_t aligns[12]; /* Alignments from 1Mb to 2Gb */ |
337 | int order, max_order; | 363 | int order, max_order; |
338 | struct resource *b_res = find_free_bus_resource(bus, type); | 364 | struct resource *b_res = find_free_bus_resource(bus, type); |
365 | unsigned int mem64_mask = 0; | ||
339 | 366 | ||
340 | if (!b_res) | 367 | if (!b_res) |
341 | return 0; | 368 | return 0; |
@@ -344,9 +371,12 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, unsigned long | |||
344 | max_order = 0; | 371 | max_order = 0; |
345 | size = 0; | 372 | size = 0; |
346 | 373 | ||
374 | mem64_mask = b_res->flags & IORESOURCE_MEM_64; | ||
375 | b_res->flags &= ~IORESOURCE_MEM_64; | ||
376 | |||
347 | list_for_each_entry(dev, &bus->devices, bus_list) { | 377 | list_for_each_entry(dev, &bus->devices, bus_list) { |
348 | int i; | 378 | int i; |
349 | 379 | ||
350 | for (i = 0; i < PCI_NUM_RESOURCES; i++) { | 380 | for (i = 0; i < PCI_NUM_RESOURCES; i++) { |
351 | struct resource *r = &dev->resource[i]; | 381 | struct resource *r = &dev->resource[i]; |
352 | resource_size_t r_size; | 382 | resource_size_t r_size; |
@@ -372,6 +402,7 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, unsigned long | |||
372 | aligns[order] += align; | 402 | aligns[order] += align; |
373 | if (order > max_order) | 403 | if (order > max_order) |
374 | max_order = order; | 404 | max_order = order; |
405 | mem64_mask &= r->flags & IORESOURCE_MEM_64; | ||
375 | } | 406 | } |
376 | } | 407 | } |
377 | 408 | ||
@@ -396,6 +427,7 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, unsigned long | |||
396 | b_res->start = min_align; | 427 | b_res->start = min_align; |
397 | b_res->end = size + min_align - 1; | 428 | b_res->end = size + min_align - 1; |
398 | b_res->flags |= IORESOURCE_STARTALIGN; | 429 | b_res->flags |= IORESOURCE_STARTALIGN; |
430 | b_res->flags |= mem64_mask; | ||
399 | return 1; | 431 | return 1; |
400 | } | 432 | } |
401 | 433 | ||