2008-04-28 20:14:26 +04:00
# undef DEBUG
# include <linux/bitmap.h>
# include <linux/init.h>
2009-06-19 17:05:26 +04:00
# include <linux/smp.h>
2010-10-07 17:08:54 +04:00
# include <linux/irq.h>
2008-04-28 20:14:26 +04:00
# include <asm/io.h>
# include <asm/gic.h>
# include <asm/gcmpregs.h>
# include <linux/hardirq.h>
# include <asm-generic/bitops/find.h>
static unsigned long _gic_base ;
2009-07-10 12:54:09 +04:00
static unsigned int _irqbase ;
static unsigned int gic_irq_flags [ GIC_NUM_INTRS ] ;
# define GIC_IRQ_FLAG_EDGE 0x0001
2008-04-28 20:14:26 +04:00
2009-07-10 12:54:09 +04:00
struct gic_pcpu_mask pcpu_masks [ NR_CPUS ] ;
2008-04-28 20:14:26 +04:00
static struct gic_pending_regs pending_regs [ NR_CPUS ] ;
static struct gic_intrmask_regs intrmask_regs [ NR_CPUS ] ;
void gic_send_ipi ( unsigned int intr )
{
pr_debug ( " CPU%d: %s status %08x \n " , smp_processor_id ( ) , __func__ ,
read_c0_status ( ) ) ;
GICWRITE ( GIC_REG ( SHARED , GIC_SH_WEDGE ) , 0x80000000 | intr ) ;
}
/* This is Malta specific and needs to be exported */
2009-07-10 12:54:09 +04:00
static void __init vpe_local_setup ( unsigned int numvpes )
2008-04-28 20:14:26 +04:00
{
int i ;
unsigned long timer_interrupt = 5 , perf_interrupt = 5 ;
unsigned int vpe_ctl ;
/*
* Setup the default performance counter timer interrupts
* for all VPEs
*/
for ( i = 0 ; i < numvpes ; i + + ) {
GICWRITE ( GIC_REG ( VPE_LOCAL , GIC_VPE_OTHER_ADDR ) , i ) ;
/* Are Interrupts locally routable? */
GICREAD ( GIC_REG ( VPE_OTHER , GIC_VPE_CTL ) , vpe_ctl ) ;
if ( vpe_ctl & GIC_VPE_CTL_TIMER_RTBL_MSK )
GICWRITE ( GIC_REG ( VPE_OTHER , GIC_VPE_TIMER_MAP ) ,
GIC_MAP_TO_PIN_MSK | timer_interrupt ) ;
if ( vpe_ctl & GIC_VPE_CTL_PERFCNT_RTBL_MSK )
GICWRITE ( GIC_REG ( VPE_OTHER , GIC_VPE_PERFCTR_MAP ) ,
GIC_MAP_TO_PIN_MSK | perf_interrupt ) ;
}
}
unsigned int gic_get_int ( void )
{
unsigned int i ;
unsigned long * pending , * intrmask , * pcpu_mask ;
unsigned long * pending_abs , * intrmask_abs ;
/* Get per-cpu bitmaps */
pending = pending_regs [ smp_processor_id ( ) ] . pending ;
intrmask = intrmask_regs [ smp_processor_id ( ) ] . intrmask ;
pcpu_mask = pcpu_masks [ smp_processor_id ( ) ] . pcpu_mask ;
pending_abs = ( unsigned long * ) GIC_REG_ABS_ADDR ( SHARED ,
GIC_SH_PEND_31_0_OFS ) ;
intrmask_abs = ( unsigned long * ) GIC_REG_ABS_ADDR ( SHARED ,
GIC_SH_MASK_31_0_OFS ) ;
for ( i = 0 ; i < BITS_TO_LONGS ( GIC_NUM_INTRS ) ; i + + ) {
GICREAD ( * pending_abs , pending [ i ] ) ;
GICREAD ( * intrmask_abs , intrmask [ i ] ) ;
pending_abs + + ;
intrmask_abs + + ;
}
bitmap_and ( pending , pending , intrmask , GIC_NUM_INTRS ) ;
bitmap_and ( pending , pending , pcpu_mask , GIC_NUM_INTRS ) ;
i = find_first_bit ( pending , GIC_NUM_INTRS ) ;
pr_debug ( " CPU%d: %s pend=%d \n " , smp_processor_id ( ) , __func__ , i ) ;
return i ;
}
static unsigned int gic_irq_startup ( unsigned int irq )
{
irq - = _irqbase ;
2009-07-10 12:54:09 +04:00
pr_debug ( " CPU%d: %s: irq%d \n " , smp_processor_id ( ) , __func__ , irq ) ;
GIC_SET_INTR_MASK ( irq ) ;
2008-04-28 20:14:26 +04:00
return 0 ;
}
static void gic_irq_ack ( unsigned int irq )
{
irq - = _irqbase ;
2009-07-10 12:54:09 +04:00
pr_debug ( " CPU%d: %s: irq%d \n " , smp_processor_id ( ) , __func__ , irq ) ;
GIC_CLR_INTR_MASK ( irq ) ;
2008-04-28 20:14:26 +04:00
2009-07-10 12:54:09 +04:00
if ( gic_irq_flags [ irq ] & GIC_IRQ_FLAG_EDGE )
2008-04-28 20:14:26 +04:00
GICWRITE ( GIC_REG ( SHARED , GIC_SH_WEDGE ) , irq ) ;
}
static void gic_mask_irq ( unsigned int irq )
{
irq - = _irqbase ;
2009-07-10 12:54:09 +04:00
pr_debug ( " CPU%d: %s: irq%d \n " , smp_processor_id ( ) , __func__ , irq ) ;
GIC_CLR_INTR_MASK ( irq ) ;
2008-04-28 20:14:26 +04:00
}
static void gic_unmask_irq ( unsigned int irq )
{
irq - = _irqbase ;
2009-07-10 12:54:09 +04:00
pr_debug ( " CPU%d: %s: irq%d \n " , smp_processor_id ( ) , __func__ , irq ) ;
GIC_SET_INTR_MASK ( irq ) ;
2008-04-28 20:14:26 +04:00
}
# ifdef CONFIG_SMP
static DEFINE_SPINLOCK ( gic_lock ) ;
2009-04-28 04:59:21 +04:00
static int gic_set_affinity ( unsigned int irq , const struct cpumask * cpumask )
2008-04-28 20:14:26 +04:00
{
cpumask_t tmp = CPU_MASK_NONE ;
unsigned long flags ;
int i ;
irq - = _irqbase ;
2010-09-12 09:10:52 +04:00
pr_debug ( " %s(%d) called \n " , __func__ , irq ) ;
2008-12-13 13:50:26 +03:00
cpumask_and ( & tmp , cpumask , cpu_online_mask ) ;
2008-04-28 20:14:26 +04:00
if ( cpus_empty ( tmp ) )
2009-04-28 04:59:21 +04:00
return - 1 ;
2008-04-28 20:14:26 +04:00
/* Assumption : cpumask refers to a single CPU */
spin_lock_irqsave ( & gic_lock , flags ) ;
for ( ; ; ) {
/* Re-route this IRQ */
GIC_SH_MAP_TO_VPE_SMASK ( irq , first_cpu ( tmp ) ) ;
/* Update the pcpu_masks */
for ( i = 0 ; i < NR_CPUS ; i + + )
clear_bit ( irq , pcpu_masks [ i ] . pcpu_mask ) ;
set_bit ( irq , pcpu_masks [ first_cpu ( tmp ) ] . pcpu_mask ) ;
}
2009-01-13 02:27:13 +03:00
cpumask_copy ( irq_desc [ irq ] . affinity , cpumask ) ;
2008-04-28 20:14:26 +04:00
spin_unlock_irqrestore ( & gic_lock , flags ) ;
2009-04-28 04:59:21 +04:00
return 0 ;
2008-04-28 20:14:26 +04:00
}
# endif
static struct irq_chip gic_irq_controller = {
. name = " MIPS GIC " ,
. startup = gic_irq_startup ,
. ack = gic_irq_ack ,
. mask = gic_mask_irq ,
. mask_ack = gic_mask_irq ,
. unmask = gic_unmask_irq ,
. eoi = gic_unmask_irq ,
# ifdef CONFIG_SMP
. set_affinity = gic_set_affinity ,
# endif
} ;
2009-07-10 12:54:09 +04:00
static void __init gic_setup_intr ( unsigned int intr , unsigned int cpu ,
unsigned int pin , unsigned int polarity , unsigned int trigtype ,
unsigned int flags )
2008-04-28 20:14:26 +04:00
{
/* Setup Intr to Pin mapping */
if ( pin & GIC_MAP_TO_NMI_MSK ) {
GICWRITE ( GIC_REG_ADDR ( SHARED , GIC_SH_MAP_TO_PIN ( intr ) ) , pin ) ;
/* FIXME: hack to route NMI to all cpu's */
for ( cpu = 0 ; cpu < NR_CPUS ; cpu + = 32 ) {
GICWRITE ( GIC_REG_ADDR ( SHARED ,
GIC_SH_MAP_TO_VPE_REG_OFF ( intr , cpu ) ) ,
0xffffffff ) ;
}
} else {
GICWRITE ( GIC_REG_ADDR ( SHARED , GIC_SH_MAP_TO_PIN ( intr ) ) ,
GIC_MAP_TO_PIN_MSK | pin ) ;
/* Setup Intr to CPU mapping */
GIC_SH_MAP_TO_VPE_SMASK ( intr , cpu ) ;
}
/* Setup Intr Polarity */
GIC_SET_POLARITY ( intr , polarity ) ;
/* Setup Intr Trigger Type */
GIC_SET_TRIGGER ( intr , trigtype ) ;
/* Init Intr Masks */
2009-07-10 12:54:09 +04:00
GIC_CLR_INTR_MASK ( intr ) ;
/* Initialise per-cpu Interrupt software masks */
if ( flags & GIC_FLAG_IPI )
set_bit ( intr , pcpu_masks [ cpu ] . pcpu_mask ) ;
if ( flags & GIC_FLAG_TRANSPARENT )
GIC_SET_INTR_MASK ( intr ) ;
if ( trigtype = = GIC_TRIG_EDGE )
gic_irq_flags [ intr ] | = GIC_IRQ_FLAG_EDGE ;
2008-04-28 20:14:26 +04:00
}
2009-07-10 12:54:09 +04:00
static void __init gic_basic_init ( int numintrs , int numvpes ,
struct gic_intr_map * intrmap , int mapsize )
2008-04-28 20:14:26 +04:00
{
unsigned int i , cpu ;
/* Setup defaults */
2009-07-10 12:54:09 +04:00
for ( i = 0 ; i < numintrs ; i + + ) {
2008-04-28 20:14:26 +04:00
GIC_SET_POLARITY ( i , GIC_POL_POS ) ;
GIC_SET_TRIGGER ( i , GIC_TRIG_LEVEL ) ;
2009-07-10 12:54:09 +04:00
GIC_CLR_INTR_MASK ( i ) ;
if ( i < GIC_NUM_INTRS )
gic_irq_flags [ i ] = 0 ;
2008-04-28 20:14:26 +04:00
}
/* Setup specifics */
2009-07-10 12:54:09 +04:00
for ( i = 0 ; i < mapsize ; i + + ) {
cpu = intrmap [ i ] . cpunum ;
2010-09-17 20:07:48 +04:00
if ( cpu = = GIC_UNUSED )
2008-04-28 20:14:26 +04:00
continue ;
2009-07-10 12:54:09 +04:00
if ( cpu = = 0 & & i ! = 0 & & intrmap [ i ] . flags = = 0 )
2009-06-18 03:22:25 +04:00
continue ;
2009-07-10 12:54:09 +04:00
gic_setup_intr ( i ,
intrmap [ i ] . cpunum ,
intrmap [ i ] . pin ,
intrmap [ i ] . polarity ,
intrmap [ i ] . trigtype ,
intrmap [ i ] . flags ) ;
2008-04-28 20:14:26 +04:00
}
vpe_local_setup ( numvpes ) ;
for ( i = _irqbase ; i < ( _irqbase + numintrs ) ; i + + )
set_irq_chip ( i , & gic_irq_controller ) ;
}
void __init gic_init ( unsigned long gic_base_addr ,
unsigned long gic_addrspace_size ,
struct gic_intr_map * intr_map , unsigned int intr_map_size ,
unsigned int irqbase )
{
unsigned int gicconfig ;
2009-07-10 12:54:09 +04:00
int numvpes , numintrs ;
2008-04-28 20:14:26 +04:00
_gic_base = ( unsigned long ) ioremap_nocache ( gic_base_addr ,
gic_addrspace_size ) ;
_irqbase = irqbase ;
GICREAD ( GIC_REG ( SHARED , GIC_SH_CONFIG ) , gicconfig ) ;
numintrs = ( gicconfig & GIC_SH_CONFIG_NUMINTRS_MSK ) > >
GIC_SH_CONFIG_NUMINTRS_SHF ;
numintrs = ( ( numintrs + 1 ) * 8 ) ;
numvpes = ( gicconfig & GIC_SH_CONFIG_NUMVPES_MSK ) > >
GIC_SH_CONFIG_NUMVPES_SHF ;
pr_debug ( " %s called \n " , __func__ ) ;
2009-07-10 12:54:09 +04:00
gic_basic_init ( numintrs , numvpes , intr_map , intr_map_size ) ;
2008-04-28 20:14:26 +04:00
}