2005-04-16 15:20:36 -07:00
/*
* linux / arch / m32r / kernel / smpboot . c
* orig : i386 2.4 .10
*
* M32R SMP booting functions
*
* Copyright ( c ) 2001 , 2002 , 2003 Hitoshi Yamamoto
*
* Taken from i386 version .
* ( c ) 1995 Alan Cox , Building # 3 < alan @ redhat . com >
* ( c ) 1998 , 1999 , 2000 Ingo Molnar < mingo @ redhat . com >
*
* Much of the core SMP work is based on previous work by Thomas Radke , to
* whom a great many thanks are extended .
*
* Thanks to Intel for making available several different Pentium ,
* Pentium Pro and Pentium - II / Xeon MP machines .
* Original development of Linux SMP code supported by Caldera .
*
* This code is released under the GNU General Public License version 2 or
* later .
*
* Fixes
* Felix Koop : NR_CPUS used properly
* Jose Renau : Handle single CPU case .
* Alan Cox : By repeated request
* 8 ) - Total BogoMIP report .
* Greg Wright : Fix for kernel stacks panic .
* Erich Boleyn : MP v1 .4 and additional changes .
* Matthias Sattler : Changes for 2.1 kernel map .
* Michel Lespinasse : Changes for 2.1 kernel map .
* Michael Chastain : Change trampoline . S to gnu as .
* Alan Cox : Dumb bug : ' B ' step PPro ' s are fine
* Ingo Molnar : Added APIC timers , based on code
* from Jose Renau
* Ingo Molnar : various cleanups and rewrites
* Tigran Aivazian : fixed " 0.00 in /proc/uptime on SMP " bug .
* Maciej W . Rozycki : Bits for genuine 82489 DX APICs
* Martin J . Bligh : Added support for multi - quad systems
*/
2006-04-10 22:53:18 -07:00
# include <linux/module.h>
2008-10-16 19:00:57 +02:00
# include <linux/cpu.h>
2005-04-16 15:20:36 -07:00
# include <linux/init.h>
2006-04-10 22:53:18 -07:00
# include <linux/kernel.h>
2005-04-16 15:20:36 -07:00
# include <linux/mm.h>
2008-02-07 00:16:18 -08:00
# include <linux/sched.h>
2007-07-30 22:00:47 +09:00
# include <linux/err.h>
2005-04-16 15:20:36 -07:00
# include <linux/irq.h>
# include <linux/bootmem.h>
# include <linux/delay.h>
# include <asm/io.h>
# include <asm/pgalloc.h>
# include <asm/tlbflush.h>
# define DEBUG_SMP
# ifdef DEBUG_SMP
# define Dprintk(x...) printk(x)
# else
# define Dprintk(x...)
# endif
extern cpumask_t cpu_initialized ;
/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/
/* Data structures and variables */
/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/
/* Processor that is doing the boot up */
static unsigned int bsp_phys_id = - 1 ;
/* Bitmask of physically existing CPUs */
physid_mask_t phys_cpu_present_map ;
cpumask_t cpu_bootout_map ;
cpumask_t cpu_bootin_map ;
static cpumask_t cpu_callin_map ;
2006-04-10 22:53:18 -07:00
cpumask_t cpu_callout_map ;
EXPORT_SYMBOL ( cpu_callout_map ) ;
2005-04-16 15:20:36 -07:00
/* Per CPU bogomips and other parameters */
struct cpuinfo_m32r cpu_data [ NR_CPUS ] __cacheline_aligned ;
static int cpucount ;
static cpumask_t smp_commenced_mask ;
extern struct {
void * spi ;
unsigned short ss ;
} stack_start ;
/* which physical physical ID maps to which logical CPU number */
static volatile int physid_2_cpu [ NR_CPUS ] ;
2005-08-23 22:47:17 +01:00
# define physid_to_cpu(physid) physid_2_cpu[physid]
2005-04-16 15:20:36 -07:00
/* which logical CPU number maps to which physical ID */
volatile int cpu_2_physid [ NR_CPUS ] ;
DEFINE_PER_CPU ( int , prof_multiplier ) = 1 ;
DEFINE_PER_CPU ( int , prof_old_multiplier ) = 1 ;
DEFINE_PER_CPU ( int , prof_counter ) = 1 ;
spinlock_t ipi_lock [ NR_IPIS ] ;
static unsigned int calibration_result ;
/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/
/* Function Prototypes */
/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/
void smp_prepare_boot_cpu ( void ) ;
void smp_prepare_cpus ( unsigned int ) ;
static void init_ipi_lock ( void ) ;
static void do_boot_cpu ( int ) ;
int __cpu_up ( unsigned int ) ;
void smp_cpus_done ( unsigned int ) ;
int start_secondary ( void * ) ;
static void smp_callin ( void ) ;
static void smp_online ( void ) ;
static void show_mp_info ( int ) ;
static void smp_store_cpu_info ( int ) ;
static void show_cpu_info ( int ) ;
int setup_profiling_timer ( unsigned int ) ;
static void init_cpu_to_physid ( void ) ;
static void map_cpu_to_physid ( int , int ) ;
static void unmap_cpu_to_physid ( int , int ) ;
/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/
2007-10-20 01:14:39 +02:00
/* Boot up APs Routines : BSP */
2005-04-16 15:20:36 -07:00
/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/
void __devinit smp_prepare_boot_cpu ( void )
{
bsp_phys_id = hard_smp_processor_id ( ) ;
physid_set ( bsp_phys_id , phys_cpu_present_map ) ;
cpu_set ( 0 , cpu_online_map ) ; /* BSP's cpu_id == 0 */
cpu_set ( 0 , cpu_callout_map ) ;
cpu_set ( 0 , cpu_callin_map ) ;
/*
* Initialize the logical to physical CPU number mapping
*/
init_cpu_to_physid ( ) ;
map_cpu_to_physid ( 0 , bsp_phys_id ) ;
current_thread_info ( ) - > cpu = 0 ;
}
/*==========================================================================*
* Name : smp_prepare_cpus ( old smp_boot_cpus )
*
* Description : This routine boot up APs .
*
* Born on Date : 2002.02 .05
*
* Arguments : NONE
*
* Returns : void ( cannot fail )
*
* Modification log :
* Date Who Description
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* 2003 - 06 - 24 hy modify for linux - 2.5 .69
*
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
void __init smp_prepare_cpus ( unsigned int max_cpus )
{
int phys_id ;
unsigned long nr_cpu ;
nr_cpu = inl ( M32R_FPGA_NUM_OF_CPUS_PORTL ) ;
if ( nr_cpu > NR_CPUS ) {
printk ( KERN_INFO " NUM_OF_CPUS reg. value [%ld] > NR_CPU [%d] " ,
nr_cpu , NR_CPUS ) ;
goto smp_done ;
}
for ( phys_id = 0 ; phys_id < nr_cpu ; phys_id + + )
physid_set ( phys_id , phys_cpu_present_map ) ;
2006-04-10 22:53:18 -07:00
# ifndef CONFIG_HOTPLUG_CPU
2009-09-24 09:34:47 -06:00
init_cpu_present ( & cpu_possible_map ) ;
2006-04-10 22:53:18 -07:00
# endif
2005-04-16 15:20:36 -07:00
show_mp_info ( nr_cpu ) ;
init_ipi_lock ( ) ;
/*
* Setup boot CPU information
*/
smp_store_cpu_info ( 0 ) ; /* Final full version of the data */
/*
* If SMP should be disabled , then really disable it !
*/
if ( ! max_cpus ) {
printk ( KERN_INFO " SMP mode deactivated by commandline. \n " ) ;
goto smp_done ;
}
/*
* Now scan the CPU present map and fire up the other CPUs .
*/
Dprintk ( " CPU present map : %lx \n " , physids_coerce ( phys_cpu_present_map ) ) ;
for ( phys_id = 0 ; phys_id < NR_CPUS ; phys_id + + ) {
/*
* Don ' t even attempt to start the boot CPU !
*/
if ( phys_id = = bsp_phys_id )
continue ;
if ( ! physid_isset ( phys_id , phys_cpu_present_map ) )
continue ;
2009-09-21 17:04:03 -07:00
if ( max_cpus < = cpucount + 1 )
2005-04-16 15:20:36 -07:00
continue ;
do_boot_cpu ( phys_id ) ;
/*
* Make sure we unmap all failed CPUs
*/
if ( physid_to_cpu ( phys_id ) = = - 1 ) {
physid_clear ( phys_id , phys_cpu_present_map ) ;
printk ( " phys CPU#%d not responding - " \
" cannot use it. \n " , phys_id ) ;
}
}
smp_done :
Dprintk ( " Boot done. \n " ) ;
}
/*
* init_ipi_lock : Initialize IPI locks .
*/
static void __init init_ipi_lock ( void )
{
int ipi ;
for ( ipi = 0 ; ipi < NR_IPIS ; ipi + + )
spin_lock_init ( & ipi_lock [ ipi ] ) ;
}
/*==========================================================================*
* Name : do_boot_cpu
*
* Description : This routine boot up one AP .
*
* Born on Date : 2002.02 .05
*
* Arguments : phys_id - Target CPU physical ID
*
* Returns : void ( cannot fail )
*
* Modification log :
* Date Who Description
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* 2003 - 06 - 24 hy modify for linux - 2.5 .69
*
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static void __init do_boot_cpu ( int phys_id )
{
struct task_struct * idle ;
unsigned long send_status , boot_status ;
int timeout , cpu_id ;
cpu_id = + + cpucount ;
/*
* We can ' t use kernel_thread since we must avoid to
* reschedule the child .
*/
idle = fork_idle ( cpu_id ) ;
if ( IS_ERR ( idle ) )
panic ( " failed fork for CPU#%d. " , cpu_id ) ;
idle - > thread . lr = ( unsigned long ) start_secondary ;
map_cpu_to_physid ( cpu_id , phys_id ) ;
/* So we see what's up */
printk ( " Booting processor %d/%d \n " , phys_id , cpu_id ) ;
stack_start . spi = ( void * ) idle - > thread . sp ;
2006-01-12 01:05:52 -08:00
task_thread_info ( idle ) - > cpu = cpu_id ;
2005-04-16 15:20:36 -07:00
/*
* Send Startup IPI
* 1. IPI received by CPU # ( phys_id ) .
* 2. CPU # ( phys_id ) enter startup_AP ( arch / m32r / kernel / head . S )
* 3. CPU # ( phys_id ) enter start_secondary ( )
*/
send_status = 0 ;
boot_status = 0 ;
cpu_set ( phys_id , cpu_bootout_map ) ;
/* Send Startup IPI */
send_IPI_mask_phys ( cpumask_of_cpu ( phys_id ) , CPU_BOOT_IPI , 0 ) ;
Dprintk ( " Waiting for send to finish... \n " ) ;
timeout = 0 ;
/* Wait 100[ms] */
do {
Dprintk ( " + " ) ;
udelay ( 1000 ) ;
send_status = ! cpu_isset ( phys_id , cpu_bootin_map ) ;
} while ( send_status & & ( timeout + + < 100 ) ) ;
Dprintk ( " After Startup. \n " ) ;
if ( ! send_status ) {
/*
* allow APs to start initializing .
*/
Dprintk ( " Before Callout %d. \n " , cpu_id ) ;
cpu_set ( cpu_id , cpu_callout_map ) ;
Dprintk ( " After Callout %d. \n " , cpu_id ) ;
/*
* Wait 5 s total for a response
*/
for ( timeout = 0 ; timeout < 5000 ; timeout + + ) {
if ( cpu_isset ( cpu_id , cpu_callin_map ) )
break ; /* It has booted */
udelay ( 1000 ) ;
}
if ( cpu_isset ( cpu_id , cpu_callin_map ) ) {
/* number CPUs logically, starting from 1 (BSP is 0) */
Dprintk ( " OK. \n " ) ;
} else {
boot_status = 1 ;
printk ( " Not responding. \n " ) ;
}
} else
printk ( " IPI never delivered??? \n " ) ;
if ( send_status | | boot_status ) {
unmap_cpu_to_physid ( cpu_id , phys_id ) ;
cpu_clear ( cpu_id , cpu_callout_map ) ;
cpu_clear ( cpu_id , cpu_callin_map ) ;
cpu_clear ( cpu_id , cpu_initialized ) ;
cpucount - - ;
}
}
2007-01-10 23:15:34 -08:00
int __cpuinit __cpu_up ( unsigned int cpu_id )
2005-04-16 15:20:36 -07:00
{
int timeout ;
cpu_set ( cpu_id , smp_commenced_mask ) ;
/*
* Wait 5 s total for a response
*/
for ( timeout = 0 ; timeout < 5000 ; timeout + + ) {
if ( cpu_isset ( cpu_id , cpu_online_map ) )
break ;
udelay ( 1000 ) ;
}
if ( ! cpu_isset ( cpu_id , cpu_online_map ) )
BUG ( ) ;
return 0 ;
}
void __init smp_cpus_done ( unsigned int max_cpus )
{
int cpu_id , timeout ;
unsigned long bogosum = 0 ;
for ( timeout = 0 ; timeout < 5000 ; timeout + + ) {
if ( cpus_equal ( cpu_callin_map , cpu_online_map ) )
break ;
udelay ( 1000 ) ;
}
if ( ! cpus_equal ( cpu_callin_map , cpu_online_map ) )
BUG ( ) ;
for ( cpu_id = 0 ; cpu_id < num_online_cpus ( ) ; cpu_id + + )
show_cpu_info ( cpu_id ) ;
/*
* Allow the user to impress friends .
*/
Dprintk ( " Before bogomips. \n " ) ;
if ( cpucount ) {
for_each_cpu_mask ( cpu_id , cpu_online_map )
bogosum + = cpu_data [ cpu_id ] . loops_per_jiffy ;
printk ( KERN_INFO " Total of %d processors activated " \
" (%lu.%02lu BogoMIPS). \n " , cpucount + 1 ,
bogosum / ( 500000 / HZ ) ,
( bogosum / ( 5000 / HZ ) ) % 100 ) ;
Dprintk ( " Before bogocount - setting activated=1. \n " ) ;
}
}
/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/
2007-10-20 01:14:39 +02:00
/* Activate a secondary processor Routines */
2005-04-16 15:20:36 -07:00
/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/
/*==========================================================================*
* Name : start_secondary
*
* Description : This routine activate a secondary processor .
*
* Born on Date : 2002.02 .05
*
* Arguments : * unused - currently unused .
*
* Returns : void ( cannot fail )
*
* Modification log :
* Date Who Description
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* 2003 - 06 - 24 hy modify for linux - 2.5 .69
*
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
int __init start_secondary ( void * unused )
{
cpu_init ( ) ;
2005-11-08 21:39:01 -08:00
preempt_disable ( ) ;
2005-04-16 15:20:36 -07:00
smp_callin ( ) ;
while ( ! cpu_isset ( smp_processor_id ( ) , smp_commenced_mask ) )
cpu_relax ( ) ;
smp_online ( ) ;
/*
* low - memory mappings have been cleared , flush them from
* the local TLBs too .
*/
local_flush_tlb_all ( ) ;
cpu_idle ( ) ;
return 0 ;
}
/*==========================================================================*
* Name : smp_callin
*
* Description : This routine activate a secondary processor .
*
* Born on Date : 2002.02 .05
*
* Arguments : NONE
*
* Returns : void ( cannot fail )
*
* Modification log :
* Date Who Description
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* 2003 - 06 - 24 hy modify for linux - 2.5 .69
*
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static void __init smp_callin ( void )
{
int phys_id = hard_smp_processor_id ( ) ;
int cpu_id = smp_processor_id ( ) ;
unsigned long timeout ;
if ( cpu_isset ( cpu_id , cpu_callin_map ) ) {
printk ( " huh, phys CPU#%d, CPU#%d already present?? \n " ,
phys_id , cpu_id ) ;
BUG ( ) ;
}
Dprintk ( " CPU#%d (phys ID: %d) waiting for CALLOUT \n " , cpu_id , phys_id ) ;
/* Waiting 2s total for startup (udelay is not yet working) */
timeout = jiffies + ( 2 * HZ ) ;
while ( time_before ( jiffies , timeout ) ) {
/* Has the boot CPU finished it's STARTUP sequence ? */
if ( cpu_isset ( cpu_id , cpu_callout_map ) )
break ;
cpu_relax ( ) ;
}
if ( ! time_before ( jiffies , timeout ) ) {
printk ( " BUG: CPU#%d started up but did not get a callout! \n " ,
cpu_id ) ;
BUG ( ) ;
}
/* Allow the master to continue. */
cpu_set ( cpu_id , cpu_callin_map ) ;
}
static void __init smp_online ( void )
{
int cpu_id = smp_processor_id ( ) ;
2008-09-07 16:57:22 +02:00
notify_cpu_starting ( cpu_id ) ;
2005-04-16 15:20:36 -07:00
local_irq_enable ( ) ;
/* Get our bogomips. */
calibrate_delay ( ) ;
/* Save our processor parameters */
smp_store_cpu_info ( cpu_id ) ;
cpu_set ( cpu_id , cpu_online_map ) ;
}
/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/
2007-10-20 01:14:39 +02:00
/* Boot up CPUs common Routines */
2005-04-16 15:20:36 -07:00
/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/
static void __init show_mp_info ( int nr_cpu )
{
int i ;
char cpu_model0 [ 17 ] , cpu_model1 [ 17 ] , cpu_ver [ 9 ] ;
strncpy ( cpu_model0 , ( char * ) M32R_FPGA_CPU_NAME_ADDR , 16 ) ;
strncpy ( cpu_model1 , ( char * ) M32R_FPGA_MODEL_ID_ADDR , 16 ) ;
strncpy ( cpu_ver , ( char * ) M32R_FPGA_VERSION_ADDR , 8 ) ;
cpu_model0 [ 16 ] = ' \0 ' ;
for ( i = 15 ; i > = 0 ; i - - ) {
if ( cpu_model0 [ i ] ! = ' ' )
break ;
cpu_model0 [ i ] = ' \0 ' ;
}
cpu_model1 [ 16 ] = ' \0 ' ;
for ( i = 15 ; i > = 0 ; i - - ) {
if ( cpu_model1 [ i ] ! = ' ' )
break ;
cpu_model1 [ i ] = ' \0 ' ;
}
cpu_ver [ 8 ] = ' \0 ' ;
for ( i = 7 ; i > = 0 ; i - - ) {
if ( cpu_ver [ i ] ! = ' ' )
break ;
cpu_ver [ i ] = ' \0 ' ;
}
printk ( KERN_INFO " M32R-mp information \n " ) ;
printk ( KERN_INFO " On-chip CPUs : %d \n " , nr_cpu ) ;
printk ( KERN_INFO " CPU model : %s/%s(%s) \n " , cpu_model0 ,
cpu_model1 , cpu_ver ) ;
}
/*
* The bootstrap kernel entry code has set these up . Save them for
* a given CPU
*/
static void __init smp_store_cpu_info ( int cpu_id )
{
struct cpuinfo_m32r * ci = cpu_data + cpu_id ;
* ci = boot_cpu_data ;
ci - > loops_per_jiffy = loops_per_jiffy ;
}
static void __init show_cpu_info ( int cpu_id )
{
struct cpuinfo_m32r * ci = & cpu_data [ cpu_id ] ;
printk ( " CPU#%d : " , cpu_id ) ;
# define PRINT_CLOCK(name, value) \
printk ( name " clock %d.%02dMHz " , \
( ( value ) / 1000000 ) , ( ( value ) % 1000000 ) / 10000 )
PRINT_CLOCK ( " CPU " , ( int ) ci - > cpu_clock ) ;
PRINT_CLOCK ( " , Bus " , ( int ) ci - > bus_clock ) ;
printk ( " , loops_per_jiffy[%ld] \n " , ci - > loops_per_jiffy ) ;
}
/*
* the frequency of the profiling timer can be changed
* by writing a multiplier value into / proc / profile .
*/
int setup_profiling_timer ( unsigned int multiplier )
{
int i ;
/*
* Sanity check . [ at least 500 APIC cycles should be
* between APIC interrupts as a rule of thumb , to avoid
* irqs flooding us ]
*/
if ( ( ! multiplier ) | | ( calibration_result / multiplier < 500 ) )
return - EINVAL ;
/*
* Set the new multiplier for each CPU . CPUs don ' t start using the
* new values until the next timer interrupt in which they do process
* accounting . At that time they also adjust their APIC timers
* accordingly .
*/
2009-01-01 10:12:14 +10:30
for_each_possible_cpu ( i )
2005-04-16 15:20:36 -07:00
per_cpu ( prof_multiplier , i ) = multiplier ;
return 0 ;
}
/* Initialize all maps between cpu number and apicids */
static void __init init_cpu_to_physid ( void )
{
int i ;
for ( i = 0 ; i < NR_CPUS ; i + + ) {
cpu_2_physid [ i ] = - 1 ;
physid_2_cpu [ i ] = - 1 ;
}
}
/*
* set up a mapping between cpu and apicid . Uses logical apicids for multiquad ,
* else physical apic ids
*/
static void __init map_cpu_to_physid ( int cpu_id , int phys_id )
{
physid_2_cpu [ phys_id ] = cpu_id ;
cpu_2_physid [ cpu_id ] = phys_id ;
}
/*
* undo a mapping between cpu and apicid . Uses logical apicids for multiquad ,
* else physical apic ids
*/
static void __init unmap_cpu_to_physid ( int cpu_id , int phys_id )
{
physid_2_cpu [ phys_id ] = - 1 ;
cpu_2_physid [ cpu_id ] = - 1 ;
}