2008-02-26 11:04:17 -08:00
/*
* AMD Family 10 h mmconfig enablement
*/
# include <linux/types.h>
# include <linux/mm.h>
# include <linux/string.h>
# include <linux/pci.h>
2008-04-14 16:08:25 -07:00
# include <linux/dmi.h>
2010-02-10 01:20:07 -08:00
# include <linux/range.h>
2008-02-26 11:04:17 -08:00
# include <asm/pci-direct.h>
# include <linux/sort.h>
# include <asm/io.h>
# include <asm/msr.h>
# include <asm/acpi.h>
2008-05-02 23:42:01 +02:00
# include <asm/mmconfig.h>
2008-12-27 18:32:28 +05:30
# include <asm/pci_x86.h>
2008-04-14 16:08:25 -07:00
2008-02-26 11:04:17 -08:00
struct pci_hostbridge_probe {
u32 bus ;
u32 slot ;
u32 vendor ;
u32 device ;
} ;
static u64 __cpuinitdata fam10h_pci_mmconf_base ;
static struct pci_hostbridge_probe pci_probes [ ] __cpuinitdata = {
{ 0 , 0x18 , PCI_VENDOR_ID_AMD , 0x1200 } ,
{ 0xff , 0 , PCI_VENDOR_ID_AMD , 0x1200 } ,
} ;
static int __cpuinit cmp_range ( const void * x1 , const void * x2 )
{
const struct range * r1 = x1 ;
const struct range * r2 = x2 ;
int start1 , start2 ;
start1 = r1 - > start > > 32 ;
start2 = r2 - > start > > 32 ;
return start1 - start2 ;
}
2010-11-16 08:25:08 +00:00
# define MMCONF_UNIT (1ULL << FAM10H_MMIO_CONF_BASE_SHIFT)
# define MMCONF_MASK (~(MMCONF_UNIT - 1))
# define MMCONF_SIZE (MMCONF_UNIT << 8)
/* need to avoid (0xfd<<32), (0xfe<<32), and (0xff<<32), ht used space */
2008-02-26 11:04:17 -08:00
# define FAM10H_PCI_MMCONF_BASE (0xfcULL<<32)
2010-11-16 08:25:08 +00:00
# define BASE_VALID(b) ((b) + MMCONF_SIZE <= (0xfdULL<<32) || (b) >= (1ULL<<40))
2008-02-26 11:04:17 -08:00
static void __cpuinit get_fam10h_pci_mmconf_base ( void )
{
int i ;
unsigned bus ;
unsigned slot ;
int found ;
u64 val ;
u32 address ;
u64 tom2 ;
u64 base = FAM10H_PCI_MMCONF_BASE ;
int hi_mmio_num ;
struct range range [ 8 ] ;
/* only try to get setting from BSP */
2010-11-16 08:25:08 +00:00
if ( fam10h_pci_mmconf_base )
2008-02-26 11:04:17 -08:00
return ;
if ( ! early_pci_allowed ( ) )
2010-11-16 08:25:08 +00:00
return ;
2008-02-26 11:04:17 -08:00
found = 0 ;
for ( i = 0 ; i < ARRAY_SIZE ( pci_probes ) ; i + + ) {
u32 id ;
u16 device ;
u16 vendor ;
bus = pci_probes [ i ] . bus ;
slot = pci_probes [ i ] . slot ;
id = read_pci_config ( bus , slot , 0 , PCI_VENDOR_ID ) ;
vendor = id & 0xffff ;
device = ( id > > 16 ) & 0xffff ;
if ( pci_probes [ i ] . vendor = = vendor & &
pci_probes [ i ] . device = = device ) {
found = 1 ;
break ;
}
}
if ( ! found )
2010-11-16 08:25:08 +00:00
return ;
2008-02-26 11:04:17 -08:00
/* SYS_CFG */
address = MSR_K8_SYSCFG ;
rdmsrl ( address , val ) ;
/* TOP_MEM2 is not enabled? */
if ( ! ( val & ( 1 < < 21 ) ) ) {
2010-11-16 08:25:08 +00:00
tom2 = 1ULL < < 32 ;
2008-02-26 11:04:17 -08:00
} else {
/* TOP_MEM2 */
address = MSR_K8_TOP_MEM2 ;
rdmsrl ( address , val ) ;
2010-11-16 08:25:08 +00:00
tom2 = max ( val & 0xffffff800000ULL , 1ULL < < 32 ) ;
2008-02-26 11:04:17 -08:00
}
if ( base < = tom2 )
2010-11-16 08:25:08 +00:00
base = ( tom2 + 2 * MMCONF_UNIT - 1 ) & MMCONF_MASK ;
2008-02-26 11:04:17 -08:00
/*
* need to check if the range is in the high mmio range that is
* above 4 G
*/
hi_mmio_num = 0 ;
for ( i = 0 ; i < 8 ; i + + ) {
u32 reg ;
u64 start ;
u64 end ;
reg = read_pci_config ( bus , slot , 1 , 0x80 + ( i < < 3 ) ) ;
if ( ! ( reg & 3 ) )
continue ;
2010-11-16 08:25:08 +00:00
start = ( u64 ) ( reg & 0xffffff00 ) < < 8 ; /* 39:16 on 31:8*/
2008-02-26 11:04:17 -08:00
reg = read_pci_config ( bus , slot , 1 , 0x84 + ( i < < 3 ) ) ;
2010-11-16 08:25:08 +00:00
end = ( ( u64 ) ( reg & 0xffffff00 ) < < 8 ) | 0xffff ; /* 39:16 on 31:8*/
2008-02-26 11:04:17 -08:00
2010-11-16 08:25:08 +00:00
if ( end < tom2 )
2008-02-26 11:04:17 -08:00
continue ;
range [ hi_mmio_num ] . start = start ;
range [ hi_mmio_num ] . end = end ;
hi_mmio_num + + ;
}
if ( ! hi_mmio_num )
goto out ;
/* sort the range */
sort ( range , hi_mmio_num , sizeof ( struct range ) , cmp_range , NULL ) ;
if ( range [ hi_mmio_num - 1 ] . end < base )
goto out ;
2010-11-16 08:25:08 +00:00
if ( range [ 0 ] . start > base + MMCONF_SIZE )
2008-02-26 11:04:17 -08:00
goto out ;
/* need to find one window */
2010-11-16 08:25:08 +00:00
base = ( range [ 0 ] . start & MMCONF_MASK ) - MMCONF_UNIT ;
2008-02-26 11:04:17 -08:00
if ( ( base > tom2 ) & & BASE_VALID ( base ) )
goto out ;
2010-11-16 08:25:08 +00:00
base = ( range [ hi_mmio_num - 1 ] . end + MMCONF_UNIT ) & MMCONF_MASK ;
if ( BASE_VALID ( base ) )
2008-02-26 11:04:17 -08:00
goto out ;
/* need to find window between ranges */
2010-11-16 08:25:08 +00:00
for ( i = 1 ; i < hi_mmio_num ; i + + ) {
base = ( range [ i - 1 ] . end + MMCONF_UNIT ) & MMCONF_MASK ;
val = range [ i ] . start & MMCONF_MASK ;
if ( val > = base + MMCONF_SIZE & & BASE_VALID ( base ) )
goto out ;
2008-02-26 11:04:17 -08:00
}
return ;
2010-11-16 08:25:08 +00:00
2008-02-26 11:04:17 -08:00
out :
fam10h_pci_mmconf_base = base ;
}
void __cpuinit fam10h_check_enable_mmcfg ( void )
{
u64 val ;
u32 address ;
2008-04-14 16:08:25 -07:00
if ( ! ( pci_probe & PCI_CHECK_ENABLE_AMD_MMCONF ) )
return ;
2008-02-26 11:04:17 -08:00
address = MSR_FAM10H_MMIO_CONF_BASE ;
rdmsrl ( address , val ) ;
/* try to make sure that AP's setting is identical to BSP setting */
if ( val & FAM10H_MMIO_CONF_ENABLE ) {
unsigned busnbits ;
busnbits = ( val > > FAM10H_MMIO_CONF_BUSRANGE_SHIFT ) &
FAM10H_MMIO_CONF_BUSRANGE_MASK ;
/* only trust the one handle 256 buses, if acpi=off */
if ( ! acpi_pci_disabled | | busnbits > = 8 ) {
2010-11-16 08:25:08 +00:00
u64 base = val & MMCONF_MASK ;
if ( ! fam10h_pci_mmconf_base ) {
2008-02-26 11:04:17 -08:00
fam10h_pci_mmconf_base = base ;
return ;
} else if ( fam10h_pci_mmconf_base = = base )
return ;
}
}
/*
* if it is not enabled , try to enable it and assume only one segment
* with 256 buses
*/
get_fam10h_pci_mmconf_base ( ) ;
2010-11-16 08:25:08 +00:00
if ( ! fam10h_pci_mmconf_base ) {
pci_probe & = ~ PCI_CHECK_ENABLE_AMD_MMCONF ;
2008-02-26 11:04:17 -08:00
return ;
2010-11-16 08:25:08 +00:00
}
2008-02-26 11:04:17 -08:00
printk ( KERN_INFO " Enable MMCONFIG on AMD Family 10h \n " ) ;
val & = ~ ( ( FAM10H_MMIO_CONF_BASE_MASK < < FAM10H_MMIO_CONF_BASE_SHIFT ) |
( FAM10H_MMIO_CONF_BUSRANGE_MASK < < FAM10H_MMIO_CONF_BUSRANGE_SHIFT ) ) ;
val | = fam10h_pci_mmconf_base | ( 8 < < FAM10H_MMIO_CONF_BUSRANGE_SHIFT ) |
FAM10H_MMIO_CONF_ENABLE ;
wrmsrl ( address , val ) ;
}
2008-04-14 16:08:25 -07:00
2010-11-04 15:23:58 +00:00
static int __init set_check_enable_amd_mmconf ( const struct dmi_system_id * d )
2008-04-14 16:08:25 -07:00
{
pci_probe | = PCI_CHECK_ENABLE_AMD_MMCONF ;
return 0 ;
}
2010-11-04 15:23:58 +00:00
static const struct dmi_system_id __initconst mmconf_dmi_table [ ] = {
2008-04-14 16:08:25 -07:00
{
. callback = set_check_enable_amd_mmconf ,
. ident = " Sun Microsystems Machine " ,
. matches = {
DMI_MATCH ( DMI_SYS_VENDOR , " Sun Microsystems " ) ,
} ,
} ,
{ }
} ;
2010-11-04 15:23:58 +00:00
/* Called from a __cpuinit function, but only on the BSP. */
void __ref check_enable_amd_mmconf_dmi ( void )
2008-04-14 16:08:25 -07:00
{
dmi_check_system ( mmconf_dmi_table ) ;
}