diff options
author | Yinghai Lu <yinghai.lu@amd.com> | 2006-11-29 16:53:10 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2007-02-07 18:50:03 -0500 |
commit | 07eddf3d597f2d009a37a4e8c7c32a1ffe992f3e (patch) | |
tree | 6ed35aff253bdf7aa01572687fcff5f0b7241606 /drivers | |
parent | 5331be090567d9335476f876b2d85427cd7c4426 (diff) |
PCI: check szhi when sz is 0 when 64 bit iomem bigger than 4G
For pci mem resource that size is bigger than 4G, the sz returned by
pc_size will be 0.
So that resource is skipped, and register contained hi address will be
treated as another 32bit resource. We need to use sz64 and pci_sz64 for
64 bit resource for clear logical. Typical usages for this: Opteron
system with co-processor and the co-processor could take more than 4G
RAM as pre-fetchable mem resource.
Signed-off-by: Yinghai Lu <yinghai.lu@amd.com>
Cc: Andi Kleen <ak@suse.de>
Cc: Andrew Morton <akpm@osdl.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/pci/probe.c | 69 |
1 files changed, 56 insertions, 13 deletions
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 0e0401dd02cb..9d7788428642 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c | |||
@@ -144,6 +144,32 @@ static u32 pci_size(u32 base, u32 maxbase, u32 mask) | |||
144 | return size; | 144 | return size; |
145 | } | 145 | } |
146 | 146 | ||
147 | static u64 pci_size64(u64 base, u64 maxbase, u64 mask) | ||
148 | { | ||
149 | u64 size = mask & maxbase; /* Find the significant bits */ | ||
150 | if (!size) | ||
151 | return 0; | ||
152 | |||
153 | /* Get the lowest of them to find the decode size, and | ||
154 | from that the extent. */ | ||
155 | size = (size & ~(size-1)) - 1; | ||
156 | |||
157 | /* base == maxbase can be valid only if the BAR has | ||
158 | already been programmed with all 1s. */ | ||
159 | if (base == maxbase && ((base | size) & mask) != mask) | ||
160 | return 0; | ||
161 | |||
162 | return size; | ||
163 | } | ||
164 | |||
165 | static inline int is_64bit_memory(u32 mask) | ||
166 | { | ||
167 | if ((mask & (PCI_BASE_ADDRESS_SPACE|PCI_BASE_ADDRESS_MEM_TYPE_MASK)) == | ||
168 | (PCI_BASE_ADDRESS_SPACE_MEMORY|PCI_BASE_ADDRESS_MEM_TYPE_64)) | ||
169 | return 1; | ||
170 | return 0; | ||
171 | } | ||
172 | |||
147 | static void pci_read_bases(struct pci_dev *dev, unsigned int howmany, int rom) | 173 | static void pci_read_bases(struct pci_dev *dev, unsigned int howmany, int rom) |
148 | { | 174 | { |
149 | unsigned int pos, reg, next; | 175 | unsigned int pos, reg, next; |
@@ -151,6 +177,10 @@ static void pci_read_bases(struct pci_dev *dev, unsigned int howmany, int rom) | |||
151 | struct resource *res; | 177 | struct resource *res; |
152 | 178 | ||
153 | for(pos=0; pos<howmany; pos = next) { | 179 | for(pos=0; pos<howmany; pos = next) { |
180 | u64 l64; | ||
181 | u64 sz64; | ||
182 | u32 raw_sz; | ||
183 | |||
154 | next = pos+1; | 184 | next = pos+1; |
155 | res = &dev->resource[pos]; | 185 | res = &dev->resource[pos]; |
156 | res->name = pci_name(dev); | 186 | res->name = pci_name(dev); |
@@ -163,9 +193,16 @@ static void pci_read_bases(struct pci_dev *dev, unsigned int howmany, int rom) | |||
163 | continue; | 193 | continue; |
164 | if (l == 0xffffffff) | 194 | if (l == 0xffffffff) |
165 | l = 0; | 195 | l = 0; |
166 | if ((l & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_MEMORY) { | 196 | raw_sz = sz; |
197 | if ((l & PCI_BASE_ADDRESS_SPACE) == | ||
198 | PCI_BASE_ADDRESS_SPACE_MEMORY) { | ||
167 | sz = pci_size(l, sz, (u32)PCI_BASE_ADDRESS_MEM_MASK); | 199 | sz = pci_size(l, sz, (u32)PCI_BASE_ADDRESS_MEM_MASK); |
168 | if (!sz) | 200 | /* |
201 | * For 64bit prefetchable memory sz could be 0, if the | ||
202 | * real size is bigger than 4G, so we need to check | ||
203 | * szhi for that. | ||
204 | */ | ||
205 | if (!is_64bit_memory(l) && !sz) | ||
169 | continue; | 206 | continue; |
170 | res->start = l & PCI_BASE_ADDRESS_MEM_MASK; | 207 | res->start = l & PCI_BASE_ADDRESS_MEM_MASK; |
171 | res->flags |= l & ~PCI_BASE_ADDRESS_MEM_MASK; | 208 | res->flags |= l & ~PCI_BASE_ADDRESS_MEM_MASK; |
@@ -178,30 +215,36 @@ static void pci_read_bases(struct pci_dev *dev, unsigned int howmany, int rom) | |||
178 | } | 215 | } |
179 | res->end = res->start + (unsigned long) sz; | 216 | res->end = res->start + (unsigned long) sz; |
180 | res->flags |= pci_calc_resource_flags(l); | 217 | res->flags |= pci_calc_resource_flags(l); |
181 | if ((l & (PCI_BASE_ADDRESS_SPACE | PCI_BASE_ADDRESS_MEM_TYPE_MASK)) | 218 | if (is_64bit_memory(l)) { |
182 | == (PCI_BASE_ADDRESS_SPACE_MEMORY | PCI_BASE_ADDRESS_MEM_TYPE_64)) { | ||
183 | u32 szhi, lhi; | 219 | u32 szhi, lhi; |
220 | |||
184 | pci_read_config_dword(dev, reg+4, &lhi); | 221 | pci_read_config_dword(dev, reg+4, &lhi); |
185 | pci_write_config_dword(dev, reg+4, ~0); | 222 | pci_write_config_dword(dev, reg+4, ~0); |
186 | pci_read_config_dword(dev, reg+4, &szhi); | 223 | pci_read_config_dword(dev, reg+4, &szhi); |
187 | pci_write_config_dword(dev, reg+4, lhi); | 224 | pci_write_config_dword(dev, reg+4, lhi); |
188 | szhi = pci_size(lhi, szhi, 0xffffffff); | 225 | sz64 = ((u64)szhi << 32) | raw_sz; |
226 | l64 = ((u64)lhi << 32) | l; | ||
227 | sz64 = pci_size64(l64, sz64, PCI_BASE_ADDRESS_MEM_MASK); | ||
189 | next++; | 228 | next++; |
190 | #if BITS_PER_LONG == 64 | 229 | #if BITS_PER_LONG == 64 |
191 | res->start |= ((unsigned long) lhi) << 32; | 230 | if (!sz64) { |
192 | res->end = res->start + sz; | 231 | res->start = 0; |
193 | if (szhi) { | 232 | res->end = 0; |
194 | /* This BAR needs > 4GB? Wow. */ | 233 | res->flags = 0; |
195 | res->end |= (unsigned long)szhi<<32; | 234 | continue; |
196 | } | 235 | } |
236 | res->start = l64 & PCI_BASE_ADDRESS_MEM_MASK; | ||
237 | res->end = res->start + sz64; | ||
197 | #else | 238 | #else |
198 | if (szhi) { | 239 | if (sz64 > 0x100000000ULL) { |
199 | printk(KERN_ERR "PCI: Unable to handle 64-bit BAR for device %s\n", pci_name(dev)); | 240 | printk(KERN_ERR "PCI: Unable to handle 64-bit " |
241 | "BAR for device %s\n", pci_name(dev)); | ||
200 | res->start = 0; | 242 | res->start = 0; |
201 | res->flags = 0; | 243 | res->flags = 0; |
202 | } else if (lhi) { | 244 | } else if (lhi) { |
203 | /* 64-bit wide address, treat as disabled */ | 245 | /* 64-bit wide address, treat as disabled */ |
204 | pci_write_config_dword(dev, reg, l & ~(u32)PCI_BASE_ADDRESS_MEM_MASK); | 246 | pci_write_config_dword(dev, reg, |
247 | l & ~(u32)PCI_BASE_ADDRESS_MEM_MASK); | ||
205 | pci_write_config_dword(dev, reg+4, 0); | 248 | pci_write_config_dword(dev, reg+4, 0); |
206 | res->start = 0; | 249 | res->start = 0; |
207 | res->end = sz; | 250 | res->end = sz; |