2007-02-13 15:26:20 +03:00
/*
* mmconfig - shared . c - Low - level direct PCI config space access via
* MMCONFIG - common code between i386 and x86 - 64.
*
* This code does :
2007-02-13 15:26:20 +03:00
* - known chipset handling
2007-02-13 15:26:20 +03:00
* - ACPI decoding and validation
*
* Per - architecture code takes care of the mappings and accesses
* themselves .
*/
# include <linux/pci.h>
# include <linux/init.h>
# include <linux/acpi.h>
2009-08-14 23:37:50 +04:00
# include <linux/sfi_acpi.h>
2007-02-13 15:26:20 +03:00
# include <linux/bitmap.h>
2009-10-24 01:20:33 +04:00
# include <linux/dmi.h>
2007-02-13 15:26:20 +03:00
# include <asm/e820.h>
2008-12-27 16:02:28 +03:00
# include <asm/pci_x86.h>
2009-08-14 23:37:50 +04:00
# include <asm/acpi.h>
2007-02-13 15:26:20 +03:00
2009-07-29 00:48:02 +04:00
# define PREFIX "PCI: "
2009-07-29 00:45:54 +04:00
2007-07-21 19:10:34 +04:00
/* Indicate if the mmcfg resources have been placed into the resource table. */
static int __initdata pci_mmcfg_resources_inserted ;
2009-11-14 03:34:49 +03:00
LIST_HEAD ( pci_mmcfg_list ) ;
2009-11-14 03:34:54 +03:00
static __init void pci_mmconfig_remove ( struct pci_mmcfg_region * cfg )
{
if ( cfg - > res . parent )
release_resource ( & cfg - > res ) ;
list_del ( & cfg - > list ) ;
kfree ( cfg ) ;
}
2009-11-14 03:33:53 +03:00
static __init void free_all_mmcfg ( void )
{
2009-11-14 03:34:49 +03:00
struct pci_mmcfg_region * cfg , * tmp ;
2009-11-14 03:34:29 +03:00
2009-11-14 03:33:53 +03:00
pci_mmcfg_arch_free ( ) ;
2009-11-14 03:34:54 +03:00
list_for_each_entry_safe ( cfg , tmp , & pci_mmcfg_list , list )
pci_mmconfig_remove ( cfg ) ;
2009-11-14 03:34:49 +03:00
}
static __init void list_add_sorted ( struct pci_mmcfg_region * new )
{
struct pci_mmcfg_region * cfg ;
/* keep list sorted by segment and starting bus number */
list_for_each_entry ( cfg , & pci_mmcfg_list , list ) {
if ( cfg - > segment > new - > segment | |
( cfg - > segment = = new - > segment & &
cfg - > start_bus > = new - > start_bus ) ) {
list_add_tail ( & new - > list , & cfg - > list ) ;
return ;
}
}
list_add_tail ( & new - > list , & pci_mmcfg_list ) ;
2009-11-14 03:33:53 +03:00
}
2009-11-14 03:34:13 +03:00
static __init struct pci_mmcfg_region * pci_mmconfig_add ( int segment , int start ,
int end , u64 addr )
2009-03-20 06:55:35 +03:00
{
2009-11-14 03:34:13 +03:00
struct pci_mmcfg_region * new ;
2009-11-14 03:34:29 +03:00
int num_buses ;
struct resource * res ;
2009-03-20 06:55:35 +03:00
2009-11-14 03:34:03 +03:00
if ( addr = = 0 )
return NULL ;
2009-11-14 03:34:49 +03:00
new = kzalloc ( sizeof ( * new ) , GFP_KERNEL ) ;
2009-03-20 06:55:35 +03:00
if ( ! new )
2009-11-14 03:33:53 +03:00
return NULL ;
2009-03-20 06:55:35 +03:00
2009-11-14 03:34:24 +03:00
new - > address = addr ;
new - > segment = segment ;
new - > start_bus = start ;
new - > end_bus = end ;
2009-11-14 03:33:53 +03:00
2009-11-14 03:34:49 +03:00
list_add_sorted ( new ) ;
2009-11-14 03:34:29 +03:00
num_buses = end - start + 1 ;
res = & new - > res ;
res - > start = addr + PCI_MMCFG_BUS_OFFSET ( start ) ;
res - > end = addr + PCI_MMCFG_BUS_OFFSET ( num_buses ) - 1 ;
res - > flags = IORESOURCE_MEM | IORESOURCE_BUSY ;
snprintf ( new - > name , PCI_MMCFG_RESOURCE_NAME_LEN ,
" PCI MMCONFIG %04x [bus %02x-%02x] " , segment , start , end ) ;
res - > name = new - > name ;
2009-11-14 03:34:59 +03:00
printk ( KERN_INFO PREFIX " MMCONFIG for domain %04x [bus %02x-%02x] at "
" %pR (base %#lx) \n " , segment , start , end , & new - > res ,
( unsigned long ) addr ) ;
2009-11-14 03:34:49 +03:00
return new ;
2009-03-20 06:55:35 +03:00
}
2009-11-14 03:35:04 +03:00
struct pci_mmcfg_region * pci_mmconfig_lookup ( int segment , int bus )
{
struct pci_mmcfg_region * cfg ;
list_for_each_entry ( cfg , & pci_mmcfg_list , list )
if ( cfg - > segment = = segment & &
cfg - > start_bus < = bus & & bus < = cfg - > end_bus )
return cfg ;
return NULL ;
}
2007-02-13 15:26:20 +03:00
static const char __init * pci_mmcfg_e7520 ( void )
2007-02-13 15:26:20 +03:00
{
u32 win ;
2008-02-29 10:56:50 +03:00
raw_pci_ops - > read ( 0 , 0 , PCI_DEVFN ( 0 , 0 ) , 0xce , 2 , & win ) ;
2007-02-13 15:26:20 +03:00
2007-05-02 21:27:22 +04:00
win = win & 0xf000 ;
2009-03-20 06:55:35 +03:00
if ( win = = 0x0000 | | win = = 0xf000 )
return NULL ;
2009-11-14 03:33:53 +03:00
if ( pci_mmconfig_add ( 0 , 0 , 255 , win < < 16 ) = = NULL )
2009-03-20 06:55:35 +03:00
return NULL ;
2007-02-13 15:26:20 +03:00
return " Intel Corporation E7520 Memory Controller Hub " ;
}
2007-02-13 15:26:20 +03:00
static const char __init * pci_mmcfg_intel_945 ( void )
2007-02-13 15:26:20 +03:00
{
u32 pciexbar , mask = 0 , len = 0 ;
2008-02-29 10:56:50 +03:00
raw_pci_ops - > read ( 0 , 0 , PCI_DEVFN ( 0 , 0 ) , 0x48 , 4 , & pciexbar ) ;
2007-02-13 15:26:20 +03:00
/* Enable bit */
if ( ! ( pciexbar & 1 ) )
2009-03-20 06:55:35 +03:00
return NULL ;
2007-02-13 15:26:20 +03:00
/* Size bits */
switch ( ( pciexbar > > 1 ) & 3 ) {
case 0 :
mask = 0xf0000000U ;
len = 0x10000000U ;
break ;
case 1 :
mask = 0xf8000000U ;
len = 0x08000000U ;
break ;
case 2 :
mask = 0xfc000000U ;
len = 0x04000000U ;
break ;
default :
2009-03-20 06:55:35 +03:00
return NULL ;
2007-02-13 15:26:20 +03:00
}
/* Errata #2, things break when not aligned on a 256Mb boundary */
/* Can only happen in 64M/128M mode */
if ( ( pciexbar & mask ) & 0x0fffffffU )
2009-03-20 06:55:35 +03:00
return NULL ;
2007-02-13 15:26:20 +03:00
2007-05-02 21:27:22 +04:00
/* Don't hit the APIC registers and their friends */
if ( ( pciexbar & mask ) > = 0xf0000000U )
2009-03-20 06:55:35 +03:00
return NULL ;
2009-11-14 03:33:53 +03:00
if ( pci_mmconfig_add ( 0 , 0 , ( len > > 20 ) - 1 , pciexbar & mask ) = = NULL )
2009-03-20 06:55:35 +03:00
return NULL ;
2007-02-13 15:26:20 +03:00
return " Intel Corporation 945G/GZ/P/PL Express Memory Controller Hub " ;
}
2008-02-19 14:13:02 +03:00
static const char __init * pci_mmcfg_amd_fam10h ( void )
{
u32 low , high , address ;
u64 base , msr ;
int i ;
2009-11-14 03:33:53 +03:00
unsigned segnbits = 0 , busnbits , end_bus ;
2008-02-19 14:13:02 +03:00
2008-04-15 03:08:25 +04:00
if ( ! ( pci_probe & PCI_CHECK_ENABLE_AMD_MMCONF ) )
return NULL ;
2008-02-19 14:13:02 +03:00
address = MSR_FAM10H_MMIO_CONF_BASE ;
if ( rdmsr_safe ( address , & low , & high ) )
return NULL ;
msr = high ;
msr < < = 32 ;
msr | = low ;
/* mmconfig is not enable */
if ( ! ( msr & FAM10H_MMIO_CONF_ENABLE ) )
return NULL ;
base = msr & ( FAM10H_MMIO_CONF_BASE_MASK < < FAM10H_MMIO_CONF_BASE_SHIFT ) ;
busnbits = ( msr > > FAM10H_MMIO_CONF_BUSRANGE_SHIFT ) &
FAM10H_MMIO_CONF_BUSRANGE_MASK ;
/*
* only handle bus 0 ?
* need to skip it
*/
if ( ! busnbits )
return NULL ;
if ( busnbits > 8 ) {
segnbits = busnbits - 8 ;
busnbits = 8 ;
}
2009-11-14 03:33:53 +03:00
end_bus = ( 1 < < busnbits ) - 1 ;
2009-03-20 06:55:35 +03:00
for ( i = 0 ; i < ( 1 < < segnbits ) ; i + + )
2009-11-14 03:33:53 +03:00
if ( pci_mmconfig_add ( i , 0 , end_bus ,
base + ( 1 < < 28 ) * i ) = = NULL ) {
free_all_mmcfg ( ) ;
return NULL ;
}
2008-02-19 14:13:02 +03:00
return " AMD Family 10h NB " ;
}
2009-03-20 06:57:56 +03:00
static bool __initdata mcp55_checked ;
static const char __init * pci_mmcfg_nvidia_mcp55 ( void )
{
int bus ;
int mcp55_mmconf_found = 0 ;
static const u32 extcfg_regnum = 0x90 ;
static const u32 extcfg_regsize = 4 ;
static const u32 extcfg_enable_mask = 1 < < 31 ;
static const u32 extcfg_start_mask = 0xff < < 16 ;
static const int extcfg_start_shift = 16 ;
static const u32 extcfg_size_mask = 0x3 < < 28 ;
static const int extcfg_size_shift = 28 ;
static const int extcfg_sizebus [ ] = { 0x100 , 0x80 , 0x40 , 0x20 } ;
static const u32 extcfg_base_mask [ ] = { 0x7ff8 , 0x7ffc , 0x7ffe , 0x7fff } ;
static const int extcfg_base_lshift = 25 ;
/*
* do check if amd fam10h already took over
*/
2009-11-14 03:34:49 +03:00
if ( ! acpi_disabled | | ! list_empty ( & pci_mmcfg_list ) | | mcp55_checked )
2009-03-20 06:57:56 +03:00
return NULL ;
mcp55_checked = true ;
for ( bus = 0 ; bus < 256 ; bus + + ) {
u64 base ;
u32 l , extcfg ;
u16 vendor , device ;
int start , size_index , end ;
raw_pci_ops - > read ( 0 , bus , PCI_DEVFN ( 0 , 0 ) , 0 , 4 , & l ) ;
vendor = l & 0xffff ;
device = ( l > > 16 ) & 0xffff ;
if ( PCI_VENDOR_ID_NVIDIA ! = vendor | | 0x0369 ! = device )
continue ;
raw_pci_ops - > read ( 0 , bus , PCI_DEVFN ( 0 , 0 ) , extcfg_regnum ,
extcfg_regsize , & extcfg ) ;
if ( ! ( extcfg & extcfg_enable_mask ) )
continue ;
size_index = ( extcfg & extcfg_size_mask ) > > extcfg_size_shift ;
base = extcfg & extcfg_base_mask [ size_index ] ;
/* base could > 4G */
base < < = extcfg_base_lshift ;
start = ( extcfg & extcfg_start_mask ) > > extcfg_start_shift ;
end = start + extcfg_sizebus [ size_index ] - 1 ;
2009-11-14 03:33:53 +03:00
if ( pci_mmconfig_add ( 0 , start , end , base ) = = NULL )
continue ;
2009-03-20 06:57:56 +03:00
mcp55_mmconf_found + + ;
}
if ( ! mcp55_mmconf_found )
return NULL ;
return " nVidia MCP55 " ;
}
2007-02-13 15:26:20 +03:00
struct pci_mmcfg_hostbridge_probe {
2008-02-19 14:13:02 +03:00
u32 bus ;
u32 devfn ;
2007-02-13 15:26:20 +03:00
u32 vendor ;
u32 device ;
const char * ( * probe ) ( void ) ;
} ;
2007-02-13 15:26:20 +03:00
static struct pci_mmcfg_hostbridge_probe pci_mmcfg_probes [ ] __initdata = {
2008-02-19 14:13:02 +03:00
{ 0 , PCI_DEVFN ( 0 , 0 ) , PCI_VENDOR_ID_INTEL ,
PCI_DEVICE_ID_INTEL_E7520_MCH , pci_mmcfg_e7520 } ,
{ 0 , PCI_DEVFN ( 0 , 0 ) , PCI_VENDOR_ID_INTEL ,
PCI_DEVICE_ID_INTEL_82945G_HB , pci_mmcfg_intel_945 } ,
{ 0 , PCI_DEVFN ( 0x18 , 0 ) , PCI_VENDOR_ID_AMD ,
0x1200 , pci_mmcfg_amd_fam10h } ,
{ 0xff , PCI_DEVFN ( 0 , 0 ) , PCI_VENDOR_ID_AMD ,
0x1200 , pci_mmcfg_amd_fam10h } ,
2009-03-20 06:57:56 +03:00
{ 0 , PCI_DEVFN ( 0 , 0 ) , PCI_VENDOR_ID_NVIDIA ,
0x0369 , pci_mmcfg_nvidia_mcp55 } ,
2007-02-13 15:26:20 +03:00
} ;
2009-03-20 06:55:35 +03:00
static void __init pci_mmcfg_check_end_bus_number ( void )
{
2009-11-14 03:34:44 +03:00
struct pci_mmcfg_region * cfg , * cfgx ;
2009-03-20 06:55:35 +03:00
/* last one*/
2009-11-14 03:34:49 +03:00
cfg = list_entry ( pci_mmcfg_list . prev , typeof ( * cfg ) , list ) ;
if ( cfg )
2009-11-14 03:34:18 +03:00
if ( cfg - > end_bus < cfg - > start_bus )
cfg - > end_bus = 255 ;
2009-03-20 06:55:35 +03:00
2009-11-14 03:34:49 +03:00
if ( list_is_singular ( & pci_mmcfg_list ) )
return ;
2009-03-20 06:55:35 +03:00
2009-11-14 03:34:49 +03:00
/* don't overlap please */
list_for_each_entry ( cfg , & pci_mmcfg_list , list ) {
2009-11-14 03:34:18 +03:00
if ( cfg - > end_bus < cfg - > start_bus )
cfg - > end_bus = 255 ;
2009-03-20 06:55:35 +03:00
2009-11-14 03:34:49 +03:00
cfgx = list_entry ( cfg - > list . next , typeof ( * cfg ) , list ) ;
if ( cfg ! = cfgx & & cfg - > end_bus > = cfgx - > start_bus )
2009-11-14 03:34:18 +03:00
cfg - > end_bus = cfgx - > start_bus - 1 ;
2009-03-20 06:55:35 +03:00
}
}
2007-02-13 15:26:20 +03:00
static int __init pci_mmcfg_check_hostbridge ( void )
{
u32 l ;
2008-02-19 14:13:02 +03:00
u32 bus , devfn ;
2007-02-13 15:26:20 +03:00
u16 vendor , device ;
int i ;
const char * name ;
2008-02-29 10:56:50 +03:00
if ( ! raw_pci_ops )
return 0 ;
2009-11-14 03:33:53 +03:00
free_all_mmcfg ( ) ;
2007-02-13 15:26:20 +03:00
2009-03-20 06:55:35 +03:00
for ( i = 0 ; i < ARRAY_SIZE ( pci_mmcfg_probes ) ; i + + ) {
2008-02-19 14:13:02 +03:00
bus = pci_mmcfg_probes [ i ] . bus ;
devfn = pci_mmcfg_probes [ i ] . devfn ;
2008-02-29 10:56:50 +03:00
raw_pci_ops - > read ( 0 , bus , devfn , 0 , 4 , & l ) ;
2008-02-19 14:13:02 +03:00
vendor = l & 0xffff ;
device = ( l > > 16 ) & 0xffff ;
2009-03-20 06:55:35 +03:00
name = NULL ;
2007-02-13 15:26:20 +03:00
if ( pci_mmcfg_probes [ i ] . vendor = = vendor & &
pci_mmcfg_probes [ i ] . device = = device )
2007-02-13 15:26:20 +03:00
name = pci_mmcfg_probes [ i ] . probe ( ) ;
2009-03-20 06:55:35 +03:00
if ( name )
2009-11-14 03:34:59 +03:00
printk ( KERN_INFO PREFIX " %s with MMCONFIG support \n " ,
2009-03-20 06:55:35 +03:00
name ) ;
2007-02-13 15:26:20 +03:00
}
2009-03-20 06:55:35 +03:00
/* some end_bus_number is crazy, fix it */
pci_mmcfg_check_end_bus_number ( ) ;
2009-11-14 03:34:49 +03:00
return ! list_empty ( & pci_mmcfg_list ) ;
2007-02-13 15:26:20 +03:00
}
2008-09-04 23:04:32 +04:00
static void __init pci_mmcfg_insert_resources ( void )
2007-02-13 15:26:20 +03:00
{
2009-11-14 03:34:29 +03:00
struct pci_mmcfg_region * cfg ;
2007-02-13 15:26:20 +03:00
2009-11-14 03:34:49 +03:00
list_for_each_entry ( cfg , & pci_mmcfg_list , list )
2009-11-14 03:34:29 +03:00
insert_resource ( & iomem_resource , & cfg - > res ) ;
2007-07-21 19:10:34 +04:00
/* Mark that the resources have been inserted. */
pci_mmcfg_resources_inserted = 1 ;
2007-02-13 15:26:20 +03:00
}
2008-02-15 12:27:20 +03:00
static acpi_status __init check_mcfg_resource ( struct acpi_resource * res ,
void * data )
{
struct resource * mcfg_res = data ;
struct acpi_resource_address64 address ;
acpi_status status ;
if ( res - > type = = ACPI_RESOURCE_TYPE_FIXED_MEMORY32 ) {
struct acpi_resource_fixed_memory32 * fixmem32 =
& res - > data . fixed_memory32 ;
if ( ! fixmem32 )
return AE_OK ;
if ( ( mcfg_res - > start > = fixmem32 - > address ) & &
2009-06-03 11:13:13 +04:00
( mcfg_res - > end < ( fixmem32 - > address +
2008-02-15 12:27:20 +03:00
fixmem32 - > address_length ) ) ) {
mcfg_res - > flags = 1 ;
return AE_CTRL_TERMINATE ;
}
}
if ( ( res - > type ! = ACPI_RESOURCE_TYPE_ADDRESS32 ) & &
( res - > type ! = ACPI_RESOURCE_TYPE_ADDRESS64 ) )
return AE_OK ;
status = acpi_resource_to_address64 ( res , & address ) ;
if ( ACPI_FAILURE ( status ) | |
( address . address_length < = 0 ) | |
( address . resource_type ! = ACPI_MEMORY_RANGE ) )
return AE_OK ;
if ( ( mcfg_res - > start > = address . minimum ) & &
2009-06-03 11:13:13 +04:00
( mcfg_res - > end < ( address . minimum + address . address_length ) ) ) {
2008-02-15 12:27:20 +03:00
mcfg_res - > flags = 1 ;
return AE_CTRL_TERMINATE ;
}
return AE_OK ;
}
static acpi_status __init find_mboard_resource ( acpi_handle handle , u32 lvl ,
void * context , void * * rv )
{
struct resource * mcfg_res = context ;
acpi_walk_resources ( handle , METHOD_NAME__CRS ,
check_mcfg_resource , context ) ;
if ( mcfg_res - > flags )
return AE_CTRL_TERMINATE ;
return AE_OK ;
}
2008-07-19 00:22:36 +04:00
static int __init is_acpi_reserved ( u64 start , u64 end , unsigned not_used )
2008-02-15 12:27:20 +03:00
{
struct resource mcfg_res ;
mcfg_res . start = start ;
2009-06-03 11:13:13 +04:00
mcfg_res . end = end - 1 ;
2008-02-15 12:27:20 +03:00
mcfg_res . flags = 0 ;
acpi_get_devices ( " PNP0C01 " , find_mboard_resource , & mcfg_res , NULL ) ;
if ( ! mcfg_res . flags )
acpi_get_devices ( " PNP0C02 " , find_mboard_resource , & mcfg_res ,
NULL ) ;
return mcfg_res . flags ;
}
2008-07-19 00:22:36 +04:00
typedef int ( * check_reserved_t ) ( u64 start , u64 end , unsigned type ) ;
static int __init is_mmconf_reserved ( check_reserved_t is_reserved ,
2009-11-14 03:34:59 +03:00
struct pci_mmcfg_region * cfg , int with_e820 )
2008-07-19 00:22:36 +04:00
{
2009-11-14 03:34:34 +03:00
u64 addr = cfg - > res . start ;
u64 size = resource_size ( & cfg - > res ) ;
2008-07-19 00:22:36 +04:00
u64 old_size = size ;
2009-11-14 03:34:29 +03:00
int valid = 0 , num_buses ;
2008-07-19 00:22:36 +04:00
2009-04-18 12:43:46 +04:00
while ( ! is_reserved ( addr , addr + size , E820_RESERVED ) ) {
2008-07-19 00:22:36 +04:00
size > > = 1 ;
if ( size < ( 16UL < < 20 ) )
break ;
}
if ( size > = ( 16UL < < 20 ) | | size = = old_size ) {
2009-11-14 03:34:59 +03:00
printk ( KERN_INFO PREFIX " MMCONFIG at %pR reserved in %s \n " ,
& cfg - > res ,
with_e820 ? " E820 " : " ACPI motherboard resources " ) ;
2008-07-19 00:22:36 +04:00
valid = 1 ;
if ( old_size ! = size ) {
2009-11-14 03:34:18 +03:00
/* update end_bus */
cfg - > end_bus = cfg - > start_bus + ( ( size > > 20 ) - 1 ) ;
2009-11-14 03:34:29 +03:00
num_buses = cfg - > end_bus - cfg - > start_bus + 1 ;
cfg - > res . end = cfg - > res . start +
PCI_MMCFG_BUS_OFFSET ( num_buses ) - 1 ;
snprintf ( cfg - > name , PCI_MMCFG_RESOURCE_NAME_LEN ,
" PCI MMCONFIG %04x [bus %02x-%02x] " ,
cfg - > segment , cfg - > start_bus , cfg - > end_bus ) ;
2009-11-14 03:34:59 +03:00
printk ( KERN_INFO PREFIX
" MMCONFIG for %04x [bus%02x-%02x] "
" at %pR (base %#lx) (size reduced!) \n " ,
cfg - > segment , cfg - > start_bus , cfg - > end_bus ,
& cfg - > res , ( unsigned long ) cfg - > address ) ;
2008-07-19 00:22:36 +04:00
}
}
return valid ;
}
2008-02-29 10:56:50 +03:00
static void __init pci_mmcfg_reject_broken ( int early )
2007-02-13 15:26:20 +03:00
{
2009-11-14 03:34:44 +03:00
struct pci_mmcfg_region * cfg ;
2007-02-13 15:26:20 +03:00
2009-11-14 03:34:49 +03:00
list_for_each_entry ( cfg , & pci_mmcfg_list , list ) {
2009-11-14 03:34:29 +03:00
int valid = 0 ;
2008-07-19 00:22:36 +04:00
2009-08-14 23:37:50 +04:00
if ( ! early & & ! acpi_disabled )
2009-11-14 03:34:59 +03:00
valid = is_mmconf_reserved ( is_acpi_reserved , cfg , 0 ) ;
2008-02-15 12:30:14 +03:00
if ( valid )
continue ;
if ( ! early )
2009-11-14 03:34:59 +03:00
printk ( KERN_ERR FW_BUG PREFIX
" MMCONFIG at %pR not reserved in "
" ACPI motherboard resources \n " , & cfg - > res ) ;
2008-07-19 00:22:36 +04:00
2008-02-15 12:30:14 +03:00
/* Don't try to do this check unless configuration
2008-02-29 10:56:50 +03:00
type 1 is available . how about type 2 ? */
2008-07-19 00:22:36 +04:00
if ( raw_pci_ops )
2009-11-14 03:34:59 +03:00
valid = is_mmconf_reserved ( e820_all_mapped , cfg , 1 ) ;
2008-02-15 12:30:14 +03:00
if ( ! valid )
goto reject ;
2007-02-13 15:26:20 +03:00
}
2008-02-15 12:27:20 +03:00
2007-02-13 15:26:20 +03:00
return ;
reject :
2009-11-14 03:34:59 +03:00
printk ( KERN_INFO PREFIX " not using MMCONFIG \n " ) ;
2009-11-14 03:33:53 +03:00
free_all_mmcfg ( ) ;
2007-02-13 15:26:20 +03:00
}
2008-02-15 12:30:14 +03:00
static int __initdata known_bridge ;
2008-02-15 12:27:20 +03:00
2009-10-24 01:20:33 +04:00
static int __init acpi_mcfg_check_entry ( struct acpi_table_mcfg * mcfg ,
struct acpi_mcfg_allocation * cfg )
2009-06-12 07:53:55 +04:00
{
2009-10-24 01:20:33 +04:00
int year ;
if ( cfg - > address < 0xFFFFFFFF )
return 0 ;
2009-06-12 07:53:55 +04:00
if ( ! strcmp ( mcfg - > header . oem_id , " SGI " ) )
2009-10-24 01:20:33 +04:00
return 0 ;
2009-06-12 07:53:55 +04:00
2009-10-24 01:20:33 +04:00
if ( mcfg - > header . revision > = 1 ) {
if ( dmi_get_date ( DMI_BIOS_DATE , & year , NULL , NULL ) & &
year > = 2010 )
return 0 ;
}
2009-11-14 03:34:59 +03:00
printk ( KERN_ERR PREFIX " MCFG region for %04x [bus %02x-%02x] at %#llx "
2009-10-24 01:20:33 +04:00
" is above 4GB, ignored \n " , cfg - > pci_segment ,
cfg - > start_bus_number , cfg - > end_bus_number , cfg - > address ) ;
return - EINVAL ;
2009-06-12 07:53:55 +04:00
}
static int __init pci_parse_mcfg ( struct acpi_table_header * header )
{
struct acpi_table_mcfg * mcfg ;
2009-11-14 03:33:47 +03:00
struct acpi_mcfg_allocation * cfg_table , * cfg ;
2009-06-12 07:53:55 +04:00
unsigned long i ;
2009-11-14 03:33:53 +03:00
int entries ;
2009-06-12 07:53:55 +04:00
if ( ! header )
return - EINVAL ;
mcfg = ( struct acpi_table_mcfg * ) header ;
/* how many config structures do we have */
2009-11-14 03:33:53 +03:00
free_all_mmcfg ( ) ;
2009-11-14 03:33:42 +03:00
entries = 0 ;
2009-06-12 07:53:55 +04:00
i = header - > length - sizeof ( struct acpi_table_mcfg ) ;
while ( i > = sizeof ( struct acpi_mcfg_allocation ) ) {
2009-11-14 03:33:42 +03:00
entries + + ;
2009-06-12 07:53:55 +04:00
i - = sizeof ( struct acpi_mcfg_allocation ) ;
} ;
2009-11-14 03:33:42 +03:00
if ( entries = = 0 ) {
2009-06-12 07:53:55 +04:00
printk ( KERN_ERR PREFIX " MMCONFIG has no entries \n " ) ;
return - ENODEV ;
}
2009-11-14 03:33:47 +03:00
cfg_table = ( struct acpi_mcfg_allocation * ) & mcfg [ 1 ] ;
2009-11-14 03:33:42 +03:00
for ( i = 0 ; i < entries ; i + + ) {
2009-11-14 03:33:47 +03:00
cfg = & cfg_table [ i ] ;
if ( acpi_mcfg_check_entry ( mcfg , cfg ) ) {
2009-11-14 03:33:53 +03:00
free_all_mmcfg ( ) ;
2009-06-12 07:53:55 +04:00
return - ENODEV ;
}
2009-11-14 03:33:53 +03:00
if ( pci_mmconfig_add ( cfg - > pci_segment , cfg - > start_bus_number ,
cfg - > end_bus_number , cfg - > address ) = = NULL ) {
printk ( KERN_WARNING PREFIX
" no memory for MCFG entries \n " ) ;
free_all_mmcfg ( ) ;
return - ENOMEM ;
}
2009-06-12 07:53:55 +04:00
}
return 0 ;
}
2008-05-12 17:43:37 +04:00
static void __init __pci_mmcfg_init ( int early )
2007-02-13 15:26:20 +03:00
{
2008-02-15 12:27:20 +03:00
/* MMCONFIG disabled */
2007-02-13 15:26:20 +03:00
if ( ( pci_probe & PCI_PROBE_MMCONF ) = = 0 )
return ;
2008-02-15 12:27:20 +03:00
/* MMCONFIG already enabled */
2008-02-15 12:30:14 +03:00
if ( ! early & & ! ( pci_probe & PCI_PROBE_MASK & ~ PCI_PROBE_MMCONF ) )
2008-02-15 12:27:20 +03:00
return ;
2007-02-13 15:26:20 +03:00
2008-02-15 12:30:14 +03:00
/* for late to exit */
if ( known_bridge )
return ;
2008-02-15 12:27:20 +03:00
2008-02-29 10:56:50 +03:00
if ( early ) {
2008-02-15 12:30:14 +03:00
if ( pci_mmcfg_check_hostbridge ( ) )
known_bridge = 1 ;
}
2009-03-20 06:55:35 +03:00
if ( ! known_bridge )
2009-08-14 23:37:50 +04:00
acpi_sfi_table_parse ( ACPI_SIG_MCFG , pci_parse_mcfg ) ;
2009-03-20 06:55:35 +03:00
pci_mmcfg_reject_broken ( early ) ;
2007-02-13 15:26:20 +03:00
2009-11-14 03:34:49 +03:00
if ( list_empty ( & pci_mmcfg_list ) )
2007-02-13 15:26:20 +03:00
return ;
2008-09-04 23:04:32 +04:00
if ( pci_mmcfg_arch_init ( ) )
2007-02-13 15:26:20 +03:00
pci_probe = ( pci_probe & ~ PCI_PROBE_MASK ) | PCI_PROBE_MMCONF ;
2008-09-04 23:04:32 +04:00
else {
2007-07-21 19:10:34 +04:00
/*
* Signal not to attempt to insert mmcfg resources because
* the architecture mmcfg setup could not initialize .
*/
pci_mmcfg_resources_inserted = 1 ;
2007-02-13 15:26:20 +03:00
}
}
2007-07-21 19:10:34 +04:00
2008-02-29 10:56:50 +03:00
void __init pci_mmcfg_early_init ( void )
2008-02-15 12:30:14 +03:00
{
2008-02-29 10:56:50 +03:00
__pci_mmcfg_init ( 1 ) ;
2008-02-15 12:30:14 +03:00
}
void __init pci_mmcfg_late_init ( void )
{
2008-02-29 10:56:50 +03:00
__pci_mmcfg_init ( 0 ) ;
2008-02-15 12:30:14 +03:00
}
2007-07-21 19:10:34 +04:00
static int __init pci_mmcfg_late_insert_resources ( void )
{
/*
* If resources are already inserted or we are not using MMCONFIG ,
* don ' t insert the resources .
*/
if ( ( pci_mmcfg_resources_inserted = = 1 ) | |
( pci_probe & PCI_PROBE_MMCONF ) = = 0 | |
2009-11-14 03:34:49 +03:00
list_empty ( & pci_mmcfg_list ) )
2007-07-21 19:10:34 +04:00
return 1 ;
/*
* Attempt to insert the mmcfg resources but not with the busy flag
* marked so it won ' t cause request errors when __request_region is
* called .
*/
2008-09-04 23:04:32 +04:00
pci_mmcfg_insert_resources ( ) ;
2007-07-21 19:10:34 +04:00
return 0 ;
}
/*
* Perform MMCONFIG resource insertion after PCI initialization to allow for
* misprogrammed MCFG tables that state larger sizes but actually conflict
* with other system resources .
*/
late_initcall ( pci_mmcfg_late_insert_resources ) ;