2008-01-30 13:32:36 +01:00
/*
2005-04-16 15:20:36 -07:00
* AMD K8 NUMA support .
* Discover the memory map and associated nodes .
2008-01-30 13:32:36 +01:00
*
2005-04-16 15:20:36 -07:00
* This version reads it directly from the K8 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/module.h>
# include <linux/nodemask.h>
# 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>
# include <asm/e820.h>
# 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>
2008-05-12 15:43:35 +02:00
# include <asm/k8.h>
2005-04-16 15:20:36 -07:00
2009-09-25 15:20:00 -07:00
static struct bootnode __initdata nodes [ 8 ] ;
static nodemask_t __initdata nodes_parsed = NODE_MASK_NONE ;
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
2008-01-30 13:32:36 +01:00
return - 1 ;
2005-04-16 15:20:36 -07:00
}
2008-02-19 03:21:06 -08:00
static __init void early_get_boot_cpu_id ( void )
{
/*
* need to get boot_cpu_id so can use that to create apicid_to_node
* in k8_scan_nodes ( )
*/
/*
* Find possible boot - time SMP configuration :
*/
2008-06-19 12:15:01 -07:00
# ifdef CONFIG_X86_MPPARSE
2008-02-19 03:21:06 -08:00
early_find_smp_config ( ) ;
2008-06-19 12:15:01 -07:00
# endif
2008-02-19 03:21:06 -08:00
# ifdef CONFIG_ACPI
/*
* Read APIC information from ACPI tables .
*/
early_acpi_boot_init ( ) ;
# endif
2008-06-19 12:15:01 -07:00
# ifdef CONFIG_X86_MPPARSE
2008-02-19 03:21:06 -08:00
/*
* get boot - time SMP configuration :
*/
if ( smp_found_config )
early_get_smp_config ( ) ;
2008-06-19 12:15:01 -07:00
# endif
2008-02-19 03:21:06 -08:00
early_init_lapic_mapping ( ) ;
}
2009-09-25 15:20:00 -07:00
int __init k8_get_nodes ( struct bootnode * physnodes )
2008-01-30 13:32:36 +01:00
{
2009-09-25 15:20:00 -07:00
int i ;
int ret = 0 ;
for_each_node_mask ( i , nodes_parsed ) {
physnodes [ ret ] . start = nodes [ i ] . start ;
physnodes [ ret ] . end = nodes [ i ] . end ;
ret + + ;
}
return ret ;
}
int __init k8_numa_init ( unsigned long start_pfn , unsigned long end_pfn )
{
unsigned long start = PFN_PHYS ( start_pfn ) ;
unsigned long end = PFN_PHYS ( end_pfn ) ;
unsigned numnodes ;
2005-04-16 15:20:36 -07:00
unsigned long prevbase ;
2009-09-25 15:20:00 -07:00
int i , nb , found = 0 ;
2008-05-12 15:43:35 +02:00
u32 nodeid , reg ;
2005-04-16 15:20:36 -07:00
2006-09-26 10:52:41 +02:00
if ( ! early_pci_allowed ( ) )
return - 1 ;
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 )
return - 1 ;
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 + + ) {
unsigned long base , limit ;
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 ) ;
2008-01-30 13:32:36 +01:00
nodeid = limit & 7 ;
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 ) {
2009-09-25 15:19:47 -07:00
pr_info ( " Ignoring excess node %d (%lx:%lx) \n " , 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
2008-01-30 13:32:36 +01:00
if ( ! limit ) {
2009-09-25 15:19:47 -07:00
pr_info ( " Skipping node entry %d (base %lx) \n " ,
i , base ) ;
2005-04-16 15:20:36 -07:00
continue ;
}
if ( ( base > > 8 ) & 3 | | ( limit > > 8 ) & 3 ) {
2009-09-25 15:19:47 -07:00
pr_err ( " Node %d using interleaving mode %lx/%lx \n " ,
nodeid , ( base > > 8 ) & 3 , ( limit > > 8 ) & 3 ) ;
2008-01-30 13:32:36 +01:00
return - 1 ;
}
2009-09-25 15:20:00 -07:00
if ( node_isset ( nodeid , 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 ;
limit < < = 24 ;
2005-04-16 15:20:36 -07:00
limit | = ( 1 < < 24 ) - 1 ;
2005-11-05 17:25:54 +01:00
limit + + ;
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 ) {
2009-09-25 15:19:47 -07: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 ) {
2009-09-25 15:19:47 -07:00
pr_err ( " Node map not sorted %lx,%lx \n " ,
2008-01-30 13:32:36 +01:00
prevbase , base ) ;
2005-04-16 15:20:36 -07:00
return - 1 ;
}
2008-01-30 13:32:36 +01:00
2009-09-25 15:19:47 -07:00
pr_info ( " Node %d MemBase %016lx Limit %016lx \n " ,
nodeid , base , limit ) ;
2008-01-30 13:32:36 +01:00
2005-04-16 15:20:36 -07:00
found + + ;
2008-01-30 13:32:36 +01:00
nodes [ nodeid ] . start = base ;
2005-04-16 15:20:36 -07:00
nodes [ nodeid ] . end = limit ;
prevbase = base ;
2009-09-25 15:20:00 -07:00
node_set ( nodeid , nodes_parsed ) ;
2008-01-30 13:32:36 +01:00
}
2005-04-16 15:20:36 -07:00
if ( ! found )
2008-01-30 13:32:36 +01:00
return - 1 ;
2009-09-25 15:20:00 -07:00
return 0 ;
}
2005-04-16 15:20:36 -07:00
2009-09-25 15:20:00 -07:00
int __init k8_scan_nodes ( void )
{
unsigned int bits ;
unsigned int cores ;
unsigned int apicid_base ;
int i ;
BUG_ON ( nodes_empty ( nodes_parsed ) ) ;
node_possible_map = nodes_parsed ;
2008-03-25 10:14:35 -07:00
memnode_shift = compute_hash_shift ( nodes , 8 , NULL ) ;
2008-01-30 13:32:36 +01:00
if ( memnode_shift < 0 ) {
2009-09-25 15:19:47 -07:00
pr_err ( " No NUMA node hash function found. Contact maintainer \n " ) ;
2008-01-30 13:32:36 +01:00
return - 1 ;
}
2009-09-25 15:19:47 -07:00
pr_info ( " Using node hash shift of %d \n " , memnode_shift ) ;
2005-04-16 15:20:36 -07:00
2008-01-30 13:30:39 +01:00
/* use the coreid bits from early_identify_cpu */
bits = boot_cpu_data . x86_coreid_bits ;
cores = ( 1 < < bits ) ;
2008-02-19 03:21:06 -08:00
apicid_base = 0 ;
/* need to get boot_cpu_id early for system with apicid lifting */
early_get_boot_cpu_id ( ) ;
if ( boot_cpu_physical_apicid > 0 ) {
2009-09-25 15:19:47 -07:00
pr_info ( " BSP APIC ID: %02x \n " , boot_cpu_physical_apicid ) ;
2008-02-19 03:21:06 -08:00
apicid_base = boot_cpu_physical_apicid ;
}
2008-01-30 13:30:39 +01:00
2009-09-25 15:20:00 -07:00
for_each_node_mask ( i , node_possible_map ) {
int j ;
2009-01-05 18:39:01 -08:00
e820_register_active_regions ( i ,
nodes [ i ] . start > > PAGE_SHIFT ,
nodes [ i ] . end > > PAGE_SHIFT ) ;
for ( j = apicid_base ; j < cores + apicid_base ; j + + )
apicid_to_node [ ( i < < bits ) + j ] = i ;
setup_node_bootmem ( i , nodes [ i ] . start , nodes [ i ] . end ) ;
2005-04-16 15:20:36 -07:00
}
numa_init_array ( ) ;
return 0 ;
2008-01-30 13:32:36 +01:00
}