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 | |
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>
-rw-r--r-- | arch/x86/include/asm/pci.h | 1 | ||||
-rw-r--r-- | drivers/pci/bus.c | 7 | ||||
-rw-r--r-- | drivers/pci/probe.c | 9 | ||||
-rw-r--r-- | drivers/pci/setup-bus.c | 52 | ||||
-rw-r--r-- | include/linux/ioport.h | 2 | ||||
-rw-r--r-- | include/linux/pci.h | 4 |
6 files changed, 62 insertions, 13 deletions
diff --git a/arch/x86/include/asm/pci.h b/arch/x86/include/asm/pci.h index b51a1e8b0baf..927958d13c19 100644 --- a/arch/x86/include/asm/pci.h +++ b/arch/x86/include/asm/pci.h | |||
@@ -130,6 +130,7 @@ extern void pci_iommu_alloc(void); | |||
130 | 130 | ||
131 | /* generic pci stuff */ | 131 | /* generic pci stuff */ |
132 | #include <asm-generic/pci.h> | 132 | #include <asm-generic/pci.h> |
133 | #define PCIBIOS_MAX_MEM_32 0xffffffff | ||
133 | 134 | ||
134 | #ifdef CONFIG_NUMA | 135 | #ifdef CONFIG_NUMA |
135 | /* Returns the node based on pci bus */ | 136 | /* Returns the node based on pci bus */ |
diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c index 97a8194063b5..40af27f31043 100644 --- a/drivers/pci/bus.c +++ b/drivers/pci/bus.c | |||
@@ -41,9 +41,14 @@ pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res, | |||
41 | void *alignf_data) | 41 | void *alignf_data) |
42 | { | 42 | { |
43 | int i, ret = -ENOMEM; | 43 | int i, ret = -ENOMEM; |
44 | resource_size_t max = -1; | ||
44 | 45 | ||
45 | type_mask |= IORESOURCE_IO | IORESOURCE_MEM; | 46 | type_mask |= IORESOURCE_IO | IORESOURCE_MEM; |
46 | 47 | ||
48 | /* don't allocate too high if the pref mem doesn't support 64bit*/ | ||
49 | if (!(res->flags & IORESOURCE_MEM_64)) | ||
50 | max = PCIBIOS_MAX_MEM_32; | ||
51 | |||
47 | for (i = 0; i < PCI_BUS_NUM_RESOURCES; i++) { | 52 | for (i = 0; i < PCI_BUS_NUM_RESOURCES; i++) { |
48 | struct resource *r = bus->resource[i]; | 53 | struct resource *r = bus->resource[i]; |
49 | if (!r) | 54 | if (!r) |
@@ -62,7 +67,7 @@ pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res, | |||
62 | /* Ok, try it out.. */ | 67 | /* Ok, try it out.. */ |
63 | ret = allocate_resource(r, res, size, | 68 | ret = allocate_resource(r, res, size, |
64 | r->start ? : min, | 69 | r->start ? : min, |
65 | -1, align, | 70 | max, align, |
66 | alignf, alignf_data); | 71 | alignf, alignf_data); |
67 | if (ret == 0) | 72 | if (ret == 0) |
68 | break; | 73 | break; |
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index f1ae2475ffff..b962326e3d95 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c | |||
@@ -193,7 +193,7 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type, | |||
193 | res->flags |= pci_calc_resource_flags(l) | IORESOURCE_SIZEALIGN; | 193 | res->flags |= pci_calc_resource_flags(l) | IORESOURCE_SIZEALIGN; |
194 | if (type == pci_bar_io) { | 194 | if (type == pci_bar_io) { |
195 | l &= PCI_BASE_ADDRESS_IO_MASK; | 195 | l &= PCI_BASE_ADDRESS_IO_MASK; |
196 | mask = PCI_BASE_ADDRESS_IO_MASK & 0xffff; | 196 | mask = PCI_BASE_ADDRESS_IO_MASK & IO_SPACE_LIMIT; |
197 | } else { | 197 | } else { |
198 | l &= PCI_BASE_ADDRESS_MEM_MASK; | 198 | l &= PCI_BASE_ADDRESS_MEM_MASK; |
199 | mask = (u32)PCI_BASE_ADDRESS_MEM_MASK; | 199 | mask = (u32)PCI_BASE_ADDRESS_MEM_MASK; |
@@ -237,6 +237,8 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type, | |||
237 | dev_printk(KERN_DEBUG, &dev->dev, | 237 | dev_printk(KERN_DEBUG, &dev->dev, |
238 | "reg %x 64bit mmio: %pR\n", pos, res); | 238 | "reg %x 64bit mmio: %pR\n", pos, res); |
239 | } | 239 | } |
240 | |||
241 | res->flags |= IORESOURCE_MEM_64; | ||
240 | } else { | 242 | } else { |
241 | sz = pci_size(l, sz, mask); | 243 | sz = pci_size(l, sz, mask); |
242 | 244 | ||
@@ -362,7 +364,10 @@ void __devinit pci_read_bridge_bases(struct pci_bus *child) | |||
362 | } | 364 | } |
363 | } | 365 | } |
364 | if (base <= limit) { | 366 | if (base <= limit) { |
365 | res->flags = (mem_base_lo & PCI_MEMORY_RANGE_TYPE_MASK) | IORESOURCE_MEM | IORESOURCE_PREFETCH; | 367 | res->flags = (mem_base_lo & PCI_PREF_RANGE_TYPE_MASK) | |
368 | IORESOURCE_MEM | IORESOURCE_PREFETCH; | ||
369 | if (res->flags & PCI_PREF_RANGE_TYPE_64) | ||
370 | res->flags |= IORESOURCE_MEM_64; | ||
366 | res->start = base; | 371 | res->start = base; |
367 | res->end = limit + 0xfffff; | 372 | res->end = limit + 0xfffff; |
368 | dev_printk(KERN_DEBUG, &dev->dev, "bridge %sbit mmio pref: %pR\n", | 373 | dev_printk(KERN_DEBUG, &dev->dev, "bridge %sbit mmio pref: %pR\n", |
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 | ||
diff --git a/include/linux/ioport.h b/include/linux/ioport.h index 32e4b2f72294..786e7b8cece9 100644 --- a/include/linux/ioport.h +++ b/include/linux/ioport.h | |||
@@ -49,6 +49,8 @@ struct resource_list { | |||
49 | #define IORESOURCE_SIZEALIGN 0x00020000 /* size indicates alignment */ | 49 | #define IORESOURCE_SIZEALIGN 0x00020000 /* size indicates alignment */ |
50 | #define IORESOURCE_STARTALIGN 0x00040000 /* start field is alignment */ | 50 | #define IORESOURCE_STARTALIGN 0x00040000 /* start field is alignment */ |
51 | 51 | ||
52 | #define IORESOURCE_MEM_64 0x00100000 | ||
53 | |||
52 | #define IORESOURCE_EXCLUSIVE 0x08000000 /* Userland may not map this resource */ | 54 | #define IORESOURCE_EXCLUSIVE 0x08000000 /* Userland may not map this resource */ |
53 | #define IORESOURCE_DISABLED 0x10000000 | 55 | #define IORESOURCE_DISABLED 0x10000000 |
54 | #define IORESOURCE_UNSET 0x20000000 | 56 | #define IORESOURCE_UNSET 0x20000000 |
diff --git a/include/linux/pci.h b/include/linux/pci.h index 72698d89e767..6dfa47d25ba4 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h | |||
@@ -1097,6 +1097,10 @@ static inline struct pci_dev *pci_get_bus_and_slot(unsigned int bus, | |||
1097 | 1097 | ||
1098 | #include <asm/pci.h> | 1098 | #include <asm/pci.h> |
1099 | 1099 | ||
1100 | #ifndef PCIBIOS_MAX_MEM_32 | ||
1101 | #define PCIBIOS_MAX_MEM_32 (-1) | ||
1102 | #endif | ||
1103 | |||
1100 | /* these helpers provide future and backwards compatibility | 1104 | /* these helpers provide future and backwards compatibility |
1101 | * for accessing popular PCI BAR info */ | 1105 | * for accessing popular PCI BAR info */ |
1102 | #define pci_resource_start(dev, bar) ((dev)->resource[(bar)].start) | 1106 | #define pci_resource_start(dev, bar) ((dev)->resource[(bar)].start) |