2014-03-21 14:44:08 +04:00
/*
* Copyright ( C ) 2010 , 2011 , 2012 , Lemote , Inc .
* Author : Chen Huacai , chenhc @ lemote . com
*
* 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 . See the
* GNU General Public License for more details .
*
*/
# include <linux/init.h>
# include <linux/cpu.h>
# include <linux/sched.h>
2017-02-08 20:51:36 +03:00
# include <linux/sched/hotplug.h>
2017-02-08 20:51:37 +03:00
# include <linux/sched/task_stack.h>
2014-03-21 14:44:08 +04:00
# include <linux/smp.h>
# include <linux/cpufreq.h>
2018-09-12 00:49:21 +03:00
# include <linux/kexec.h>
2014-03-21 14:44:08 +04:00
# include <asm/processor.h>
# include <asm/time.h>
# include <asm/clock.h>
# include <asm/tlbflush.h>
2014-03-21 14:44:09 +04:00
# include <asm/cacheflush.h>
2014-03-21 14:44:08 +04:00
# include <loongson.h>
2014-11-04 09:13:27 +03:00
# include <workarounds.h>
2014-03-21 14:44:08 +04:00
# include "smp.h"
2014-03-21 14:44:09 +04:00
DEFINE_PER_CPU ( int , cpu_state ) ;
2014-06-26 07:41:30 +04:00
static void * ipi_set0_regs [ 16 ] ;
static void * ipi_clear0_regs [ 16 ] ;
static void * ipi_status0_regs [ 16 ] ;
static void * ipi_en0_regs [ 16 ] ;
static void * ipi_mailbox_buf [ 16 ] ;
2016-01-21 16:09:49 +03:00
static uint32_t core0_c0count [ NR_CPUS ] ;
2014-06-26 07:41:30 +04:00
2014-03-21 14:44:08 +04:00
/* read a 32bit value from ipi register */
# define loongson3_ipi_read32(addr) readl(addr)
/* read a 64bit value from ipi register */
# define loongson3_ipi_read64(addr) readq(addr)
/* write a 32bit value to ipi register */
# define loongson3_ipi_write32(action, addr) \
do { \
writel ( action , addr ) ; \
__wbflush ( ) ; \
} while ( 0 )
/* write a 64bit value to ipi register */
# define loongson3_ipi_write64(action, addr) \
do { \
writeq ( action , addr ) ; \
__wbflush ( ) ; \
} while ( 0 )
2014-06-26 07:41:30 +04:00
static void ipi_set0_regs_init ( void )
{
ipi_set0_regs [ 0 ] = ( void * )
( SMP_CORE_GROUP0_BASE + SMP_CORE0_OFFSET + SET0 ) ;
ipi_set0_regs [ 1 ] = ( void * )
( SMP_CORE_GROUP0_BASE + SMP_CORE1_OFFSET + SET0 ) ;
ipi_set0_regs [ 2 ] = ( void * )
( SMP_CORE_GROUP0_BASE + SMP_CORE2_OFFSET + SET0 ) ;
ipi_set0_regs [ 3 ] = ( void * )
( SMP_CORE_GROUP0_BASE + SMP_CORE3_OFFSET + SET0 ) ;
ipi_set0_regs [ 4 ] = ( void * )
( SMP_CORE_GROUP1_BASE + SMP_CORE0_OFFSET + SET0 ) ;
ipi_set0_regs [ 5 ] = ( void * )
( SMP_CORE_GROUP1_BASE + SMP_CORE1_OFFSET + SET0 ) ;
ipi_set0_regs [ 6 ] = ( void * )
( SMP_CORE_GROUP1_BASE + SMP_CORE2_OFFSET + SET0 ) ;
ipi_set0_regs [ 7 ] = ( void * )
( SMP_CORE_GROUP1_BASE + SMP_CORE3_OFFSET + SET0 ) ;
ipi_set0_regs [ 8 ] = ( void * )
( SMP_CORE_GROUP2_BASE + SMP_CORE0_OFFSET + SET0 ) ;
ipi_set0_regs [ 9 ] = ( void * )
( SMP_CORE_GROUP2_BASE + SMP_CORE1_OFFSET + SET0 ) ;
ipi_set0_regs [ 10 ] = ( void * )
( SMP_CORE_GROUP2_BASE + SMP_CORE2_OFFSET + SET0 ) ;
ipi_set0_regs [ 11 ] = ( void * )
( SMP_CORE_GROUP2_BASE + SMP_CORE3_OFFSET + SET0 ) ;
ipi_set0_regs [ 12 ] = ( void * )
( SMP_CORE_GROUP3_BASE + SMP_CORE0_OFFSET + SET0 ) ;
ipi_set0_regs [ 13 ] = ( void * )
( SMP_CORE_GROUP3_BASE + SMP_CORE1_OFFSET + SET0 ) ;
ipi_set0_regs [ 14 ] = ( void * )
( SMP_CORE_GROUP3_BASE + SMP_CORE2_OFFSET + SET0 ) ;
ipi_set0_regs [ 15 ] = ( void * )
( SMP_CORE_GROUP3_BASE + SMP_CORE3_OFFSET + SET0 ) ;
}
2014-03-21 14:44:08 +04:00
2014-06-26 07:41:30 +04:00
static void ipi_clear0_regs_init ( void )
{
ipi_clear0_regs [ 0 ] = ( void * )
( SMP_CORE_GROUP0_BASE + SMP_CORE0_OFFSET + CLEAR0 ) ;
ipi_clear0_regs [ 1 ] = ( void * )
( SMP_CORE_GROUP0_BASE + SMP_CORE1_OFFSET + CLEAR0 ) ;
ipi_clear0_regs [ 2 ] = ( void * )
( SMP_CORE_GROUP0_BASE + SMP_CORE2_OFFSET + CLEAR0 ) ;
ipi_clear0_regs [ 3 ] = ( void * )
( SMP_CORE_GROUP0_BASE + SMP_CORE3_OFFSET + CLEAR0 ) ;
ipi_clear0_regs [ 4 ] = ( void * )
( SMP_CORE_GROUP1_BASE + SMP_CORE0_OFFSET + CLEAR0 ) ;
ipi_clear0_regs [ 5 ] = ( void * )
( SMP_CORE_GROUP1_BASE + SMP_CORE1_OFFSET + CLEAR0 ) ;
ipi_clear0_regs [ 6 ] = ( void * )
( SMP_CORE_GROUP1_BASE + SMP_CORE2_OFFSET + CLEAR0 ) ;
ipi_clear0_regs [ 7 ] = ( void * )
( SMP_CORE_GROUP1_BASE + SMP_CORE3_OFFSET + CLEAR0 ) ;
ipi_clear0_regs [ 8 ] = ( void * )
( SMP_CORE_GROUP2_BASE + SMP_CORE0_OFFSET + CLEAR0 ) ;
ipi_clear0_regs [ 9 ] = ( void * )
( SMP_CORE_GROUP2_BASE + SMP_CORE1_OFFSET + CLEAR0 ) ;
ipi_clear0_regs [ 10 ] = ( void * )
( SMP_CORE_GROUP2_BASE + SMP_CORE2_OFFSET + CLEAR0 ) ;
ipi_clear0_regs [ 11 ] = ( void * )
( SMP_CORE_GROUP2_BASE + SMP_CORE3_OFFSET + CLEAR0 ) ;
ipi_clear0_regs [ 12 ] = ( void * )
( SMP_CORE_GROUP3_BASE + SMP_CORE0_OFFSET + CLEAR0 ) ;
ipi_clear0_regs [ 13 ] = ( void * )
( SMP_CORE_GROUP3_BASE + SMP_CORE1_OFFSET + CLEAR0 ) ;
ipi_clear0_regs [ 14 ] = ( void * )
( SMP_CORE_GROUP3_BASE + SMP_CORE2_OFFSET + CLEAR0 ) ;
ipi_clear0_regs [ 15 ] = ( void * )
( SMP_CORE_GROUP3_BASE + SMP_CORE3_OFFSET + CLEAR0 ) ;
}
2014-03-21 14:44:08 +04:00
2014-06-26 07:41:30 +04:00
static void ipi_status0_regs_init ( void )
{
ipi_status0_regs [ 0 ] = ( void * )
( SMP_CORE_GROUP0_BASE + SMP_CORE0_OFFSET + STATUS0 ) ;
ipi_status0_regs [ 1 ] = ( void * )
( SMP_CORE_GROUP0_BASE + SMP_CORE1_OFFSET + STATUS0 ) ;
ipi_status0_regs [ 2 ] = ( void * )
( SMP_CORE_GROUP0_BASE + SMP_CORE2_OFFSET + STATUS0 ) ;
ipi_status0_regs [ 3 ] = ( void * )
( SMP_CORE_GROUP0_BASE + SMP_CORE3_OFFSET + STATUS0 ) ;
ipi_status0_regs [ 4 ] = ( void * )
( SMP_CORE_GROUP1_BASE + SMP_CORE0_OFFSET + STATUS0 ) ;
ipi_status0_regs [ 5 ] = ( void * )
( SMP_CORE_GROUP1_BASE + SMP_CORE1_OFFSET + STATUS0 ) ;
ipi_status0_regs [ 6 ] = ( void * )
( SMP_CORE_GROUP1_BASE + SMP_CORE2_OFFSET + STATUS0 ) ;
ipi_status0_regs [ 7 ] = ( void * )
( SMP_CORE_GROUP1_BASE + SMP_CORE3_OFFSET + STATUS0 ) ;
ipi_status0_regs [ 8 ] = ( void * )
( SMP_CORE_GROUP2_BASE + SMP_CORE0_OFFSET + STATUS0 ) ;
ipi_status0_regs [ 9 ] = ( void * )
( SMP_CORE_GROUP2_BASE + SMP_CORE1_OFFSET + STATUS0 ) ;
ipi_status0_regs [ 10 ] = ( void * )
( SMP_CORE_GROUP2_BASE + SMP_CORE2_OFFSET + STATUS0 ) ;
ipi_status0_regs [ 11 ] = ( void * )
( SMP_CORE_GROUP2_BASE + SMP_CORE3_OFFSET + STATUS0 ) ;
ipi_status0_regs [ 12 ] = ( void * )
( SMP_CORE_GROUP3_BASE + SMP_CORE0_OFFSET + STATUS0 ) ;
ipi_status0_regs [ 13 ] = ( void * )
( SMP_CORE_GROUP3_BASE + SMP_CORE1_OFFSET + STATUS0 ) ;
ipi_status0_regs [ 14 ] = ( void * )
( SMP_CORE_GROUP3_BASE + SMP_CORE2_OFFSET + STATUS0 ) ;
ipi_status0_regs [ 15 ] = ( void * )
( SMP_CORE_GROUP3_BASE + SMP_CORE3_OFFSET + STATUS0 ) ;
}
2014-03-21 14:44:08 +04:00
2014-06-26 07:41:30 +04:00
static void ipi_en0_regs_init ( void )
{
ipi_en0_regs [ 0 ] = ( void * )
( SMP_CORE_GROUP0_BASE + SMP_CORE0_OFFSET + EN0 ) ;
ipi_en0_regs [ 1 ] = ( void * )
( SMP_CORE_GROUP0_BASE + SMP_CORE1_OFFSET + EN0 ) ;
ipi_en0_regs [ 2 ] = ( void * )
( SMP_CORE_GROUP0_BASE + SMP_CORE2_OFFSET + EN0 ) ;
ipi_en0_regs [ 3 ] = ( void * )
( SMP_CORE_GROUP0_BASE + SMP_CORE3_OFFSET + EN0 ) ;
ipi_en0_regs [ 4 ] = ( void * )
( SMP_CORE_GROUP1_BASE + SMP_CORE0_OFFSET + EN0 ) ;
ipi_en0_regs [ 5 ] = ( void * )
( SMP_CORE_GROUP1_BASE + SMP_CORE1_OFFSET + EN0 ) ;
ipi_en0_regs [ 6 ] = ( void * )
( SMP_CORE_GROUP1_BASE + SMP_CORE2_OFFSET + EN0 ) ;
ipi_en0_regs [ 7 ] = ( void * )
( SMP_CORE_GROUP1_BASE + SMP_CORE3_OFFSET + EN0 ) ;
ipi_en0_regs [ 8 ] = ( void * )
( SMP_CORE_GROUP2_BASE + SMP_CORE0_OFFSET + EN0 ) ;
ipi_en0_regs [ 9 ] = ( void * )
( SMP_CORE_GROUP2_BASE + SMP_CORE1_OFFSET + EN0 ) ;
ipi_en0_regs [ 10 ] = ( void * )
( SMP_CORE_GROUP2_BASE + SMP_CORE2_OFFSET + EN0 ) ;
ipi_en0_regs [ 11 ] = ( void * )
( SMP_CORE_GROUP2_BASE + SMP_CORE3_OFFSET + EN0 ) ;
ipi_en0_regs [ 12 ] = ( void * )
( SMP_CORE_GROUP3_BASE + SMP_CORE0_OFFSET + EN0 ) ;
ipi_en0_regs [ 13 ] = ( void * )
( SMP_CORE_GROUP3_BASE + SMP_CORE1_OFFSET + EN0 ) ;
ipi_en0_regs [ 14 ] = ( void * )
( SMP_CORE_GROUP3_BASE + SMP_CORE2_OFFSET + EN0 ) ;
ipi_en0_regs [ 15 ] = ( void * )
( SMP_CORE_GROUP3_BASE + SMP_CORE3_OFFSET + EN0 ) ;
}
2014-03-21 14:44:08 +04:00
2014-06-26 07:41:30 +04:00
static void ipi_mailbox_buf_init ( void )
{
ipi_mailbox_buf [ 0 ] = ( void * )
( SMP_CORE_GROUP0_BASE + SMP_CORE0_OFFSET + BUF ) ;
ipi_mailbox_buf [ 1 ] = ( void * )
( SMP_CORE_GROUP0_BASE + SMP_CORE1_OFFSET + BUF ) ;
ipi_mailbox_buf [ 2 ] = ( void * )
( SMP_CORE_GROUP0_BASE + SMP_CORE2_OFFSET + BUF ) ;
ipi_mailbox_buf [ 3 ] = ( void * )
( SMP_CORE_GROUP0_BASE + SMP_CORE3_OFFSET + BUF ) ;
ipi_mailbox_buf [ 4 ] = ( void * )
( SMP_CORE_GROUP1_BASE + SMP_CORE0_OFFSET + BUF ) ;
ipi_mailbox_buf [ 5 ] = ( void * )
( SMP_CORE_GROUP1_BASE + SMP_CORE1_OFFSET + BUF ) ;
ipi_mailbox_buf [ 6 ] = ( void * )
( SMP_CORE_GROUP1_BASE + SMP_CORE2_OFFSET + BUF ) ;
ipi_mailbox_buf [ 7 ] = ( void * )
( SMP_CORE_GROUP1_BASE + SMP_CORE3_OFFSET + BUF ) ;
ipi_mailbox_buf [ 8 ] = ( void * )
( SMP_CORE_GROUP2_BASE + SMP_CORE0_OFFSET + BUF ) ;
ipi_mailbox_buf [ 9 ] = ( void * )
( SMP_CORE_GROUP2_BASE + SMP_CORE1_OFFSET + BUF ) ;
ipi_mailbox_buf [ 10 ] = ( void * )
( SMP_CORE_GROUP2_BASE + SMP_CORE2_OFFSET + BUF ) ;
ipi_mailbox_buf [ 11 ] = ( void * )
( SMP_CORE_GROUP2_BASE + SMP_CORE3_OFFSET + BUF ) ;
ipi_mailbox_buf [ 12 ] = ( void * )
( SMP_CORE_GROUP3_BASE + SMP_CORE0_OFFSET + BUF ) ;
ipi_mailbox_buf [ 13 ] = ( void * )
( SMP_CORE_GROUP3_BASE + SMP_CORE1_OFFSET + BUF ) ;
ipi_mailbox_buf [ 14 ] = ( void * )
( SMP_CORE_GROUP3_BASE + SMP_CORE2_OFFSET + BUF ) ;
ipi_mailbox_buf [ 15 ] = ( void * )
( SMP_CORE_GROUP3_BASE + SMP_CORE3_OFFSET + BUF ) ;
}
2014-03-21 14:44:08 +04:00
/*
* Simple enough , just poke the appropriate ipi register
*/
static void loongson3_send_ipi_single ( int cpu , unsigned int action )
{
2014-11-04 09:13:26 +03:00
loongson3_ipi_write32 ( ( u32 ) action , ipi_set0_regs [ cpu_logical_map ( cpu ) ] ) ;
2014-03-21 14:44:08 +04:00
}
static void
loongson3_send_ipi_mask ( const struct cpumask * mask , unsigned int action )
{
unsigned int i ;
for_each_cpu ( i , mask )
2014-11-04 09:13:26 +03:00
loongson3_ipi_write32 ( ( u32 ) action , ipi_set0_regs [ cpu_logical_map ( i ) ] ) ;
2014-03-21 14:44:08 +04:00
}
2017-06-22 18:06:52 +03:00
# define IPI_IRQ_OFFSET 6
void loongson3_send_irq_by_ipi ( int cpu , int irqs )
{
loongson3_ipi_write32 ( irqs < < IPI_IRQ_OFFSET , ipi_set0_regs [ cpu_logical_map ( cpu ) ] ) ;
}
2014-03-21 14:44:08 +04:00
void loongson3_ipi_interrupt ( struct pt_regs * regs )
{
2014-03-21 14:44:09 +04:00
int i , cpu = smp_processor_id ( ) ;
2017-06-22 18:06:52 +03:00
unsigned int action , c0count , irqs ;
2014-03-21 14:44:08 +04:00
/* Load the ipi register to figure out what we're supposed to do */
2014-11-04 09:13:26 +03:00
action = loongson3_ipi_read32 ( ipi_status0_regs [ cpu_logical_map ( cpu ) ] ) ;
2017-06-22 18:06:52 +03:00
irqs = action > > IPI_IRQ_OFFSET ;
2014-03-21 14:44:08 +04:00
/* Clear the ipi register to clear the interrupt */
2014-11-04 09:13:26 +03:00
loongson3_ipi_write32 ( ( u32 ) action , ipi_clear0_regs [ cpu_logical_map ( cpu ) ] ) ;
2014-03-21 14:44:08 +04:00
if ( action & SMP_RESCHEDULE_YOURSELF )
scheduler_ipi ( ) ;
2015-07-24 18:57:49 +03:00
if ( action & SMP_CALL_FUNCTION ) {
irq_enter ( ) ;
generic_smp_call_function_interrupt ( ) ;
irq_exit ( ) ;
}
2014-03-21 14:44:09 +04:00
if ( action & SMP_ASK_C0COUNT ) {
BUG_ON ( cpu ! = 0 ) ;
c0count = read_c0_count ( ) ;
2016-01-21 16:09:49 +03:00
c0count = c0count ? c0count : 1 ;
for ( i = 1 ; i < nr_cpu_ids ; i + + )
core0_c0count [ i ] = c0count ;
__wbflush ( ) ; /* Let others see the result ASAP */
2014-03-21 14:44:09 +04:00
}
2017-06-22 18:06:52 +03:00
if ( irqs ) {
int irq ;
while ( ( irq = ffs ( irqs ) ) ) {
do_IRQ ( irq - 1 ) ;
irqs & = ~ ( 1 < < ( irq - 1 ) ) ;
}
}
2014-03-21 14:44:08 +04:00
}
2016-01-21 16:09:49 +03:00
# define MAX_LOOPS 800
2014-03-21 14:44:08 +04:00
/*
* SMP init and finish on secondary CPUs
*/
static void loongson3_init_secondary ( void )
{
int i ;
2014-03-21 14:44:09 +04:00
uint32_t initcount ;
unsigned int cpu = smp_processor_id ( ) ;
2014-03-21 14:44:08 +04:00
unsigned int imask = STATUSF_IP7 | STATUSF_IP6 |
STATUSF_IP3 | STATUSF_IP2 ;
/* Set interrupt mask, but don't enable */
change_c0_status ( ST0_IM , imask ) ;
2014-11-04 09:13:26 +03:00
for ( i = 0 ; i < num_possible_cpus ( ) ; i + + )
loongson3_ipi_write32 ( 0xffffffff , ipi_en0_regs [ cpu_logical_map ( i ) ] ) ;
2014-03-21 14:44:09 +04:00
per_cpu ( cpu_state , cpu ) = CPU_ONLINE ;
2017-08-13 05:49:35 +03:00
cpu_set_core ( & cpu_data [ cpu ] ,
cpu_logical_map ( cpu ) % loongson_sysconf . cores_per_package ) ;
2014-11-04 09:13:26 +03:00
cpu_data [ cpu ] . package =
cpu_logical_map ( cpu ) / loongson_sysconf . cores_per_package ;
2014-03-21 14:44:09 +04:00
i = 0 ;
2016-01-21 16:09:49 +03:00
core0_c0count [ cpu ] = 0 ;
2014-03-21 14:44:09 +04:00
loongson3_send_ipi_single ( 0 , SMP_ASK_C0COUNT ) ;
2016-01-21 16:09:49 +03:00
while ( ! core0_c0count [ cpu ] ) {
2014-03-21 14:44:09 +04:00
i + + ;
cpu_relax ( ) ;
}
if ( i > MAX_LOOPS )
i = MAX_LOOPS ;
2016-01-21 16:09:49 +03:00
if ( cpu_data [ cpu ] . package )
initcount = core0_c0count [ cpu ] + i ;
else /* Local access is faster for loops */
initcount = core0_c0count [ cpu ] + i / 2 ;
2014-03-21 14:44:09 +04:00
write_c0_count ( initcount ) ;
2014-03-21 14:44:08 +04:00
}
static void loongson3_smp_finish ( void )
{
2014-11-04 09:13:26 +03:00
int cpu = smp_processor_id ( ) ;
2014-03-21 14:44:08 +04:00
write_c0_compare ( read_c0_count ( ) + mips_hpt_frequency / HZ ) ;
local_irq_enable ( ) ;
loongson3_ipi_write64 ( 0 ,
2018-08-31 21:49:20 +03:00
ipi_mailbox_buf [ cpu_logical_map ( cpu ) ] + 0x0 ) ;
2014-03-21 14:44:08 +04:00
pr_info ( " CPU#%d finished, CP0_ST=%x \n " ,
smp_processor_id ( ) , read_c0_status ( ) ) ;
}
static void __init loongson3_smp_setup ( void )
{
2014-11-04 09:13:26 +03:00
int i = 0 , num = 0 ; /* i: physical id, num: logical id */
2014-03-21 14:44:08 +04:00
init_cpu_possible ( cpu_none_mask ) ;
/* For unified kernel, NR_CPUS is the maximum possible value,
* loongson_sysconf . nr_cpus is the really present value */
2014-11-04 09:13:26 +03:00
while ( i < loongson_sysconf . nr_cpus ) {
if ( loongson_sysconf . reserved_cpus_mask & ( 1 < < i ) ) {
/* Reserved physical CPU cores */
__cpu_number_map [ i ] = - 1 ;
} else {
__cpu_number_map [ i ] = num ;
__cpu_logical_map [ num ] = i ;
set_cpu_possible ( num , true ) ;
num + + ;
}
i + + ;
2014-03-21 14:44:08 +04:00
}
2014-11-04 09:13:26 +03:00
pr_info ( " Detected %i available CPU(s) \n " , num ) ;
while ( num < loongson_sysconf . nr_cpus ) {
__cpu_logical_map [ num ] = - 1 ;
num + + ;
}
2014-06-26 07:41:30 +04:00
ipi_set0_regs_init ( ) ;
ipi_clear0_regs_init ( ) ;
ipi_status0_regs_init ( ) ;
ipi_en0_regs_init ( ) ;
ipi_mailbox_buf_init ( ) ;
2017-08-13 05:49:35 +03:00
cpu_set_core ( & cpu_data [ 0 ] ,
cpu_logical_map ( 0 ) % loongson_sysconf . cores_per_package ) ;
2014-11-04 09:13:26 +03:00
cpu_data [ 0 ] . package = cpu_logical_map ( 0 ) / loongson_sysconf . cores_per_package ;
2014-03-21 14:44:08 +04:00
}
static void __init loongson3_prepare_cpus ( unsigned int max_cpus )
{
2014-03-21 14:44:09 +04:00
init_cpu_present ( cpu_possible_mask ) ;
per_cpu ( cpu_state , smp_processor_id ( ) ) = CPU_ONLINE ;
2014-03-21 14:44:08 +04:00
}
/*
* Setup the PC , SP , and GP of a secondary processor and start it runing !
*/
2017-08-13 05:49:40 +03:00
static int loongson3_boot_secondary ( int cpu , struct task_struct * idle )
2014-03-21 14:44:08 +04:00
{
unsigned long startargs [ 4 ] ;
pr_info ( " Booting CPU#%d... \n " , cpu ) ;
/* startargs[] are initial PC, SP and GP for secondary CPU */
startargs [ 0 ] = ( unsigned long ) & smp_bootstrap ;
startargs [ 1 ] = ( unsigned long ) __KSTK_TOS ( idle ) ;
startargs [ 2 ] = ( unsigned long ) task_thread_info ( idle ) ;
startargs [ 3 ] = 0 ;
pr_debug ( " CPU#%d, func_pc=%lx, sp=%lx, gp=%lx \n " ,
cpu , startargs [ 0 ] , startargs [ 1 ] , startargs [ 2 ] ) ;
2014-11-04 09:13:26 +03:00
loongson3_ipi_write64 ( startargs [ 3 ] ,
2018-08-31 21:49:20 +03:00
ipi_mailbox_buf [ cpu_logical_map ( cpu ) ] + 0x18 ) ;
2014-11-04 09:13:26 +03:00
loongson3_ipi_write64 ( startargs [ 2 ] ,
2018-08-31 21:49:20 +03:00
ipi_mailbox_buf [ cpu_logical_map ( cpu ) ] + 0x10 ) ;
2014-11-04 09:13:26 +03:00
loongson3_ipi_write64 ( startargs [ 1 ] ,
2018-08-31 21:49:20 +03:00
ipi_mailbox_buf [ cpu_logical_map ( cpu ) ] + 0x8 ) ;
2014-11-04 09:13:26 +03:00
loongson3_ipi_write64 ( startargs [ 0 ] ,
2018-08-31 21:49:20 +03:00
ipi_mailbox_buf [ cpu_logical_map ( cpu ) ] + 0x0 ) ;
2017-08-13 05:49:40 +03:00
return 0 ;
2014-03-21 14:44:08 +04:00
}
2014-03-21 14:44:09 +04:00
# ifdef CONFIG_HOTPLUG_CPU
static int loongson3_cpu_disable ( void )
{
unsigned long flags ;
unsigned int cpu = smp_processor_id ( ) ;
if ( cpu = = 0 )
return - EBUSY ;
set_cpu_online ( cpu , false ) ;
2016-07-13 16:12:45 +03:00
calculate_cpu_foreign_map ( ) ;
2014-03-21 14:44:09 +04:00
local_irq_save ( flags ) ;
fixup_irqs ( ) ;
local_irq_restore ( flags ) ;
local_flush_tlb_all ( ) ;
return 0 ;
}
static void loongson3_cpu_die ( unsigned int cpu )
{
while ( per_cpu ( cpu_state , cpu ) ! = CPU_DEAD )
cpu_relax ( ) ;
mb ( ) ;
}
/* To shutdown a core in Loongson 3, the target core should go to CKSEG1 and
* flush all L1 entries at first . Then , another core ( usually Core 0 ) can
* safely disable the clock of the target core . loongson3_play_dead ( ) is
* called via CKSEG1 ( uncached and unmmaped ) */
2016-03-03 04:45:09 +03:00
static void loongson3a_r1_play_dead ( int * state_addr )
2014-03-21 14:44:09 +04:00
{
register int val ;
register long cpuid , core , node , count ;
register void * addr , * base , * initfunc ;
__asm__ __volatile__ (
" .set push \n "
" .set noreorder \n "
" li %[addr], 0x80000000 \n " /* KSEG0 */
" 1: cache 0, 0(%[addr]) \n " /* flush L1 ICache */
" cache 0, 1(%[addr]) \n "
" cache 0, 2(%[addr]) \n "
" cache 0, 3(%[addr]) \n "
" cache 1, 0(%[addr]) \n " /* flush L1 DCache */
" cache 1, 1(%[addr]) \n "
" cache 1, 2(%[addr]) \n "
" cache 1, 3(%[addr]) \n "
" addiu %[sets], %[sets], -1 \n "
" bnez %[sets], 1b \n "
" addiu %[addr], %[addr], 0x20 \n "
" li %[val], 0x7 \n " /* *state_addr = CPU_DEAD; */
" sw %[val], (%[state_addr]) \n "
" sync \n "
" cache 21, (%[state_addr]) \n " /* flush entry of *state_addr */
" .set pop \n "
: [ addr ] " =&r " ( addr ) , [ val ] " =&r " ( val )
: [ state_addr ] " r " ( state_addr ) ,
[ sets ] " r " ( cpu_data [ smp_processor_id ( ) ] . dcache . sets ) ) ;
__asm__ __volatile__ (
" .set push \n "
" .set noreorder \n "
" .set mips64 \n "
" mfc0 %[cpuid], $15, 1 \n "
" andi %[cpuid], 0x3ff \n "
" dli %[base], 0x900000003ff01000 \n "
" andi %[core], %[cpuid], 0x3 \n "
" sll %[core], 8 \n " /* get core id */
" or %[base], %[base], %[core] \n "
" andi %[node], %[cpuid], 0xc \n "
" dsll %[node], 42 \n " /* get node id */
" or %[base], %[base], %[node] \n "
" 1: li %[count], 0x100 \n " /* wait for init loop */
" 2: bnez %[count], 2b \n " /* limit mailbox access */
" addiu %[count], -1 \n "
" ld %[initfunc], 0x20(%[base]) \n " /* get PC via mailbox */
" beqz %[initfunc], 1b \n "
" nop \n "
" ld $sp, 0x28(%[base]) \n " /* get SP via mailbox */
" ld $gp, 0x30(%[base]) \n " /* get GP via mailbox */
" ld $a1, 0x38(%[base]) \n "
" jr %[initfunc] \n " /* jump to initial PC */
" nop \n "
" .set pop \n "
: [ core ] " =&r " ( core ) , [ node ] " =&r " ( node ) ,
[ base ] " =&r " ( base ) , [ cpuid ] " =&r " ( cpuid ) ,
[ count ] " =&r " ( count ) , [ initfunc ] " =&r " ( initfunc )
: /* No Input */
: " a1 " ) ;
}
2017-06-22 18:06:48 +03:00
static void loongson3a_r2r3_play_dead ( int * state_addr )
2016-03-03 04:45:09 +03:00
{
register int val ;
register long cpuid , core , node , count ;
register void * addr , * base , * initfunc ;
__asm__ __volatile__ (
" .set push \n "
" .set noreorder \n "
" li %[addr], 0x80000000 \n " /* KSEG0 */
" 1: cache 0, 0(%[addr]) \n " /* flush L1 ICache */
" cache 0, 1(%[addr]) \n "
" cache 0, 2(%[addr]) \n "
" cache 0, 3(%[addr]) \n "
" cache 1, 0(%[addr]) \n " /* flush L1 DCache */
" cache 1, 1(%[addr]) \n "
" cache 1, 2(%[addr]) \n "
" cache 1, 3(%[addr]) \n "
" addiu %[sets], %[sets], -1 \n "
" bnez %[sets], 1b \n "
" addiu %[addr], %[addr], 0x40 \n "
" li %[addr], 0x80000000 \n " /* KSEG0 */
" 2: cache 2, 0(%[addr]) \n " /* flush L1 VCache */
" cache 2, 1(%[addr]) \n "
" cache 2, 2(%[addr]) \n "
" cache 2, 3(%[addr]) \n "
" cache 2, 4(%[addr]) \n "
" cache 2, 5(%[addr]) \n "
" cache 2, 6(%[addr]) \n "
" cache 2, 7(%[addr]) \n "
" cache 2, 8(%[addr]) \n "
" cache 2, 9(%[addr]) \n "
" cache 2, 10(%[addr]) \n "
" cache 2, 11(%[addr]) \n "
" cache 2, 12(%[addr]) \n "
" cache 2, 13(%[addr]) \n "
" cache 2, 14(%[addr]) \n "
" cache 2, 15(%[addr]) \n "
" addiu %[vsets], %[vsets], -1 \n "
" bnez %[vsets], 2b \n "
" addiu %[addr], %[addr], 0x40 \n "
" li %[val], 0x7 \n " /* *state_addr = CPU_DEAD; */
" sw %[val], (%[state_addr]) \n "
" sync \n "
" cache 21, (%[state_addr]) \n " /* flush entry of *state_addr */
" .set pop \n "
: [ addr ] " =&r " ( addr ) , [ val ] " =&r " ( val )
: [ state_addr ] " r " ( state_addr ) ,
[ sets ] " r " ( cpu_data [ smp_processor_id ( ) ] . dcache . sets ) ,
[ vsets ] " r " ( cpu_data [ smp_processor_id ( ) ] . vcache . sets ) ) ;
__asm__ __volatile__ (
" .set push \n "
" .set noreorder \n "
" .set mips64 \n "
" mfc0 %[cpuid], $15, 1 \n "
" andi %[cpuid], 0x3ff \n "
" dli %[base], 0x900000003ff01000 \n "
" andi %[core], %[cpuid], 0x3 \n "
" sll %[core], 8 \n " /* get core id */
" or %[base], %[base], %[core] \n "
" andi %[node], %[cpuid], 0xc \n "
" dsll %[node], 42 \n " /* get node id */
" or %[base], %[base], %[node] \n "
" 1: li %[count], 0x100 \n " /* wait for init loop */
" 2: bnez %[count], 2b \n " /* limit mailbox access */
" addiu %[count], -1 \n "
" ld %[initfunc], 0x20(%[base]) \n " /* get PC via mailbox */
" beqz %[initfunc], 1b \n "
" nop \n "
" ld $sp, 0x28(%[base]) \n " /* get SP via mailbox */
" ld $gp, 0x30(%[base]) \n " /* get GP via mailbox */
" ld $a1, 0x38(%[base]) \n "
" jr %[initfunc] \n " /* jump to initial PC */
" nop \n "
" .set pop \n "
: [ core ] " =&r " ( core ) , [ node ] " =&r " ( node ) ,
[ base ] " =&r " ( base ) , [ cpuid ] " =&r " ( cpuid ) ,
[ count ] " =&r " ( count ) , [ initfunc ] " =&r " ( initfunc )
: /* No Input */
: " a1 " ) ;
}
2014-06-26 07:41:30 +04:00
static void loongson3b_play_dead ( int * state_addr )
{
register int val ;
register long cpuid , core , node , count ;
register void * addr , * base , * initfunc ;
__asm__ __volatile__ (
" .set push \n "
" .set noreorder \n "
" li %[addr], 0x80000000 \n " /* KSEG0 */
" 1: cache 0, 0(%[addr]) \n " /* flush L1 ICache */
" cache 0, 1(%[addr]) \n "
" cache 0, 2(%[addr]) \n "
" cache 0, 3(%[addr]) \n "
" cache 1, 0(%[addr]) \n " /* flush L1 DCache */
" cache 1, 1(%[addr]) \n "
" cache 1, 2(%[addr]) \n "
" cache 1, 3(%[addr]) \n "
" addiu %[sets], %[sets], -1 \n "
" bnez %[sets], 1b \n "
" addiu %[addr], %[addr], 0x20 \n "
" li %[val], 0x7 \n " /* *state_addr = CPU_DEAD; */
" sw %[val], (%[state_addr]) \n "
" sync \n "
" cache 21, (%[state_addr]) \n " /* flush entry of *state_addr */
" .set pop \n "
: [ addr ] " =&r " ( addr ) , [ val ] " =&r " ( val )
: [ state_addr ] " r " ( state_addr ) ,
[ sets ] " r " ( cpu_data [ smp_processor_id ( ) ] . dcache . sets ) ) ;
__asm__ __volatile__ (
" .set push \n "
" .set noreorder \n "
" .set mips64 \n "
" mfc0 %[cpuid], $15, 1 \n "
" andi %[cpuid], 0x3ff \n "
" dli %[base], 0x900000003ff01000 \n "
" andi %[core], %[cpuid], 0x3 \n "
" sll %[core], 8 \n " /* get core id */
" or %[base], %[base], %[core] \n "
" andi %[node], %[cpuid], 0xc \n "
" dsll %[node], 42 \n " /* get node id */
" or %[base], %[base], %[node] \n "
" dsrl %[node], 30 \n " /* 15:14 */
" or %[base], %[base], %[node] \n "
" 1: li %[count], 0x100 \n " /* wait for init loop */
" 2: bnez %[count], 2b \n " /* limit mailbox access */
" addiu %[count], -1 \n "
" ld %[initfunc], 0x20(%[base]) \n " /* get PC via mailbox */
" beqz %[initfunc], 1b \n "
" nop \n "
" ld $sp, 0x28(%[base]) \n " /* get SP via mailbox */
" ld $gp, 0x30(%[base]) \n " /* get GP via mailbox */
" ld $a1, 0x38(%[base]) \n "
" jr %[initfunc] \n " /* jump to initial PC */
" nop \n "
" .set pop \n "
: [ core ] " =&r " ( core ) , [ node ] " =&r " ( node ) ,
[ base ] " =&r " ( base ) , [ cpuid ] " =&r " ( cpuid ) ,
[ count ] " =&r " ( count ) , [ initfunc ] " =&r " ( initfunc )
: /* No Input */
: " a1 " ) ;
}
2014-03-21 14:44:09 +04:00
void play_dead ( void )
{
int * state_addr ;
unsigned int cpu = smp_processor_id ( ) ;
void ( * play_dead_at_ckseg1 ) ( int * ) ;
idle_task_exit ( ) ;
2016-03-03 04:45:09 +03:00
switch ( read_c0_prid ( ) & PRID_REV_MASK ) {
case PRID_REV_LOONGSON3A_R1 :
2014-06-26 07:41:30 +04:00
default :
play_dead_at_ckseg1 =
2016-03-03 04:45:09 +03:00
( void * ) CKSEG1ADDR ( ( unsigned long ) loongson3a_r1_play_dead ) ;
break ;
case PRID_REV_LOONGSON3A_R2 :
2018-04-28 06:21:25 +03:00
case PRID_REV_LOONGSON3A_R3_0 :
case PRID_REV_LOONGSON3A_R3_1 :
2016-03-03 04:45:09 +03:00
play_dead_at_ckseg1 =
2017-06-22 18:06:48 +03:00
( void * ) CKSEG1ADDR ( ( unsigned long ) loongson3a_r2r3_play_dead ) ;
2014-06-26 07:41:30 +04:00
break ;
2016-03-03 04:45:09 +03:00
case PRID_REV_LOONGSON3B_R1 :
case PRID_REV_LOONGSON3B_R2 :
2014-06-26 07:41:30 +04:00
play_dead_at_ckseg1 =
( void * ) CKSEG1ADDR ( ( unsigned long ) loongson3b_play_dead ) ;
break ;
}
2014-03-21 14:44:09 +04:00
state_addr = & per_cpu ( cpu_state , cpu ) ;
mb ( ) ;
play_dead_at_ckseg1 ( state_addr ) ;
}
2016-09-06 20:04:52 +03:00
static int loongson3_disable_clock ( unsigned int cpu )
2014-06-26 07:41:30 +04:00
{
2017-08-13 05:49:35 +03:00
uint64_t core_id = cpu_core ( & cpu_data [ cpu ] ) ;
2014-06-26 07:41:30 +04:00
uint64_t package_id = cpu_data [ cpu ] . package ;
2016-03-03 04:45:09 +03:00
if ( ( read_c0_prid ( ) & PRID_REV_MASK ) = = PRID_REV_LOONGSON3A_R1 ) {
2014-06-26 07:41:30 +04:00
LOONGSON_CHIPCFG ( package_id ) & = ~ ( 1 < < ( 12 + core_id ) ) ;
2016-03-03 04:45:09 +03:00
} else {
2014-11-04 09:13:27 +03:00
if ( ! ( loongson_sysconf . workarounds & WORKAROUND_CPUHOTPLUG ) )
2014-06-26 07:41:30 +04:00
LOONGSON_FREQCTRL ( package_id ) & = ~ ( 1 < < ( core_id * 4 + 3 ) ) ;
}
2016-09-06 20:04:52 +03:00
return 0 ;
2014-06-26 07:41:30 +04:00
}
2016-09-06 20:04:52 +03:00
static int loongson3_enable_clock ( unsigned int cpu )
2014-06-26 07:41:30 +04:00
{
2017-08-13 05:49:35 +03:00
uint64_t core_id = cpu_core ( & cpu_data [ cpu ] ) ;
2014-06-26 07:41:30 +04:00
uint64_t package_id = cpu_data [ cpu ] . package ;
2016-03-03 04:45:09 +03:00
if ( ( read_c0_prid ( ) & PRID_REV_MASK ) = = PRID_REV_LOONGSON3A_R1 ) {
2014-06-26 07:41:30 +04:00
LOONGSON_CHIPCFG ( package_id ) | = 1 < < ( 12 + core_id ) ;
2016-03-03 04:45:09 +03:00
} else {
2014-11-04 09:13:27 +03:00
if ( ! ( loongson_sysconf . workarounds & WORKAROUND_CPUHOTPLUG ) )
2014-06-26 07:41:30 +04:00
LOONGSON_FREQCTRL ( package_id ) | = 1 < < ( core_id * 4 + 3 ) ;
}
2016-09-06 20:04:52 +03:00
return 0 ;
2014-03-21 14:44:09 +04:00
}
static int register_loongson3_notifier ( void )
{
2016-09-06 20:04:52 +03:00
return cpuhp_setup_state_nocalls ( CPUHP_MIPS_SOC_PREPARE ,
" mips/loongson:prepare " ,
loongson3_enable_clock ,
loongson3_disable_clock ) ;
2014-03-21 14:44:09 +04:00
}
early_initcall ( register_loongson3_notifier ) ;
# endif
2017-07-19 11:21:03 +03:00
const struct plat_smp_ops loongson3_smp_ops = {
2014-03-21 14:44:08 +04:00
. send_ipi_single = loongson3_send_ipi_single ,
. send_ipi_mask = loongson3_send_ipi_mask ,
. init_secondary = loongson3_init_secondary ,
. smp_finish = loongson3_smp_finish ,
. boot_secondary = loongson3_boot_secondary ,
. smp_setup = loongson3_smp_setup ,
. prepare_cpus = loongson3_prepare_cpus ,
2014-03-21 14:44:09 +04:00
# ifdef CONFIG_HOTPLUG_CPU
. cpu_disable = loongson3_cpu_disable ,
. cpu_die = loongson3_cpu_die ,
# endif
2018-09-12 00:49:21 +03:00
# ifdef CONFIG_KEXEC
. kexec_nonboot_cpu = kexec_nonboot_cpu_jump ,
# endif
2014-03-21 14:44:08 +04:00
} ;