diff options
Diffstat (limited to 'drivers/pci')
-rw-r--r-- | drivers/pci/probe.c | 242 |
1 files changed, 123 insertions, 119 deletions
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index b1724cf31b66..3b690c3512f3 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c | |||
@@ -163,12 +163,9 @@ static inline unsigned int pci_calc_resource_flags(unsigned int flags) | |||
163 | return IORESOURCE_MEM; | 163 | return IORESOURCE_MEM; |
164 | } | 164 | } |
165 | 165 | ||
166 | /* | 166 | static u64 pci_size(u64 base, u64 maxbase, u64 mask) |
167 | * Find the extent of a PCI decode.. | ||
168 | */ | ||
169 | static u32 pci_size(u32 base, u32 maxbase, u32 mask) | ||
170 | { | 167 | { |
171 | u32 size = mask & maxbase; /* Find the significant bits */ | 168 | u64 size = mask & maxbase; /* Find the significant bits */ |
172 | if (!size) | 169 | if (!size) |
173 | return 0; | 170 | return 0; |
174 | 171 | ||
@@ -184,135 +181,142 @@ static u32 pci_size(u32 base, u32 maxbase, u32 mask) | |||
184 | return size; | 181 | return size; |
185 | } | 182 | } |
186 | 183 | ||
187 | static u64 pci_size64(u64 base, u64 maxbase, u64 mask) | 184 | enum pci_bar_type { |
188 | { | 185 | pci_bar_unknown, /* Standard PCI BAR probe */ |
189 | u64 size = mask & maxbase; /* Find the significant bits */ | 186 | pci_bar_io, /* An io port BAR */ |
190 | if (!size) | 187 | pci_bar_mem32, /* A 32-bit memory BAR */ |
191 | return 0; | 188 | pci_bar_mem64, /* A 64-bit memory BAR */ |
189 | }; | ||
192 | 190 | ||
193 | /* Get the lowest of them to find the decode size, and | 191 | static inline enum pci_bar_type decode_bar(struct resource *res, u32 bar) |
194 | from that the extent. */ | 192 | { |
195 | size = (size & ~(size-1)) - 1; | 193 | if ((bar & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO) { |
194 | res->flags = bar & ~PCI_BASE_ADDRESS_IO_MASK; | ||
195 | return pci_bar_io; | ||
196 | } | ||
196 | 197 | ||
197 | /* base == maxbase can be valid only if the BAR has | 198 | res->flags = bar & ~PCI_BASE_ADDRESS_MEM_MASK; |
198 | already been programmed with all 1s. */ | ||
199 | if (base == maxbase && ((base | size) & mask) != mask) | ||
200 | return 0; | ||
201 | 199 | ||
202 | return size; | 200 | if (res->flags == PCI_BASE_ADDRESS_MEM_TYPE_64) |
201 | return pci_bar_mem64; | ||
202 | return pci_bar_mem32; | ||
203 | } | 203 | } |
204 | 204 | ||
205 | static inline int is_64bit_memory(u32 mask) | 205 | /* |
206 | * If the type is not unknown, we assume that the lowest bit is 'enable'. | ||
207 | * Returns 1 if the BAR was 64-bit and 0 if it was 32-bit. | ||
208 | */ | ||
209 | static int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type, | ||
210 | struct resource *res, unsigned int pos) | ||
206 | { | 211 | { |
207 | if ((mask & (PCI_BASE_ADDRESS_SPACE|PCI_BASE_ADDRESS_MEM_TYPE_MASK)) == | 212 | u32 l, sz, mask; |
208 | (PCI_BASE_ADDRESS_SPACE_MEMORY|PCI_BASE_ADDRESS_MEM_TYPE_64)) | ||
209 | return 1; | ||
210 | return 0; | ||
211 | } | ||
212 | 213 | ||
213 | static void pci_read_bases(struct pci_dev *dev, unsigned int howmany, int rom) | 214 | mask = type ? ~PCI_ROM_ADDRESS_ENABLE : ~0; |
214 | { | ||
215 | unsigned int pos, reg, next; | ||
216 | u32 l, sz; | ||
217 | struct resource *res; | ||
218 | 215 | ||
219 | for(pos=0; pos<howmany; pos = next) { | 216 | res->name = pci_name(dev); |
220 | u64 l64; | ||
221 | u64 sz64; | ||
222 | u32 raw_sz; | ||
223 | 217 | ||
224 | next = pos+1; | 218 | pci_read_config_dword(dev, pos, &l); |
225 | res = &dev->resource[pos]; | 219 | pci_write_config_dword(dev, pos, mask); |
226 | res->name = pci_name(dev); | 220 | pci_read_config_dword(dev, pos, &sz); |
227 | reg = PCI_BASE_ADDRESS_0 + (pos << 2); | 221 | pci_write_config_dword(dev, pos, l); |
228 | pci_read_config_dword(dev, reg, &l); | 222 | |
229 | pci_write_config_dword(dev, reg, ~0); | 223 | /* |
230 | pci_read_config_dword(dev, reg, &sz); | 224 | * All bits set in sz means the device isn't working properly. |
231 | pci_write_config_dword(dev, reg, l); | 225 | * If the BAR isn't implemented, all bits must be 0. If it's a |
232 | if (!sz || sz == 0xffffffff) | 226 | * memory BAR or a ROM, bit 0 must be clear; if it's an io BAR, bit |
233 | continue; | 227 | * 1 must be clear. |
234 | if (l == 0xffffffff) | 228 | */ |
235 | l = 0; | 229 | if (!sz || sz == 0xffffffff) |
236 | raw_sz = sz; | 230 | goto fail; |
237 | if ((l & PCI_BASE_ADDRESS_SPACE) == | 231 | |
238 | PCI_BASE_ADDRESS_SPACE_MEMORY) { | 232 | /* |
239 | sz = pci_size(l, sz, (u32)PCI_BASE_ADDRESS_MEM_MASK); | 233 | * I don't know how l can have all bits set. Copied from old code. |
240 | /* | 234 | * Maybe it fixes a bug on some ancient platform. |
241 | * For 64bit prefetchable memory sz could be 0, if the | 235 | */ |
242 | * real size is bigger than 4G, so we need to check | 236 | if (l == 0xffffffff) |
243 | * szhi for that. | 237 | l = 0; |
244 | */ | 238 | |
245 | if (!is_64bit_memory(l) && !sz) | 239 | if (type == pci_bar_unknown) { |
246 | continue; | 240 | type = decode_bar(res, l); |
247 | res->start = l & PCI_BASE_ADDRESS_MEM_MASK; | 241 | res->flags |= pci_calc_resource_flags(l) | IORESOURCE_SIZEALIGN; |
248 | res->flags |= l & ~PCI_BASE_ADDRESS_MEM_MASK; | 242 | if (type == pci_bar_io) { |
243 | l &= PCI_BASE_ADDRESS_IO_MASK; | ||
244 | mask = PCI_BASE_ADDRESS_IO_MASK & 0xffff; | ||
249 | } else { | 245 | } else { |
250 | sz = pci_size(l, sz, PCI_BASE_ADDRESS_IO_MASK & 0xffff); | 246 | l &= PCI_BASE_ADDRESS_MEM_MASK; |
251 | if (!sz) | 247 | mask = (u32)PCI_BASE_ADDRESS_MEM_MASK; |
252 | continue; | ||
253 | res->start = l & PCI_BASE_ADDRESS_IO_MASK; | ||
254 | res->flags |= l & ~PCI_BASE_ADDRESS_IO_MASK; | ||
255 | } | 248 | } |
256 | res->end = res->start + (unsigned long) sz; | 249 | } else { |
257 | res->flags |= pci_calc_resource_flags(l) | IORESOURCE_SIZEALIGN; | 250 | res->flags |= (l & IORESOURCE_ROM_ENABLE); |
258 | if (is_64bit_memory(l)) { | 251 | l &= PCI_ROM_ADDRESS_MASK; |
259 | u32 szhi, lhi; | 252 | mask = (u32)PCI_ROM_ADDRESS_MASK; |
260 | 253 | } | |
261 | pci_read_config_dword(dev, reg+4, &lhi); | 254 | |
262 | pci_write_config_dword(dev, reg+4, ~0); | 255 | if (type == pci_bar_mem64) { |
263 | pci_read_config_dword(dev, reg+4, &szhi); | 256 | u64 l64 = l; |
264 | pci_write_config_dword(dev, reg+4, lhi); | 257 | u64 sz64 = sz; |
265 | sz64 = ((u64)szhi << 32) | raw_sz; | 258 | u64 mask64 = mask | (u64)~0 << 32; |
266 | l64 = ((u64)lhi << 32) | l; | 259 | |
267 | sz64 = pci_size64(l64, sz64, PCI_BASE_ADDRESS_MEM_MASK); | 260 | pci_read_config_dword(dev, pos + 4, &l); |
268 | next++; | 261 | pci_write_config_dword(dev, pos + 4, ~0); |
269 | #if BITS_PER_LONG == 64 | 262 | pci_read_config_dword(dev, pos + 4, &sz); |
270 | if (!sz64) { | 263 | pci_write_config_dword(dev, pos + 4, l); |
271 | res->start = 0; | 264 | |
272 | res->end = 0; | 265 | l64 |= ((u64)l << 32); |
273 | res->flags = 0; | 266 | sz64 |= ((u64)sz << 32); |
274 | continue; | 267 | |
275 | } | 268 | sz64 = pci_size(l64, sz64, mask64); |
276 | res->start = l64 & PCI_BASE_ADDRESS_MEM_MASK; | 269 | |
277 | res->end = res->start + sz64; | 270 | if (!sz64) |
278 | #else | 271 | goto fail; |
279 | if (sz64 > 0x100000000ULL) { | 272 | |
280 | dev_err(&dev->dev, "BAR %d: can't handle 64-bit" | 273 | if ((BITS_PER_LONG < 64) && (sz64 > 0x100000000ULL)) { |
281 | " BAR\n", pos); | 274 | dev_err(&dev->dev, "can't handle 64-bit BAR\n"); |
282 | res->start = 0; | 275 | goto fail; |
283 | res->flags = 0; | 276 | } else if ((BITS_PER_LONG < 64) && l) { |
284 | } else if (lhi) { | 277 | /* Address above 32-bit boundary; disable the BAR */ |
285 | /* 64-bit wide address, treat as disabled */ | 278 | pci_write_config_dword(dev, pos, 0); |
286 | pci_write_config_dword(dev, reg, | 279 | pci_write_config_dword(dev, pos + 4, 0); |
287 | l & ~(u32)PCI_BASE_ADDRESS_MEM_MASK); | 280 | res->start = 0; |
288 | pci_write_config_dword(dev, reg+4, 0); | 281 | res->end = sz64; |
289 | res->start = 0; | 282 | } else { |
290 | res->end = sz; | 283 | res->start = l64; |
291 | } | 284 | res->end = l64 + sz64; |
292 | #endif | ||
293 | } | 285 | } |
286 | } else { | ||
287 | sz = pci_size(l, sz, mask); | ||
288 | |||
289 | if (!sz) | ||
290 | goto fail; | ||
291 | |||
292 | res->start = l; | ||
293 | res->end = l + sz; | ||
294 | } | 294 | } |
295 | |||
296 | out: | ||
297 | return (type == pci_bar_mem64) ? 1 : 0; | ||
298 | fail: | ||
299 | res->flags = 0; | ||
300 | goto out; | ||
301 | } | ||
302 | |||
303 | static void pci_read_bases(struct pci_dev *dev, unsigned int howmany, int rom) | ||
304 | { | ||
305 | unsigned int pos, reg; | ||
306 | |||
307 | for (pos = 0; pos < howmany; pos++) { | ||
308 | struct resource *res = &dev->resource[pos]; | ||
309 | reg = PCI_BASE_ADDRESS_0 + (pos << 2); | ||
310 | pos += __pci_read_base(dev, pci_bar_unknown, res, reg); | ||
311 | } | ||
312 | |||
295 | if (rom) { | 313 | if (rom) { |
314 | struct resource *res = &dev->resource[PCI_ROM_RESOURCE]; | ||
296 | dev->rom_base_reg = rom; | 315 | dev->rom_base_reg = rom; |
297 | res = &dev->resource[PCI_ROM_RESOURCE]; | 316 | res->flags = IORESOURCE_MEM | IORESOURCE_PREFETCH | |
298 | res->name = pci_name(dev); | 317 | IORESOURCE_READONLY | IORESOURCE_CACHEABLE | |
299 | pci_read_config_dword(dev, rom, &l); | 318 | IORESOURCE_SIZEALIGN; |
300 | pci_write_config_dword(dev, rom, ~PCI_ROM_ADDRESS_ENABLE); | 319 | __pci_read_base(dev, pci_bar_mem32, res, rom); |
301 | pci_read_config_dword(dev, rom, &sz); | ||
302 | pci_write_config_dword(dev, rom, l); | ||
303 | if (l == 0xffffffff) | ||
304 | l = 0; | ||
305 | if (sz && sz != 0xffffffff) { | ||
306 | sz = pci_size(l, sz, (u32)PCI_ROM_ADDRESS_MASK); | ||
307 | if (sz) { | ||
308 | res->flags = (l & IORESOURCE_ROM_ENABLE) | | ||
309 | IORESOURCE_MEM | IORESOURCE_PREFETCH | | ||
310 | IORESOURCE_READONLY | IORESOURCE_CACHEABLE | | ||
311 | IORESOURCE_SIZEALIGN; | ||
312 | res->start = l & PCI_ROM_ADDRESS_MASK; | ||
313 | res->end = res->start + (unsigned long) sz; | ||
314 | } | ||
315 | } | ||
316 | } | 320 | } |
317 | } | 321 | } |
318 | 322 | ||