2009-04-27 09:09:29 +04:00
/*
* intc - simr . c
*
2010-08-19 21:04:58 +04:00
* Interrupt controller code for the ColdFire 5208 , 5207 & 532 x parts .
*
2011-03-11 15:25:44 +03:00
* ( C ) Copyright 2009 - 2011 , Greg Ungerer < gerg @ snapgear . com >
2009-04-27 09:09:29 +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 .
*/
# 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>
2011-03-11 15:25:44 +03:00
/*
* The EDGE Port interrupts are the fixed 7 external interrupts .
* They need some special treatment , for example they need to be acked .
*/
# ifdef CONFIG_M520x
/*
* The 520 x parts only support a limited range of these external
* interrupts , only 1 , 4 and 7 ( as interrupts 65 , 66 and 67 ) .
*/
# define EINT0 64 /* Is not actually used, but spot reserved for it */
# define EINT1 65 /* EDGE Port interrupt 1 */
# define EINT4 66 /* EDGE Port interrupt 4 */
# define EINT7 67 /* EDGE Port interrupt 7 */
static unsigned int irqebitmap [ ] = { 0 , 1 , 4 , 7 } ;
2017-07-13 00:37:22 +03:00
static inline unsigned int irq2ebit ( unsigned int irq )
2011-03-11 15:25:44 +03:00
{
return irqebitmap [ irq - EINT0 ] ;
}
# else
/*
* Most of the ColdFire parts with the EDGE Port module just have
* a strait direct mapping of the 7 external interrupts . Although
* there is a bit reserved for 0 , it is not used .
*/
# 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 */
2017-07-13 00:37:22 +03:00
static inline unsigned int irq2ebit ( unsigned int irq )
2011-03-11 15:25:44 +03:00
{
return irq - EINT0 ;
}
# endif
2011-03-07 16:00:04 +03:00
/*
2012-06-07 01:28:31 +04:00
* There maybe one , two or three interrupt control units , each has 64
* interrupts . If there is no second or third unit then MCFINTC1_ * or
* MCFINTC2_ * defines will be 0 ( and code for them optimized away ) .
2011-03-07 16:00:04 +03:00
*/
2011-02-07 02:39:14 +03:00
static void intc_irq_mask ( struct irq_data * d )
2009-04-27 09:09:29 +04:00
{
2011-03-07 16:00:04 +03:00
unsigned int irq = d - > irq - MCFINT_VECBASE ;
2011-02-07 02:39:14 +03:00
2012-06-07 01:28:31 +04:00
if ( MCFINTC2_SIMR & & ( irq > 128 ) )
__raw_writeb ( irq - 128 , MCFINTC2_SIMR ) ;
else if ( MCFINTC1_SIMR & & ( irq > 64 ) )
2011-03-07 16:00:04 +03:00
__raw_writeb ( irq - 64 , MCFINTC1_SIMR ) ;
else
__raw_writeb ( irq , MCFINTC0_SIMR ) ;
2009-04-27 09:09:29 +04:00
}
2011-02-07 02:39:14 +03:00
static void intc_irq_unmask ( struct irq_data * d )
2009-04-27 09:09:29 +04:00
{
2011-03-07 16:00:04 +03:00
unsigned int irq = d - > irq - MCFINT_VECBASE ;
2011-02-07 02:39:14 +03:00
2012-06-07 01:28:31 +04:00
if ( MCFINTC2_CIMR & & ( irq > 128 ) )
__raw_writeb ( irq - 128 , MCFINTC2_CIMR ) ;
else if ( MCFINTC1_CIMR & & ( irq > 64 ) )
2011-03-07 16:00:04 +03:00
__raw_writeb ( irq - 64 , MCFINTC1_CIMR ) ;
else
__raw_writeb ( irq , MCFINTC0_CIMR ) ;
2009-04-27 09:09:29 +04:00
}
2011-03-11 15:25:44 +03:00
static void intc_irq_ack ( struct irq_data * d )
2009-04-27 09:09:29 +04:00
{
2011-03-11 15:25:44 +03:00
unsigned int ebit = irq2ebit ( d - > irq ) ;
2011-02-07 02:39:14 +03:00
2011-03-11 15:25:44 +03:00
__raw_writeb ( 0x1 < < ebit , MCFEPORT_EPFR ) ;
}
static unsigned int intc_irq_startup ( struct irq_data * d )
{
unsigned int irq = d - > irq ;
if ( ( irq > = EINT1 ) & & ( irq < = EINT7 ) ) {
unsigned int ebit = irq2ebit ( irq ) ;
u8 v ;
2012-06-07 01:28:31 +04:00
# if defined(MCFEPORT_EPDDR)
2011-03-11 15:25:44 +03:00
/* Set EPORT line as input */
v = __raw_readb ( MCFEPORT_EPDDR ) ;
__raw_writeb ( v & ~ ( 0x1 < < ebit ) , MCFEPORT_EPDDR ) ;
2012-06-07 01:28:31 +04:00
# endif
2011-03-11 15:25:44 +03:00
/* Set EPORT line as interrupt source */
v = __raw_readb ( MCFEPORT_EPIER ) ;
__raw_writeb ( v | ( 0x1 < < ebit ) , MCFEPORT_EPIER ) ;
}
irq - = MCFINT_VECBASE ;
2012-06-07 01:28:31 +04:00
if ( MCFINTC2_ICR0 & & ( irq > 128 ) )
__raw_writeb ( 5 , MCFINTC2_ICR0 + irq - 128 ) ;
else if ( MCFINTC1_ICR0 & & ( irq > 64 ) )
2011-03-07 16:00:04 +03:00
__raw_writeb ( 5 , MCFINTC1_ICR0 + irq - 64 ) ;
else
__raw_writeb ( 5 , MCFINTC0_ICR0 + irq ) ;
2011-03-11 15:25:44 +03:00
intc_irq_unmask ( d ) ;
return 0 ;
}
static int intc_irq_set_type ( struct irq_data * d , unsigned int type )
{
unsigned int ebit , 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 15:31:17 +04:00
irq_set_handler ( irq , handle_edge_irq ) ;
2011-03-11 15:25:44 +03:00
ebit = irq2ebit ( irq ) * 2 ;
pa = __raw_readw ( MCFEPORT_EPPAR ) ;
pa = ( pa & ~ ( 0x3 < < ebit ) ) | ( tb < < ebit ) ;
__raw_writew ( pa , MCFEPORT_EPPAR ) ;
2009-04-27 09:09:29 +04:00
return 0 ;
}
static struct irq_chip intc_irq_chip = {
. name = " CF-INTC " ,
2011-03-11 15:25:44 +03:00
. irq_startup = intc_irq_startup ,
. irq_mask = intc_irq_mask ,
. irq_unmask = intc_irq_unmask ,
} ;
static struct irq_chip intc_irq_chip_edge_port = {
. name = " CF-INTC-EP " ,
. irq_startup = intc_irq_startup ,
2011-02-07 02:39:14 +03:00
. irq_mask = intc_irq_mask ,
. irq_unmask = intc_irq_unmask ,
2011-03-11 15:25:44 +03:00
. irq_ack = intc_irq_ack ,
2011-02-07 02:39:14 +03:00
. irq_set_type = intc_irq_set_type ,
2009-04-27 09:09:29 +04:00
} ;
void __init init_IRQ ( void )
{
2011-03-07 16:00:04 +03:00
int irq , eirq ;
2009-04-27 09:09:29 +04:00
2009-05-06 08:28:25 +04:00
/* Mask all interrupt sources */
__raw_writeb ( 0xff , MCFINTC0_SIMR ) ;
if ( MCFINTC1_SIMR )
__raw_writeb ( 0xff , MCFINTC1_SIMR ) ;
2012-06-07 01:28:31 +04:00
if ( MCFINTC2_SIMR )
__raw_writeb ( 0xff , MCFINTC2_SIMR ) ;
2009-05-06 08:28:25 +04:00
2012-06-07 01:28:31 +04:00
eirq = MCFINT_VECBASE + 64 + ( MCFINTC1_ICR0 ? 64 : 0 ) +
( MCFINTC2_ICR0 ? 64 : 0 ) ;
2011-03-07 16:00:04 +03:00
for ( irq = MCFINT_VECBASE ; ( irq < eirq ) ; irq + + ) {
2011-03-11 15:25:44 +03:00
if ( ( irq > = EINT1 ) & & ( irq < = EINT7 ) )
2011-03-28 15:31:17 +04:00
irq_set_chip ( irq , & intc_irq_chip_edge_port ) ;
2011-03-11 15:25:44 +03:00
else
2011-03-28 15:31:17 +04: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 09:09:29 +04:00
}
}