2005-04-16 15:20:36 -07:00
/*
* linux / arch / arm / mach - imx / irq . c
*
* Copyright ( C ) 1999 ARM Limited
* Copyright ( C ) 2002 Shane Nay ( shane @ minirl . com )
*
* 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 Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*
* 03 / 03 / 2004 Sascha Hauer < sascha @ saschahauer . de >
* Copied from the motorola bsp package and added gpio demux
* interrupt handler
*/
# include <linux/init.h>
# include <linux/list.h>
# include <linux/timer.h>
2008-08-05 16:14:15 +01:00
# include <mach/hardware.h>
2005-04-16 15:20:36 -07:00
# include <asm/irq.h>
# include <asm/io.h>
# include <asm/mach/irq.h>
/*
*
* We simply use the ENABLE DISABLE registers inside of the IMX
* to turn on / off specific interrupts . FIXME - We should
* also add support for the accelerated interrupt controller
* by putting offets to irq jump code in the appropriate
* places .
*
*/
2007-11-18 22:54:24 +01:00
# define INTCNTL_OFF 0x00
# define NIMASK_OFF 0x04
# define INTENNUM_OFF 0x08
# define INTDISNUM_OFF 0x0C
# define INTENABLEH_OFF 0x10
# define INTENABLEL_OFF 0x14
# define INTTYPEH_OFF 0x18
# define INTTYPEL_OFF 0x1C
# define NIPRIORITY_OFF(x) (0x20+4*(7-(x)))
# define NIVECSR_OFF 0x40
# define FIVECSR_OFF 0x44
# define INTSRCH_OFF 0x48
# define INTSRCL_OFF 0x4C
# define INTFRCH_OFF 0x50
# define INTFRCL_OFF 0x54
# define NIPNDH_OFF 0x58
# define NIPNDL_OFF 0x5C
# define FIPNDH_OFF 0x60
# define FIPNDL_OFF 0x64
2005-04-16 15:20:36 -07:00
# define VA_AITC_BASE IO_ADDRESS(IMX_AITC_BASE)
2007-11-18 22:54:24 +01:00
# define IMX_AITC_INTCNTL (VA_AITC_BASE + INTCNTL_OFF)
# define IMX_AITC_NIMASK (VA_AITC_BASE + NIMASK_OFF)
2005-04-16 15:20:36 -07:00
# define IMX_AITC_INTENNUM (VA_AITC_BASE + INTENNUM_OFF)
2007-11-18 22:54:24 +01:00
# define IMX_AITC_INTDISNUM (VA_AITC_BASE + INTDISNUM_OFF)
# define IMX_AITC_INTENABLEH (VA_AITC_BASE + INTENABLEH_OFF)
# define IMX_AITC_INTENABLEL (VA_AITC_BASE + INTENABLEL_OFF)
# define IMX_AITC_INTTYPEH (VA_AITC_BASE + INTTYPEH_OFF)
# define IMX_AITC_INTTYPEL (VA_AITC_BASE + INTTYPEL_OFF)
# define IMX_AITC_NIPRIORITY(x) (VA_AITC_BASE + NIPRIORITY_OFF(x))
# define IMX_AITC_NIVECSR (VA_AITC_BASE + NIVECSR_OFF)
# define IMX_AITC_FIVECSR (VA_AITC_BASE + FIVECSR_OFF)
# define IMX_AITC_INTSRCH (VA_AITC_BASE + INTSRCH_OFF)
# define IMX_AITC_INTSRCL (VA_AITC_BASE + INTSRCL_OFF)
# define IMX_AITC_INTFRCH (VA_AITC_BASE + INTFRCH_OFF)
# define IMX_AITC_INTFRCL (VA_AITC_BASE + INTFRCL_OFF)
# define IMX_AITC_NIPNDH (VA_AITC_BASE + NIPNDH_OFF)
# define IMX_AITC_NIPNDL (VA_AITC_BASE + NIPNDL_OFF)
# define IMX_AITC_FIPNDH (VA_AITC_BASE + FIPNDH_OFF)
# define IMX_AITC_FIPNDL (VA_AITC_BASE + FIPNDL_OFF)
2005-04-16 15:20:36 -07:00
#if 0
# define DEBUG_IRQ(fmt...) printk(fmt)
# else
# define DEBUG_IRQ(fmt...) do { } while (0)
# endif
static void
imx_mask_irq ( unsigned int irq )
{
__raw_writel ( irq , IMX_AITC_INTDISNUM ) ;
}
static void
imx_unmask_irq ( unsigned int irq )
{
__raw_writel ( irq , IMX_AITC_INTENNUM ) ;
}
static int
imx_gpio_irq_type ( unsigned int _irq , unsigned int type )
{
unsigned int irq_type = 0 , irq , reg , bit ;
irq = _irq - IRQ_GPIOA ( 0 ) ;
reg = irq > > 5 ;
bit = 1 < < ( irq % 32 ) ;
2008-07-27 04:23:31 +01:00
if ( type = = IRQ_TYPE_PROBE ) {
2005-04-16 15:20:36 -07:00
/* Don't mess with enabled GPIOs using preconfigured edges or
GPIOs set to alternate function during probe */
/* TODO: support probe */
// if ((GPIO_IRQ_rising_edge[idx] | GPIO_IRQ_falling_edge[idx]) &
// GPIO_bit(gpio))
// return 0;
// if (GAFR(gpio) & (0x3 << (((gpio) & 0xf)*2)))
// return 0;
2008-07-27 04:23:31 +01:00
// type = IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING;
2005-04-16 15:20:36 -07:00
}
GIUS ( reg ) | = bit ;
DDIR ( reg ) & = ~ ( bit ) ;
DEBUG_IRQ ( " setting type of irq %d to " , _irq ) ;
2008-07-27 04:23:31 +01:00
if ( type & IRQ_TYPE_EDGE_RISING ) {
2005-04-16 15:20:36 -07:00
DEBUG_IRQ ( " rising edges \n " ) ;
irq_type = 0x0 ;
}
2008-07-27 04:23:31 +01:00
if ( type & IRQ_TYPE_EDGE_FALLING ) {
2005-04-16 15:20:36 -07:00
DEBUG_IRQ ( " falling edges \n " ) ;
irq_type = 0x1 ;
}
2008-07-27 04:23:31 +01:00
if ( type & IRQ_TYPE_LEVEL_LOW ) {
2005-04-16 15:20:36 -07:00
DEBUG_IRQ ( " low level \n " ) ;
irq_type = 0x3 ;
}
2008-07-27 04:23:31 +01:00
if ( type & IRQ_TYPE_LEVEL_HIGH ) {
2005-04-16 15:20:36 -07:00
DEBUG_IRQ ( " high level \n " ) ;
irq_type = 0x2 ;
}
if ( irq % 32 < 16 ) {
ICR1 ( reg ) = ( ICR1 ( reg ) & ~ ( 0x3 < < ( ( irq % 16 ) * 2 ) ) ) |
( irq_type < < ( ( irq % 16 ) * 2 ) ) ;
} else {
ICR2 ( reg ) = ( ICR2 ( reg ) & ~ ( 0x3 < < ( ( irq % 16 ) * 2 ) ) ) |
( irq_type < < ( ( irq % 16 ) * 2 ) ) ;
}
return 0 ;
}
static void
imx_gpio_ack_irq ( unsigned int irq )
{
2008-03-04 15:08:02 -08:00
DEBUG_IRQ ( " %s: irq %d \n " , __func__ , irq ) ;
2006-06-08 22:46:48 +01:00
ISR ( IRQ_TO_REG ( irq ) ) = 1 < < ( ( irq - IRQ_GPIOA ( 0 ) ) % 32 ) ;
2005-04-16 15:20:36 -07:00
}
static void
imx_gpio_mask_irq ( unsigned int irq )
{
2008-03-04 15:08:02 -08:00
DEBUG_IRQ ( " %s: irq %d \n " , __func__ , irq ) ;
2005-04-16 15:20:36 -07:00
IMR ( IRQ_TO_REG ( irq ) ) & = ~ ( 1 < < ( ( irq - IRQ_GPIOA ( 0 ) ) % 32 ) ) ;
}
static void
imx_gpio_unmask_irq ( unsigned int irq )
{
2008-03-04 15:08:02 -08:00
DEBUG_IRQ ( " %s: irq %d \n " , __func__ , irq ) ;
2005-04-16 15:20:36 -07:00
IMR ( IRQ_TO_REG ( irq ) ) | = 1 < < ( ( irq - IRQ_GPIOA ( 0 ) ) % 32 ) ;
}
static void
imx_gpio_handler ( unsigned int mask , unsigned int irq ,
2006-11-23 11:41:32 +00:00
struct irq_desc * desc )
2005-04-16 15:20:36 -07:00
{
desc = irq_desc + irq ;
while ( mask ) {
if ( mask & 1 ) {
DEBUG_IRQ ( " handling irq %d \n " , irq ) ;
2006-10-06 10:53:39 -07:00
desc_handle_irq ( irq , desc ) ;
2005-04-16 15:20:36 -07:00
}
irq + + ;
desc + + ;
mask > > = 1 ;
}
}
static void
2006-11-23 11:41:32 +00:00
imx_gpioa_demux_handler ( unsigned int irq_unused , struct irq_desc * desc )
2005-04-16 15:20:36 -07:00
{
unsigned int mask , irq ;
mask = ISR ( 0 ) ;
irq = IRQ_GPIOA ( 0 ) ;
2006-10-06 10:53:39 -07:00
imx_gpio_handler ( mask , irq , desc ) ;
2005-04-16 15:20:36 -07:00
}
static void
2006-11-23 11:41:32 +00:00
imx_gpiob_demux_handler ( unsigned int irq_unused , struct irq_desc * desc )
2005-04-16 15:20:36 -07:00
{
unsigned int mask , irq ;
mask = ISR ( 1 ) ;
irq = IRQ_GPIOB ( 0 ) ;
2006-10-06 10:53:39 -07:00
imx_gpio_handler ( mask , irq , desc ) ;
2005-04-16 15:20:36 -07:00
}
static void
2006-11-23 11:41:32 +00:00
imx_gpioc_demux_handler ( unsigned int irq_unused , struct irq_desc * desc )
2005-04-16 15:20:36 -07:00
{
unsigned int mask , irq ;
mask = ISR ( 2 ) ;
irq = IRQ_GPIOC ( 0 ) ;
2006-10-06 10:53:39 -07:00
imx_gpio_handler ( mask , irq , desc ) ;
2005-04-16 15:20:36 -07:00
}
static void
2006-11-23 11:41:32 +00:00
imx_gpiod_demux_handler ( unsigned int irq_unused , struct irq_desc * desc )
2005-04-16 15:20:36 -07:00
{
unsigned int mask , irq ;
mask = ISR ( 3 ) ;
irq = IRQ_GPIOD ( 0 ) ;
2006-10-06 10:53:39 -07:00
imx_gpio_handler ( mask , irq , desc ) ;
2005-04-16 15:20:36 -07:00
}
2006-08-01 22:26:25 +01:00
static struct irq_chip imx_internal_chip = {
. name = " MPU " ,
2005-04-16 15:20:36 -07:00
. ack = imx_mask_irq ,
. mask = imx_mask_irq ,
. unmask = imx_unmask_irq ,
} ;
2006-08-01 22:26:25 +01:00
static struct irq_chip imx_gpio_chip = {
. name = " GPIO " ,
2005-04-16 15:20:36 -07:00
. ack = imx_gpio_ack_irq ,
. mask = imx_gpio_mask_irq ,
. unmask = imx_gpio_unmask_irq ,
2005-09-04 19:43:13 +01:00
. set_type = imx_gpio_irq_type ,
2005-04-16 15:20:36 -07:00
} ;
void __init
imx_init_irq ( void )
{
unsigned int irq ;
DEBUG_IRQ ( " Initializing imx interrupts \n " ) ;
2007-11-18 22:54:24 +01:00
/* Disable all interrupts initially. */
/* Do not rely on the bootloader. */
__raw_writel ( 0 , IMX_AITC_INTENABLEH ) ;
__raw_writel ( 0 , IMX_AITC_INTENABLEL ) ;
/* Mask all GPIO interrupts as well */
2005-04-16 15:20:36 -07:00
IMR ( 0 ) = 0 ;
IMR ( 1 ) = 0 ;
IMR ( 2 ) = 0 ;
IMR ( 3 ) = 0 ;
for ( irq = 0 ; irq < IMX_IRQS ; irq + + ) {
set_irq_chip ( irq , & imx_internal_chip ) ;
2006-11-23 11:41:32 +00:00
set_irq_handler ( irq , handle_level_irq ) ;
2005-04-16 15:20:36 -07:00
set_irq_flags ( irq , IRQF_VALID ) ;
}
for ( irq = IRQ_GPIOA ( 0 ) ; irq < IRQ_GPIOD ( 32 ) ; irq + + ) {
set_irq_chip ( irq , & imx_gpio_chip ) ;
2006-11-23 11:41:32 +00:00
set_irq_handler ( irq , handle_edge_irq ) ;
2005-04-16 15:20:36 -07:00
set_irq_flags ( irq , IRQF_VALID ) ;
}
set_irq_chained_handler ( GPIO_INT_PORTA , imx_gpioa_demux_handler ) ;
set_irq_chained_handler ( GPIO_INT_PORTB , imx_gpiob_demux_handler ) ;
set_irq_chained_handler ( GPIO_INT_PORTC , imx_gpioc_demux_handler ) ;
set_irq_chained_handler ( GPIO_INT_PORTD , imx_gpiod_demux_handler ) ;
2007-11-18 22:54:24 +01:00
/* Release masking of interrupts according to priority */
__raw_writel ( - 1 , IMX_AITC_NIMASK ) ;
2005-04-16 15:20:36 -07:00
}