2009-04-27 15:38:03 +10:00
/*
2010-08-19 19:04:58 +02:00
* intc - 2. c
*
2010-09-01 15:23:28 +02:00
* General interrupt controller code for the many ColdFire cores that use
* interrupt controllers with 63 interrupt sources , organized as 56 fully -
* programmable + 7 fixed - level interrupt sources . This includes the 523 x
* family , the 5270 , 5271 , 5274 , 5275 , and the 528 x family which have two such
* controllers , and the 547 x and 548 x families which have only one of them .
2009-04-27 15:38:03 +10:00
*
2011-03-11 17:06:58 +10:00
* The external 7 fixed interrupts are part the the Edge Port unit of these
* ColdFire parts . They can be configured as level or edge triggered .
*
* ( C ) Copyright 2009 - 2011 , Greg Ungerer < gerg @ snapgear . com >
2009-04-27 15:38:03 +10: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 .
*/
# include <linux/types.h>
# include <linux/init.h>
# include <linux/kernel.h>
# include <linux/interrupt.h>
# include <linux/irq.h>
# include <linux/io.h>
# include <asm/coldfire.h>
# include <asm/mcfsim.h>
# include <asm/traps.h>
/*
2010-09-01 15:23:28 +02:00
* Bit definitions for the ICR family of registers .
*/
# define MCFSIM_ICR_LEVEL(l) ((l)<<3) /* Level l intr */
# define MCFSIM_ICR_PRI(p) (p) /* Priority p intr */
2011-03-11 17:06:58 +10:00
/*
* The EDGE Port interrupts are the fixed 7 external interrupts .
* They need some special treatment , for example they need to be acked .
*/
# define EINT0 64 /* Is not actually used, but spot reserved for it */
# define EINT1 65 /* EDGE Port interrupt 1 */
# define EINT7 71 /* EDGE Port interrupt 7 */
2010-09-01 15:23:28 +02:00
# ifdef MCFICM_INTC1
# define NR_VECS 128
# else
# define NR_VECS 64
# endif
2009-04-27 15:38:03 +10:00
2011-02-06 23:39:14 +00:00
static void intc_irq_mask ( struct irq_data * d )
2009-04-27 15:38:03 +10:00
{
2011-03-07 17:21:43 +10:00
unsigned int irq = d - > irq - MCFINT_VECBASE ;
unsigned long imraddr ;
u32 val , imrbit ;
2011-02-06 23:39:14 +00:00
2010-09-01 15:23:28 +02:00
# ifdef MCFICM_INTC1
2011-03-07 17:21:43 +10:00
imraddr = ( irq & 0x40 ) ? MCFICM_INTC1 : MCFICM_INTC0 ;
2010-09-01 15:23:28 +02:00
# else
2011-03-07 17:21:43 +10:00
imraddr = MCFICM_INTC0 ;
2010-09-01 15:23:28 +02:00
# endif
2011-03-07 17:21:43 +10:00
imraddr + = ( irq & 0x20 ) ? MCFINTC_IMRH : MCFINTC_IMRL ;
imrbit = 0x1 < < ( irq & 0x1f ) ;
2009-04-27 15:38:03 +10:00
2011-03-07 17:21:43 +10:00
val = __raw_readl ( imraddr ) ;
__raw_writel ( val | imrbit , imraddr ) ;
2009-04-27 15:38:03 +10:00
}
2011-02-06 23:39:14 +00:00
static void intc_irq_unmask ( struct irq_data * d )
2009-04-27 15:38:03 +10:00
{
2011-03-07 17:21:43 +10:00
unsigned int irq = d - > irq - MCFINT_VECBASE ;
2011-03-07 17:42:28 +10:00
unsigned long imraddr ;
2011-03-07 17:21:43 +10:00
u32 val , imrbit ;
2011-02-06 23:39:14 +00:00
2010-09-01 15:23:28 +02:00
# ifdef MCFICM_INTC1
2011-03-07 17:42:28 +10:00
imraddr = ( irq & 0x40 ) ? MCFICM_INTC1 : MCFICM_INTC0 ;
2010-09-01 15:23:28 +02:00
# else
2011-03-07 17:42:28 +10:00
imraddr = MCFICM_INTC0 ;
2010-09-01 15:23:28 +02:00
# endif
2011-03-07 17:42:28 +10:00
imraddr + = ( ( irq & 0x20 ) ? MCFINTC_IMRH : MCFINTC_IMRL ) ;
2011-03-07 17:21:43 +10:00
imrbit = 0x1 < < ( irq & 0x1f ) ;
2009-04-27 15:38:03 +10:00
2011-03-07 17:21:43 +10:00
/* Don't set the "maskall" bit! */
if ( ( irq & 0x20 ) = = 0 )
imrbit | = 0x1 ;
2009-04-27 15:38:03 +10:00
2011-03-07 17:21:43 +10:00
val = __raw_readl ( imraddr ) ;
__raw_writel ( val & ~ imrbit , imraddr ) ;
2009-04-27 15:38:03 +10:00
}
2011-03-11 17:06:58 +10:00
/*
* Only the external ( or EDGE Port ) interrupts need to be acknowledged
* here , as part of the IRQ handler . They only really need to be ack ' ed
* if they are in edge triggered mode , but there is no harm in doing it
* for all types .
*/
static void intc_irq_ack ( struct irq_data * d )
2010-09-09 17:12:53 +10:00
{
2011-03-11 17:06:58 +10:00
unsigned int irq = d - > irq ;
__raw_writeb ( 0x1 < < ( irq - EINT0 ) , MCFEPORT_EPFR ) ;
2010-09-09 17:12:53 +10:00
}
2011-03-07 17:42:28 +10:00
/*
* Each vector needs a unique priority and level associated with it .
* We don ' t really care so much what they are , we don ' t rely on the
* traditional priority interrupt scheme of the m68k / ColdFire . This
* only needs to be set once for an interrupt , and we will never change
* these values once we have set them .
*/
static u8 intc_intpri = MCFSIM_ICR_LEVEL ( 6 ) | MCFSIM_ICR_PRI ( 6 ) ;
static unsigned int intc_irq_startup ( struct irq_data * d )
{
unsigned int irq = d - > irq - MCFINT_VECBASE ;
unsigned long icraddr ;
# ifdef MCFICM_INTC1
icraddr = ( irq & 0x40 ) ? MCFICM_INTC1 : MCFICM_INTC0 ;
# else
icraddr = MCFICM_INTC0 ;
# endif
icraddr + = MCFINTC_ICR0 + ( irq & 0x3f ) ;
if ( __raw_readb ( icraddr ) = = 0 )
__raw_writeb ( intc_intpri - - , icraddr ) ;
2011-03-11 17:06:58 +10:00
irq = d - > irq ;
if ( ( irq > = EINT1 ) & & ( irq < = EINT7 ) ) {
u8 v ;
irq - = EINT0 ;
/* Set EPORT line as input */
v = __raw_readb ( MCFEPORT_EPDDR ) ;
__raw_writeb ( v & ~ ( 0x1 < < irq ) , MCFEPORT_EPDDR ) ;
/* Set EPORT line as interrupt source */
v = __raw_readb ( MCFEPORT_EPIER ) ;
__raw_writeb ( v | ( 0x1 < < irq ) , MCFEPORT_EPIER ) ;
}
2011-03-07 17:42:28 +10:00
intc_irq_unmask ( d ) ;
return 0 ;
}
2011-03-11 17:06:58 +10:00
static int intc_irq_set_type ( struct irq_data * d , unsigned int type )
{
unsigned int irq = d - > irq ;
u16 pa , tb ;
switch ( type ) {
case IRQ_TYPE_EDGE_RISING :
tb = 0x1 ;
break ;
case IRQ_TYPE_EDGE_FALLING :
tb = 0x2 ;
break ;
case IRQ_TYPE_EDGE_BOTH :
tb = 0x3 ;
break ;
default :
/* Level triggered */
tb = 0 ;
break ;
}
if ( tb )
2011-03-28 13:31:17 +02:00
irq_set_handler ( irq , handle_edge_irq ) ;
2011-03-11 17:06:58 +10:00
irq - = EINT0 ;
pa = __raw_readw ( MCFEPORT_EPPAR ) ;
pa = ( pa & ~ ( 0x3 < < ( irq * 2 ) ) ) | ( tb < < ( irq * 2 ) ) ;
__raw_writew ( pa , MCFEPORT_EPPAR ) ;
return 0 ;
}
2009-04-27 15:38:03 +10:00
static struct irq_chip intc_irq_chip = {
. name = " CF-INTC " ,
2011-03-07 17:42:28 +10:00
. irq_startup = intc_irq_startup ,
2011-02-06 23:39:14 +00:00
. irq_mask = intc_irq_mask ,
. irq_unmask = intc_irq_unmask ,
2011-03-11 17:06:58 +10:00
} ;
static struct irq_chip intc_irq_chip_edge_port = {
. name = " CF-INTC-EP " ,
. irq_startup = intc_irq_startup ,
. irq_mask = intc_irq_mask ,
. irq_unmask = intc_irq_unmask ,
. irq_ack = intc_irq_ack ,
2011-02-06 23:39:14 +00:00
. irq_set_type = intc_irq_set_type ,
2009-04-27 15:38:03 +10:00
} ;
void __init init_IRQ ( void )
{
int irq ;
/* Mask all interrupt sources */
2011-03-05 22:17:17 +10:00
__raw_writel ( 0x1 , MCFICM_INTC0 + MCFINTC_IMRL ) ;
2010-09-01 15:23:28 +02:00
# ifdef MCFICM_INTC1
2011-03-05 22:17:17 +10:00
__raw_writel ( 0x1 , MCFICM_INTC1 + MCFINTC_IMRL ) ;
2010-09-01 15:23:28 +02:00
# endif
2009-04-27 15:38:03 +10:00
2011-03-07 17:21:43 +10:00
for ( irq = MCFINT_VECBASE ; ( irq < MCFINT_VECBASE + NR_VECS ) ; irq + + ) {
2011-03-11 17:06:58 +10:00
if ( ( irq > = EINT1 ) & & ( irq < = EINT7 ) )
2011-03-28 13:31:17 +02:00
irq_set_chip ( irq , & intc_irq_chip_edge_port ) ;
2011-03-11 17:06:58 +10:00
else
2011-03-28 13:31:17 +02:00
irq_set_chip ( irq , & intc_irq_chip ) ;
irq_set_irq_type ( irq , IRQ_TYPE_LEVEL_HIGH ) ;
irq_set_handler ( irq , handle_level_irq ) ;
2009-04-27 15:38:03 +10:00
}
}