2 * mmconfig-shared.c - Low-level direct PCI config space access via
3 * MMCONFIG - common code between i386 and x86-64.
6 * - known chipset handling
7 * - ACPI decoding and validation
9 * Per-architecture code takes care of the mappings and accesses
13 #include <linux/pci.h>
14 #include <linux/init.h>
15 #include <linux/acpi.h>
16 #include <linux/bitmap.h>
18 #include <asm/pci_x86.h>
20 /* aperture is up to 256MB but BIOS may reserve less */
21 #define MMCONFIG_APER_MIN (2 * 1024*1024)
22 #define MMCONFIG_APER_MAX (256 * 1024*1024)
24 /* Indicate if the mmcfg resources have been placed into the resource table. */
25 static int __initdata pci_mmcfg_resources_inserted
;
27 static const char __init
*pci_mmcfg_e7520(void)
30 raw_pci_ops
->read(0, 0, PCI_DEVFN(0, 0), 0xce, 2, &win
);
33 if(win
== 0x0000 || win
== 0xf000)
34 pci_mmcfg_config_num
= 0;
36 pci_mmcfg_config_num
= 1;
37 pci_mmcfg_config
= kzalloc(sizeof(pci_mmcfg_config
[0]), GFP_KERNEL
);
38 if (!pci_mmcfg_config
)
40 pci_mmcfg_config
[0].address
= win
<< 16;
41 pci_mmcfg_config
[0].pci_segment
= 0;
42 pci_mmcfg_config
[0].start_bus_number
= 0;
43 pci_mmcfg_config
[0].end_bus_number
= 255;
46 return "Intel Corporation E7520 Memory Controller Hub";
49 static const char __init
*pci_mmcfg_intel_945(void)
51 u32 pciexbar
, mask
= 0, len
= 0;
53 pci_mmcfg_config_num
= 1;
55 raw_pci_ops
->read(0, 0, PCI_DEVFN(0, 0), 0x48, 4, &pciexbar
);
59 pci_mmcfg_config_num
= 0;
62 switch ((pciexbar
>> 1) & 3) {
76 pci_mmcfg_config_num
= 0;
79 /* Errata #2, things break when not aligned on a 256Mb boundary */
80 /* Can only happen in 64M/128M mode */
82 if ((pciexbar
& mask
) & 0x0fffffffU
)
83 pci_mmcfg_config_num
= 0;
85 /* Don't hit the APIC registers and their friends */
86 if ((pciexbar
& mask
) >= 0xf0000000U
)
87 pci_mmcfg_config_num
= 0;
89 if (pci_mmcfg_config_num
) {
90 pci_mmcfg_config
= kzalloc(sizeof(pci_mmcfg_config
[0]), GFP_KERNEL
);
91 if (!pci_mmcfg_config
)
93 pci_mmcfg_config
[0].address
= pciexbar
& mask
;
94 pci_mmcfg_config
[0].pci_segment
= 0;
95 pci_mmcfg_config
[0].start_bus_number
= 0;
96 pci_mmcfg_config
[0].end_bus_number
= (len
>> 20) - 1;
99 return "Intel Corporation 945G/GZ/P/PL Express Memory Controller Hub";
102 static const char __init
*pci_mmcfg_amd_fam10h(void)
104 u32 low
, high
, address
;
107 unsigned segnbits
= 0, busnbits
;
109 if (!(pci_probe
& PCI_CHECK_ENABLE_AMD_MMCONF
))
112 address
= MSR_FAM10H_MMIO_CONF_BASE
;
113 if (rdmsr_safe(address
, &low
, &high
))
120 /* mmconfig is not enable */
121 if (!(msr
& FAM10H_MMIO_CONF_ENABLE
))
124 base
= msr
& (FAM10H_MMIO_CONF_BASE_MASK
<<FAM10H_MMIO_CONF_BASE_SHIFT
);
126 busnbits
= (msr
>> FAM10H_MMIO_CONF_BUSRANGE_SHIFT
) &
127 FAM10H_MMIO_CONF_BUSRANGE_MASK
;
130 * only handle bus 0 ?
137 segnbits
= busnbits
- 8;
141 pci_mmcfg_config_num
= (1 << segnbits
);
142 pci_mmcfg_config
= kzalloc(sizeof(pci_mmcfg_config
[0]) *
143 pci_mmcfg_config_num
, GFP_KERNEL
);
144 if (!pci_mmcfg_config
)
147 for (i
= 0; i
< (1 << segnbits
); i
++) {
148 pci_mmcfg_config
[i
].address
= base
+ (1<<28) * i
;
149 pci_mmcfg_config
[i
].pci_segment
= i
;
150 pci_mmcfg_config
[i
].start_bus_number
= 0;
151 pci_mmcfg_config
[i
].end_bus_number
= (1 << busnbits
) - 1;
154 return "AMD Family 10h NB";
157 static bool __initdata mcp55_checked
;
158 static const char __init
*pci_mmcfg_nvidia_mcp55(void)
161 int mcp55_mmconf_found
= 0;
163 static const u32 extcfg_regnum
= 0x90;
164 static const u32 extcfg_regsize
= 4;
165 static const u32 extcfg_enable_mask
= 1<<31;
166 static const u32 extcfg_start_mask
= 0xff<<16;
167 static const int extcfg_start_shift
= 16;
168 static const u32 extcfg_size_mask
= 0x3<<28;
169 static const int extcfg_size_shift
= 28;
170 static const int extcfg_sizebus
[] = {0x100, 0x80, 0x40, 0x20};
171 static const u32 extcfg_base_mask
[] = {0x7ff8, 0x7ffc, 0x7ffe, 0x7fff};
172 static const int extcfg_base_lshift
= 25;
175 * do check if amd fam10h already took over
177 if (!acpi_disabled
|| pci_mmcfg_config_num
|| mcp55_checked
)
180 mcp55_checked
= true;
181 for (bus
= 0; bus
< 256; bus
++) {
185 int start
, size_index
, end
;
187 raw_pci_ops
->read(0, bus
, PCI_DEVFN(0, 0), 0, 4, &l
);
189 device
= (l
>> 16) & 0xffff;
191 if (PCI_VENDOR_ID_NVIDIA
!= vendor
|| 0x0369 != device
)
194 raw_pci_ops
->read(0, bus
, PCI_DEVFN(0, 0), extcfg_regnum
,
195 extcfg_regsize
, &extcfg
);
197 if (!(extcfg
& extcfg_enable_mask
))
200 if (extend_mmcfg(1) == -1)
203 size_index
= (extcfg
& extcfg_size_mask
) >> extcfg_size_shift
;
204 base
= extcfg
& extcfg_base_mask
[size_index
];
205 /* base could > 4G */
206 base
<<= extcfg_base_lshift
;
207 start
= (extcfg
& extcfg_start_mask
) >> extcfg_start_shift
;
208 end
= start
+ extcfg_sizebus
[size_index
] - 1;
209 fill_one_mmcfg(base
, 0, start
, end
);
210 mcp55_mmconf_found
++;
213 if (!mcp55_mmconf_found
)
216 return "nVidia MCP55";
219 struct pci_mmcfg_hostbridge_probe
{
224 const char *(*probe
)(void);
227 static struct pci_mmcfg_hostbridge_probe pci_mmcfg_probes
[] __initdata
= {
228 { 0, PCI_DEVFN(0, 0), PCI_VENDOR_ID_INTEL
,
229 PCI_DEVICE_ID_INTEL_E7520_MCH
, pci_mmcfg_e7520
},
230 { 0, PCI_DEVFN(0, 0), PCI_VENDOR_ID_INTEL
,
231 PCI_DEVICE_ID_INTEL_82945G_HB
, pci_mmcfg_intel_945
},
232 { 0, PCI_DEVFN(0x18, 0), PCI_VENDOR_ID_AMD
,
233 0x1200, pci_mmcfg_amd_fam10h
},
234 { 0xff, PCI_DEVFN(0, 0), PCI_VENDOR_ID_AMD
,
235 0x1200, pci_mmcfg_amd_fam10h
},
236 { 0, PCI_DEVFN(0, 0), PCI_VENDOR_ID_NVIDIA
,
237 0x0369, pci_mmcfg_nvidia_mcp55
},
240 static int __init
pci_mmcfg_check_hostbridge(void)
251 pci_mmcfg_config_num
= 0;
252 pci_mmcfg_config
= NULL
;
255 for (i
= 0; !name
&& i
< ARRAY_SIZE(pci_mmcfg_probes
); i
++) {
256 bus
= pci_mmcfg_probes
[i
].bus
;
257 devfn
= pci_mmcfg_probes
[i
].devfn
;
258 raw_pci_ops
->read(0, bus
, devfn
, 0, 4, &l
);
260 device
= (l
>> 16) & 0xffff;
262 if (pci_mmcfg_probes
[i
].vendor
== vendor
&&
263 pci_mmcfg_probes
[i
].device
== device
)
264 name
= pci_mmcfg_probes
[i
].probe();
268 printk(KERN_INFO
"PCI: Found %s %s MMCONFIG support.\n",
269 name
, pci_mmcfg_config_num
? "with" : "without");
275 static void __init
pci_mmcfg_insert_resources(void)
277 #define PCI_MMCFG_RESOURCE_NAME_LEN 19
279 struct resource
*res
;
283 res
= kcalloc(PCI_MMCFG_RESOURCE_NAME_LEN
+ sizeof(*res
),
284 pci_mmcfg_config_num
, GFP_KERNEL
);
286 printk(KERN_ERR
"PCI: Unable to allocate MMCONFIG resources\n");
290 names
= (void *)&res
[pci_mmcfg_config_num
];
291 for (i
= 0; i
< pci_mmcfg_config_num
; i
++, res
++) {
292 struct acpi_mcfg_allocation
*cfg
= &pci_mmcfg_config
[i
];
293 num_buses
= cfg
->end_bus_number
- cfg
->start_bus_number
+ 1;
295 snprintf(names
, PCI_MMCFG_RESOURCE_NAME_LEN
, "PCI MMCONFIG %u",
297 res
->start
= cfg
->address
;
298 res
->end
= res
->start
+ (num_buses
<< 20) - 1;
299 res
->flags
= IORESOURCE_MEM
| IORESOURCE_BUSY
;
300 insert_resource(&iomem_resource
, res
);
301 names
+= PCI_MMCFG_RESOURCE_NAME_LEN
;
304 /* Mark that the resources have been inserted. */
305 pci_mmcfg_resources_inserted
= 1;
308 static acpi_status __init
check_mcfg_resource(struct acpi_resource
*res
,
311 struct resource
*mcfg_res
= data
;
312 struct acpi_resource_address64 address
;
315 if (res
->type
== ACPI_RESOURCE_TYPE_FIXED_MEMORY32
) {
316 struct acpi_resource_fixed_memory32
*fixmem32
=
317 &res
->data
.fixed_memory32
;
320 if ((mcfg_res
->start
>= fixmem32
->address
) &&
321 (mcfg_res
->end
< (fixmem32
->address
+
322 fixmem32
->address_length
))) {
324 return AE_CTRL_TERMINATE
;
327 if ((res
->type
!= ACPI_RESOURCE_TYPE_ADDRESS32
) &&
328 (res
->type
!= ACPI_RESOURCE_TYPE_ADDRESS64
))
331 status
= acpi_resource_to_address64(res
, &address
);
332 if (ACPI_FAILURE(status
) ||
333 (address
.address_length
<= 0) ||
334 (address
.resource_type
!= ACPI_MEMORY_RANGE
))
337 if ((mcfg_res
->start
>= address
.minimum
) &&
338 (mcfg_res
->end
< (address
.minimum
+ address
.address_length
))) {
340 return AE_CTRL_TERMINATE
;
345 static acpi_status __init
find_mboard_resource(acpi_handle handle
, u32 lvl
,
346 void *context
, void **rv
)
348 struct resource
*mcfg_res
= context
;
350 acpi_walk_resources(handle
, METHOD_NAME__CRS
,
351 check_mcfg_resource
, context
);
354 return AE_CTRL_TERMINATE
;
359 static int __init
is_acpi_reserved(u64 start
, u64 end
, unsigned not_used
)
361 struct resource mcfg_res
;
363 mcfg_res
.start
= start
;
367 acpi_get_devices("PNP0C01", find_mboard_resource
, &mcfg_res
, NULL
);
370 acpi_get_devices("PNP0C02", find_mboard_resource
, &mcfg_res
,
373 return mcfg_res
.flags
;
376 typedef int (*check_reserved_t
)(u64 start
, u64 end
, unsigned type
);
378 static int __init
is_mmconf_reserved(check_reserved_t is_reserved
,
379 u64 addr
, u64 size
, int i
,
380 typeof(pci_mmcfg_config
[0]) *cfg
, int with_e820
)
385 while (!is_reserved(addr
, addr
+ size
- 1, E820_RESERVED
)) {
387 if (size
< (16UL<<20))
391 if (size
>= (16UL<<20) || size
== old_size
) {
393 "PCI: MCFG area at %Lx reserved in %s\n",
394 addr
, with_e820
?"E820":"ACPI motherboard resources");
397 if (old_size
!= size
) {
398 /* update end_bus_number */
399 cfg
->end_bus_number
= cfg
->start_bus_number
+ ((size
>>20) - 1);
400 printk(KERN_NOTICE
"PCI: updated MCFG configuration %d: base %lx "
401 "segment %hu buses %u - %u\n",
402 i
, (unsigned long)cfg
->address
, cfg
->pci_segment
,
403 (unsigned int)cfg
->start_bus_number
,
404 (unsigned int)cfg
->end_bus_number
);
411 static void __init
pci_mmcfg_reject_broken(int early
)
413 typeof(pci_mmcfg_config
[0]) *cfg
;
416 if ((pci_mmcfg_config_num
== 0) ||
417 (pci_mmcfg_config
== NULL
) ||
418 (pci_mmcfg_config
[0].address
== 0))
421 cfg
= &pci_mmcfg_config
[0];
423 for (i
= 0; i
< pci_mmcfg_config_num
; i
++) {
427 cfg
= &pci_mmcfg_config
[i
];
428 addr
= cfg
->start_bus_number
;
430 addr
+= cfg
->address
;
431 size
= cfg
->end_bus_number
+ 1 - cfg
->start_bus_number
;
433 printk(KERN_NOTICE
"PCI: MCFG configuration %d: base %lx "
434 "segment %hu buses %u - %u\n",
435 i
, (unsigned long)cfg
->address
, cfg
->pci_segment
,
436 (unsigned int)cfg
->start_bus_number
,
437 (unsigned int)cfg
->end_bus_number
);
440 valid
= is_mmconf_reserved(is_acpi_reserved
, addr
, size
, i
, cfg
, 0);
446 printk(KERN_ERR
"PCI: BIOS Bug: MCFG area at %Lx is not"
447 " reserved in ACPI motherboard resources\n",
450 /* Don't try to do this check unless configuration
451 type 1 is available. how about type 2 ?*/
453 valid
= is_mmconf_reserved(e820_all_mapped
, addr
, size
, i
, cfg
, 1);
462 printk(KERN_INFO
"PCI: Not using MMCONFIG.\n");
463 pci_mmcfg_arch_free();
464 kfree(pci_mmcfg_config
);
465 pci_mmcfg_config
= NULL
;
466 pci_mmcfg_config_num
= 0;
469 static int __initdata known_bridge
;
471 static void __init
__pci_mmcfg_init(int early
)
473 /* MMCONFIG disabled */
474 if ((pci_probe
& PCI_PROBE_MMCONF
) == 0)
477 /* MMCONFIG already enabled */
478 if (!early
&& !(pci_probe
& PCI_PROBE_MASK
& ~PCI_PROBE_MMCONF
))
481 /* for late to exit */
486 if (pci_mmcfg_check_hostbridge())
491 acpi_table_parse(ACPI_SIG_MCFG
, acpi_parse_mcfg
);
492 pci_mmcfg_reject_broken(early
);
495 if ((pci_mmcfg_config_num
== 0) ||
496 (pci_mmcfg_config
== NULL
) ||
497 (pci_mmcfg_config
[0].address
== 0))
500 if (pci_mmcfg_arch_init())
501 pci_probe
= (pci_probe
& ~PCI_PROBE_MASK
) | PCI_PROBE_MMCONF
;
504 * Signal not to attempt to insert mmcfg resources because
505 * the architecture mmcfg setup could not initialize.
507 pci_mmcfg_resources_inserted
= 1;
511 void __init
pci_mmcfg_early_init(void)
516 void __init
pci_mmcfg_late_init(void)
521 static int __init
pci_mmcfg_late_insert_resources(void)
524 * If resources are already inserted or we are not using MMCONFIG,
525 * don't insert the resources.
527 if ((pci_mmcfg_resources_inserted
== 1) ||
528 (pci_probe
& PCI_PROBE_MMCONF
) == 0 ||
529 (pci_mmcfg_config_num
== 0) ||
530 (pci_mmcfg_config
== NULL
) ||
531 (pci_mmcfg_config
[0].address
== 0))
535 * Attempt to insert the mmcfg resources but not with the busy flag
536 * marked so it won't cause request errors when __request_region is
539 pci_mmcfg_insert_resources();
545 * Perform MMCONFIG resource insertion after PCI initialization to allow for
546 * misprogrammed MCFG tables that state larger sizes but actually conflict
547 * with other system resources.
549 late_initcall(pci_mmcfg_late_insert_resources
);