2012-09-01 01:13:07 +04:00
/*
* This file is subject to the terms and conditions of the GNU General Public
* License . See the file " COPYING " in the main directory of this archive
* for more details .
*
* Copyright ( C ) 2008 Ralf Baechle ( ralf @ linux - mips . org )
* Copyright ( C ) 2012 MIPS Technologies , Inc . All rights reserved .
*/
2008-04-28 20:14:26 +04:00
# 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>
2012-09-01 01:05:37 +04:00
unsigned long _gic_base ;
unsigned int gic_irq_base ;
unsigned int gic_irq_flags [ GIC_NUM_INTRS ] ;
2008-04-28 20:14:26 +04:00
2012-09-01 01:05:37 +04:00
static 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 )
{
GICWRITE ( GIC_REG ( SHARED , GIC_SH_WEDGE ) , 0x80000000 | intr ) ;
}
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
{
2012-09-01 01:13:07 +04:00
unsigned long timer_interrupt = GIC_INT_TMR ;
unsigned long perf_interrupt = GIC_INT_PERFCTR ;
2008-04-28 20:14:26 +04:00
unsigned int vpe_ctl ;
2012-09-01 01:13:07 +04:00
int i ;
2008-04-28 20:14:26 +04:00
/*
* 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 ) ;
2012-09-01 01:13:07 +04:00
return find_first_bit ( pending , GIC_NUM_INTRS ) ;
2008-04-28 20:14:26 +04:00
}
2011-03-24 00:08:58 +03:00
static void gic_mask_irq ( struct irq_data * d )
2008-04-28 20:14:26 +04:00
{
2012-09-01 01:13:07 +04:00
GIC_CLR_INTR_MASK ( d - > irq - gic_irq_base ) ;
2008-04-28 20:14:26 +04:00
}
2011-03-24 00:08:58 +03:00
static void gic_unmask_irq ( struct irq_data * d )
2008-04-28 20:14:26 +04:00
{
2012-09-01 01:13:07 +04:00
GIC_SET_INTR_MASK ( d - > irq - gic_irq_base ) ;
2008-04-28 20:14:26 +04:00
}
# ifdef CONFIG_SMP
static DEFINE_SPINLOCK ( gic_lock ) ;
2011-03-24 00:08:58 +03:00
static int gic_set_affinity ( struct irq_data * d , const struct cpumask * cpumask ,
bool force )
2008-04-28 20:14:26 +04:00
{
2012-09-01 01:13:07 +04:00
unsigned int irq = ( d - > irq - gic_irq_base ) ;
2008-04-28 20:14:26 +04:00
cpumask_t tmp = CPU_MASK_NONE ;
unsigned long flags ;
int i ;
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 ) ;
}
2011-03-24 00:08:58 +03:00
cpumask_copy ( d - > affinity , cpumask ) ;
2008-04-28 20:14:26 +04:00
spin_unlock_irqrestore ( & gic_lock , flags ) ;
2011-03-24 00:08:58 +03:00
return IRQ_SET_MASK_OK_NOCOPY ;
2008-04-28 20:14:26 +04:00
}
# endif
static struct irq_chip gic_irq_controller = {
2011-03-24 00:08:58 +03:00
. name = " MIPS GIC " ,
. irq_ack = gic_irq_ack ,
. irq_mask = gic_mask_irq ,
. irq_mask_ack = gic_mask_irq ,
. irq_unmask = gic_unmask_irq ,
. irq_eoi = gic_unmask_irq ,
2008-04-28 20:14:26 +04:00
# ifdef CONFIG_SMP
2011-03-24 00:08:58 +03:00
. irq_set_affinity = gic_set_affinity ,
2008-04-28 20:14:26 +04:00
# 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 ) ;
2012-09-01 01:13:07 +04:00
2009-07-10 12:54:09 +04:00
/* 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 )
2012-09-01 01:05:37 +04:00
gic_irq_flags [ intr ] | = GIC_TRIG_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 ) ;
}
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 ) ;
2012-09-01 01:05:37 +04:00
gic_irq_base = irqbase ;
2008-04-28 20:14:26 +04:00
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 ;
2009-07-10 12:54:09 +04:00
gic_basic_init ( numintrs , numvpes , intr_map , intr_map_size ) ;
2012-09-01 01:05:37 +04:00
gic_platform_init ( numintrs , & gic_irq_controller ) ;
2008-04-28 20:14:26 +04:00
}