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>
2011-11-23 01:26:09 +04:00
# include <linux/irq.h>
# include <linux/of.h>
# include <linux/of_address.h>
# include <linux/of_irq.h>
# include <linux/irqdomain.h>
# include <linux/err.h>
2006-01-09 20:05:41 +03:00
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>
2011-11-02 21:12:50 +04:00
void __iomem * at91_aic_base ;
2011-11-23 01:26:09 +04:00
static struct irq_domain * at91_aic_domain ;
static struct device_node * at91_aic_np ;
2006-01-09 20:05:41 +03:00
2010-11-29 12:26:19 +03:00
static void at91_aic_mask_irq ( struct irq_data * d )
2006-01-09 20:05:41 +03:00
{
/* Disable interrupt on AIC */
2011-11-23 01:26:09 +04:00
at91_aic_write ( AT91_AIC_IDCR , 1 < < d - > hwirq ) ;
2006-01-09 20:05:41 +03:00
}
2010-11-29 12:26:19 +03:00
static void at91_aic_unmask_irq ( struct irq_data * d )
2006-01-09 20:05:41 +03:00
{
/* Enable interrupt on AIC */
2011-11-23 01:26:09 +04:00
at91_aic_write ( AT91_AIC_IECR , 1 < < d - > hwirq ) ;
2006-01-09 20:05:41 +03:00
}
2006-11-30 12:01:47 +03:00
unsigned int at91_extern_irq ;
2011-11-23 01:26:09 +04:00
# define is_extern_irq(hwirq) ((1 << (hwirq)) & at91_extern_irq)
2006-11-30 12:01:47 +03:00
2010-11-29 12:26:19 +03:00
static int at91_aic_set_type ( struct irq_data * d , 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 :
2011-11-23 01:26:09 +04:00
if ( ( d - > hwirq = = AT91_ID_FIQ ) | | is_extern_irq ( d - > hwirq ) ) /* only supported on external interrupts */
2006-11-30 12:01:47 +03:00
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 :
2011-11-23 01:26:09 +04:00
if ( ( d - > hwirq = = AT91_ID_FIQ ) | | is_extern_irq ( d - > hwirq ) ) /* only supported on external interrupts */
2006-11-30 12:01:47 +03:00
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 ;
}
2011-11-23 01:26:09 +04:00
smr = at91_aic_read ( AT91_AIC_SMR ( d - > hwirq ) ) & ~ AT91_AIC_SRCTYPE ;
at91_aic_write ( AT91_AIC_SMR ( d - > hwirq ) , smr | srctype ) ;
2006-01-09 20:05:41 +03:00
return 0 ;
}
2006-06-19 18:26:53 +04:00
# ifdef CONFIG_PM
static u32 wakeups ;
static u32 backups ;
2010-11-29 12:26:19 +03:00
static int at91_aic_set_wake ( struct irq_data * d , unsigned value )
2006-06-19 18:26:53 +04:00
{
2011-11-23 01:26:09 +04:00
if ( unlikely ( d - > hwirq > = NR_AIC_IRQS ) )
2006-06-19 18:26:53 +04:00
return - EINVAL ;
if ( value )
2011-11-23 01:26:09 +04:00
wakeups | = ( 1 < < d - > hwirq ) ;
2006-06-19 18:26:53 +04:00
else
2011-11-23 01:26:09 +04:00
wakeups & = ~ ( 1 < < d - > hwirq ) ;
2006-06-19 18:26:53 +04:00
return 0 ;
}
void at91_irq_suspend ( void )
{
2011-11-02 21:12:50 +04:00
backups = at91_aic_read ( AT91_AIC_IMR ) ;
at91_aic_write ( AT91_AIC_IDCR , backups ) ;
at91_aic_write ( AT91_AIC_IECR , wakeups ) ;
2006-06-19 18:26:53 +04:00
}
void at91_irq_resume ( void )
{
2011-11-02 21:12:50 +04:00
at91_aic_write ( AT91_AIC_IDCR , wakeups ) ;
at91_aic_write ( AT91_AIC_IECR , backups ) ;
2006-06-19 18:26:53 +04:00
}
# 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 " ,
2010-11-29 12:26:19 +03:00
. irq_ack = at91_aic_mask_irq ,
. irq_mask = at91_aic_mask_irq ,
. irq_unmask = at91_aic_unmask_irq ,
. irq_set_type = at91_aic_set_type ,
. irq_set_wake = at91_aic_set_wake ,
2006-01-09 20:05:41 +03:00
} ;
2012-02-14 21:08:14 +04:00
static void __init at91_aic_hw_init ( unsigned int spu_vector )
{
int i ;
/*
* Perform 8 End Of Interrupt Command to make sure AIC
* will not Lock out nIRQ
*/
for ( i = 0 ; i < 8 ; i + + )
at91_aic_write ( AT91_AIC_EOICR , 0 ) ;
/*
* Spurious Interrupt ID in Spurious Vector Register .
* When there is no current interrupt , the IRQ Vector Register
* reads the value stored in AIC_SPU
*/
at91_aic_write ( AT91_AIC_SPU , spu_vector ) ;
/* No debugging in AIC: Debug (Protect) Control Register */
at91_aic_write ( AT91_AIC_DCR , 0 ) ;
/* Disable and clear all interrupts initially */
at91_aic_write ( AT91_AIC_IDCR , 0xFFFFFFFF ) ;
at91_aic_write ( AT91_AIC_ICCR , 0xFFFFFFFF ) ;
}
2011-11-23 01:26:09 +04:00
# if defined(CONFIG_OF)
2012-02-14 21:08:14 +04:00
static int at91_aic_irq_map ( struct irq_domain * h , unsigned int virq ,
irq_hw_number_t hw )
2011-11-23 01:26:09 +04:00
{
2012-02-14 21:08:14 +04:00
/* Put virq number in Source Vector Register */
at91_aic_write ( AT91_AIC_SVR ( hw ) , virq ) ;
/* Active Low interrupt, without priority */
at91_aic_write ( AT91_AIC_SMR ( hw ) , AT91_AIC_SRCTYPE_LOW ) ;
irq_set_chip_and_handler ( virq , & at91_aic_chip , handle_level_irq ) ;
set_irq_flags ( virq , IRQF_VALID | IRQF_PROBE ) ;
2011-11-23 01:26:09 +04:00
return 0 ;
}
2012-02-14 21:08:14 +04:00
static struct irq_domain_ops at91_aic_irq_ops = {
. map = at91_aic_irq_map ,
. xlate = irq_domain_xlate_twocell ,
2011-11-23 01:26:09 +04:00
} ;
2012-02-14 21:08:14 +04:00
int __init at91_aic_of_init ( struct device_node * node ,
struct device_node * parent )
2011-11-23 01:26:09 +04:00
{
2012-02-14 21:08:14 +04:00
at91_aic_base = of_iomap ( node , 0 ) ;
at91_aic_np = node ;
at91_aic_domain = irq_domain_add_linear ( at91_aic_np , NR_AIC_IRQS ,
& at91_aic_irq_ops , NULL ) ;
if ( ! at91_aic_domain )
panic ( " Unable to add AIC irq domain (DT) \n " ) ;
irq_set_default_host ( at91_aic_domain ) ;
at91_aic_hw_init ( NR_AIC_IRQS ) ;
return 0 ;
2011-11-23 01:26:09 +04:00
}
# endif
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 ;
2011-11-23 01:26:09 +04:00
int irq_base ;
2006-01-09 20:05:41 +03:00
2012-02-14 21:08:14 +04:00
at91_aic_base = ioremap ( AT91_AIC , 512 ) ;
2011-11-02 21:12:50 +04:00
if ( ! at91_aic_base )
2011-11-23 01:26:09 +04:00
panic ( " Unable to ioremap AIC registers \n " ) ;
/* Add irq domain for AIC */
irq_base = irq_alloc_descs ( - 1 , 0 , NR_AIC_IRQS , 0 ) ;
if ( irq_base < 0 ) {
WARN ( 1 , " Cannot allocate irq_descs, assuming pre-allocated \n " ) ;
irq_base = 0 ;
}
at91_aic_domain = irq_domain_add_legacy ( at91_aic_np , NR_AIC_IRQS ,
irq_base , 0 ,
& irq_domain_simple_ops , NULL ) ;
if ( ! at91_aic_domain )
panic ( " Unable to add AIC irq domain \n " ) ;
2011-11-02 21:12:50 +04:00
2012-02-14 21:08:14 +04:00
irq_set_default_host ( at91_aic_domain ) ;
2006-01-09 20:05:41 +03:00
/*
* 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 + + ) {
2011-11-23 01:26:09 +04:00
/* Put hardware irq number in Source Vector Register: */
2011-11-02 21:12:50 +04:00
at91_aic_write ( AT91_AIC_SVR ( i ) , i ) ;
2006-07-05 20:22:52 +04:00
/* Active Low interrupt, with the specified priority */
2011-11-02 21:12:50 +04:00
at91_aic_write ( AT91_AIC_SMR ( i ) , AT91_AIC_SRCTYPE_LOW | priority [ i ] ) ;
2006-01-09 20:05:41 +03:00
2011-03-24 15:35:09 +03:00
irq_set_chip_and_handler ( i , & at91_aic_chip , handle_level_irq ) ;
2006-01-09 20:05:41 +03:00
set_irq_flags ( i , IRQF_VALID | IRQF_PROBE ) ;
}
2012-02-14 21:08:14 +04:00
at91_aic_hw_init ( NR_AIC_IRQS ) ;
2006-01-09 20:05:41 +03:00
}