2005-04-16 15:20:36 -07:00
/*
* Written by : Patricia Gaughen , IBM Corporation
*
* Copyright ( C ) 2002 , IBM Corp .
*
* All rights reserved .
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful , but
* WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE , GOOD TITLE or
* NON INFRINGEMENT . See the GNU General Public License for more
* details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*
* Send feedback to < gone @ us . ibm . com >
*/
# include <linux/mm.h>
# include <linux/bootmem.h>
# include <linux/mmzone.h>
# include <linux/module.h>
# include <linux/nodemask.h>
# include <asm/numaq.h>
# include <asm/topology.h>
2005-07-28 21:16:18 -07:00
# include <asm/processor.h>
2008-11-17 15:19:53 -08:00
# include <asm/genapic.h>
2008-06-03 19:35:04 -07:00
# include <asm/e820.h>
2008-07-19 18:01:16 -07:00
# include <asm/setup.h>
2005-04-16 15:20:36 -07:00
# define MB_TO_PAGES(addr) ((addr) << (20 - PAGE_SHIFT))
/*
* Function : smp_dump_qct ( )
*
* Description : gets memory layout from the quad config table . This
* function also updates node_online_map with the nodes ( quads ) present .
*/
static void __init smp_dump_qct ( void )
{
int node ;
struct eachquadmem * eq ;
struct sys_cfg_data * scd =
( struct sys_cfg_data * ) __va ( SYS_CFG_DATA_PRIV_ADDR ) ;
nodes_clear ( node_online_map ) ;
for_each_node ( node ) {
if ( scd - > quads_present31_0 & ( 1 < < node ) ) {
node_set_online ( node ) ;
eq = & scd - > eq [ node ] ;
/* Convert to pages */
node_start_pfn [ node ] = MB_TO_PAGES (
eq - > hi_shrd_mem_start - eq - > priv_mem_size ) ;
node_end_pfn [ node ] = MB_TO_PAGES (
eq - > hi_shrd_mem_start + eq - > hi_shrd_mem_size ) ;
2008-06-03 19:35:04 -07:00
e820_register_active_regions ( node , node_start_pfn [ node ] ,
node_end_pfn [ node ] ) ;
2005-04-16 15:20:36 -07:00
memory_present ( node ,
node_start_pfn [ node ] , node_end_pfn [ node ] ) ;
node_remap_size [ node ] = node_memmap_size_bytes ( node ,
node_start_pfn [ node ] ,
node_end_pfn [ node ] ) ;
}
}
}
2008-07-19 18:01:16 -07:00
2008-08-20 18:18:26 +02:00
void __cpuinit numaq_tsc_disable ( void )
2008-07-19 18:01:16 -07:00
{
if ( ! found_numaq )
return ;
if ( num_online_nodes ( ) > 1 ) {
printk ( KERN_DEBUG " NUMAQ: disabling TSC \n " ) ;
setup_clear_cpu_cap ( X86_FEATURE_TSC ) ;
}
}
2008-07-19 18:02:26 -07:00
static int __init numaq_pre_time_init ( void )
{
numaq_tsc_disable ( ) ;
return 0 ;
}
2008-07-19 18:01:16 -07:00
int found_numaq ;
/*
* Have to match translation table entries to main table entries by counter
* hence the mpc_record variable . . . . can ' t see a less disgusting way of
* doing this . . . .
*/
struct mpc_config_translation {
unsigned char mpc_type ;
unsigned char trans_len ;
unsigned char trans_type ;
unsigned char trans_quad ;
unsigned char trans_global ;
unsigned char trans_local ;
unsigned short trans_reserved ;
} ;
/* x86_quirks member */
static int mpc_record ;
static struct mpc_config_translation * translation_table [ MAX_MPC_ENTRY ]
__cpuinitdata ;
static inline int generate_logical_apicid ( int quad , int phys_apicid )
{
return ( quad < < 4 ) + ( phys_apicid ? phys_apicid < < 1 : 1 ) ;
}
/* x86_quirks member */
2009-01-03 15:48:52 +05:30
static int mpc_apic_id ( struct mpc_cpu * m )
2008-07-19 18:01:16 -07:00
{
int quad = translation_table [ mpc_record ] - > trans_quad ;
2009-01-04 21:58:25 +05:30
int logical_apicid = generate_logical_apicid ( quad , m - > apicid ) ;
2008-07-19 18:01:16 -07:00
printk ( KERN_DEBUG " Processor #%d %u:%u APIC version %d (quad %d, apic %d) \n " ,
2009-01-04 21:58:25 +05:30
m - > apicid , ( m - > cpufeature & CPU_FAMILY_MASK ) > > 8 ,
( m - > cpufeature & CPU_MODEL_MASK ) > > 4 ,
m - > apicver , quad , logical_apicid ) ;
2008-07-19 18:01:16 -07:00
return logical_apicid ;
}
int mp_bus_id_to_node [ MAX_MP_BUSSES ] ;
int mp_bus_id_to_local [ MAX_MP_BUSSES ] ;
/* x86_quirks member */
2009-01-03 15:47:32 +05:30
static void mpc_oem_bus_info ( struct mpc_bus * m , char * name )
2008-07-19 18:01:16 -07:00
{
int quad = translation_table [ mpc_record ] - > trans_quad ;
int local = translation_table [ mpc_record ] - > trans_local ;
2009-01-04 21:59:26 +05:30
mp_bus_id_to_node [ m - > busid ] = quad ;
mp_bus_id_to_local [ m - > busid ] = local ;
2008-07-19 18:01:16 -07:00
printk ( KERN_INFO " Bus #%d is %s (node %d) \n " ,
2009-01-04 21:59:26 +05:30
m - > busid , name , quad ) ;
2008-07-19 18:01:16 -07:00
}
int quad_local_to_mp_bus_id [ NR_CPUS / 4 ] [ 4 ] ;
/* x86_quirks member */
2009-01-03 15:47:32 +05:30
static void mpc_oem_pci_bus ( struct mpc_bus * m )
2008-07-19 18:01:16 -07:00
{
int quad = translation_table [ mpc_record ] - > trans_quad ;
int local = translation_table [ mpc_record ] - > trans_local ;
2009-01-04 21:59:26 +05:30
quad_local_to_mp_bus_id [ quad ] [ local ] = m - > busid ;
2008-07-19 18:01:16 -07:00
}
static void __init MP_translation_info ( struct mpc_config_translation * m )
{
printk ( KERN_INFO
" Translation: record %d, type %d, quad %d, global %d, local %d \n " ,
mpc_record , m - > trans_type , m - > trans_quad , m - > trans_global ,
m - > trans_local ) ;
if ( mpc_record > = MAX_MPC_ENTRY )
printk ( KERN_ERR " MAX_MPC_ENTRY exceeded! \n " ) ;
else
translation_table [ mpc_record ] = m ; /* stash this for later */
if ( m - > trans_quad < MAX_NUMNODES & & ! node_online ( m - > trans_quad ) )
node_set_online ( m - > trans_quad ) ;
}
static int __init mpf_checksum ( unsigned char * mp , int len )
{
int sum = 0 ;
while ( len - - )
sum + = * mp + + ;
return sum & 0xFF ;
}
/*
* Read / parse the MPC oem tables
*/
2009-01-03 15:52:54 +05:30
static void __init smp_read_mpc_oem ( struct mpc_oemtable * oemtable ,
2008-07-19 18:01:16 -07:00
unsigned short oemsize )
{
int count = sizeof ( * oemtable ) ; /* the header size */
unsigned char * oemptr = ( ( unsigned char * ) oemtable ) + count ;
mpc_record = 0 ;
printk ( KERN_INFO " Found an OEM MPC table at %8p - parsing it ... \n " ,
oemtable ) ;
2009-01-04 22:00:46 +05:30
if ( memcmp ( oemtable - > signature , MPC_OEM_SIGNATURE , 4 ) ) {
2008-07-19 18:01:16 -07:00
printk ( KERN_WARNING
" SMP mpc oemtable: bad signature [%c%c%c%c]! \n " ,
2009-01-04 22:00:46 +05:30
oemtable - > signature [ 0 ] , oemtable - > signature [ 1 ] ,
oemtable - > signature [ 2 ] , oemtable - > signature [ 3 ] ) ;
2008-07-19 18:01:16 -07:00
return ;
}
2009-01-04 22:00:46 +05:30
if ( mpf_checksum ( ( unsigned char * ) oemtable , oemtable - > length ) ) {
2008-07-19 18:01:16 -07:00
printk ( KERN_WARNING " SMP oem mptable: checksum error! \n " ) ;
return ;
}
2009-01-04 22:00:46 +05:30
while ( count < oemtable - > length ) {
2008-07-19 18:01:16 -07:00
switch ( * oemptr ) {
case MP_TRANSLATION :
{
struct mpc_config_translation * m =
( struct mpc_config_translation * ) oemptr ;
MP_translation_info ( m ) ;
oemptr + = sizeof ( * m ) ;
count + = sizeof ( * m ) ;
+ + mpc_record ;
break ;
}
default :
{
printk ( KERN_WARNING
" Unrecognised OEM table entry type! - %d \n " ,
( int ) * oemptr ) ;
return ;
}
}
}
}
2008-07-25 02:14:28 -07:00
static int __init numaq_setup_ioapic_ids ( void )
{
/* so can skip it */
return 1 ;
}
2008-11-17 15:19:53 -08:00
static int __init numaq_update_genapic ( void )
{
genapic - > wakeup_cpu = wakeup_secondary_cpu_via_nmi ;
return 0 ;
}
2008-07-19 18:01:16 -07:00
static struct x86_quirks numaq_x86_quirks __initdata = {
2008-07-19 18:02:26 -07:00
. arch_pre_time_init = numaq_pre_time_init ,
2008-07-19 18:01:16 -07:00
. arch_time_init = NULL ,
. arch_pre_intr_init = NULL ,
. arch_memory_setup = NULL ,
. arch_intr_init = NULL ,
. arch_trap_init = NULL ,
. mach_get_smp_config = NULL ,
. mach_find_smp_config = NULL ,
. mpc_record = & mpc_record ,
. mpc_apic_id = mpc_apic_id ,
. mpc_oem_bus_info = mpc_oem_bus_info ,
. mpc_oem_pci_bus = mpc_oem_pci_bus ,
. smp_read_mpc_oem = smp_read_mpc_oem ,
2008-07-25 02:14:28 -07:00
. setup_ioapic_ids = numaq_setup_ioapic_ids ,
2008-11-17 15:19:53 -08:00
. update_genapic = numaq_update_genapic ,
2008-07-19 18:01:16 -07:00
} ;
2009-01-03 15:46:57 +05:30
void numaq_mps_oem_check ( struct mpc_table * mpc , char * oem , char * productid )
2008-07-19 18:01:16 -07:00
{
if ( strncmp ( oem , " IBM NUMA " , 8 ) )
printk ( " Warning! Not a NUMA-Q system! \n " ) ;
else
found_numaq = 1 ;
}
2008-06-03 10:25:54 -07:00
static __init void early_check_numaq ( void )
{
/*
* Find possible boot - time SMP configuration :
*/
early_find_smp_config ( ) ;
/*
* get boot - time SMP configuration :
*/
if ( smp_found_config )
early_get_smp_config ( ) ;
2008-07-19 18:01:16 -07:00
if ( found_numaq )
x86_quirks = & numaq_x86_quirks ;
2008-06-03 10:25:54 -07:00
}
2005-04-16 15:20:36 -07:00
int __init get_memcfg_numaq ( void )
{
2008-06-03 10:25:54 -07:00
early_check_numaq ( ) ;
if ( ! found_numaq )
return 0 ;
2005-04-16 15:20:36 -07:00
smp_dump_qct ( ) ;
return 1 ;
}