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>
2014-10-20 23:03:55 +04:00
# include <linux/clocksource.h>
2008-04-28 20:14:26 +04:00
# include <linux/init.h>
2014-09-19 01:47:24 +04:00
# include <linux/interrupt.h>
2014-10-20 23:03:55 +04:00
# include <linux/irq.h>
2014-10-20 23:03:53 +04:00
# include <linux/irqchip/mips-gic.h>
2014-11-12 22:43:38 +03:00
# include <linux/of_address.h>
2014-09-19 01:47:24 +04:00
# include <linux/sched.h>
2009-06-19 17:05:26 +04:00
# include <linux/smp.h>
2008-04-28 20:14:26 +04:00
2014-11-12 22:43:38 +03:00
# include <asm/mips-cm.h>
2012-09-01 01:18:49 +04:00
# include <asm/setup.h>
# include <asm/traps.h>
2008-04-28 20:14:26 +04:00
2014-11-12 22:43:38 +03:00
# include <dt-bindings/interrupt-controller/mips-gic.h>
# include "irqchip.h"
2013-04-11 01:27:04 +04:00
unsigned int gic_present ;
2012-09-01 01:18:49 +04:00
2014-07-17 12:20:53 +04:00
struct gic_pcpu_mask {
2014-09-19 01:47:25 +04:00
DECLARE_BITMAP ( pcpu_mask , GIC_MAX_INTRS ) ;
2014-07-17 12:20:53 +04:00
} ;
2014-10-20 23:03:52 +04:00
static void __iomem * gic_base ;
2012-09-01 01:05:37 +04:00
static struct gic_pcpu_mask pcpu_masks [ NR_CPUS ] ;
2014-09-19 01:47:21 +04:00
static DEFINE_SPINLOCK ( gic_lock ) ;
2014-09-19 01:47:23 +04:00
static struct irq_domain * gic_irq_domain ;
2014-09-19 01:47:25 +04:00
static int gic_shared_intrs ;
2014-09-19 01:47:27 +04:00
static int gic_vpes ;
2014-09-19 01:47:28 +04:00
static unsigned int gic_cpu_pin ;
2015-01-19 18:38:24 +03:00
static unsigned int timer_cpu_pin ;
2014-09-19 01:47:26 +04:00
static struct irq_chip gic_level_irq_controller , gic_edge_irq_controller ;
2008-04-28 20:14:26 +04:00
2014-09-19 01:47:24 +04:00
static void __gic_irq_dispatch ( void ) ;
2014-10-20 23:03:52 +04:00
static inline unsigned int gic_read ( unsigned int reg )
{
return __raw_readl ( gic_base + reg ) ;
}
static inline void gic_write ( unsigned int reg , unsigned int val )
{
__raw_writel ( val , gic_base + reg ) ;
}
static inline void gic_update_bits ( unsigned int reg , unsigned int mask ,
unsigned int val )
{
unsigned int regval ;
regval = gic_read ( reg ) ;
regval & = ~ mask ;
regval | = val ;
gic_write ( reg , regval ) ;
}
static inline void gic_reset_mask ( unsigned int intr )
{
gic_write ( GIC_REG ( SHARED , GIC_SH_RMASK ) + GIC_INTR_OFS ( intr ) ,
1 < < GIC_INTR_BIT ( intr ) ) ;
}
static inline void gic_set_mask ( unsigned int intr )
{
gic_write ( GIC_REG ( SHARED , GIC_SH_SMASK ) + GIC_INTR_OFS ( intr ) ,
1 < < GIC_INTR_BIT ( intr ) ) ;
}
static inline void gic_set_polarity ( unsigned int intr , unsigned int pol )
{
gic_update_bits ( GIC_REG ( SHARED , GIC_SH_SET_POLARITY ) +
GIC_INTR_OFS ( intr ) , 1 < < GIC_INTR_BIT ( intr ) ,
pol < < GIC_INTR_BIT ( intr ) ) ;
}
static inline void gic_set_trigger ( unsigned int intr , unsigned int trig )
{
gic_update_bits ( GIC_REG ( SHARED , GIC_SH_SET_TRIGGER ) +
GIC_INTR_OFS ( intr ) , 1 < < GIC_INTR_BIT ( intr ) ,
trig < < GIC_INTR_BIT ( intr ) ) ;
}
static inline void gic_set_dual_edge ( unsigned int intr , unsigned int dual )
{
gic_update_bits ( GIC_REG ( SHARED , GIC_SH_SET_DUAL ) + GIC_INTR_OFS ( intr ) ,
1 < < GIC_INTR_BIT ( intr ) ,
dual < < GIC_INTR_BIT ( intr ) ) ;
}
static inline void gic_map_to_pin ( unsigned int intr , unsigned int pin )
{
gic_write ( GIC_REG ( SHARED , GIC_SH_INTR_MAP_TO_PIN_BASE ) +
GIC_SH_MAP_TO_PIN ( intr ) , GIC_MAP_TO_PIN_MSK | pin ) ;
}
static inline void gic_map_to_vpe ( unsigned int intr , unsigned int vpe )
{
gic_write ( GIC_REG ( SHARED , GIC_SH_INTR_MAP_TO_VPE_BASE ) +
GIC_SH_MAP_TO_VPE_REG_OFF ( intr , vpe ) ,
GIC_SH_MAP_TO_VPE_REG_BIT ( vpe ) ) ;
}
2014-10-20 23:03:59 +04:00
# ifdef CONFIG_CLKSRC_MIPS_GIC
2013-04-11 01:28:36 +04:00
cycle_t gic_read_count ( void )
{
unsigned int hi , hi2 , lo ;
do {
2014-10-20 23:03:52 +04:00
hi = gic_read ( GIC_REG ( SHARED , GIC_SH_COUNTER_63_32 ) ) ;
lo = gic_read ( GIC_REG ( SHARED , GIC_SH_COUNTER_31_00 ) ) ;
hi2 = gic_read ( GIC_REG ( SHARED , GIC_SH_COUNTER_63_32 ) ) ;
2013-04-11 01:28:36 +04:00
} while ( hi2 ! = hi ) ;
return ( ( ( cycle_t ) hi ) < < 32 ) + lo ;
}
2013-04-11 01:30:12 +04:00
2014-10-20 23:03:49 +04:00
unsigned int gic_get_count_width ( void )
{
unsigned int bits , config ;
2014-10-20 23:03:52 +04:00
config = gic_read ( GIC_REG ( SHARED , GIC_SH_CONFIG ) ) ;
2014-10-20 23:03:49 +04:00
bits = 32 + 4 * ( ( config & GIC_SH_CONFIG_COUNTBITS_MSK ) > >
GIC_SH_CONFIG_COUNTBITS_SHF ) ;
return bits ;
}
2013-04-11 01:30:12 +04:00
void gic_write_compare ( cycle_t cnt )
{
2014-10-20 23:03:52 +04:00
gic_write ( GIC_REG ( VPE_LOCAL , GIC_VPE_COMPARE_HI ) ,
2013-04-11 01:30:12 +04:00
( int ) ( cnt > > 32 ) ) ;
2014-10-20 23:03:52 +04:00
gic_write ( GIC_REG ( VPE_LOCAL , GIC_VPE_COMPARE_LO ) ,
2013-04-11 01:30:12 +04:00
( int ) ( cnt & 0xffffffff ) ) ;
}
2014-03-05 15:35:53 +04:00
void gic_write_cpu_compare ( cycle_t cnt , int cpu )
{
unsigned long flags ;
local_irq_save ( flags ) ;
2014-10-20 23:03:52 +04:00
gic_write ( GIC_REG ( VPE_LOCAL , GIC_VPE_OTHER_ADDR ) , cpu ) ;
gic_write ( GIC_REG ( VPE_OTHER , GIC_VPE_COMPARE_HI ) ,
2014-03-05 15:35:53 +04:00
( int ) ( cnt > > 32 ) ) ;
2014-10-20 23:03:52 +04:00
gic_write ( GIC_REG ( VPE_OTHER , GIC_VPE_COMPARE_LO ) ,
2014-03-05 15:35:53 +04:00
( int ) ( cnt & 0xffffffff ) ) ;
local_irq_restore ( flags ) ;
}
2013-04-11 01:30:12 +04:00
cycle_t gic_read_compare ( void )
{
unsigned int hi , lo ;
2014-10-20 23:03:52 +04:00
hi = gic_read ( GIC_REG ( VPE_LOCAL , GIC_VPE_COMPARE_HI ) ) ;
lo = gic_read ( GIC_REG ( VPE_LOCAL , GIC_VPE_COMPARE_LO ) ) ;
2013-04-11 01:30:12 +04:00
return ( ( ( cycle_t ) hi ) < < 32 ) + lo ;
}
2013-04-11 01:28:36 +04:00
# endif
2014-09-19 01:47:27 +04:00
static bool gic_local_irq_is_routable ( int intr )
{
u32 vpe_ctl ;
/* All local interrupts are routable in EIC mode. */
if ( cpu_has_veic )
return true ;
2014-10-20 23:03:52 +04:00
vpe_ctl = gic_read ( GIC_REG ( VPE_LOCAL , GIC_VPE_CTL ) ) ;
2014-09-19 01:47:27 +04:00
switch ( intr ) {
case GIC_LOCAL_INT_TIMER :
return vpe_ctl & GIC_VPE_CTL_TIMER_RTBL_MSK ;
case GIC_LOCAL_INT_PERFCTR :
return vpe_ctl & GIC_VPE_CTL_PERFCNT_RTBL_MSK ;
case GIC_LOCAL_INT_FDC :
return vpe_ctl & GIC_VPE_CTL_FDC_RTBL_MSK ;
case GIC_LOCAL_INT_SWINT0 :
case GIC_LOCAL_INT_SWINT1 :
return vpe_ctl & GIC_VPE_CTL_SWINT_RTBL_MSK ;
default :
return true ;
}
}
2014-09-19 01:47:28 +04:00
static void gic_bind_eic_interrupt ( int irq , int set )
2012-09-01 01:18:49 +04:00
{
/* Convert irq vector # to hw int # */
irq - = GIC_PIN_TO_VEC_OFFSET ;
/* Set irq to use shadow set */
2014-10-20 23:03:52 +04:00
gic_write ( GIC_REG ( VPE_LOCAL , GIC_VPE_EIC_SHADOW_SET_BASE ) +
GIC_VPE_EIC_SS ( irq ) , set ) ;
2012-09-01 01:18:49 +04:00
}
2008-04-28 20:14:26 +04:00
void gic_send_ipi ( unsigned int intr )
{
2014-10-20 23:03:57 +04:00
gic_write ( GIC_REG ( SHARED , GIC_SH_WEDGE ) , GIC_SH_WEDGE_SET ( intr ) ) ;
2008-04-28 20:14:26 +04:00
}
2014-09-19 01:47:27 +04:00
int gic_get_c0_compare_int ( void )
{
if ( ! gic_local_irq_is_routable ( GIC_LOCAL_INT_TIMER ) )
return MIPS_CPU_IRQ_BASE + cp0_compare_irq ;
return irq_create_mapping ( gic_irq_domain ,
GIC_LOCAL_TO_HWIRQ ( GIC_LOCAL_INT_TIMER ) ) ;
}
int gic_get_c0_perfcount_int ( void )
{
if ( ! gic_local_irq_is_routable ( GIC_LOCAL_INT_PERFCTR ) ) {
/* Is the erformance counter shared with the timer? */
if ( cp0_perfcount_irq < 0 )
return - 1 ;
return MIPS_CPU_IRQ_BASE + cp0_perfcount_irq ;
}
return irq_create_mapping ( gic_irq_domain ,
GIC_LOCAL_TO_HWIRQ ( GIC_LOCAL_INT_PERFCTR ) ) ;
}
2015-01-19 14:51:29 +03:00
static void gic_handle_shared_int ( void )
2008-04-28 20:14:26 +04:00
{
2015-01-19 14:51:29 +03:00
unsigned int i , intr , virq ;
2014-10-20 23:03:56 +04:00
unsigned long * pcpu_mask ;
2014-10-20 23:03:52 +04:00
unsigned long pending_reg , intrmask_reg ;
2014-10-20 23:03:56 +04:00
DECLARE_BITMAP ( pending , GIC_MAX_INTRS ) ;
DECLARE_BITMAP ( intrmask , GIC_MAX_INTRS ) ;
2008-04-28 20:14:26 +04:00
/* Get per-cpu bitmaps */
pcpu_mask = pcpu_masks [ smp_processor_id ( ) ] . pcpu_mask ;
2014-10-20 23:03:54 +04:00
pending_reg = GIC_REG ( SHARED , GIC_SH_PEND ) ;
intrmask_reg = GIC_REG ( SHARED , GIC_SH_MASK ) ;
2008-04-28 20:14:26 +04:00
2014-09-19 01:47:25 +04:00
for ( i = 0 ; i < BITS_TO_LONGS ( gic_shared_intrs ) ; i + + ) {
2014-10-20 23:03:52 +04:00
pending [ i ] = gic_read ( pending_reg ) ;
intrmask [ i ] = gic_read ( intrmask_reg ) ;
pending_reg + = 0x4 ;
intrmask_reg + = 0x4 ;
2008-04-28 20:14:26 +04:00
}
2014-09-19 01:47:25 +04:00
bitmap_and ( pending , pending , intrmask , gic_shared_intrs ) ;
bitmap_and ( pending , pending , pcpu_mask , gic_shared_intrs ) ;
2008-04-28 20:14:26 +04:00
2015-01-19 14:51:29 +03:00
intr = find_first_bit ( pending , gic_shared_intrs ) ;
while ( intr ! = gic_shared_intrs ) {
virq = irq_linear_revmap ( gic_irq_domain ,
GIC_SHARED_TO_HWIRQ ( intr ) ) ;
do_IRQ ( virq ) ;
/* go to next pending bit */
bitmap_clear ( pending , intr , 1 ) ;
intr = find_first_bit ( pending , gic_shared_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
{
2014-10-20 23:03:52 +04:00
gic_reset_mask ( GIC_HWIRQ_TO_SHARED ( d - > hwirq ) ) ;
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
{
2014-10-20 23:03:52 +04:00
gic_set_mask ( GIC_HWIRQ_TO_SHARED ( d - > hwirq ) ) ;
2008-04-28 20:14:26 +04:00
}
2014-09-19 01:47:20 +04:00
static void gic_ack_irq ( struct irq_data * d )
{
2014-09-19 01:47:27 +04:00
unsigned int irq = GIC_HWIRQ_TO_SHARED ( d - > hwirq ) ;
2014-09-19 01:47:23 +04:00
2014-10-20 23:03:57 +04:00
gic_write ( GIC_REG ( SHARED , GIC_SH_WEDGE ) , GIC_SH_WEDGE_CLR ( irq ) ) ;
2014-09-19 01:47:20 +04:00
}
2014-09-19 01:47:21 +04:00
static int gic_set_type ( struct irq_data * d , unsigned int type )
{
2014-09-19 01:47:27 +04:00
unsigned int irq = GIC_HWIRQ_TO_SHARED ( d - > hwirq ) ;
2014-09-19 01:47:21 +04:00
unsigned long flags ;
bool is_edge ;
spin_lock_irqsave ( & gic_lock , flags ) ;
switch ( type & IRQ_TYPE_SENSE_MASK ) {
case IRQ_TYPE_EDGE_FALLING :
2014-10-20 23:03:52 +04:00
gic_set_polarity ( irq , GIC_POL_NEG ) ;
gic_set_trigger ( irq , GIC_TRIG_EDGE ) ;
gic_set_dual_edge ( irq , GIC_TRIG_DUAL_DISABLE ) ;
2014-09-19 01:47:21 +04:00
is_edge = true ;
break ;
case IRQ_TYPE_EDGE_RISING :
2014-10-20 23:03:52 +04:00
gic_set_polarity ( irq , GIC_POL_POS ) ;
gic_set_trigger ( irq , GIC_TRIG_EDGE ) ;
gic_set_dual_edge ( irq , GIC_TRIG_DUAL_DISABLE ) ;
2014-09-19 01:47:21 +04:00
is_edge = true ;
break ;
case IRQ_TYPE_EDGE_BOTH :
/* polarity is irrelevant in this case */
2014-10-20 23:03:52 +04:00
gic_set_trigger ( irq , GIC_TRIG_EDGE ) ;
gic_set_dual_edge ( irq , GIC_TRIG_DUAL_ENABLE ) ;
2014-09-19 01:47:21 +04:00
is_edge = true ;
break ;
case IRQ_TYPE_LEVEL_LOW :
2014-10-20 23:03:52 +04:00
gic_set_polarity ( irq , GIC_POL_NEG ) ;
gic_set_trigger ( irq , GIC_TRIG_LEVEL ) ;
gic_set_dual_edge ( irq , GIC_TRIG_DUAL_DISABLE ) ;
2014-09-19 01:47:21 +04:00
is_edge = false ;
break ;
case IRQ_TYPE_LEVEL_HIGH :
default :
2014-10-20 23:03:52 +04:00
gic_set_polarity ( irq , GIC_POL_POS ) ;
gic_set_trigger ( irq , GIC_TRIG_LEVEL ) ;
gic_set_dual_edge ( irq , GIC_TRIG_DUAL_DISABLE ) ;
2014-09-19 01:47:21 +04:00
is_edge = false ;
break ;
}
if ( is_edge ) {
2014-09-19 01:47:26 +04:00
__irq_set_chip_handler_name_locked ( d - > irq ,
& gic_edge_irq_controller ,
handle_edge_irq , NULL ) ;
2014-09-19 01:47:21 +04:00
} else {
2014-09-19 01:47:26 +04:00
__irq_set_chip_handler_name_locked ( d - > irq ,
& gic_level_irq_controller ,
handle_level_irq , NULL ) ;
2014-09-19 01:47:21 +04:00
}
spin_unlock_irqrestore ( & gic_lock , flags ) ;
2008-04-28 20:14:26 +04:00
2014-09-19 01:47:21 +04:00
return 0 ;
}
# ifdef CONFIG_SMP
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
{
2014-09-19 01:47:27 +04:00
unsigned int irq = GIC_HWIRQ_TO_SHARED ( d - > hwirq ) ;
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 ) )
2014-09-19 01:47:22 +04:00
return - EINVAL ;
2008-04-28 20:14:26 +04:00
/* Assumption : cpumask refers to a single CPU */
spin_lock_irqsave ( & gic_lock , flags ) ;
2013-06-21 14:13:08 +04:00
/* Re-route this IRQ */
2014-10-20 23:03:52 +04:00
gic_map_to_vpe ( irq , first_cpu ( tmp ) ) ;
2013-06-21 14:13:08 +04:00
/* 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 ) ;
2008-04-28 20:14:26 +04:00
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
2014-09-19 01:47:26 +04:00
static struct irq_chip gic_level_irq_controller = {
. name = " MIPS GIC " ,
. irq_mask = gic_mask_irq ,
. irq_unmask = gic_unmask_irq ,
. irq_set_type = gic_set_type ,
# ifdef CONFIG_SMP
. irq_set_affinity = gic_set_affinity ,
# endif
} ;
static struct irq_chip gic_edge_irq_controller = {
2011-03-24 00:08:58 +03:00
. name = " MIPS GIC " ,
2014-09-19 01:47:20 +04:00
. irq_ack = gic_ack_irq ,
2011-03-24 00:08:58 +03:00
. irq_mask = gic_mask_irq ,
. irq_unmask = gic_unmask_irq ,
2014-09-19 01:47:21 +04:00
. irq_set_type = gic_set_type ,
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
} ;
2015-01-19 14:51:29 +03:00
static void gic_handle_local_int ( void )
2014-09-19 01:47:27 +04:00
{
unsigned long pending , masked ;
2015-01-19 14:51:29 +03:00
unsigned int intr , virq ;
2014-09-19 01:47:27 +04:00
2014-10-20 23:03:52 +04:00
pending = gic_read ( GIC_REG ( VPE_LOCAL , GIC_VPE_PEND ) ) ;
masked = gic_read ( GIC_REG ( VPE_LOCAL , GIC_VPE_MASK ) ) ;
2014-09-19 01:47:27 +04:00
bitmap_and ( & pending , & pending , & masked , GIC_NUM_LOCAL_INTRS ) ;
2015-01-19 14:51:29 +03:00
intr = find_first_bit ( & pending , GIC_NUM_LOCAL_INTRS ) ;
while ( intr ! = GIC_NUM_LOCAL_INTRS ) {
virq = irq_linear_revmap ( gic_irq_domain ,
GIC_LOCAL_TO_HWIRQ ( intr ) ) ;
do_IRQ ( virq ) ;
/* go to next pending bit */
bitmap_clear ( & pending , intr , 1 ) ;
intr = find_first_bit ( & pending , GIC_NUM_LOCAL_INTRS ) ;
}
2014-09-19 01:47:27 +04:00
}
static void gic_mask_local_irq ( struct irq_data * d )
{
int intr = GIC_HWIRQ_TO_LOCAL ( d - > hwirq ) ;
2014-10-20 23:03:52 +04:00
gic_write ( GIC_REG ( VPE_LOCAL , GIC_VPE_RMASK ) , 1 < < intr ) ;
2014-09-19 01:47:27 +04:00
}
static void gic_unmask_local_irq ( struct irq_data * d )
{
int intr = GIC_HWIRQ_TO_LOCAL ( d - > hwirq ) ;
2014-10-20 23:03:52 +04:00
gic_write ( GIC_REG ( VPE_LOCAL , GIC_VPE_SMASK ) , 1 < < intr ) ;
2014-09-19 01:47:27 +04:00
}
static struct irq_chip gic_local_irq_controller = {
. name = " MIPS GIC Local " ,
. irq_mask = gic_mask_local_irq ,
. irq_unmask = gic_unmask_local_irq ,
} ;
static void gic_mask_local_irq_all_vpes ( struct irq_data * d )
{
int intr = GIC_HWIRQ_TO_LOCAL ( d - > hwirq ) ;
int i ;
unsigned long flags ;
spin_lock_irqsave ( & gic_lock , flags ) ;
for ( i = 0 ; i < gic_vpes ; i + + ) {
2014-10-20 23:03:52 +04:00
gic_write ( GIC_REG ( VPE_LOCAL , GIC_VPE_OTHER_ADDR ) , i ) ;
gic_write ( GIC_REG ( VPE_OTHER , GIC_VPE_RMASK ) , 1 < < intr ) ;
2014-09-19 01:47:27 +04:00
}
spin_unlock_irqrestore ( & gic_lock , flags ) ;
}
static void gic_unmask_local_irq_all_vpes ( struct irq_data * d )
{
int intr = GIC_HWIRQ_TO_LOCAL ( d - > hwirq ) ;
int i ;
unsigned long flags ;
spin_lock_irqsave ( & gic_lock , flags ) ;
for ( i = 0 ; i < gic_vpes ; i + + ) {
2014-10-20 23:03:52 +04:00
gic_write ( GIC_REG ( VPE_LOCAL , GIC_VPE_OTHER_ADDR ) , i ) ;
gic_write ( GIC_REG ( VPE_OTHER , GIC_VPE_SMASK ) , 1 < < intr ) ;
2014-09-19 01:47:27 +04:00
}
spin_unlock_irqrestore ( & gic_lock , flags ) ;
}
static struct irq_chip gic_all_vpes_local_irq_controller = {
. name = " MIPS GIC Local " ,
. irq_mask = gic_mask_local_irq_all_vpes ,
. irq_unmask = gic_unmask_local_irq_all_vpes ,
} ;
2014-09-19 01:47:24 +04:00
static void __gic_irq_dispatch ( void )
2008-04-28 20:14:26 +04:00
{
2015-01-19 14:51:29 +03:00
gic_handle_local_int ( ) ;
gic_handle_shared_int ( ) ;
2014-09-19 01:47:24 +04:00
}
2008-04-28 20:14:26 +04:00
2014-09-19 01:47:24 +04:00
static void gic_irq_dispatch ( unsigned int irq , struct irq_desc * desc )
{
__gic_irq_dispatch ( ) ;
}
# ifdef CONFIG_MIPS_GIC_IPI
static int gic_resched_int_base ;
static int gic_call_int_base ;
unsigned int plat_ipi_resched_int_xlate ( unsigned int cpu )
{
return gic_resched_int_base + cpu ;
}
2008-04-28 20:14:26 +04:00
2014-09-19 01:47:24 +04:00
unsigned int plat_ipi_call_int_xlate ( unsigned int cpu )
{
return gic_call_int_base + cpu ;
}
2008-04-28 20:14:26 +04:00
2014-09-19 01:47:24 +04:00
static irqreturn_t ipi_resched_interrupt ( int irq , void * dev_id )
{
scheduler_ipi ( ) ;
return IRQ_HANDLED ;
}
static irqreturn_t ipi_call_interrupt ( int irq , void * dev_id )
{
smp_call_function_interrupt ( ) ;
return IRQ_HANDLED ;
}
2014-07-17 12:20:55 +04:00
2014-09-19 01:47:24 +04:00
static struct irqaction irq_resched = {
. handler = ipi_resched_interrupt ,
. flags = IRQF_PERCPU ,
. name = " IPI resched "
} ;
static struct irqaction irq_call = {
. handler = ipi_call_interrupt ,
. flags = IRQF_PERCPU ,
. name = " IPI call "
} ;
static __init void gic_ipi_init_one ( unsigned int intr , int cpu ,
struct irqaction * action )
{
2014-09-19 01:47:27 +04:00
int virq = irq_create_mapping ( gic_irq_domain ,
GIC_SHARED_TO_HWIRQ ( intr ) ) ;
2014-09-19 01:47:24 +04:00
int i ;
2014-10-20 23:03:52 +04:00
gic_map_to_vpe ( intr , cpu ) ;
2014-09-19 01:47:23 +04:00
for ( i = 0 ; i < NR_CPUS ; i + + )
clear_bit ( intr , pcpu_masks [ i ] . pcpu_mask ) ;
2014-07-17 12:20:55 +04:00
set_bit ( intr , pcpu_masks [ cpu ] . pcpu_mask ) ;
2014-09-19 01:47:24 +04:00
irq_set_irq_type ( virq , IRQ_TYPE_EDGE_RISING ) ;
irq_set_handler ( virq , handle_percpu_irq ) ;
setup_irq ( virq , action ) ;
2008-04-28 20:14:26 +04:00
}
2014-09-19 01:47:24 +04:00
static __init void gic_ipi_init ( void )
2008-04-28 20:14:26 +04:00
{
2014-09-19 01:47:24 +04:00
int i ;
/* Use last 2 * NR_CPUS interrupts as IPIs */
2014-09-19 01:47:25 +04:00
gic_resched_int_base = gic_shared_intrs - nr_cpu_ids ;
2014-09-19 01:47:24 +04:00
gic_call_int_base = gic_resched_int_base - nr_cpu_ids ;
for ( i = 0 ; i < nr_cpu_ids ; i + + ) {
gic_ipi_init_one ( gic_call_int_base + i , i , & irq_call ) ;
gic_ipi_init_one ( gic_resched_int_base + i , i , & irq_resched ) ;
}
}
# else
static inline void gic_ipi_init ( void )
{
}
# endif
2014-09-19 01:47:27 +04:00
static void __init gic_basic_init ( void )
2014-09-19 01:47:24 +04:00
{
unsigned int i ;
2012-09-01 01:18:49 +04:00
board_bind_eic_interrupt = & gic_bind_eic_interrupt ;
2008-04-28 20:14:26 +04:00
/* Setup defaults */
2014-09-19 01:47:25 +04:00
for ( i = 0 ; i < gic_shared_intrs ; i + + ) {
2014-10-20 23:03:52 +04:00
gic_set_polarity ( i , GIC_POL_POS ) ;
gic_set_trigger ( i , GIC_TRIG_LEVEL ) ;
gic_reset_mask ( i ) ;
2008-04-28 20:14:26 +04:00
}
2014-09-19 01:47:27 +04:00
for ( i = 0 ; i < gic_vpes ; i + + ) {
unsigned int j ;
2014-10-20 23:03:52 +04:00
gic_write ( GIC_REG ( VPE_LOCAL , GIC_VPE_OTHER_ADDR ) , i ) ;
2014-09-19 01:47:27 +04:00
for ( j = 0 ; j < GIC_NUM_LOCAL_INTRS ; j + + ) {
if ( ! gic_local_irq_is_routable ( j ) )
continue ;
2014-10-20 23:03:52 +04:00
gic_write ( GIC_REG ( VPE_OTHER , GIC_VPE_RMASK ) , 1 < < j ) ;
2014-09-19 01:47:27 +04:00
}
}
2008-04-28 20:14:26 +04:00
}
2014-09-19 01:47:27 +04:00
static int gic_local_irq_domain_map ( struct irq_domain * d , unsigned int virq ,
irq_hw_number_t hw )
2014-09-19 01:47:23 +04:00
{
2014-09-19 01:47:27 +04:00
int intr = GIC_HWIRQ_TO_LOCAL ( hw ) ;
int ret = 0 ;
int i ;
unsigned long flags ;
if ( ! gic_local_irq_is_routable ( intr ) )
return - EPERM ;
/*
* HACK : These are all really percpu interrupts , but the rest
* of the MIPS kernel code does not use the percpu IRQ API for
* the CP0 timer and performance counter interrupts .
*/
if ( intr ! = GIC_LOCAL_INT_TIMER & & intr ! = GIC_LOCAL_INT_PERFCTR ) {
irq_set_chip_and_handler ( virq ,
& gic_local_irq_controller ,
handle_percpu_devid_irq ) ;
irq_set_percpu_devid ( virq ) ;
} else {
irq_set_chip_and_handler ( virq ,
& gic_all_vpes_local_irq_controller ,
handle_percpu_irq ) ;
}
spin_lock_irqsave ( & gic_lock , flags ) ;
for ( i = 0 ; i < gic_vpes ; i + + ) {
u32 val = GIC_MAP_TO_PIN_MSK | gic_cpu_pin ;
2014-10-20 23:03:52 +04:00
gic_write ( GIC_REG ( VPE_LOCAL , GIC_VPE_OTHER_ADDR ) , i ) ;
2014-09-19 01:47:27 +04:00
switch ( intr ) {
case GIC_LOCAL_INT_WD :
2014-10-20 23:03:52 +04:00
gic_write ( GIC_REG ( VPE_OTHER , GIC_VPE_WD_MAP ) , val ) ;
2014-09-19 01:47:27 +04:00
break ;
case GIC_LOCAL_INT_COMPARE :
2014-10-20 23:03:52 +04:00
gic_write ( GIC_REG ( VPE_OTHER , GIC_VPE_COMPARE_MAP ) , val ) ;
2014-09-19 01:47:27 +04:00
break ;
case GIC_LOCAL_INT_TIMER :
2015-01-19 18:38:24 +03:00
/* CONFIG_MIPS_CMP workaround (see __gic_init) */
val = GIC_MAP_TO_PIN_MSK | timer_cpu_pin ;
2014-10-20 23:03:52 +04:00
gic_write ( GIC_REG ( VPE_OTHER , GIC_VPE_TIMER_MAP ) , val ) ;
2014-09-19 01:47:27 +04:00
break ;
case GIC_LOCAL_INT_PERFCTR :
2014-10-20 23:03:52 +04:00
gic_write ( GIC_REG ( VPE_OTHER , GIC_VPE_PERFCTR_MAP ) , val ) ;
2014-09-19 01:47:27 +04:00
break ;
case GIC_LOCAL_INT_SWINT0 :
2014-10-20 23:03:52 +04:00
gic_write ( GIC_REG ( VPE_OTHER , GIC_VPE_SWINT0_MAP ) , val ) ;
2014-09-19 01:47:27 +04:00
break ;
case GIC_LOCAL_INT_SWINT1 :
2014-10-20 23:03:52 +04:00
gic_write ( GIC_REG ( VPE_OTHER , GIC_VPE_SWINT1_MAP ) , val ) ;
2014-09-19 01:47:27 +04:00
break ;
case GIC_LOCAL_INT_FDC :
2014-10-20 23:03:52 +04:00
gic_write ( GIC_REG ( VPE_OTHER , GIC_VPE_FDC_MAP ) , val ) ;
2014-09-19 01:47:27 +04:00
break ;
default :
pr_err ( " Invalid local IRQ %d \n " , intr ) ;
ret = - EINVAL ;
break ;
}
}
spin_unlock_irqrestore ( & gic_lock , flags ) ;
return ret ;
}
static int gic_shared_irq_domain_map ( struct irq_domain * d , unsigned int virq ,
irq_hw_number_t hw )
{
int intr = GIC_HWIRQ_TO_SHARED ( hw ) ;
2014-09-19 01:47:23 +04:00
unsigned long flags ;
2014-09-19 01:47:26 +04:00
irq_set_chip_and_handler ( virq , & gic_level_irq_controller ,
handle_level_irq ) ;
2014-09-19 01:47:23 +04:00
spin_lock_irqsave ( & gic_lock , flags ) ;
2014-10-20 23:03:52 +04:00
gic_map_to_pin ( intr , gic_cpu_pin ) ;
2014-09-19 01:47:23 +04:00
/* Map to VPE 0 by default */
2014-10-20 23:03:52 +04:00
gic_map_to_vpe ( intr , 0 ) ;
2014-09-19 01:47:27 +04:00
set_bit ( intr , pcpu_masks [ 0 ] . pcpu_mask ) ;
2014-09-19 01:47:23 +04:00
spin_unlock_irqrestore ( & gic_lock , flags ) ;
return 0 ;
}
2014-09-19 01:47:27 +04:00
static int gic_irq_domain_map ( struct irq_domain * d , unsigned int virq ,
irq_hw_number_t hw )
{
if ( GIC_HWIRQ_TO_LOCAL ( hw ) < GIC_NUM_LOCAL_INTRS )
return gic_local_irq_domain_map ( d , virq , hw ) ;
return gic_shared_irq_domain_map ( d , virq , hw ) ;
}
2014-11-12 22:43:38 +03:00
static int gic_irq_domain_xlate ( struct irq_domain * d , struct device_node * ctrlr ,
const u32 * intspec , unsigned int intsize ,
irq_hw_number_t * out_hwirq ,
unsigned int * out_type )
{
if ( intsize ! = 3 )
return - EINVAL ;
if ( intspec [ 0 ] = = GIC_SHARED )
* out_hwirq = GIC_SHARED_TO_HWIRQ ( intspec [ 1 ] ) ;
else if ( intspec [ 0 ] = = GIC_LOCAL )
* out_hwirq = GIC_LOCAL_TO_HWIRQ ( intspec [ 1 ] ) ;
else
return - EINVAL ;
* out_type = intspec [ 2 ] & IRQ_TYPE_SENSE_MASK ;
return 0 ;
}
2014-09-19 01:47:23 +04:00
static struct irq_domain_ops gic_irq_domain_ops = {
. map = gic_irq_domain_map ,
2014-11-12 22:43:38 +03:00
. xlate = gic_irq_domain_xlate ,
2014-09-19 01:47:23 +04:00
} ;
2014-11-12 22:43:38 +03:00
static void __init __gic_init ( unsigned long gic_base_addr ,
unsigned long gic_addrspace_size ,
unsigned int cpu_vec , unsigned int irqbase ,
struct device_node * node )
2008-04-28 20:14:26 +04:00
{
unsigned int gicconfig ;
2014-10-20 23:03:52 +04:00
gic_base = ioremap_nocache ( gic_base_addr , gic_addrspace_size ) ;
2008-04-28 20:14:26 +04:00
2014-10-20 23:03:52 +04:00
gicconfig = gic_read ( GIC_REG ( SHARED , GIC_SH_CONFIG ) ) ;
2014-09-19 01:47:25 +04:00
gic_shared_intrs = ( gicconfig & GIC_SH_CONFIG_NUMINTRS_MSK ) > >
2008-04-28 20:14:26 +04:00
GIC_SH_CONFIG_NUMINTRS_SHF ;
2014-09-19 01:47:25 +04:00
gic_shared_intrs = ( ( gic_shared_intrs + 1 ) * 8 ) ;
2008-04-28 20:14:26 +04:00
2014-09-19 01:47:27 +04:00
gic_vpes = ( gicconfig & GIC_SH_CONFIG_NUMVPES_MSK ) > >
2008-04-28 20:14:26 +04:00
GIC_SH_CONFIG_NUMVPES_SHF ;
2014-09-19 01:47:27 +04:00
gic_vpes = gic_vpes + 1 ;
2008-04-28 20:14:26 +04:00
2014-09-19 01:47:24 +04:00
if ( cpu_has_veic ) {
/* Always use vector 1 in EIC mode */
gic_cpu_pin = 0 ;
2015-01-19 18:38:24 +03:00
timer_cpu_pin = gic_cpu_pin ;
2014-09-19 01:47:24 +04:00
set_vi_handler ( gic_cpu_pin + GIC_PIN_TO_VEC_OFFSET ,
__gic_irq_dispatch ) ;
} else {
gic_cpu_pin = cpu_vec - GIC_CPU_PIN_OFFSET ;
irq_set_chained_handler ( MIPS_CPU_IRQ_BASE + cpu_vec ,
gic_irq_dispatch ) ;
2015-01-19 18:38:24 +03:00
/*
* With the CMP implementation of SMP ( deprecated ) , other CPUs
* are started by the bootloader and put into a timer based
* waiting poll loop . We must not re - route those CPU ' s local
* timer interrupts as the wait instruction will never finish ,
* so just handle whatever CPU interrupt it is routed to by
* default .
*
* This workaround should be removed when CMP support is
* dropped .
*/
if ( IS_ENABLED ( CONFIG_MIPS_CMP ) & &
gic_local_irq_is_routable ( GIC_LOCAL_INT_TIMER ) ) {
timer_cpu_pin = gic_read ( GIC_REG ( VPE_LOCAL ,
GIC_VPE_TIMER_MAP ) ) &
GIC_MAP_MSK ;
irq_set_chained_handler ( MIPS_CPU_IRQ_BASE +
GIC_CPU_PIN_OFFSET +
timer_cpu_pin ,
gic_irq_dispatch ) ;
} else {
timer_cpu_pin = gic_cpu_pin ;
}
2014-09-19 01:47:24 +04:00
}
2014-11-12 22:43:38 +03:00
gic_irq_domain = irq_domain_add_simple ( node , GIC_NUM_LOCAL_INTRS +
2014-09-19 01:47:27 +04:00
gic_shared_intrs , irqbase ,
2014-09-19 01:47:23 +04:00
& gic_irq_domain_ops , NULL ) ;
if ( ! gic_irq_domain )
panic ( " Failed to add GIC IRQ domain " ) ;
2012-09-01 01:05:37 +04:00
2014-09-19 01:47:27 +04:00
gic_basic_init ( ) ;
2014-09-19 01:47:24 +04:00
gic_ipi_init ( ) ;
2008-04-28 20:14:26 +04:00
}
2014-11-12 22:43:38 +03:00
void __init gic_init ( unsigned long gic_base_addr ,
unsigned long gic_addrspace_size ,
unsigned int cpu_vec , unsigned int irqbase )
{
__gic_init ( gic_base_addr , gic_addrspace_size , cpu_vec , irqbase , NULL ) ;
}
static int __init gic_of_init ( struct device_node * node ,
struct device_node * parent )
{
struct resource res ;
unsigned int cpu_vec , i = 0 , reserved = 0 ;
phys_addr_t gic_base ;
size_t gic_len ;
/* Find the first available CPU vector. */
while ( ! of_property_read_u32_index ( node , " mti,reserved-cpu-vectors " ,
i + + , & cpu_vec ) )
reserved | = BIT ( cpu_vec ) ;
for ( cpu_vec = 2 ; cpu_vec < 8 ; cpu_vec + + ) {
if ( ! ( reserved & BIT ( cpu_vec ) ) )
break ;
}
if ( cpu_vec = = 8 ) {
pr_err ( " No CPU vectors available for GIC \n " ) ;
return - ENODEV ;
}
if ( of_address_to_resource ( node , 0 , & res ) ) {
/*
* Probe the CM for the GIC base address if not specified
* in the device - tree .
*/
if ( mips_cm_present ( ) ) {
gic_base = read_gcr_gic_base ( ) &
~ CM_GCR_GIC_BASE_GICEN_MSK ;
gic_len = 0x20000 ;
} else {
pr_err ( " Failed to get GIC memory range \n " ) ;
return - ENODEV ;
}
} else {
gic_base = res . start ;
gic_len = resource_size ( & res ) ;
}
if ( mips_cm_present ( ) )
write_gcr_gic_base ( gic_base | CM_GCR_GIC_BASE_GICEN_MSK ) ;
gic_present = true ;
__gic_init ( gic_base , gic_len , cpu_vec , 0 , node ) ;
return 0 ;
}
IRQCHIP_DECLARE ( mips_gic , " mti,gic " , gic_of_init ) ;