2005-04-17 02:20:36 +04: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>
# include <asm/hardware.h>
# 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 .
*
*/
# define INTENNUM_OFF 0x8
# define INTDISNUM_OFF 0xC
# define VA_AITC_BASE IO_ADDRESS(IMX_AITC_BASE)
# define IMX_AITC_INTDISNUM (VA_AITC_BASE + INTDISNUM_OFF)
# define IMX_AITC_INTENNUM (VA_AITC_BASE + INTENNUM_OFF)
#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 ) ;
if ( type = = IRQT_PROBE ) {
/* 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;
// type = __IRQT_RISEDGE | __IRQT_FALEDGE;
}
GIUS ( reg ) | = bit ;
DDIR ( reg ) & = ~ ( bit ) ;
DEBUG_IRQ ( " setting type of irq %d to " , _irq ) ;
if ( type & __IRQT_RISEDGE ) {
DEBUG_IRQ ( " rising edges \n " ) ;
irq_type = 0x0 ;
}
if ( type & __IRQT_FALEDGE ) {
DEBUG_IRQ ( " falling edges \n " ) ;
irq_type = 0x1 ;
}
if ( type & __IRQT_LOWLVL ) {
DEBUG_IRQ ( " low level \n " ) ;
irq_type = 0x3 ;
}
if ( type & __IRQT_HIGHLVL ) {
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 )
{
DEBUG_IRQ ( " %s: irq %d \n " , __FUNCTION__ , irq ) ;
2006-06-09 01:46:48 +04:00
ISR ( IRQ_TO_REG ( irq ) ) = 1 < < ( ( irq - IRQ_GPIOA ( 0 ) ) % 32 ) ;
2005-04-17 02:20:36 +04:00
}
static void
imx_gpio_mask_irq ( unsigned int irq )
{
DEBUG_IRQ ( " %s: irq %d \n " , __FUNCTION__ , irq ) ;
IMR ( IRQ_TO_REG ( irq ) ) & = ~ ( 1 < < ( ( irq - IRQ_GPIOA ( 0 ) ) % 32 ) ) ;
}
static void
imx_gpio_unmask_irq ( unsigned int irq )
{
DEBUG_IRQ ( " %s: irq %d \n " , __FUNCTION__ , irq ) ;
IMR ( IRQ_TO_REG ( irq ) ) | = 1 < < ( ( irq - IRQ_GPIOA ( 0 ) ) % 32 ) ;
}
static void
imx_gpio_handler ( unsigned int mask , unsigned int irq ,
struct irqdesc * desc , struct pt_regs * regs )
{
desc = irq_desc + irq ;
while ( mask ) {
if ( mask & 1 ) {
DEBUG_IRQ ( " handling irq %d \n " , irq ) ;
2005-09-04 22:45:00 +04:00
desc_handle_irq ( irq , desc , regs ) ;
2005-04-17 02:20:36 +04:00
}
irq + + ;
desc + + ;
mask > > = 1 ;
}
}
static void
imx_gpioa_demux_handler ( unsigned int irq_unused , struct irqdesc * desc ,
struct pt_regs * regs )
{
unsigned int mask , irq ;
mask = ISR ( 0 ) ;
irq = IRQ_GPIOA ( 0 ) ;
imx_gpio_handler ( mask , irq , desc , regs ) ;
}
static void
imx_gpiob_demux_handler ( unsigned int irq_unused , struct irqdesc * desc ,
struct pt_regs * regs )
{
unsigned int mask , irq ;
mask = ISR ( 1 ) ;
irq = IRQ_GPIOB ( 0 ) ;
imx_gpio_handler ( mask , irq , desc , regs ) ;
}
static void
imx_gpioc_demux_handler ( unsigned int irq_unused , struct irqdesc * desc ,
struct pt_regs * regs )
{
unsigned int mask , irq ;
mask = ISR ( 2 ) ;
irq = IRQ_GPIOC ( 0 ) ;
imx_gpio_handler ( mask , irq , desc , regs ) ;
}
static void
imx_gpiod_demux_handler ( unsigned int irq_unused , struct irqdesc * desc ,
struct pt_regs * regs )
{
unsigned int mask , irq ;
mask = ISR ( 3 ) ;
irq = IRQ_GPIOD ( 0 ) ;
imx_gpio_handler ( mask , irq , desc , regs ) ;
}
static struct irqchip imx_internal_chip = {
. ack = imx_mask_irq ,
. mask = imx_mask_irq ,
. unmask = imx_unmask_irq ,
} ;
static struct irqchip imx_gpio_chip = {
. ack = imx_gpio_ack_irq ,
. mask = imx_gpio_mask_irq ,
. unmask = imx_gpio_unmask_irq ,
2005-09-04 22:43:13 +04:00
. set_type = imx_gpio_irq_type ,
2005-04-17 02:20:36 +04:00
} ;
void __init
imx_init_irq ( void )
{
unsigned int irq ;
DEBUG_IRQ ( " Initializing imx interrupts \n " ) ;
/* Mask all interrupts initially */
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 ) ;
set_irq_handler ( irq , do_level_IRQ ) ;
set_irq_flags ( irq , IRQF_VALID ) ;
}
for ( irq = IRQ_GPIOA ( 0 ) ; irq < IRQ_GPIOD ( 32 ) ; irq + + ) {
set_irq_chip ( irq , & imx_gpio_chip ) ;
set_irq_handler ( irq , do_edge_IRQ ) ;
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 ) ;
/* Disable all interrupts initially. */
/* In IMX this is done in the bootloader. */
}