2005-04-17 02:20:36 +04:00
/*
* Copyright 2001 MontaVista Software Inc .
* Author : Jun Sun , jsun @ mvista . com or jsun @ junsun . net
*
* Copyright ( C ) 2001 Ralf Baechle
2013-01-22 15:59:30 +04:00
* Copyright ( C ) 2005 MIPS Technologies , Inc . All rights reserved .
* Author : Maciej W . Rozycki < macro @ mips . com >
2005-04-17 02:20:36 +04:00
*
* This file define the irq handler for MIPS CPU interrupts .
*
2013-01-22 15:59:30 +04:00
* 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
2005-04-17 02:20:36 +04:00
* Free Software Foundation ; either version 2 of the License , or ( at your
* option ) any later version .
*/
/*
* Almost all MIPS CPUs define 8 interrupt sources . They are typically
* level triggered ( i . e . , cannot be cleared from CPU ; must be cleared from
* device ) . The first two are software interrupts which we don ' t really
* use or support . The last one is usually the CPU timer interrupt if
* counter register is present or , for CPUs with an external FPU , by
* convention it ' s the FPU exception interrupt .
*
* Don ' t even think about using this on SMP . You have been warned .
*
* This file exports one global function :
2007-01-07 20:14:29 +03:00
* void mips_cpu_irq_init ( void ) ;
2005-04-17 02:20:36 +04:00
*/
# include <linux/init.h>
# include <linux/interrupt.h>
# include <linux/kernel.h>
2010-10-07 17:08:54 +04:00
# include <linux/irq.h>
2015-07-08 00:11:46 +03:00
# include <linux/irqchip.h>
2013-01-31 16:20:43 +04:00
# include <linux/irqdomain.h>
2005-04-17 02:20:36 +04:00
# include <asm/irq_cpu.h>
# include <asm/mipsregs.h>
2005-08-17 17:44:26 +04:00
# include <asm/mipsmtregs.h>
2014-09-19 01:47:10 +04:00
# include <asm/setup.h>
2005-04-17 02:20:36 +04:00
2011-03-24 00:09:02 +03:00
static inline void unmask_mips_irq ( struct irq_data * d )
2005-04-17 02:20:36 +04:00
{
2011-03-24 00:09:02 +03:00
set_c0_status ( 0x100 < < ( d - > irq - MIPS_CPU_IRQ_BASE ) ) ;
2005-07-13 22:20:33 +04:00
irq_enable_hazard ( ) ;
2005-04-17 02:20:36 +04:00
}
2011-03-24 00:09:02 +03:00
static inline void mask_mips_irq ( struct irq_data * d )
2005-04-17 02:20:36 +04:00
{
2011-03-24 00:09:02 +03:00
clear_c0_status ( 0x100 < < ( d - > irq - MIPS_CPU_IRQ_BASE ) ) ;
2005-07-13 22:20:33 +04:00
irq_disable_hazard ( ) ;
2005-04-17 02:20:36 +04:00
}
2006-07-02 17:41:42 +04:00
static struct irq_chip mips_cpu_irq_controller = {
2007-01-14 18:07:25 +03:00
. name = " MIPS " ,
2011-03-24 00:09:02 +03:00
. irq_ack = mask_mips_irq ,
. irq_mask = mask_mips_irq ,
. irq_mask_ack = mask_mips_irq ,
. irq_unmask = unmask_mips_irq ,
. irq_eoi = unmask_mips_irq ,
2015-01-15 21:05:28 +03:00
. irq_disable = mask_mips_irq ,
. irq_enable = unmask_mips_irq ,
2005-04-17 02:20:36 +04:00
} ;
2005-08-17 17:44:26 +04:00
/*
* Basically the same as above but taking care of all the MT stuff
*/
2011-03-24 00:09:02 +03:00
static unsigned int mips_mt_cpu_irq_startup ( struct irq_data * d )
2005-08-17 17:44:26 +04:00
{
unsigned int vpflags = dvpe ( ) ;
2011-03-24 00:09:02 +03:00
clear_c0_cause ( 0x100 < < ( d - > irq - MIPS_CPU_IRQ_BASE ) ) ;
2005-08-17 17:44:26 +04:00
evpe ( vpflags ) ;
2011-03-24 00:09:02 +03:00
unmask_mips_irq ( d ) ;
2005-08-17 17:44:26 +04:00
return 0 ;
}
/*
* While we ack the interrupt interrupts are disabled and thus we don ' t need
* to deal with concurrency issues . Same for mips_cpu_irq_end .
*/
2011-03-24 00:09:02 +03:00
static void mips_mt_cpu_irq_ack ( struct irq_data * d )
2005-08-17 17:44:26 +04:00
{
unsigned int vpflags = dvpe ( ) ;
2011-03-24 00:09:02 +03:00
clear_c0_cause ( 0x100 < < ( d - > irq - MIPS_CPU_IRQ_BASE ) ) ;
2005-08-17 17:44:26 +04:00
evpe ( vpflags ) ;
2011-03-24 00:09:02 +03:00
mask_mips_irq ( d ) ;
2005-08-17 17:44:26 +04:00
}
2006-07-02 17:41:42 +04:00
static struct irq_chip mips_mt_cpu_irq_controller = {
2007-01-14 18:07:25 +03:00
. name = " MIPS " ,
2011-03-24 00:09:02 +03:00
. irq_startup = mips_mt_cpu_irq_startup ,
. irq_ack = mips_mt_cpu_irq_ack ,
. irq_mask = mask_mips_irq ,
. irq_mask_ack = mips_mt_cpu_irq_ack ,
. irq_unmask = unmask_mips_irq ,
. irq_eoi = unmask_mips_irq ,
2015-01-15 21:05:28 +03:00
. irq_disable = mask_mips_irq ,
. irq_enable = unmask_mips_irq ,
2005-08-17 17:44:26 +04:00
} ;
2005-04-17 02:20:36 +04:00
2014-09-19 01:47:09 +04:00
asmlinkage void __weak plat_irq_dispatch ( void )
{
unsigned long pending = read_c0_cause ( ) & read_c0_status ( ) & ST0_IM ;
int irq ;
if ( ! pending ) {
spurious_interrupt ( ) ;
return ;
}
pending > > = CAUSEB_IP ;
while ( pending ) {
irq = fls ( pending ) - 1 ;
do_IRQ ( MIPS_CPU_IRQ_BASE + irq ) ;
pending & = ~ BIT ( irq ) ;
}
}
2013-01-31 16:20:43 +04:00
static int mips_cpu_intc_map ( struct irq_domain * d , unsigned int irq ,
irq_hw_number_t hw )
{
static struct irq_chip * chip ;
if ( hw < 2 & & cpu_has_mipsmt ) {
/* Software interrupts are used for MT/CMT IPI */
chip = & mips_mt_cpu_irq_controller ;
} else {
chip = & mips_cpu_irq_controller ;
}
2014-09-19 01:47:10 +04:00
if ( cpu_has_vint )
set_vi_handler ( hw , plat_irq_dispatch ) ;
2013-01-31 16:20:43 +04:00
irq_set_chip_and_handler ( irq , chip , handle_percpu_irq ) ;
return 0 ;
}
static const struct irq_domain_ops mips_cpu_intc_irq_domain_ops = {
. map = mips_cpu_intc_map ,
. xlate = irq_domain_xlate_onecell ,
} ;
2014-09-19 01:47:07 +04:00
static void __init __mips_cpu_irq_init ( struct device_node * of_node )
2013-01-31 16:20:43 +04:00
{
struct irq_domain * domain ;
/* Mask interrupts. */
clear_c0_status ( ST0_IM ) ;
clear_c0_cause ( CAUSEF_IP ) ;
domain = irq_domain_add_legacy ( of_node , 8 , MIPS_CPU_IRQ_BASE , 0 ,
& mips_cpu_intc_irq_domain_ops , NULL ) ;
if ( ! domain )
2013-09-18 18:05:26 +04:00
panic ( " Failed to add irqdomain for MIPS CPU " ) ;
2014-09-19 01:47:07 +04:00
}
void __init mips_cpu_irq_init ( void )
{
__mips_cpu_irq_init ( NULL ) ;
}
2013-01-31 16:20:43 +04:00
2014-09-19 01:47:08 +04:00
int __init mips_cpu_irq_of_init ( struct device_node * of_node ,
struct device_node * parent )
2014-09-19 01:47:07 +04:00
{
__mips_cpu_irq_init ( of_node ) ;
2013-01-31 16:20:43 +04:00
return 0 ;
}
2015-05-24 18:11:16 +03:00
IRQCHIP_DECLARE ( cpu_intc , " mti,cpu-interrupt-controller " , mips_cpu_irq_of_init ) ;