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>
2015-07-08 00:11:46 +03:00
# include <linux/irqchip.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
2017-08-13 05:49:41 +03:00
# include <asm/mips-cps.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>
2013-04-11 01:27:04 +04:00
unsigned int gic_present ;
2017-08-13 07:36:10 +03:00
void __iomem * mips_gic_base ;
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
} ;
2015-10-12 12:40:43 +03:00
static unsigned long __gic_base_addr ;
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 ;
2015-12-08 16:20:23 +03:00
static struct irq_domain * gic_ipi_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 ;
2015-12-08 16:20:23 +03:00
DECLARE_BITMAP ( ipi_resrv , GIC_MAX_INTRS ) ;
2017-04-20 12:07:34 +03:00
DECLARE_BITMAP ( ipi_available , GIC_MAX_INTRS ) ;
2008-04-28 20:14:26 +04:00
2014-09-19 01:47:24 +04:00
static void __gic_irq_dispatch ( void ) ;
2015-07-14 12:26:09 +03:00
static inline u32 gic_read32 ( unsigned int reg )
2014-10-20 23:03:52 +04:00
{
2017-08-13 07:36:10 +03:00
return __raw_readl ( mips_gic_base + reg ) ;
2014-10-20 23:03:52 +04:00
}
2015-07-14 12:26:09 +03:00
static inline u64 gic_read64 ( unsigned int reg )
2014-10-20 23:03:52 +04:00
{
2017-08-13 07:36:10 +03:00
return __raw_readq ( mips_gic_base + reg ) ;
2014-10-20 23:03:52 +04:00
}
2015-07-14 12:26:09 +03:00
static inline unsigned long gic_read ( unsigned int reg )
2014-10-20 23:03:52 +04:00
{
2015-07-14 12:26:09 +03:00
if ( ! mips_cm_is64 )
return gic_read32 ( reg ) ;
else
return gic_read64 ( reg ) ;
}
static inline void gic_write32 ( unsigned int reg , u32 val )
{
2017-08-13 07:36:10 +03:00
return __raw_writel ( val , mips_gic_base + reg ) ;
2015-07-14 12:26:09 +03:00
}
static inline void gic_write64 ( unsigned int reg , u64 val )
{
2017-08-13 07:36:10 +03:00
return __raw_writeq ( val , mips_gic_base + reg ) ;
2015-07-14 12:26:09 +03:00
}
static inline void gic_write ( unsigned int reg , unsigned long val )
{
if ( ! mips_cm_is64 )
return gic_write32 ( reg , ( u32 ) val ) ;
else
return gic_write64 ( reg , ( u64 ) val ) ;
}
static inline void gic_update_bits ( unsigned int reg , unsigned long mask ,
unsigned long val )
{
unsigned long regval ;
2014-10-20 23:03:52 +04:00
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 ) ,
2015-07-14 12:26:09 +03:00
1ul < < GIC_INTR_BIT ( intr ) ) ;
2014-10-20 23:03:52 +04:00
}
static inline void gic_set_mask ( unsigned int intr )
{
gic_write ( GIC_REG ( SHARED , GIC_SH_SMASK ) + GIC_INTR_OFS ( intr ) ,
2015-07-14 12:26:09 +03:00
1ul < < GIC_INTR_BIT ( intr ) ) ;
2014-10-20 23:03:52 +04:00
}
static inline void gic_set_polarity ( unsigned int intr , unsigned int pol )
{
gic_update_bits ( GIC_REG ( SHARED , GIC_SH_SET_POLARITY ) +
2015-07-14 12:26:09 +03:00
GIC_INTR_OFS ( intr ) , 1ul < < GIC_INTR_BIT ( intr ) ,
( unsigned long ) pol < < GIC_INTR_BIT ( intr ) ) ;
2014-10-20 23:03:52 +04:00
}
static inline void gic_set_trigger ( unsigned int intr , unsigned int trig )
{
gic_update_bits ( GIC_REG ( SHARED , GIC_SH_SET_TRIGGER ) +
2015-07-14 12:26:09 +03:00
GIC_INTR_OFS ( intr ) , 1ul < < GIC_INTR_BIT ( intr ) ,
( unsigned long ) trig < < GIC_INTR_BIT ( intr ) ) ;
2014-10-20 23:03:52 +04:00
}
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 ) ,
2015-07-14 12:26:09 +03:00
1ul < < GIC_INTR_BIT ( intr ) ,
( unsigned long ) dual < < GIC_INTR_BIT ( intr ) ) ;
2014-10-20 23:03:52 +04:00
}
static inline void gic_map_to_pin ( unsigned int intr , unsigned int pin )
{
2015-07-14 12:26:09 +03:00
gic_write32 ( GIC_REG ( SHARED , GIC_SH_INTR_MAP_TO_PIN_BASE ) +
GIC_SH_MAP_TO_PIN ( intr ) , GIC_MAP_TO_PIN_MSK | pin ) ;
2014-10-20 23:03:52 +04:00
}
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-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 ;
2015-07-14 12:26:09 +03:00
vpe_ctl = gic_read32 ( 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
}
2015-12-08 16:20:28 +03:00
static void gic_send_ipi ( struct irq_data * d , unsigned int cpu )
2008-04-28 20:14:26 +04:00
{
2015-12-08 16:20:28 +03:00
irq_hw_number_t hwirq = GIC_HWIRQ_TO_SHARED ( irqd_to_hwirq ( d ) ) ;
gic_write ( GIC_REG ( SHARED , GIC_SH_WEDGE ) , GIC_SH_WEDGE_SET ( hwirq ) ) ;
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 ) ) {
2015-01-28 00:45:50 +03:00
/* Is the performance counter shared with the timer? */
2014-09-19 01:47:27 +04:00
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-29 14:14:09 +03:00
int gic_get_c0_fdc_int ( void )
{
if ( ! gic_local_irq_is_routable ( GIC_LOCAL_INT_FDC ) ) {
/* Is the FDC IRQ even present? */
if ( cp0_fdc_irq < 0 )
return - 1 ;
return MIPS_CPU_IRQ_BASE + cp0_fdc_irq ;
}
return irq_create_mapping ( gic_irq_domain ,
GIC_LOCAL_TO_HWIRQ ( GIC_LOCAL_INT_FDC ) ) ;
}
2015-10-12 12:40:43 +03:00
int gic_get_usm_range ( struct resource * gic_usm_res )
{
if ( ! gic_present )
return - 1 ;
gic_usm_res - > start = __gic_base_addr + USM_VISIBLE_SECTION_OFS ;
gic_usm_res - > end = gic_usm_res - > start + ( USM_VISIBLE_SECTION_SIZE - 1 ) ;
return 0 ;
}
2015-06-12 11:01:56 +03:00
static void gic_handle_shared_int ( bool chained )
2008-04-28 20:14:26 +04:00
{
2017-08-13 07:36:16 +03:00
unsigned int intr , virq ;
2014-10-20 23:03:56 +04:00
unsigned long * pcpu_mask ;
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 ;
2017-08-13 07:36:16 +03:00
if ( mips_cm_is64 ) {
__ioread64_copy ( pending , addr_gic_pend ( ) ,
DIV_ROUND_UP ( gic_shared_intrs , 64 ) ) ;
__ioread64_copy ( intrmask , addr_gic_mask ( ) ,
DIV_ROUND_UP ( gic_shared_intrs , 64 ) ) ;
} else {
__ioread32_copy ( pending , addr_gic_pend ( ) ,
DIV_ROUND_UP ( gic_shared_intrs , 32 ) ) ;
__ioread32_copy ( intrmask , addr_gic_mask ( ) ,
DIV_ROUND_UP ( gic_shared_intrs , 32 ) ) ;
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
2016-08-19 20:11:19 +03:00
for_each_set_bit ( intr , pending , gic_shared_intrs ) {
2015-01-19 14:51:29 +03:00
virq = irq_linear_revmap ( gic_irq_domain ,
GIC_SHARED_TO_HWIRQ ( intr ) ) ;
2015-06-12 11:01:56 +03:00
if ( chained )
generic_handle_irq ( virq ) ;
else
do_IRQ ( virq ) ;
2015-01-19 14:51:29 +03:00
}
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 ;
}
2015-06-23 15:41:25 +03:00
if ( is_edge )
irq_set_chip_handler_name_locked ( d , & gic_edge_irq_controller ,
handle_edge_irq , NULL ) ;
else
irq_set_chip_handler_name_locked ( d , & 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 ) ;
2015-03-05 03:19:16 +03:00
if ( cpumask_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 */
2015-09-22 21:29:10 +03:00
gic_map_to_vpe ( irq , mips_cm_vp_id ( cpumask_first ( & tmp ) ) ) ;
2013-06-21 14:13:08 +04:00
/* Update the pcpu_masks */
2016-04-21 13:31:54 +03:00
for ( i = 0 ; i < min ( gic_vpes , NR_CPUS ) ; i + + )
2013-06-21 14:13:08 +04:00
clear_bit ( irq , pcpu_masks [ i ] . pcpu_mask ) ;
2015-03-05 03:19:16 +03:00
set_bit ( irq , pcpu_masks [ cpumask_first ( & tmp ) ] . pcpu_mask ) ;
2008-04-28 20:14:26 +04:00
2015-06-01 11:05:38 +03:00
cpumask_copy ( irq_data_get_affinity_mask ( d ) , 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-12-08 16:20:28 +03:00
. ipi_send_single = gic_send_ipi ,
2008-04-28 20:14:26 +04:00
} ;
2015-06-12 11:01:56 +03:00
static void gic_handle_local_int ( bool chained )
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
2015-07-14 12:26:09 +03:00
pending = gic_read32 ( GIC_REG ( VPE_LOCAL , GIC_VPE_PEND ) ) ;
masked = gic_read32 ( GIC_REG ( VPE_LOCAL , GIC_VPE_MASK ) ) ;
2014-09-19 01:47:27 +04:00
bitmap_and ( & pending , & pending , & masked , GIC_NUM_LOCAL_INTRS ) ;
2016-09-13 19:54:27 +03:00
for_each_set_bit ( intr , & pending , GIC_NUM_LOCAL_INTRS ) {
2015-01-19 14:51:29 +03:00
virq = irq_linear_revmap ( gic_irq_domain ,
GIC_LOCAL_TO_HWIRQ ( intr ) ) ;
2015-06-12 11:01:56 +03:00
if ( chained )
generic_handle_irq ( virq ) ;
else
do_IRQ ( virq ) ;
2015-01-19 14:51:29 +03:00
}
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 ) ;
2015-07-14 12:26:09 +03:00
gic_write32 ( 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 ) ;
2015-07-14 12:26:09 +03:00
gic_write32 ( 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 + + ) {
2016-02-03 06:15:27 +03:00
gic_write ( GIC_REG ( VPE_LOCAL , GIC_VPE_OTHER_ADDR ) ,
mips_cm_vp_id ( i ) ) ;
2015-07-14 12:26:09 +03:00
gic_write32 ( 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 + + ) {
2016-02-03 06:15:27 +03:00
gic_write ( GIC_REG ( VPE_LOCAL , GIC_VPE_OTHER_ADDR ) ,
mips_cm_vp_id ( i ) ) ;
2015-07-14 12:26:09 +03:00
gic_write32 ( 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-06-12 11:01:56 +03:00
gic_handle_local_int ( false ) ;
gic_handle_shared_int ( false ) ;
2014-09-19 01:47:24 +04:00
}
2008-04-28 20:14:26 +04:00
2015-09-14 11:42:37 +03:00
static void gic_irq_dispatch ( struct irq_desc * desc )
2014-09-19 01:47:24 +04:00
{
2015-06-12 11:01:56 +03:00
gic_handle_local_int ( true ) ;
gic_handle_shared_int ( true ) ;
2014-09-19 01:47:24 +04:00
}
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 ;
2016-02-03 06:15:27 +03:00
gic_write ( GIC_REG ( VPE_LOCAL , GIC_VPE_OTHER_ADDR ) ,
mips_cm_vp_id ( 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 ;
2015-07-14 12:26:09 +03:00
gic_write32 ( 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 ;
spin_lock_irqsave ( & gic_lock , flags ) ;
for ( i = 0 ; i < gic_vpes ; i + + ) {
u32 val = GIC_MAP_TO_PIN_MSK | gic_cpu_pin ;
2016-02-03 06:15:27 +03:00
gic_write ( GIC_REG ( VPE_LOCAL , GIC_VPE_OTHER_ADDR ) ,
mips_cm_vp_id ( i ) ) ;
2014-09-19 01:47:27 +04:00
switch ( intr ) {
case GIC_LOCAL_INT_WD :
2015-07-14 12:26:09 +03:00
gic_write32 ( GIC_REG ( VPE_OTHER , GIC_VPE_WD_MAP ) , val ) ;
2014-09-19 01:47:27 +04:00
break ;
case GIC_LOCAL_INT_COMPARE :
2015-07-14 12:26:09 +03:00
gic_write32 ( 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 ;
2015-07-14 12:26:09 +03:00
gic_write32 ( GIC_REG ( VPE_OTHER , GIC_VPE_TIMER_MAP ) ,
val ) ;
2014-09-19 01:47:27 +04:00
break ;
case GIC_LOCAL_INT_PERFCTR :
2015-07-14 12:26:09 +03:00
gic_write32 ( GIC_REG ( VPE_OTHER , GIC_VPE_PERFCTR_MAP ) ,
val ) ;
2014-09-19 01:47:27 +04:00
break ;
case GIC_LOCAL_INT_SWINT0 :
2015-07-14 12:26:09 +03:00
gic_write32 ( GIC_REG ( VPE_OTHER , GIC_VPE_SWINT0_MAP ) ,
val ) ;
2014-09-19 01:47:27 +04:00
break ;
case GIC_LOCAL_INT_SWINT1 :
2015-07-14 12:26:09 +03:00
gic_write32 ( GIC_REG ( VPE_OTHER , GIC_VPE_SWINT1_MAP ) ,
val ) ;
2014-09-19 01:47:27 +04:00
break ;
case GIC_LOCAL_INT_FDC :
2015-07-14 12:26:09 +03:00
gic_write32 ( 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 ,
2015-12-08 16:20:23 +03:00
irq_hw_number_t hw , unsigned int vpe )
2014-09-19 01:47:27 +04:00
{
int intr = GIC_HWIRQ_TO_SHARED ( hw ) ;
2014-09-19 01:47:23 +04:00
unsigned long flags ;
2015-12-08 16:20:26 +03:00
int i ;
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 ) ;
2016-07-05 16:25:59 +03:00
gic_map_to_vpe ( intr , mips_cm_vp_id ( vpe ) ) ;
2016-04-21 13:31:54 +03:00
for ( i = 0 ; i < min ( gic_vpes , NR_CPUS ) ; i + + )
2015-12-08 16:20:26 +03:00
clear_bit ( intr , pcpu_masks [ i ] . pcpu_mask ) ;
2015-12-08 16:20:23 +03:00
set_bit ( intr , pcpu_masks [ vpe ] . pcpu_mask ) ;
2014-09-19 01:47:23 +04:00
spin_unlock_irqrestore ( & gic_lock , flags ) ;
return 0 ;
}
2017-04-20 12:07:35 +03:00
static int gic_irq_domain_xlate ( struct irq_domain * d , struct device_node * ctrlr ,
2015-12-08 16:20:24 +03:00
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 ;
}
2017-04-20 12:07:36 +03:00
static int gic_irq_domain_map ( struct irq_domain * d , unsigned int virq ,
irq_hw_number_t hwirq )
2015-12-08 16:20:24 +03:00
{
2017-04-20 12:07:35 +03:00
int err ;
2015-12-08 16:20:24 +03:00
2017-04-20 12:07:36 +03:00
if ( hwirq > = GIC_SHARED_HWIRQ_BASE ) {
2017-04-20 12:07:35 +03:00
/* verify that shared irqs don't conflict with an IPI irq */
if ( test_bit ( GIC_HWIRQ_TO_SHARED ( hwirq ) , ipi_resrv ) )
return - EBUSY ;
2015-12-08 16:20:24 +03:00
2017-04-20 12:07:35 +03:00
err = irq_domain_set_hwirq_and_chip ( d , virq , hwirq ,
& gic_level_irq_controller ,
NULL ) ;
if ( err )
return err ;
return gic_shared_irq_domain_map ( d , virq , hwirq , 0 ) ;
2015-12-08 16:20:24 +03:00
}
2017-04-20 12:07:35 +03:00
switch ( GIC_HWIRQ_TO_LOCAL ( hwirq ) ) {
case GIC_LOCAL_INT_TIMER :
case GIC_LOCAL_INT_PERFCTR :
case GIC_LOCAL_INT_FDC :
/*
* HACK : These are all really percpu interrupts , but
* the rest of the MIPS kernel code does not use the
* percpu IRQ API for them .
*/
err = irq_domain_set_hwirq_and_chip ( d , virq , hwirq ,
& gic_all_vpes_local_irq_controller ,
NULL ) ;
if ( err )
return err ;
2015-12-08 16:20:24 +03:00
2017-04-20 12:07:35 +03:00
irq_set_handler ( virq , handle_percpu_irq ) ;
break ;
default :
err = irq_domain_set_hwirq_and_chip ( d , virq , hwirq ,
& gic_local_irq_controller ,
NULL ) ;
if ( err )
return err ;
irq_set_handler ( virq , handle_percpu_devid_irq ) ;
irq_set_percpu_devid ( virq ) ;
break ;
}
return gic_local_irq_domain_map ( d , virq , hwirq ) ;
2015-12-08 16:20:24 +03:00
}
2017-04-20 12:07:36 +03:00
static int gic_irq_domain_alloc ( struct irq_domain * d , unsigned int virq ,
unsigned int nr_irqs , void * arg )
{
struct irq_fwspec * fwspec = arg ;
irq_hw_number_t hwirq ;
if ( fwspec - > param [ 0 ] = = GIC_SHARED )
hwirq = GIC_SHARED_TO_HWIRQ ( fwspec - > param [ 1 ] ) ;
else
hwirq = GIC_LOCAL_TO_HWIRQ ( fwspec - > param [ 1 ] ) ;
return gic_irq_domain_map ( d , virq , hwirq ) ;
}
2017-04-20 12:07:35 +03:00
void gic_irq_domain_free ( struct irq_domain * d , unsigned int virq ,
unsigned int nr_irqs )
2016-08-19 20:07:15 +03:00
{
}
2017-04-20 12:07:35 +03:00
static const struct irq_domain_ops gic_irq_domain_ops = {
. xlate = gic_irq_domain_xlate ,
. alloc = gic_irq_domain_alloc ,
. free = gic_irq_domain_free ,
2017-04-20 12:07:36 +03:00
. map = gic_irq_domain_map ,
2015-12-08 16:20:23 +03:00
} ;
static int gic_ipi_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 )
{
/*
* There ' s nothing to translate here . hwirq is dynamically allocated and
* the irq type is always edge triggered .
* */
* out_hwirq = 0 ;
* out_type = IRQ_TYPE_EDGE_RISING ;
return 0 ;
}
static int gic_ipi_domain_alloc ( struct irq_domain * d , unsigned int virq ,
unsigned int nr_irqs , void * arg )
{
struct cpumask * ipimask = arg ;
2017-04-20 12:07:35 +03:00
irq_hw_number_t hwirq , base_hwirq ;
int cpu , ret , i ;
2015-12-08 16:20:23 +03:00
2017-04-20 12:07:35 +03:00
base_hwirq = find_first_bit ( ipi_available , gic_shared_intrs ) ;
if ( base_hwirq = = gic_shared_intrs )
return - ENOMEM ;
/* check that we have enough space */
for ( i = base_hwirq ; i < nr_irqs ; i + + ) {
if ( ! test_bit ( i , ipi_available ) )
return - EBUSY ;
}
bitmap_clear ( ipi_available , base_hwirq , nr_irqs ) ;
/* map the hwirq for each cpu consecutively */
i = 0 ;
for_each_cpu ( cpu , ipimask ) {
hwirq = GIC_SHARED_TO_HWIRQ ( base_hwirq + i ) ;
ret = irq_domain_set_hwirq_and_chip ( d , virq + i , hwirq ,
& gic_edge_irq_controller ,
NULL ) ;
if ( ret )
goto error ;
2015-12-08 16:20:23 +03:00
2017-04-20 12:07:35 +03:00
ret = irq_domain_set_hwirq_and_chip ( d - > parent , virq + i , hwirq ,
2015-12-08 16:20:23 +03:00
& gic_edge_irq_controller ,
NULL ) ;
if ( ret )
goto error ;
ret = irq_set_irq_type ( virq + i , IRQ_TYPE_EDGE_RISING ) ;
if ( ret )
goto error ;
2017-04-20 12:07:35 +03:00
ret = gic_shared_irq_domain_map ( d , virq + i , hwirq , cpu ) ;
if ( ret )
goto error ;
i + + ;
2015-12-08 16:20:23 +03:00
}
return 0 ;
error :
2017-04-20 12:07:35 +03:00
bitmap_set ( ipi_available , base_hwirq , nr_irqs ) ;
2015-12-08 16:20:23 +03:00
return ret ;
}
void gic_ipi_domain_free ( struct irq_domain * d , unsigned int virq ,
unsigned int nr_irqs )
{
2017-04-20 12:07:35 +03:00
irq_hw_number_t base_hwirq ;
struct irq_data * data ;
data = irq_get_irq_data ( virq ) ;
if ( ! data )
return ;
base_hwirq = GIC_HWIRQ_TO_SHARED ( irqd_to_hwirq ( data ) ) ;
bitmap_set ( ipi_available , base_hwirq , nr_irqs ) ;
2015-12-08 16:20:23 +03:00
}
int gic_ipi_domain_match ( struct irq_domain * d , struct device_node * node ,
enum irq_domain_bus_token bus_token )
{
bool is_ipi ;
switch ( bus_token ) {
case DOMAIN_BUS_IPI :
is_ipi = d - > bus_token = = bus_token ;
2016-07-05 16:26:00 +03:00
return ( ! node | | to_of_node ( d - > fwnode ) = = node ) & & is_ipi ;
2015-12-08 16:20:23 +03:00
break ;
default :
return 0 ;
}
}
2017-06-02 11:20:56 +03:00
static const struct irq_domain_ops gic_ipi_domain_ops = {
2015-12-08 16:20:23 +03:00
. xlate = gic_ipi_domain_xlate ,
. alloc = gic_ipi_domain_alloc ,
. free = gic_ipi_domain_free ,
. match = gic_ipi_domain_match ,
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
{
2016-05-17 17:31:06 +03:00
unsigned int gicconfig , cpu ;
2015-12-08 16:20:30 +03:00
unsigned int v [ 2 ] ;
2008-04-28 20:14:26 +04:00
2015-10-12 12:40:43 +03:00
__gic_base_addr = gic_base_addr ;
2017-08-13 07:36:10 +03:00
mips_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 ) {
2016-05-17 17:31:06 +03:00
/* Set EIC mode for all VPEs */
for_each_present_cpu ( cpu ) {
gic_write ( GIC_REG ( VPE_LOCAL , GIC_VPE_OTHER_ADDR ) ,
mips_cm_vp_id ( cpu ) ) ;
gic_write ( GIC_REG ( VPE_OTHER , GIC_VPE_CTL ) ,
GIC_VPE_CTL_EIC_MODE_MSK ) ;
}
2014-09-19 01:47:24 +04:00
/* 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 ) ) {
2015-07-14 12:26:09 +03:00
timer_cpu_pin = gic_read32 ( GIC_REG ( VPE_LOCAL ,
2015-01-19 18:38:24 +03:00
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
2015-12-08 16:20:23 +03:00
gic_ipi_domain = irq_domain_add_hierarchy ( gic_irq_domain ,
IRQ_DOMAIN_FLAG_IPI_PER_CPU ,
GIC_NUM_LOCAL_INTRS + gic_shared_intrs ,
node , & gic_ipi_domain_ops , NULL ) ;
if ( ! gic_ipi_domain )
panic ( " Failed to add GIC IPI domain " ) ;
2017-06-22 13:42:50 +03:00
irq_domain_update_bus_token ( gic_ipi_domain , DOMAIN_BUS_IPI ) ;
2015-12-08 16:20:23 +03:00
2015-12-08 16:20:30 +03:00
if ( node & &
! of_property_read_u32_array ( node , " mti,reserved-ipi-vectors " , v , 2 ) ) {
bitmap_set ( ipi_resrv , v [ 0 ] , v [ 1 ] ) ;
} else {
/* Make the last 2 * gic_vpes available for IPIs */
bitmap_set ( ipi_resrv ,
gic_shared_intrs - 2 * gic_vpes ,
2 * gic_vpes ) ;
}
2015-12-08 16:20:23 +03:00
2017-04-20 12:07:34 +03:00
bitmap_copy ( ipi_available , ipi_resrv , GIC_MAX_INTRS ) ;
2014-09-19 01:47:27 +04:00
gic_basic_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 ( ) &
2017-08-13 05:49:27 +03:00
~ CM_GCR_GIC_BASE_GICEN ;
2014-11-12 22:43:38 +03:00
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 ) ;
}
2017-08-13 07:36:09 +03:00
if ( mips_cm_present ( ) ) {
2017-08-13 05:49:27 +03:00
write_gcr_gic_base ( gic_base | CM_GCR_GIC_BASE_GICEN ) ;
2017-08-13 07:36:09 +03:00
/* Ensure GIC region is enabled before trying to access it */
__sync ( ) ;
}
2014-11-12 22:43:38 +03:00
gic_present = true ;
__gic_init ( gic_base , gic_len , cpu_vec , 0 , node ) ;
return 0 ;
}
IRQCHIP_DECLARE ( mips_gic , " mti,gic " , gic_of_init ) ;