2009-07-07 03:39:11 +04:00
/*
* intc . c - - interrupt controller or ColdFire 5272 SoC
*
* ( C ) Copyright 2009 , Greg Ungerer < gerg @ snapgear . com >
*
* 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 .
*/
# include <linux/types.h>
# include <linux/init.h>
# include <linux/kernel.h>
# include <linux/interrupt.h>
2010-10-13 07:42:22 +04:00
# include <linux/kernel_stat.h>
2009-07-07 03:39:11 +04:00
# include <linux/irq.h>
# include <linux/io.h>
# include <asm/coldfire.h>
# include <asm/mcfsim.h>
# include <asm/traps.h>
/*
* The 5272 ColdFire interrupt controller is nothing like any other
* ColdFire interrupt controller - it truly is completely different .
* Given its age it is unlikely to be used on any other ColdFire CPU .
*/
/*
* The masking and priproty setting of interrupts on the 5272 is done
* via a set of 4 " Interrupt Controller Registers " ( ICR ) . There is a
* loose mapping of vector number to register and internal bits , but
* a table is the easiest and quickest way to map them .
2010-10-13 07:42:22 +04:00
*
* Note that the external interrupts are edge triggered ( unlike the
* internal interrupt sources which are level triggered ) . Which means
2011-03-31 05:57:33 +04:00
* they also need acknowledging via acknowledge bits .
2009-07-07 03:39:11 +04:00
*/
struct irqmap {
2014-08-28 08:39:45 +04:00
unsigned int icr ;
2009-07-07 03:39:11 +04:00
unsigned char index ;
unsigned char ack ;
} ;
static struct irqmap intc_irqmap [ MCFINT_VECMAX - MCFINT_VECBASE ] = {
/*MCF_IRQ_SPURIOUS*/ { . icr = 0 , . index = 0 , . ack = 0 , } ,
/*MCF_IRQ_EINT1*/ { . icr = MCFSIM_ICR1 , . index = 28 , . ack = 1 , } ,
/*MCF_IRQ_EINT2*/ { . icr = MCFSIM_ICR1 , . index = 24 , . ack = 1 , } ,
/*MCF_IRQ_EINT3*/ { . icr = MCFSIM_ICR1 , . index = 20 , . ack = 1 , } ,
/*MCF_IRQ_EINT4*/ { . icr = MCFSIM_ICR1 , . index = 16 , . ack = 1 , } ,
/*MCF_IRQ_TIMER1*/ { . icr = MCFSIM_ICR1 , . index = 12 , . ack = 0 , } ,
/*MCF_IRQ_TIMER2*/ { . icr = MCFSIM_ICR1 , . index = 8 , . ack = 0 , } ,
/*MCF_IRQ_TIMER3*/ { . icr = MCFSIM_ICR1 , . index = 4 , . ack = 0 , } ,
/*MCF_IRQ_TIMER4*/ { . icr = MCFSIM_ICR1 , . index = 0 , . ack = 0 , } ,
/*MCF_IRQ_UART1*/ { . icr = MCFSIM_ICR2 , . index = 28 , . ack = 0 , } ,
/*MCF_IRQ_UART2*/ { . icr = MCFSIM_ICR2 , . index = 24 , . ack = 0 , } ,
/*MCF_IRQ_PLIP*/ { . icr = MCFSIM_ICR2 , . index = 20 , . ack = 0 , } ,
/*MCF_IRQ_PLIA*/ { . icr = MCFSIM_ICR2 , . index = 16 , . ack = 0 , } ,
/*MCF_IRQ_USB0*/ { . icr = MCFSIM_ICR2 , . index = 12 , . ack = 0 , } ,
/*MCF_IRQ_USB1*/ { . icr = MCFSIM_ICR2 , . index = 8 , . ack = 0 , } ,
/*MCF_IRQ_USB2*/ { . icr = MCFSIM_ICR2 , . index = 4 , . ack = 0 , } ,
/*MCF_IRQ_USB3*/ { . icr = MCFSIM_ICR2 , . index = 0 , . ack = 0 , } ,
/*MCF_IRQ_USB4*/ { . icr = MCFSIM_ICR3 , . index = 28 , . ack = 0 , } ,
/*MCF_IRQ_USB5*/ { . icr = MCFSIM_ICR3 , . index = 24 , . ack = 0 , } ,
/*MCF_IRQ_USB6*/ { . icr = MCFSIM_ICR3 , . index = 20 , . ack = 0 , } ,
/*MCF_IRQ_USB7*/ { . icr = MCFSIM_ICR3 , . index = 16 , . ack = 0 , } ,
/*MCF_IRQ_DMA*/ { . icr = MCFSIM_ICR3 , . index = 12 , . ack = 0 , } ,
/*MCF_IRQ_ERX*/ { . icr = MCFSIM_ICR3 , . index = 8 , . ack = 0 , } ,
/*MCF_IRQ_ETX*/ { . icr = MCFSIM_ICR3 , . index = 4 , . ack = 0 , } ,
/*MCF_IRQ_ENTC*/ { . icr = MCFSIM_ICR3 , . index = 0 , . ack = 0 , } ,
/*MCF_IRQ_QSPI*/ { . icr = MCFSIM_ICR4 , . index = 28 , . ack = 0 , } ,
/*MCF_IRQ_EINT5*/ { . icr = MCFSIM_ICR4 , . index = 24 , . ack = 1 , } ,
/*MCF_IRQ_EINT6*/ { . icr = MCFSIM_ICR4 , . index = 20 , . ack = 1 , } ,
/*MCF_IRQ_SWTO*/ { . icr = MCFSIM_ICR4 , . index = 16 , . ack = 0 , } ,
} ;
2010-10-13 07:42:22 +04:00
/*
* The act of masking the interrupt also has a side effect of ' ack ' ing
* an interrupt on this irq ( for the external irqs ) . So this mask function
* is also an ack_mask function .
*/
2011-02-07 02:39:28 +03:00
static void intc_irq_mask ( struct irq_data * d )
2009-07-07 03:39:11 +04:00
{
2011-02-07 02:39:28 +03:00
unsigned int irq = d - > irq ;
2009-07-07 03:39:11 +04:00
if ( ( irq > = MCFINT_VECBASE ) & & ( irq < = MCFINT_VECMAX ) ) {
u32 v ;
irq - = MCFINT_VECBASE ;
v = 0x8 < < intc_irqmap [ irq ] . index ;
2012-08-17 10:48:16 +04:00
writel ( v , intc_irqmap [ irq ] . icr ) ;
2009-07-07 03:39:11 +04:00
}
}
2011-02-07 02:39:28 +03:00
static void intc_irq_unmask ( struct irq_data * d )
2009-07-07 03:39:11 +04:00
{
2011-02-07 02:39:28 +03:00
unsigned int irq = d - > irq ;
2009-07-07 03:39:11 +04:00
if ( ( irq > = MCFINT_VECBASE ) & & ( irq < = MCFINT_VECMAX ) ) {
u32 v ;
irq - = MCFINT_VECBASE ;
v = 0xd < < intc_irqmap [ irq ] . index ;
2012-08-17 10:48:16 +04:00
writel ( v , intc_irqmap [ irq ] . icr ) ;
2009-07-07 03:39:11 +04:00
}
}
2011-02-07 02:39:28 +03:00
static void intc_irq_ack ( struct irq_data * d )
2009-07-07 03:39:11 +04:00
{
2011-02-07 02:39:28 +03:00
unsigned int irq = d - > irq ;
2009-07-07 03:39:11 +04:00
/* Only external interrupts are acked */
if ( ( irq > = MCFINT_VECBASE ) & & ( irq < = MCFINT_VECMAX ) ) {
irq - = MCFINT_VECBASE ;
if ( intc_irqmap [ irq ] . ack ) {
u32 v ;
2012-08-17 10:48:16 +04:00
v = readl ( intc_irqmap [ irq ] . icr ) ;
2010-10-13 07:42:22 +04:00
v & = ( 0x7 < < intc_irqmap [ irq ] . index ) ;
v | = ( 0x8 < < intc_irqmap [ irq ] . index ) ;
2012-08-17 10:48:16 +04:00
writel ( v , intc_irqmap [ irq ] . icr ) ;
2009-07-07 03:39:11 +04:00
}
}
}
2011-02-07 02:39:28 +03:00
static int intc_irq_set_type ( struct irq_data * d , unsigned int type )
2009-07-07 03:39:11 +04:00
{
2011-02-07 02:39:28 +03:00
unsigned int irq = d - > irq ;
2010-10-13 07:42:22 +04:00
if ( ( irq > = MCFINT_VECBASE ) & & ( irq < = MCFINT_VECMAX ) ) {
irq - = MCFINT_VECBASE ;
if ( intc_irqmap [ irq ] . ack ) {
u32 v ;
2012-08-17 10:48:16 +04:00
v = readl ( MCFSIM_PITR ) ;
2010-10-13 07:42:22 +04:00
if ( type = = IRQ_TYPE_EDGE_FALLING )
v & = ~ ( 0x1 < < ( 32 - irq ) ) ;
else
v | = ( 0x1 < < ( 32 - irq ) ) ;
2012-08-17 10:48:16 +04:00
writel ( v , MCFSIM_PITR ) ;
2010-10-13 07:42:22 +04:00
}
}
2009-07-07 03:39:11 +04:00
return 0 ;
}
2010-10-13 07:42:22 +04:00
/*
* Simple flow handler to deal with the external edge triggered interrupts .
* We need to be careful with the masking / acking due to the side effects
* of masking an interrupt .
*/
2015-09-14 11:42:37 +03:00
static void intc_external_irq ( struct irq_desc * desc )
2010-10-13 07:42:22 +04:00
{
2011-03-28 15:31:17 +04:00
irq_desc_get_chip ( desc ) - > irq_ack ( & desc - > irq_data ) ;
2015-09-14 11:42:37 +03:00
handle_simple_irq ( desc ) ;
2010-10-13 07:42:22 +04:00
}
2009-07-07 03:39:11 +04:00
static struct irq_chip intc_irq_chip = {
. name = " CF-INTC " ,
2011-02-07 02:39:28 +03:00
. irq_mask = intc_irq_mask ,
. irq_unmask = intc_irq_unmask ,
. irq_mask_ack = intc_irq_mask ,
. irq_ack = intc_irq_ack ,
. irq_set_type = intc_irq_set_type ,
2009-07-07 03:39:11 +04:00
} ;
void __init init_IRQ ( void )
{
2010-10-13 07:42:22 +04:00
int irq , edge ;
2009-07-07 03:39:11 +04:00
/* Mask all interrupt sources */
2012-08-17 10:48:16 +04:00
writel ( 0x88888888 , MCFSIM_ICR1 ) ;
writel ( 0x88888888 , MCFSIM_ICR2 ) ;
writel ( 0x88888888 , MCFSIM_ICR3 ) ;
writel ( 0x88888888 , MCFSIM_ICR4 ) ;
2009-07-07 03:39:11 +04:00
for ( irq = 0 ; ( irq < NR_IRQS ) ; irq + + ) {
2011-03-28 15:31:17 +04:00
irq_set_chip ( irq , & intc_irq_chip ) ;
2010-10-13 07:42:22 +04:00
edge = 0 ;
if ( ( irq > = MCFINT_VECBASE ) & & ( irq < = MCFINT_VECMAX ) )
edge = intc_irqmap [ irq - MCFINT_VECBASE ] . ack ;
if ( edge ) {
2011-03-28 15:31:17 +04:00
irq_set_irq_type ( irq , IRQ_TYPE_EDGE_RISING ) ;
irq_set_handler ( irq , intc_external_irq ) ;
2010-10-13 07:42:22 +04:00
} else {
2011-03-28 15:31:17 +04:00
irq_set_irq_type ( irq , IRQ_TYPE_LEVEL_HIGH ) ;
irq_set_handler ( irq , handle_level_irq ) ;
2010-10-13 07:42:22 +04:00
}
2009-07-07 03:39:11 +04:00
}
}