2009-02-12 13:54:53 +00:00
/*
* Author : Kumar Gala < galak @ kernel . crashing . org >
*
* Copyright 2009 Freescale Semiconductor Inc .
*
* 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/stddef.h>
# include <linux/kernel.h>
# include <linux/smp.h>
# include <linux/threads.h>
2010-07-09 15:29:53 +10:00
# include <linux/percpu.h>
2009-02-12 13:54:53 +00:00
# include <asm/dbell.h>
2010-07-09 15:32:30 +10:00
# include <asm/irq_regs.h>
2009-02-12 13:54:53 +00:00
# ifdef CONFIG_SMP
2010-07-09 15:29:53 +10:00
struct doorbell_cpu_info {
unsigned long messages ; /* current messages bits */
unsigned int tag ; /* tag value */
} ;
2009-02-12 13:54:53 +00:00
2010-07-09 15:29:53 +10:00
static DEFINE_PER_CPU ( struct doorbell_cpu_info , doorbell_cpu_info ) ;
void doorbell_setup_this_cpu ( void )
{
struct doorbell_cpu_info * info = & __get_cpu_var ( doorbell_cpu_info ) ;
info - > messages = 0 ;
info - > tag = mfspr ( SPRN_PIR ) & 0x3fff ;
}
void doorbell_message_pass ( int target , int msg )
2009-02-12 13:54:53 +00:00
{
2010-07-09 15:29:53 +10:00
struct doorbell_cpu_info * info ;
2009-02-12 13:54:53 +00:00
int i ;
2010-07-09 15:29:53 +10:00
if ( target < NR_CPUS ) {
info = & per_cpu ( doorbell_cpu_info , target ) ;
set_bit ( msg , & info - > messages ) ;
ppc_msgsnd ( PPC_DBELL , 0 , info - > tag ) ;
2009-02-12 13:54:53 +00:00
}
2010-07-09 15:29:53 +10:00
else if ( target = = MSG_ALL_BUT_SELF ) {
2009-02-12 13:54:53 +00:00
for_each_online_cpu ( i ) {
if ( i = = smp_processor_id ( ) )
continue ;
2010-07-09 15:29:53 +10:00
info = & per_cpu ( doorbell_cpu_info , i ) ;
set_bit ( msg , & info - > messages ) ;
ppc_msgsnd ( PPC_DBELL , 0 , info - > tag ) ;
2009-02-12 13:54:53 +00:00
}
}
else { /* target == MSG_ALL */
2010-07-09 15:29:53 +10:00
for_each_online_cpu ( i ) {
info = & per_cpu ( doorbell_cpu_info , i ) ;
set_bit ( msg , & info - > messages ) ;
}
2009-02-12 13:54:53 +00:00
ppc_msgsnd ( PPC_DBELL , PPC_DBELL_MSG_BRDCAST , 0 ) ;
}
}
2010-07-09 15:25:18 +10:00
void doorbell_exception ( struct pt_regs * regs )
{
2010-07-09 15:32:30 +10:00
struct pt_regs * old_regs = set_irq_regs ( regs ) ;
2010-07-09 15:29:53 +10:00
struct doorbell_cpu_info * info = & __get_cpu_var ( doorbell_cpu_info ) ;
2010-07-09 15:25:18 +10:00
int msg ;
2010-07-09 15:29:53 +10:00
/* Warning: regs can be NULL when called from irq enable */
if ( ! info - > messages | | ( num_online_cpus ( ) < 2 ) )
2010-07-09 15:32:30 +10:00
goto out ;
2010-07-09 15:25:18 +10:00
for ( msg = 0 ; msg < 4 ; msg + + )
2010-07-09 15:29:53 +10:00
if ( test_and_clear_bit ( msg , & info - > messages ) )
2010-07-09 15:25:18 +10:00
smp_message_recv ( msg ) ;
2010-07-09 15:32:30 +10:00
out :
set_irq_regs ( old_regs ) ;
2010-07-09 15:25:18 +10:00
}
2010-07-09 15:34:00 +10:00
void doorbell_check_self ( void )
{
struct doorbell_cpu_info * info = & __get_cpu_var ( doorbell_cpu_info ) ;
if ( ! info - > messages )
return ;
ppc_msgsnd ( PPC_DBELL , 0 , info - > tag ) ;
}
2010-07-09 15:25:18 +10:00
# else /* CONFIG_SMP */
void doorbell_exception ( struct pt_regs * regs )
{
printk ( KERN_WARNING " Received doorbell on non-smp system \n " ) ;
}
# endif /* CONFIG_SMP */