diff options
Diffstat (limited to 'arch/x86/pci/mmconfig-shared.c')
-rw-r--r-- | arch/x86/pci/mmconfig-shared.c | 247 |
1 files changed, 212 insertions, 35 deletions
diff --git a/arch/x86/pci/mmconfig-shared.c b/arch/x86/pci/mmconfig-shared.c index 8d54df4dfaa..0cfebecf2a8 100644 --- a/arch/x86/pci/mmconfig-shared.c +++ b/arch/x86/pci/mmconfig-shared.c | |||
@@ -28,7 +28,7 @@ static int __initdata pci_mmcfg_resources_inserted; | |||
28 | static const char __init *pci_mmcfg_e7520(void) | 28 | static const char __init *pci_mmcfg_e7520(void) |
29 | { | 29 | { |
30 | u32 win; | 30 | u32 win; |
31 | pci_direct_conf1.read(0, 0, PCI_DEVFN(0,0), 0xce, 2, &win); | 31 | raw_pci_ops->read(0, 0, PCI_DEVFN(0, 0), 0xce, 2, &win); |
32 | 32 | ||
33 | win = win & 0xf000; | 33 | win = win & 0xf000; |
34 | if(win == 0x0000 || win == 0xf000) | 34 | if(win == 0x0000 || win == 0xf000) |
@@ -53,7 +53,7 @@ static const char __init *pci_mmcfg_intel_945(void) | |||
53 | 53 | ||
54 | pci_mmcfg_config_num = 1; | 54 | pci_mmcfg_config_num = 1; |
55 | 55 | ||
56 | pci_direct_conf1.read(0, 0, PCI_DEVFN(0,0), 0x48, 4, &pciexbar); | 56 | raw_pci_ops->read(0, 0, PCI_DEVFN(0, 0), 0x48, 4, &pciexbar); |
57 | 57 | ||
58 | /* Enable bit */ | 58 | /* Enable bit */ |
59 | if (!(pciexbar & 1)) | 59 | if (!(pciexbar & 1)) |
@@ -100,33 +100,102 @@ static const char __init *pci_mmcfg_intel_945(void) | |||
100 | return "Intel Corporation 945G/GZ/P/PL Express Memory Controller Hub"; | 100 | return "Intel Corporation 945G/GZ/P/PL Express Memory Controller Hub"; |
101 | } | 101 | } |
102 | 102 | ||
103 | static const char __init *pci_mmcfg_amd_fam10h(void) | ||
104 | { | ||
105 | u32 low, high, address; | ||
106 | u64 base, msr; | ||
107 | int i; | ||
108 | unsigned segnbits = 0, busnbits; | ||
109 | |||
110 | if (!(pci_probe & PCI_CHECK_ENABLE_AMD_MMCONF)) | ||
111 | return NULL; | ||
112 | |||
113 | address = MSR_FAM10H_MMIO_CONF_BASE; | ||
114 | if (rdmsr_safe(address, &low, &high)) | ||
115 | return NULL; | ||
116 | |||
117 | msr = high; | ||
118 | msr <<= 32; | ||
119 | msr |= low; | ||
120 | |||
121 | /* mmconfig is not enable */ | ||
122 | if (!(msr & FAM10H_MMIO_CONF_ENABLE)) | ||
123 | return NULL; | ||
124 | |||
125 | base = msr & (FAM10H_MMIO_CONF_BASE_MASK<<FAM10H_MMIO_CONF_BASE_SHIFT); | ||
126 | |||
127 | busnbits = (msr >> FAM10H_MMIO_CONF_BUSRANGE_SHIFT) & | ||
128 | FAM10H_MMIO_CONF_BUSRANGE_MASK; | ||
129 | |||
130 | /* | ||
131 | * only handle bus 0 ? | ||
132 | * need to skip it | ||
133 | */ | ||
134 | if (!busnbits) | ||
135 | return NULL; | ||
136 | |||
137 | if (busnbits > 8) { | ||
138 | segnbits = busnbits - 8; | ||
139 | busnbits = 8; | ||
140 | } | ||
141 | |||
142 | pci_mmcfg_config_num = (1 << segnbits); | ||
143 | pci_mmcfg_config = kzalloc(sizeof(pci_mmcfg_config[0]) * | ||
144 | pci_mmcfg_config_num, GFP_KERNEL); | ||
145 | if (!pci_mmcfg_config) | ||
146 | return NULL; | ||
147 | |||
148 | for (i = 0; i < (1 << segnbits); i++) { | ||
149 | pci_mmcfg_config[i].address = base + (1<<28) * i; | ||
150 | pci_mmcfg_config[i].pci_segment = i; | ||
151 | pci_mmcfg_config[i].start_bus_number = 0; | ||
152 | pci_mmcfg_config[i].end_bus_number = (1 << busnbits) - 1; | ||
153 | } | ||
154 | |||
155 | return "AMD Family 10h NB"; | ||
156 | } | ||
157 | |||
103 | struct pci_mmcfg_hostbridge_probe { | 158 | struct pci_mmcfg_hostbridge_probe { |
159 | u32 bus; | ||
160 | u32 devfn; | ||
104 | u32 vendor; | 161 | u32 vendor; |
105 | u32 device; | 162 | u32 device; |
106 | const char *(*probe)(void); | 163 | const char *(*probe)(void); |
107 | }; | 164 | }; |
108 | 165 | ||
109 | static struct pci_mmcfg_hostbridge_probe pci_mmcfg_probes[] __initdata = { | 166 | static struct pci_mmcfg_hostbridge_probe pci_mmcfg_probes[] __initdata = { |
110 | { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_E7520_MCH, pci_mmcfg_e7520 }, | 167 | { 0, PCI_DEVFN(0, 0), PCI_VENDOR_ID_INTEL, |
111 | { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82945G_HB, pci_mmcfg_intel_945 }, | 168 | PCI_DEVICE_ID_INTEL_E7520_MCH, pci_mmcfg_e7520 }, |
169 | { 0, PCI_DEVFN(0, 0), PCI_VENDOR_ID_INTEL, | ||
170 | PCI_DEVICE_ID_INTEL_82945G_HB, pci_mmcfg_intel_945 }, | ||
171 | { 0, PCI_DEVFN(0x18, 0), PCI_VENDOR_ID_AMD, | ||
172 | 0x1200, pci_mmcfg_amd_fam10h }, | ||
173 | { 0xff, PCI_DEVFN(0, 0), PCI_VENDOR_ID_AMD, | ||
174 | 0x1200, pci_mmcfg_amd_fam10h }, | ||
112 | }; | 175 | }; |
113 | 176 | ||
114 | static int __init pci_mmcfg_check_hostbridge(void) | 177 | static int __init pci_mmcfg_check_hostbridge(void) |
115 | { | 178 | { |
116 | u32 l; | 179 | u32 l; |
180 | u32 bus, devfn; | ||
117 | u16 vendor, device; | 181 | u16 vendor, device; |
118 | int i; | 182 | int i; |
119 | const char *name; | 183 | const char *name; |
120 | 184 | ||
121 | pci_direct_conf1.read(0, 0, PCI_DEVFN(0,0), 0, 4, &l); | 185 | if (!raw_pci_ops) |
122 | vendor = l & 0xffff; | 186 | return 0; |
123 | device = (l >> 16) & 0xffff; | ||
124 | 187 | ||
125 | pci_mmcfg_config_num = 0; | 188 | pci_mmcfg_config_num = 0; |
126 | pci_mmcfg_config = NULL; | 189 | pci_mmcfg_config = NULL; |
127 | name = NULL; | 190 | name = NULL; |
128 | 191 | ||
129 | for (i = 0; !name && i < ARRAY_SIZE(pci_mmcfg_probes); i++) { | 192 | for (i = 0; !name && i < ARRAY_SIZE(pci_mmcfg_probes); i++) { |
193 | bus = pci_mmcfg_probes[i].bus; | ||
194 | devfn = pci_mmcfg_probes[i].devfn; | ||
195 | raw_pci_ops->read(0, bus, devfn, 0, 4, &l); | ||
196 | vendor = l & 0xffff; | ||
197 | device = (l >> 16) & 0xffff; | ||
198 | |||
130 | if (pci_mmcfg_probes[i].vendor == vendor && | 199 | if (pci_mmcfg_probes[i].vendor == vendor && |
131 | pci_mmcfg_probes[i].device == device) | 200 | pci_mmcfg_probes[i].device == device) |
132 | name = pci_mmcfg_probes[i].probe(); | 201 | name = pci_mmcfg_probes[i].probe(); |
@@ -173,9 +242,78 @@ static void __init pci_mmcfg_insert_resources(unsigned long resource_flags) | |||
173 | pci_mmcfg_resources_inserted = 1; | 242 | pci_mmcfg_resources_inserted = 1; |
174 | } | 243 | } |
175 | 244 | ||
176 | static void __init pci_mmcfg_reject_broken(int type) | 245 | static acpi_status __init check_mcfg_resource(struct acpi_resource *res, |
246 | void *data) | ||
247 | { | ||
248 | struct resource *mcfg_res = data; | ||
249 | struct acpi_resource_address64 address; | ||
250 | acpi_status status; | ||
251 | |||
252 | if (res->type == ACPI_RESOURCE_TYPE_FIXED_MEMORY32) { | ||
253 | struct acpi_resource_fixed_memory32 *fixmem32 = | ||
254 | &res->data.fixed_memory32; | ||
255 | if (!fixmem32) | ||
256 | return AE_OK; | ||
257 | if ((mcfg_res->start >= fixmem32->address) && | ||
258 | (mcfg_res->end < (fixmem32->address + | ||
259 | fixmem32->address_length))) { | ||
260 | mcfg_res->flags = 1; | ||
261 | return AE_CTRL_TERMINATE; | ||
262 | } | ||
263 | } | ||
264 | if ((res->type != ACPI_RESOURCE_TYPE_ADDRESS32) && | ||
265 | (res->type != ACPI_RESOURCE_TYPE_ADDRESS64)) | ||
266 | return AE_OK; | ||
267 | |||
268 | status = acpi_resource_to_address64(res, &address); | ||
269 | if (ACPI_FAILURE(status) || | ||
270 | (address.address_length <= 0) || | ||
271 | (address.resource_type != ACPI_MEMORY_RANGE)) | ||
272 | return AE_OK; | ||
273 | |||
274 | if ((mcfg_res->start >= address.minimum) && | ||
275 | (mcfg_res->end < (address.minimum + address.address_length))) { | ||
276 | mcfg_res->flags = 1; | ||
277 | return AE_CTRL_TERMINATE; | ||
278 | } | ||
279 | return AE_OK; | ||
280 | } | ||
281 | |||
282 | static acpi_status __init find_mboard_resource(acpi_handle handle, u32 lvl, | ||
283 | void *context, void **rv) | ||
284 | { | ||
285 | struct resource *mcfg_res = context; | ||
286 | |||
287 | acpi_walk_resources(handle, METHOD_NAME__CRS, | ||
288 | check_mcfg_resource, context); | ||
289 | |||
290 | if (mcfg_res->flags) | ||
291 | return AE_CTRL_TERMINATE; | ||
292 | |||
293 | return AE_OK; | ||
294 | } | ||
295 | |||
296 | static int __init is_acpi_reserved(unsigned long start, unsigned long end) | ||
297 | { | ||
298 | struct resource mcfg_res; | ||
299 | |||
300 | mcfg_res.start = start; | ||
301 | mcfg_res.end = end; | ||
302 | mcfg_res.flags = 0; | ||
303 | |||
304 | acpi_get_devices("PNP0C01", find_mboard_resource, &mcfg_res, NULL); | ||
305 | |||
306 | if (!mcfg_res.flags) | ||
307 | acpi_get_devices("PNP0C02", find_mboard_resource, &mcfg_res, | ||
308 | NULL); | ||
309 | |||
310 | return mcfg_res.flags; | ||
311 | } | ||
312 | |||
313 | static void __init pci_mmcfg_reject_broken(int early) | ||
177 | { | 314 | { |
178 | typeof(pci_mmcfg_config[0]) *cfg; | 315 | typeof(pci_mmcfg_config[0]) *cfg; |
316 | int i; | ||
179 | 317 | ||
180 | if ((pci_mmcfg_config_num == 0) || | 318 | if ((pci_mmcfg_config_num == 0) || |
181 | (pci_mmcfg_config == NULL) || | 319 | (pci_mmcfg_config == NULL) || |
@@ -184,51 +322,80 @@ static void __init pci_mmcfg_reject_broken(int type) | |||
184 | 322 | ||
185 | cfg = &pci_mmcfg_config[0]; | 323 | cfg = &pci_mmcfg_config[0]; |
186 | 324 | ||
187 | /* | 325 | for (i = 0; i < pci_mmcfg_config_num; i++) { |
188 | * Handle more broken MCFG tables on Asus etc. | 326 | int valid = 0; |
189 | * They only contain a single entry for bus 0-0. | 327 | u32 size = (cfg->end_bus_number + 1) << 20; |
190 | */ | 328 | cfg = &pci_mmcfg_config[i]; |
191 | if (pci_mmcfg_config_num == 1 && | 329 | printk(KERN_NOTICE "PCI: MCFG configuration %d: base %lx " |
192 | cfg->pci_segment == 0 && | 330 | "segment %hu buses %u - %u\n", |
193 | (cfg->start_bus_number | cfg->end_bus_number) == 0) { | 331 | i, (unsigned long)cfg->address, cfg->pci_segment, |
194 | printk(KERN_ERR "PCI: start and end of bus number is 0. " | 332 | (unsigned int)cfg->start_bus_number, |
195 | "Rejected as broken MCFG.\n"); | 333 | (unsigned int)cfg->end_bus_number); |
196 | goto reject; | 334 | |
335 | if (!early && | ||
336 | is_acpi_reserved(cfg->address, cfg->address + size - 1)) { | ||
337 | printk(KERN_NOTICE "PCI: MCFG area at %Lx reserved " | ||
338 | "in ACPI motherboard resources\n", | ||
339 | cfg->address); | ||
340 | valid = 1; | ||
341 | } | ||
342 | |||
343 | if (valid) | ||
344 | continue; | ||
345 | |||
346 | if (!early) | ||
347 | printk(KERN_ERR "PCI: BIOS Bug: MCFG area at %Lx is not" | ||
348 | " reserved in ACPI motherboard resources\n", | ||
349 | cfg->address); | ||
350 | /* Don't try to do this check unless configuration | ||
351 | type 1 is available. how about type 2 ?*/ | ||
352 | if (raw_pci_ops && e820_all_mapped(cfg->address, | ||
353 | cfg->address + size - 1, | ||
354 | E820_RESERVED)) { | ||
355 | printk(KERN_NOTICE | ||
356 | "PCI: MCFG area at %Lx reserved in E820\n", | ||
357 | cfg->address); | ||
358 | valid = 1; | ||
359 | } | ||
360 | |||
361 | if (!valid) | ||
362 | goto reject; | ||
197 | } | 363 | } |
198 | 364 | ||
199 | /* | ||
200 | * Only do this check when type 1 works. If it doesn't work | ||
201 | * assume we run on a Mac and always use MCFG | ||
202 | */ | ||
203 | if (type == 1 && !e820_all_mapped(cfg->address, | ||
204 | cfg->address + MMCONFIG_APER_MIN, | ||
205 | E820_RESERVED)) { | ||
206 | printk(KERN_ERR "PCI: BIOS Bug: MCFG area at %Lx is not" | ||
207 | " E820-reserved\n", cfg->address); | ||
208 | goto reject; | ||
209 | } | ||
210 | return; | 365 | return; |
211 | 366 | ||
212 | reject: | 367 | reject: |
213 | printk(KERN_ERR "PCI: Not using MMCONFIG.\n"); | 368 | printk(KERN_ERR "PCI: Not using MMCONFIG.\n"); |
369 | pci_mmcfg_arch_free(); | ||
214 | kfree(pci_mmcfg_config); | 370 | kfree(pci_mmcfg_config); |
215 | pci_mmcfg_config = NULL; | 371 | pci_mmcfg_config = NULL; |
216 | pci_mmcfg_config_num = 0; | 372 | pci_mmcfg_config_num = 0; |
217 | } | 373 | } |
218 | 374 | ||
219 | void __init pci_mmcfg_init(int type) | 375 | static int __initdata known_bridge; |
220 | { | ||
221 | int known_bridge = 0; | ||
222 | 376 | ||
377 | void __init __pci_mmcfg_init(int early) | ||
378 | { | ||
379 | /* MMCONFIG disabled */ | ||
223 | if ((pci_probe & PCI_PROBE_MMCONF) == 0) | 380 | if ((pci_probe & PCI_PROBE_MMCONF) == 0) |
224 | return; | 381 | return; |
225 | 382 | ||
226 | if (type == 1 && pci_mmcfg_check_hostbridge()) | 383 | /* MMCONFIG already enabled */ |
227 | known_bridge = 1; | 384 | if (!early && !(pci_probe & PCI_PROBE_MASK & ~PCI_PROBE_MMCONF)) |
385 | return; | ||
386 | |||
387 | /* for late to exit */ | ||
388 | if (known_bridge) | ||
389 | return; | ||
390 | |||
391 | if (early) { | ||
392 | if (pci_mmcfg_check_hostbridge()) | ||
393 | known_bridge = 1; | ||
394 | } | ||
228 | 395 | ||
229 | if (!known_bridge) { | 396 | if (!known_bridge) { |
230 | acpi_table_parse(ACPI_SIG_MCFG, acpi_parse_mcfg); | 397 | acpi_table_parse(ACPI_SIG_MCFG, acpi_parse_mcfg); |
231 | pci_mmcfg_reject_broken(type); | 398 | pci_mmcfg_reject_broken(early); |
232 | } | 399 | } |
233 | 400 | ||
234 | if ((pci_mmcfg_config_num == 0) || | 401 | if ((pci_mmcfg_config_num == 0) || |
@@ -249,6 +416,16 @@ void __init pci_mmcfg_init(int type) | |||
249 | } | 416 | } |
250 | } | 417 | } |
251 | 418 | ||
419 | void __init pci_mmcfg_early_init(void) | ||
420 | { | ||
421 | __pci_mmcfg_init(1); | ||
422 | } | ||
423 | |||
424 | void __init pci_mmcfg_late_init(void) | ||
425 | { | ||
426 | __pci_mmcfg_init(0); | ||
427 | } | ||
428 | |||
252 | static int __init pci_mmcfg_late_insert_resources(void) | 429 | static int __init pci_mmcfg_late_insert_resources(void) |
253 | { | 430 | { |
254 | /* | 431 | /* |