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
* they also need acknowledgeing via acknowledge bits .
2009-07-07 03:39:11 +04:00
*/
struct irqmap {
unsigned char icr ;
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 .
*/
2009-07-07 03:39:11 +04:00
static void intc_irq_mask ( unsigned int irq )
{
if ( ( irq > = MCFINT_VECBASE ) & & ( irq < = MCFINT_VECMAX ) ) {
u32 v ;
irq - = MCFINT_VECBASE ;
v = 0x8 < < intc_irqmap [ irq ] . index ;
writel ( v , MCF_MBAR + intc_irqmap [ irq ] . icr ) ;
}
}
static void intc_irq_unmask ( unsigned int irq )
{
if ( ( irq > = MCFINT_VECBASE ) & & ( irq < = MCFINT_VECMAX ) ) {
u32 v ;
irq - = MCFINT_VECBASE ;
v = 0xd < < intc_irqmap [ irq ] . index ;
writel ( v , MCF_MBAR + intc_irqmap [ irq ] . icr ) ;
}
}
static void intc_irq_ack ( unsigned int irq )
{
/* Only external interrupts are acked */
if ( ( irq > = MCFINT_VECBASE ) & & ( irq < = MCFINT_VECMAX ) ) {
irq - = MCFINT_VECBASE ;
if ( intc_irqmap [ irq ] . ack ) {
u32 v ;
2010-10-13 07:42:22 +04:00
v = readl ( MCF_MBAR + intc_irqmap [ irq ] . icr ) ;
v & = ( 0x7 < < intc_irqmap [ irq ] . index ) ;
v | = ( 0x8 < < intc_irqmap [ irq ] . index ) ;
2009-07-07 03:39:11 +04:00
writel ( v , MCF_MBAR + intc_irqmap [ irq ] . icr ) ;
}
}
}
static int intc_irq_set_type ( unsigned int irq , unsigned int type )
{
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 ;
v = readl ( MCF_MBAR + MCFSIM_PITR ) ;
if ( type = = IRQ_TYPE_EDGE_FALLING )
v & = ~ ( 0x1 < < ( 32 - irq ) ) ;
else
v | = ( 0x1 < < ( 32 - irq ) ) ;
writel ( v , MCF_MBAR + MCFSIM_PITR ) ;
}
}
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 .
*/
static void intc_external_irq ( unsigned int irq , struct irq_desc * desc )
{
kstat_incr_irqs_this_cpu ( irq , desc ) ;
desc - > status | = IRQ_INPROGRESS ;
desc - > chip - > ack ( irq ) ;
handle_IRQ_event ( irq , desc - > action ) ;
desc - > status & = ~ IRQ_INPROGRESS ;
}
2009-07-07 03:39:11 +04:00
static struct irq_chip intc_irq_chip = {
. name = " CF-INTC " ,
. mask = intc_irq_mask ,
. unmask = intc_irq_unmask ,
2010-10-13 07:42:22 +04:00
. mask_ack = intc_irq_mask ,
2009-07-07 03:39:11 +04:00
. ack = intc_irq_ack ,
. set_type = intc_irq_set_type ,
} ;
void __init init_IRQ ( void )
{
2010-10-13 07:42:22 +04:00
int irq , edge ;
2009-07-07 03:39:11 +04:00
init_vectors ( ) ;
/* Mask all interrupt sources */
writel ( 0x88888888 , MCF_MBAR + MCFSIM_ICR1 ) ;
writel ( 0x88888888 , MCF_MBAR + MCFSIM_ICR2 ) ;
writel ( 0x88888888 , MCF_MBAR + MCFSIM_ICR3 ) ;
writel ( 0x88888888 , MCF_MBAR + MCFSIM_ICR4 ) ;
for ( irq = 0 ; ( irq < NR_IRQS ) ; irq + + ) {
2010-09-09 11:12:53 +04:00
set_irq_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 ) {
set_irq_type ( irq , IRQ_TYPE_EDGE_RISING ) ;
set_irq_handler ( irq , intc_external_irq ) ;
} else {
set_irq_type ( irq , IRQ_TYPE_LEVEL_HIGH ) ;
set_irq_handler ( irq , handle_level_irq ) ;
}
2009-07-07 03:39:11 +04:00
}
}