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