2005-04-17 02:20:36 +04:00
/*
2005-10-10 16:03:41 +04:00
* SMP support for pSeries machines .
2005-04-17 02:20:36 +04:00
*
* Dave Engebretsen , Peter Bergner , and
* Mike Corrigan { engebret | bergner | mikec } @ us . ibm . com
*
* Plus various changes from other IBM teams . . .
*
* 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 .
*/
# include <linux/kernel.h>
# include <linux/sched.h>
# include <linux/smp.h>
# include <linux/interrupt.h>
# include <linux/delay.h>
# include <linux/init.h>
# include <linux/spinlock.h>
# include <linux/cache.h>
# include <linux/err.h>
2011-12-22 04:26:03 +04:00
# include <linux/device.h>
2005-04-17 02:20:36 +04:00
# include <linux/cpu.h>
# include <asm/ptrace.h>
2011-07-27 03:09:06 +04:00
# include <linux/atomic.h>
2005-04-17 02:20:36 +04:00
# include <asm/irq.h>
# include <asm/page.h>
# include <asm/pgtable.h>
# include <asm/io.h>
# include <asm/prom.h>
# include <asm/smp.h>
# include <asm/paca.h>
# include <asm/machdep.h>
# include <asm/cputable.h>
2005-08-03 08:35:25 +04:00
# include <asm/firmware.h>
2005-04-17 02:20:36 +04:00
# include <asm/rtas.h>
2005-09-27 07:51:59 +04:00
# include <asm/mpic.h>
2005-11-11 15:04:40 +03:00
# include <asm/vdso_datapage.h>
2007-10-25 09:27:44 +04:00
# include <asm/cputhreads.h>
2011-04-04 07:46:58 +04:00
# include <asm/xics.h>
2012-11-14 22:49:50 +04:00
# include <asm/dbell.h>
2013-08-22 13:53:52 +04:00
# include <asm/plpar_wrappers.h>
2014-03-11 04:54:06 +04:00
# include <asm/code-patching.h>
2005-04-17 02:20:36 +04:00
2007-02-08 10:33:55 +03:00
# include "pseries.h"
2009-10-29 22:22:53 +03:00
# include "offline_states.h"
2005-11-03 07:33:31 +03:00
2005-04-17 02:20:36 +04:00
/*
2008-10-20 19:37:03 +04:00
* The Primary thread of each non - boot processor was started from the OF client
* interface by prom_hold_cpus and is spinning on secondary_hold_spinloop .
2005-04-17 02:20:36 +04:00
*/
2010-04-26 19:32:37 +04:00
static cpumask_var_t of_spin_mask ;
2005-04-17 02:20:36 +04:00
2012-11-14 22:49:50 +04:00
/*
* If we multiplex IPI mechanisms , store the appropriate XICS IPI mechanism here
*/
static void ( * xics_cause_ipi ) ( int cpu , unsigned long data ) ;
2010-04-28 17:39:41 +04:00
/* Query where a cpu is now. Return codes #defined in plpar_wrappers.h */
int smp_query_cpu_stopped ( unsigned int pcpu )
{
int cpu_status , status ;
int qcss_tok = rtas_token ( " query-cpu-stopped-state " ) ;
if ( qcss_tok = = RTAS_UNKNOWN_SERVICE ) {
2011-03-21 11:12:13 +03:00
printk_once ( KERN_INFO
" Firmware doesn't support query-cpu-stopped-state \n " ) ;
2010-04-28 17:39:41 +04:00
return QCSS_HARDWARE_ERROR ;
}
status = rtas_call ( qcss_tok , 1 , 2 , & cpu_status , pcpu ) ;
if ( status ! = 0 ) {
printk ( KERN_ERR
" RTAS query-cpu-stopped-state failed: %i \n " , status ) ;
return status ;
}
return cpu_status ;
}
2005-04-17 02:20:36 +04:00
/**
* smp_startup_cpu ( ) - start the given cpu
*
* At boot time , there is nothing to do for primary threads which were
* started from Open Firmware . For anything else , call RTAS with the
* appropriate start location .
*
* Returns :
* 0 - failure
* 1 - success
*/
2012-12-22 02:04:10 +04:00
static inline int smp_startup_cpu ( unsigned int lcpu )
2005-04-17 02:20:36 +04:00
{
int status ;
2014-03-11 04:54:06 +04:00
unsigned long start_here =
__pa ( ppc_function_entry ( generic_secondary_smp_init ) ) ;
2005-04-17 02:20:36 +04:00
unsigned int pcpu ;
2005-09-12 07:17:27 +04:00
int start_cpu ;
2005-04-17 02:20:36 +04:00
2010-04-26 19:32:37 +04:00
if ( cpumask_test_cpu ( lcpu , of_spin_mask ) )
2005-04-17 02:20:36 +04:00
/* Already started by OF and sitting in spin loop */
return 1 ;
pcpu = get_hard_smp_processor_id ( lcpu ) ;
2010-04-28 17:39:41 +04:00
/* Check to see if the CPU out of FW already for kexec */
if ( smp_query_cpu_stopped ( pcpu ) = = QCSS_NOT_STOPPED ) {
2010-04-26 19:32:37 +04:00
cpumask_set_cpu ( lcpu , of_spin_mask ) ;
2010-04-28 17:39:41 +04:00
return 1 ;
}
2005-04-17 02:20:36 +04:00
/* Fixup atomic count: it exited inside IRQ handler. */
2006-01-12 12:06:01 +03:00
task_thread_info ( paca [ lcpu ] . __current ) - > preempt_count = 0 ;
2011-03-31 23:33:08 +04:00
# ifdef CONFIG_HOTPLUG_CPU
2009-10-29 22:22:53 +03:00
if ( get_cpu_current_state ( lcpu ) = = CPU_STATE_INACTIVE )
goto out ;
2011-03-31 23:33:08 +04:00
# endif
2005-09-12 07:17:27 +04:00
/*
* If the RTAS start - cpu token does not exist then presume the
* cpu is already spinning .
*/
start_cpu = rtas_token ( " start-cpu " ) ;
if ( start_cpu = = RTAS_UNKNOWN_SERVICE )
return 1 ;
2006-02-16 06:13:53 +03:00
status = rtas_call ( start_cpu , 3 , 1 , NULL , pcpu , start_here , pcpu ) ;
2005-04-17 02:20:36 +04:00
if ( status ! = 0 ) {
printk ( KERN_ERR " start-cpu failed: %i \n " , status ) ;
return 0 ;
}
2005-09-12 07:17:27 +04:00
2011-03-31 23:33:08 +04:00
# ifdef CONFIG_HOTPLUG_CPU
2009-10-29 22:22:53 +03:00
out :
2011-03-31 23:33:08 +04:00
# endif
2005-04-17 02:20:36 +04:00
return 1 ;
}
2012-12-22 02:04:10 +04:00
static void smp_xics_setup_cpu ( int cpu )
2005-04-17 02:20:36 +04:00
{
if ( cpu ! = boot_cpuid )
xics_setup_cpu ( ) ;
2012-11-14 22:49:50 +04:00
if ( cpu_has_feature ( CPU_FTR_DBELL ) )
doorbell_setup_this_cpu ( ) ;
2005-04-17 02:20:36 +04:00
2005-08-03 08:35:25 +04:00
if ( firmware_has_feature ( FW_FEATURE_SPLPAR ) )
2005-04-17 02:20:36 +04:00
vpa_init ( cpu ) ;
2010-04-26 19:32:37 +04:00
cpumask_clear_cpu ( cpu , of_spin_mask ) ;
2011-03-31 23:33:08 +04:00
# ifdef CONFIG_HOTPLUG_CPU
2009-10-29 22:22:53 +03:00
set_cpu_current_state ( cpu , CPU_STATE_ONLINE ) ;
set_default_offline_state ( cpu ) ;
2011-03-31 23:33:08 +04:00
# endif
2005-04-17 02:20:36 +04:00
}
2012-12-22 02:04:10 +04:00
static int smp_pSeries_kick_cpu ( int nr )
2005-04-17 02:20:36 +04:00
{
BUG_ON ( nr < 0 | | nr > = NR_CPUS ) ;
if ( ! smp_startup_cpu ( nr ) )
2011-04-12 01:46:19 +04:00
return - ENOENT ;
2005-04-17 02:20:36 +04:00
/*
* The processor is currently spinning , waiting for the
* cpu_start field to become non - zero After we set cpu_start ,
* the processor will continue on to secondary_start
*/
paca [ nr ] . cpu_start = 1 ;
2011-03-31 23:33:08 +04:00
# ifdef CONFIG_HOTPLUG_CPU
2009-10-29 22:22:53 +03:00
set_preferred_offline_state ( nr , CPU_STATE_ONLINE ) ;
if ( get_cpu_current_state ( nr ) = = CPU_STATE_INACTIVE ) {
2011-03-31 23:33:08 +04:00
long rc ;
unsigned long hcpuid ;
2009-10-29 22:22:53 +03:00
hcpuid = get_hard_smp_processor_id ( nr ) ;
rc = plpar_hcall_norets ( H_PROD , hcpuid ) ;
if ( rc ! = H_SUCCESS )
2010-01-31 13:02:03 +03:00
printk ( KERN_ERR " Error: Prod to wake up processor %d "
" Ret= %ld \n " , nr , rc ) ;
2009-10-29 22:22:53 +03:00
}
2011-03-31 23:33:08 +04:00
# endif
2011-04-12 01:46:19 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2012-11-14 22:49:50 +04:00
/* Only used on systems that support multiple IPI mechanisms */
static void pSeries_cause_ipi_mux ( int cpu , unsigned long data )
{
if ( cpumask_test_cpu ( cpu , cpu_sibling_mask ( smp_processor_id ( ) ) ) )
doorbell_cause_ipi ( cpu , data ) ;
else
xics_cause_ipi ( cpu , data ) ;
}
static __init int pSeries_smp_probe ( void )
{
int ret = xics_smp_probe ( ) ;
if ( cpu_has_feature ( CPU_FTR_DBELL ) ) {
xics_cause_ipi = smp_ops - > cause_ipi ;
smp_ops - > cause_ipi = pSeries_cause_ipi_mux ;
}
return ret ;
}
2005-04-17 02:20:36 +04:00
static struct smp_ops_t pSeries_mpic_smp_ops = {
. message_pass = smp_mpic_message_pass ,
. probe = smp_mpic_probe ,
. kick_cpu = smp_pSeries_kick_cpu ,
. setup_cpu = smp_mpic_setup_cpu ,
} ;
2011-04-04 07:46:58 +04:00
2005-04-17 02:20:36 +04:00
static struct smp_ops_t pSeries_xics_smp_ops = {
2011-05-26 03:34:12 +04:00
. message_pass = NULL , /* Use smp_muxed_ipi_message_pass */
2012-11-14 22:49:50 +04:00
. cause_ipi = NULL , /* Filled at runtime by pSeries_smp_probe() */
. probe = pSeries_smp_probe ,
2005-04-17 02:20:36 +04:00
. kick_cpu = smp_pSeries_kick_cpu ,
. setup_cpu = smp_xics_setup_cpu ,
2013-08-05 23:58:35 +04:00
. cpu_bootable = smp_generic_cpu_bootable ,
2005-04-17 02:20:36 +04:00
} ;
/* This is called very early */
2006-07-03 15:36:01 +04:00
static void __init smp_init_pseries ( void )
2005-04-17 02:20:36 +04:00
{
int i ;
2008-04-24 09:13:19 +04:00
pr_debug ( " -> smp_init_pSeries() \n " ) ;
2005-04-17 02:20:36 +04:00
2010-04-26 19:32:37 +04:00
alloc_bootmem_cpumask_var ( & of_spin_mask ) ;
2013-09-25 08:02:50 +04:00
/*
* Mark threads which are still spinning in hold loops
*
* We know prom_init will not have started them if RTAS supports
* query - cpu - stopped - state .
*/
if ( rtas_token ( " query-cpu-stopped-state " ) = = RTAS_UNKNOWN_SERVICE ) {
if ( cpu_has_feature ( CPU_FTR_SMT ) ) {
for_each_present_cpu ( i ) {
if ( cpu_thread_in_core ( i ) = = 0 )
cpumask_set_cpu ( i , of_spin_mask ) ;
}
} else
cpumask_copy ( of_spin_mask , cpu_present_mask ) ;
cpumask_clear_cpu ( boot_cpuid , of_spin_mask ) ;
2005-06-20 15:43:15 +04:00
}
2005-04-17 02:20:36 +04:00
/* Non-lpar has additional take/give timebase */
if ( rtas_token ( " freeze-time-base " ) ! = RTAS_UNKNOWN_SERVICE ) {
2009-06-16 20:42:50 +04:00
smp_ops - > give_timebase = rtas_give_timebase ;
smp_ops - > take_timebase = rtas_take_timebase ;
2005-04-17 02:20:36 +04:00
}
2008-04-24 09:13:19 +04:00
pr_debug ( " <- smp_init_pSeries() \n " ) ;
2005-04-17 02:20:36 +04:00
}
2006-07-03 15:36:01 +04:00
void __init smp_init_pseries_mpic ( void )
{
smp_ops = & pSeries_mpic_smp_ops ;
smp_init_pseries ( ) ;
}
void __init smp_init_pseries_xics ( void )
{
smp_ops = & pSeries_xics_smp_ops ;
smp_init_pseries ( ) ;
}