2006-01-09 20:05:41 +03:00
/*
2007-02-05 13:42:07 +03:00
* linux / arch / arm / mach - at91 / irq . c
2006-01-09 20:05:41 +03:00
*
* Copyright ( C ) 2004 SAN People
* Copyright ( C ) 2004 ATMEL
* Copyright ( C ) Rick Bronson
*
* 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
*/
# include <linux/init.h>
# include <linux/module.h>
# include <linux/mm.h>
# include <linux/types.h>
2008-08-05 19:14:15 +04:00
# include <mach/hardware.h>
2006-01-09 20:05:41 +03:00
# include <asm/irq.h>
# include <asm/setup.h>
# include <asm/mach/arch.h>
# include <asm/mach/irq.h>
# include <asm/mach/map.h>
2006-07-05 20:22:52 +04:00
static void at91_aic_mask_irq ( unsigned int irq )
2006-01-09 20:05:41 +03:00
{
/* Disable interrupt on AIC */
at91_sys_write ( AT91_AIC_IDCR , 1 < < irq ) ;
}
2006-07-05 20:22:52 +04:00
static void at91_aic_unmask_irq ( unsigned int irq )
2006-01-09 20:05:41 +03:00
{
/* Enable interrupt on AIC */
at91_sys_write ( AT91_AIC_IECR , 1 < < irq ) ;
}
2006-11-30 12:01:47 +03:00
unsigned int at91_extern_irq ;
# define is_extern_irq(irq) ((1 << (irq)) & at91_extern_irq)
2006-07-05 20:22:52 +04:00
static int at91_aic_set_type ( unsigned irq , unsigned type )
2006-01-09 20:05:41 +03:00
{
unsigned int smr , srctype ;
switch ( type ) {
2008-07-27 07:23:31 +04:00
case IRQ_TYPE_LEVEL_HIGH :
2006-01-09 20:05:41 +03:00
srctype = AT91_AIC_SRCTYPE_HIGH ;
break ;
2008-07-27 07:23:31 +04:00
case IRQ_TYPE_EDGE_RISING :
2006-01-09 20:05:41 +03:00
srctype = AT91_AIC_SRCTYPE_RISING ;
break ;
2008-07-27 07:23:31 +04:00
case IRQ_TYPE_LEVEL_LOW :
2006-11-30 12:01:47 +03:00
if ( ( irq = = AT91_ID_FIQ ) | | is_extern_irq ( irq ) ) /* only supported on external interrupts */
srctype = AT91_AIC_SRCTYPE_LOW ;
else
2006-06-19 18:26:52 +04:00
return - EINVAL ;
2006-01-09 20:05:41 +03:00
break ;
2008-07-27 07:23:31 +04:00
case IRQ_TYPE_EDGE_FALLING :
2006-11-30 12:01:47 +03:00
if ( ( irq = = AT91_ID_FIQ ) | | is_extern_irq ( irq ) ) /* only supported on external interrupts */
srctype = AT91_AIC_SRCTYPE_FALLING ;
else
2006-06-19 18:26:52 +04:00
return - EINVAL ;
2006-01-09 20:05:41 +03:00
break ;
default :
return - EINVAL ;
}
smr = at91_sys_read ( AT91_AIC_SMR ( irq ) ) & ~ AT91_AIC_SRCTYPE ;
at91_sys_write ( AT91_AIC_SMR ( irq ) , smr | srctype ) ;
return 0 ;
}
2006-06-19 18:26:53 +04:00
# ifdef CONFIG_PM
static u32 wakeups ;
static u32 backups ;
2006-07-05 20:22:52 +04:00
static int at91_aic_set_wake ( unsigned irq , unsigned value )
2006-06-19 18:26:53 +04:00
{
if ( unlikely ( irq > = 32 ) )
return - EINVAL ;
if ( value )
wakeups | = ( 1 < < irq ) ;
else
wakeups & = ~ ( 1 < < irq ) ;
return 0 ;
}
void at91_irq_suspend ( void )
{
backups = at91_sys_read ( AT91_AIC_IMR ) ;
at91_sys_write ( AT91_AIC_IDCR , backups ) ;
at91_sys_write ( AT91_AIC_IECR , wakeups ) ;
}
void at91_irq_resume ( void )
{
at91_sys_write ( AT91_AIC_IDCR , wakeups ) ;
at91_sys_write ( AT91_AIC_IECR , backups ) ;
}
# else
2006-07-05 20:22:52 +04:00
# define at91_aic_set_wake NULL
2006-06-19 18:26:53 +04:00
# endif
2006-08-02 01:26:25 +04:00
static struct irq_chip at91_aic_chip = {
. name = " AIC " ,
2006-07-05 20:22:52 +04:00
. ack = at91_aic_mask_irq ,
. mask = at91_aic_mask_irq ,
. unmask = at91_aic_unmask_irq ,
. set_type = at91_aic_set_type ,
. set_wake = at91_aic_set_wake ,
2006-01-09 20:05:41 +03:00
} ;
/*
* Initialize the AIC interrupt controller .
*/
2006-07-05 20:22:52 +04:00
void __init at91_aic_init ( unsigned int priority [ NR_AIC_IRQS ] )
2006-01-09 20:05:41 +03:00
{
unsigned int i ;
/*
* The IVR is used by macro get_irqnr_and_base to read and verify .
* The irq number is NR_AIC_IRQS when a spurious interrupt has occurred .
*/
for ( i = 0 ; i < NR_AIC_IRQS ; i + + ) {
/* Put irq number in Source Vector Register: */
at91_sys_write ( AT91_AIC_SVR ( i ) , i ) ;
2006-07-05 20:22:52 +04:00
/* Active Low interrupt, with the specified priority */
2006-01-09 20:05:41 +03:00
at91_sys_write ( AT91_AIC_SMR ( i ) , AT91_AIC_SRCTYPE_LOW | priority [ i ] ) ;
2006-07-05 20:22:52 +04:00
set_irq_chip ( i , & at91_aic_chip ) ;
2006-11-23 14:41:32 +03:00
set_irq_handler ( i , handle_level_irq ) ;
2006-01-09 20:05:41 +03:00
set_irq_flags ( i , IRQF_VALID | IRQF_PROBE ) ;
/* Perform 8 End Of Interrupt Command to make sure AIC will not Lock out nIRQ */
if ( i < 8 )
at91_sys_write ( AT91_AIC_EOICR , 0 ) ;
}
/*
* Spurious Interrupt ID in Spurious Vector Register is NR_AIC_IRQS
* When there is no current interrupt , the IRQ Vector Register reads the value stored in AIC_SPU
*/
at91_sys_write ( AT91_AIC_SPU , NR_AIC_IRQS ) ;
/* No debugging in AIC: Debug (Protect) Control Register */
at91_sys_write ( AT91_AIC_DCR , 0 ) ;
/* Disable and clear all interrupts initially */
at91_sys_write ( AT91_AIC_IDCR , 0xFFFFFFFF ) ;
at91_sys_write ( AT91_AIC_ICCR , 0xFFFFFFFF ) ;
}