aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYinghai Lu <yinghai@kernel.org>2009-04-23 23:48:32 -0400
committerJesse Barnes <jbarnes@virtuousgeek.org>2009-06-11 15:04:06 -0400
commit1f82de10d6b1d845155363c895c552e61b36b51a (patch)
tree3e93b9d1c97ae48509133fbbec9c81b4823816a5
parent67b5db6502ddd27d65dea43bf036abbd82d0dfc9 (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.h1
-rw-r--r--drivers/pci/bus.c7
-rw-r--r--drivers/pci/probe.c9
-rw-r--r--drivers/pci/setup-bus.c52
-rw-r--r--include/linux/ioport.h2
-rw-r--r--include/linux/pci.h4
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, &region, bus->resource[2]); 204 pcibios_resource_to_bus(bridge, &region, 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)