2006-11-23 00:46:51 +01:00
/*
* PS3 SMP routines .
*
* Copyright ( C ) 2006 Sony Computer Entertainment Inc .
* Copyright 2006 Sony Corp .
*
* 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 ; version 2 of the License .
*
* 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 .
*
* 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 . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*/
# include <linux/kernel.h>
# include <linux/smp.h>
# include <asm/machdep.h>
# include <asm/udbg.h>
# include "platform.h"
# if defined(DEBUG)
# define DBG(fmt...) udbg_printf(fmt)
# else
# define DBG(fmt...) do{if(0)printk(fmt);}while(0)
# endif
static irqreturn_t ipi_function_handler ( int irq , void * msg )
{
smp_message_recv ( ( int ) ( long ) msg ) ;
return IRQ_HANDLED ;
}
/**
* virqs - a per cpu array of virqs for ipi use
*/
# define MSG_COUNT 4
static DEFINE_PER_CPU ( unsigned int , virqs [ MSG_COUNT ] ) ;
static const char * names [ MSG_COUNT ] = {
" ipi call " ,
" ipi reschedule " ,
" ipi migrate " ,
" ipi debug brk "
} ;
static void do_message_pass ( int target , int msg )
{
int result ;
unsigned int virq ;
if ( msg > = MSG_COUNT ) {
DBG ( " %s:%d: bad msg: %d \n " , __func__ , __LINE__ , msg ) ;
return ;
}
virq = per_cpu ( virqs , target ) [ msg ] ;
result = ps3_send_event_locally ( virq ) ;
if ( result )
DBG ( " %s:%d: ps3_send_event_locally(%d, %d) failed "
" (%d) \n " , __func__ , __LINE__ , target , msg , result ) ;
}
static void ps3_smp_message_pass ( int target , int msg )
{
int cpu ;
if ( target < NR_CPUS )
do_message_pass ( target , msg ) ;
else if ( target = = MSG_ALL_BUT_SELF ) {
for_each_online_cpu ( cpu )
if ( cpu ! = smp_processor_id ( ) )
do_message_pass ( cpu , msg ) ;
} else {
for_each_online_cpu ( cpu )
do_message_pass ( cpu , msg ) ;
}
}
static int ps3_smp_probe ( void )
{
return 2 ;
}
static void __init ps3_smp_setup_cpu ( int cpu )
{
int result ;
unsigned int * virqs = per_cpu ( virqs , cpu ) ;
int i ;
DBG ( " -> %s:%d: (%d) \n " , __func__ , __LINE__ , cpu ) ;
/*
* Check assumptions on virqs [ ] indexing . If this
* check fails , then a different mapping of PPC_MSG_
* to index needs to be setup .
*/
BUILD_BUG_ON ( PPC_MSG_CALL_FUNCTION ! = 0 ) ;
BUILD_BUG_ON ( PPC_MSG_RESCHEDULE ! = 1 ) ;
BUILD_BUG_ON ( PPC_MSG_DEBUGGER_BREAK ! = 3 ) ;
for ( i = 0 ; i < MSG_COUNT ; i + + ) {
2007-05-01 07:01:01 +10:00
result = ps3_event_receive_port_setup ( cpu , & virqs [ i ] ) ;
2006-11-23 00:46:51 +01:00
if ( result )
continue ;
DBG ( " %s:%d: (%d, %d) => virq %u \n " ,
__func__ , __LINE__ , cpu , i , virqs [ i ] ) ;
2007-05-12 03:41:59 +10:00
result = request_irq ( virqs [ i ] , ipi_function_handler ,
IRQF_DISABLED , names [ i ] , ( void * ) ( long ) i ) ;
2006-11-23 00:46:51 +01:00
2007-05-12 03:41:59 +10:00
if ( result )
virqs [ i ] = NO_IRQ ;
2006-11-23 00:46:51 +01:00
}
ps3_register_ipi_debug_brk ( cpu , virqs [ PPC_MSG_DEBUGGER_BREAK ] ) ;
DBG ( " <- %s:%d: (%d) \n " , __func__ , __LINE__ , cpu ) ;
}
void ps3_smp_cleanup_cpu ( int cpu )
{
unsigned int * virqs = per_cpu ( virqs , cpu ) ;
int i ;
DBG ( " -> %s:%d: (%d) \n " , __func__ , __LINE__ , cpu ) ;
2007-05-01 07:01:01 +10:00
2006-11-23 00:46:51 +01:00
for ( i = 0 ; i < MSG_COUNT ; i + + ) {
free_irq ( virqs [ i ] , ( void * ) ( long ) i ) ;
2007-05-01 07:01:01 +10:00
ps3_event_receive_port_destroy ( virqs [ i ] ) ;
2006-11-23 00:46:51 +01:00
virqs [ i ] = NO_IRQ ;
}
2007-05-01 07:01:01 +10:00
2006-11-23 00:46:51 +01:00
DBG ( " <- %s:%d: (%d) \n " , __func__ , __LINE__ , cpu ) ;
}
static struct smp_ops_t ps3_smp_ops = {
. probe = ps3_smp_probe ,
. message_pass = ps3_smp_message_pass ,
. kick_cpu = smp_generic_kick_cpu ,
. setup_cpu = ps3_smp_setup_cpu ,
} ;
void smp_init_ps3 ( void )
{
DBG ( " -> %s \n " , __func__ ) ;
smp_ops = & ps3_smp_ops ;
DBG ( " <- %s \n " , __func__ ) ;
}