2008-01-30 15:32:36 +03:00
/*
2010-10-29 19:14:30 +04:00
* AMD NUMA support .
2005-04-17 02:20:36 +04:00
* Discover the memory map and associated nodes .
2008-01-30 15:32:36 +03:00
*
2010-10-29 19:14:30 +04:00
* This version reads it directly from the AMD northbridge .
2008-01-30 15:32:36 +03:00
*
2005-04-17 02:20:36 +04:00
* Copyright 2002 , 2003 Andi Kleen , SuSE Labs .
*/
# include <linux/kernel.h>
# include <linux/init.h>
# include <linux/string.h>
# include <linux/module.h>
# include <linux/nodemask.h>
2010-08-26 00:39:17 +04:00
# include <linux/memblock.h>
2011-05-02 19:24:48 +04:00
# include <linux/bootmem.h>
2010-08-26 00:39:17 +04:00
2005-04-17 02:20:36 +04:00
# include <asm/io.h>
# include <linux/pci_ids.h>
2008-02-19 14:21:06 +03:00
# include <linux/acpi.h>
2005-04-17 02:20:36 +04:00
# include <asm/types.h>
# include <asm/mmzone.h>
# include <asm/proto.h>
# include <asm/e820.h>
# include <asm/pci-direct.h>
# include <asm/numa.h>
2008-02-19 14:21:06 +03:00
# include <asm/mpspec.h>
# include <asm/apic.h>
2010-09-17 20:03:43 +04:00
# include <asm/amd_nb.h>
2005-04-17 02:20:36 +04:00
2010-12-23 04:23:51 +03:00
static unsigned char __initdata nodeids [ 8 ] ;
2009-09-26 02:20:00 +04:00
2005-04-17 02:20:36 +04:00
static __init int find_northbridge ( void )
{
2008-01-30 15:32:36 +03:00
int num ;
2005-04-17 02:20:36 +04:00
2008-01-30 15:32:36 +03:00
for ( num = 0 ; num < 32 ; num + + ) {
2005-04-17 02:20:36 +04:00
u32 header ;
2008-01-30 15:32:36 +03:00
header = read_pci_config ( 0 , num , 0 , 0x00 ) ;
2008-01-30 15:34:12 +03:00
if ( header ! = ( PCI_VENDOR_ID_AMD | ( 0x1100 < < 16 ) ) & &
header ! = ( PCI_VENDOR_ID_AMD | ( 0x1200 < < 16 ) ) & &
header ! = ( PCI_VENDOR_ID_AMD | ( 0x1300 < < 16 ) ) )
2008-01-30 15:32:36 +03:00
continue ;
2005-04-17 02:20:36 +04:00
2008-01-30 15:32:36 +03:00
header = read_pci_config ( 0 , num , 1 , 0x00 ) ;
2008-01-30 15:34:12 +03:00
if ( header ! = ( PCI_VENDOR_ID_AMD | ( 0x1101 < < 16 ) ) & &
header ! = ( PCI_VENDOR_ID_AMD | ( 0x1201 < < 16 ) ) & &
header ! = ( PCI_VENDOR_ID_AMD | ( 0x1301 < < 16 ) ) )
2008-01-30 15:32:36 +03:00
continue ;
return num ;
}
2005-04-17 02:20:36 +04:00
2011-02-16 14:13:06 +03:00
return - ENOENT ;
2005-04-17 02:20:36 +04:00
}
2008-02-19 14:21:06 +03:00
static __init void early_get_boot_cpu_id ( void )
{
/*
2010-07-21 21:03:58 +04:00
* need to get the APIC ID of the BSP so can use that to
2010-10-29 19:14:30 +04:00
* create apicid_to_node in amd_scan_nodes ( )
2008-02-19 14:21:06 +03:00
*/
2008-06-19 23:15:01 +04:00
# ifdef CONFIG_X86_MPPARSE
2008-02-19 14:21:06 +03:00
/*
* get boot - time SMP configuration :
*/
if ( smp_found_config )
early_get_smp_config ( ) ;
2008-06-19 23:15:01 +04:00
# endif
2009-09-26 02:20:00 +04:00
}
2011-02-16 14:13:06 +03:00
int __init amd_numa_init ( void )
2009-09-26 02:20:00 +04:00
{
2011-05-02 19:24:48 +04:00
u64 start = PFN_PHYS ( 0 ) ;
u64 end = PFN_PHYS ( max_pfn ) ;
2009-09-26 02:20:00 +04:00
unsigned numnodes ;
2011-05-02 19:24:48 +04:00
u64 prevbase ;
2011-02-16 14:13:07 +03:00
int i , j , nb ;
2008-05-12 17:43:35 +04:00
u32 nodeid , reg ;
2011-02-16 14:13:07 +03:00
unsigned int bits , cores , apicid_base ;
2005-04-17 02:20:36 +04:00
2006-09-26 12:52:41 +04:00
if ( ! early_pci_allowed ( ) )
2011-02-16 14:13:06 +03:00
return - EINVAL ;
2006-09-26 12:52:41 +04:00
2008-01-30 15:32:36 +03:00
nb = find_northbridge ( ) ;
if ( nb < 0 )
2005-04-17 02:20:36 +04:00
return nb ;
2009-09-26 02:19:47 +04:00
pr_info ( " Scanning NUMA topology in Northbridge %d \n " , nb ) ;
2005-04-17 02:20:36 +04:00
2008-01-30 15:32:36 +03:00
reg = read_pci_config ( 0 , nb , 0 , 0x60 ) ;
2005-04-17 02:20:36 +04:00
numnodes = ( ( reg > > 4 ) & 0xF ) + 1 ;
2007-05-02 21:27:21 +04:00
if ( numnodes < = 1 )
2011-02-16 14:13:06 +03:00
return - ENOENT ;
2005-04-17 02:20:36 +04:00
2009-09-26 02:20:00 +04:00
pr_info ( " Number of physical nodes %d \n " , numnodes ) ;
2005-04-17 02:20:36 +04:00
prevbase = 0 ;
2008-01-30 15:32:36 +03:00
for ( i = 0 ; i < 8 ; i + + ) {
2011-05-02 19:24:48 +04:00
u64 base , limit ;
2008-01-30 15:32:36 +03:00
2005-04-17 02:20:36 +04:00
base = read_pci_config ( 0 , nb , 1 , 0x40 + i * 8 ) ;
limit = read_pci_config ( 0 , nb , 1 , 0x44 + i * 8 ) ;
2010-12-23 04:23:51 +03:00
nodeids [ i ] = nodeid = limit & 7 ;
2008-01-30 15:32:36 +03:00
if ( ( base & 3 ) = = 0 ) {
2005-04-17 02:20:36 +04:00
if ( i < numnodes )
2009-09-26 02:19:47 +04:00
pr_info ( " Skipping disabled node %d \n " , i ) ;
2005-04-17 02:20:36 +04:00
continue ;
2008-01-30 15:32:36 +03:00
}
2005-04-17 02:20:36 +04:00
if ( nodeid > = numnodes ) {
2011-05-02 19:24:48 +04:00
pr_info ( " Ignoring excess node %d (%Lx:%Lx) \n " , nodeid ,
2009-09-26 02:19:47 +04:00
base , limit ) ;
2005-04-17 02:20:36 +04:00
continue ;
2008-01-30 15:32:36 +03:00
}
2005-04-17 02:20:36 +04:00
2008-01-30 15:32:36 +03:00
if ( ! limit ) {
2011-05-02 19:24:48 +04:00
pr_info ( " Skipping node entry %d (base %Lx) \n " ,
2009-09-26 02:19:47 +04:00
i , base ) ;
2005-04-17 02:20:36 +04:00
continue ;
}
if ( ( base > > 8 ) & 3 | | ( limit > > 8 ) & 3 ) {
2011-05-02 19:24:48 +04:00
pr_err ( " Node %d using interleaving mode %Lx/%Lx \n " ,
2009-09-26 02:19:47 +04:00
nodeid , ( base > > 8 ) & 3 , ( limit > > 8 ) & 3 ) ;
2011-02-16 14:13:06 +03:00
return - EINVAL ;
2008-01-30 15:32:36 +03:00
}
2011-02-16 19:11:09 +03:00
if ( node_isset ( nodeid , numa_nodes_parsed ) ) {
2009-09-26 02:19:47 +04:00
pr_info ( " Node %d already present, skipping \n " ,
nodeid ) ;
2005-04-17 02:20:36 +04:00
continue ;
}
2008-01-30 15:32:36 +03:00
limit > > = 16 ;
2005-11-05 19:25:54 +03:00
limit + + ;
2013-03-14 22:34:35 +04:00
limit < < = 24 ;
2005-04-17 02:20:36 +04:00
2009-09-26 02:20:00 +04:00
if ( limit > end )
limit = end ;
2005-04-17 02:20:36 +04:00
if ( limit < = base )
2008-01-30 15:32:36 +03:00
continue ;
2005-04-17 02:20:36 +04:00
base > > = 16 ;
2008-01-30 15:32:36 +03:00
base < < = 24 ;
if ( base < start )
base = start ;
if ( limit > end )
limit = end ;
if ( limit = = base ) {
2009-09-26 02:19:47 +04:00
pr_err ( " Empty node %d \n " , nodeid ) ;
2008-01-30 15:32:36 +03:00
continue ;
2005-04-17 02:20:36 +04:00
}
2008-01-30 15:32:36 +03:00
if ( limit < base ) {
2011-05-02 19:24:48 +04:00
pr_err ( " Node %d bogus settings %Lx-%Lx. \n " ,
2008-01-30 15:32:36 +03:00
nodeid , base , limit ) ;
2005-04-17 02:20:36 +04:00
continue ;
2008-01-30 15:32:36 +03:00
}
2005-04-17 02:20:36 +04:00
/* Could sort here, but pun for now. Should not happen anyroads. */
2008-01-30 15:32:36 +03:00
if ( prevbase > base ) {
2011-05-02 19:24:48 +04:00
pr_err ( " Node map not sorted %Lx,%Lx \n " ,
2008-01-30 15:32:36 +03:00
prevbase , base ) ;
2011-02-16 14:13:06 +03:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
}
2008-01-30 15:32:36 +03:00
2011-05-02 19:24:48 +04:00
pr_info ( " Node %d MemBase %016Lx Limit %016Lx \n " ,
2009-09-26 02:19:47 +04:00
nodeid , base , limit ) ;
2008-01-30 15:32:36 +03:00
2005-04-17 02:20:36 +04:00
prevbase = base ;
2011-02-16 19:11:09 +03:00
numa_add_memblk ( nodeid , base , limit ) ;
2011-02-16 19:11:09 +03:00
node_set ( nodeid , numa_nodes_parsed ) ;
2008-01-30 15:32:36 +03:00
}
2005-04-17 02:20:36 +04:00
2011-02-16 19:11:09 +03:00
if ( ! nodes_weight ( numa_nodes_parsed ) )
2011-02-16 14:13:06 +03:00
return - ENOENT ;
2011-02-16 14:13:07 +03:00
/*
* We seem to have valid NUMA configuration . Map apicids to nodes
* using the coreid bits from early_identify_cpu .
*/
bits = boot_cpu_data . x86_coreid_bits ;
cores = 1 < < bits ;
apicid_base = 0 ;
/* get the APIC ID of the BSP early for systems with apicid lifting */
early_get_boot_cpu_id ( ) ;
if ( boot_cpu_physical_apicid > 0 ) {
pr_info ( " BSP APIC ID: %02x \n " , boot_cpu_physical_apicid ) ;
apicid_base = boot_cpu_physical_apicid ;
}
2011-02-16 19:11:09 +03:00
for_each_node_mask ( i , numa_nodes_parsed )
2011-02-16 14:13:07 +03:00
for ( j = apicid_base ; j < cores + apicid_base ; j + + )
set_apicid_to_node ( ( i < < bits ) + j , i ) ;
2009-09-26 02:20:00 +04:00
return 0 ;
}