diff options
| -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 | ||
