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
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 ( ) ;
}
2005-04-16 15:20:36 -07:00
int __init k8_scan_nodes ( unsigned long start , unsigned long end )
2008-01-30 13:32:36 +01:00
{
2008-05-12 15:43:35 +02:00
unsigned numnodes , cores , bits , apicid_base ;
2005-04-16 15:20:36 -07:00
unsigned long prevbase ;
2006-03-25 16:29:12 +01:00
struct bootnode nodes [ 8 ] ;
2008-05-12 15:43:35 +02:00
int i , j , 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 ;
2008-01-30 13:32:36 +01:00
printk ( KERN_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
printk ( KERN_INFO " Number of nodes %d \n " , numnodes ) ;
2008-01-30 13:32:36 +01:00
memset ( & nodes , 0 , sizeof ( nodes ) ) ;
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 )
2008-01-30 13:32:36 +01:00
printk ( " 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 ) {
printk ( " Ignoring excess node %d (%lx:%lx) \n " , nodeid ,
2008-01-30 13:32:36 +01: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 ) {
printk ( KERN_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 ) {
2008-01-30 13:32:36 +01:00
printk ( KERN_ERR " Node %d using interleaving mode %lx/%lx \n " ,
nodeid , ( base > > 8 ) & 3 , ( limit > > 8 ) & 3 ) ;
return - 1 ;
}
2007-05-02 19:27:20 +02:00
if ( node_isset ( nodeid , node_possible_map ) ) {
2008-01-30 13:32:36 +01:00
printk ( KERN_INFO " Node %d already present. Skipping \n " ,
2005-04-16 15:20:36 -07:00
nodeid ) ;
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
2008-06-24 22:14:09 -07:00
if ( limit > max_pfn < < PAGE_SHIFT )
limit = max_pfn < < PAGE_SHIFT ;
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 ) {
printk ( KERN_ERR " Empty node %d \n " , nodeid ) ;
continue ;
2005-04-16 15:20:36 -07:00
}
2008-01-30 13:32:36 +01:00
if ( limit < base ) {
2005-04-16 15:20:36 -07:00
printk ( KERN_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 ) {
2005-04-16 15:20:36 -07:00
printk ( KERN_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
printk ( KERN_INFO " Node %d MemBase %016lx Limit %016lx \n " ,
nodeid , base , limit ) ;
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 ;
2007-05-02 19:27:20 +02:00
node_set ( nodeid , node_possible_map ) ;
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 ;
2005-04-16 15:20:36 -07:00
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 ) {
printk ( KERN_ERR " No NUMA node hash function found. Contact maintainer \n " ) ;
return - 1 ;
}
printk ( KERN_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 ) {
printk ( KERN_INFO " BSP APIC ID: %02x \n " ,
boot_cpu_physical_apicid ) ;
apicid_base = boot_cpu_physical_apicid ;
}
2008-01-30 13:30:39 +01:00
2005-04-16 15:20:36 -07:00
for ( i = 0 ; i < 8 ; i + + ) {
2009-01-05 18:39:01 -08:00
if ( nodes [ i ] . start = = nodes [ i ] . end )
continue ;
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
}