2005-11-10 14:26:51 +00:00
/*
2006-10-03 23:01:26 +02:00
* linux / arch / arm / mach - omap2 / irq . c
2005-11-10 14:26:51 +00:00
*
* Interrupt handler for OMAP2 boards .
*
* Copyright ( C ) 2005 Nokia Corporation
* Author : Paul Mundt < paul . mundt @ nokia . com >
*
* 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/kernel.h>
# include <linux/init.h>
# include <linux/interrupt.h>
2008-10-09 17:51:28 +03:00
# include <linux/io.h>
2008-08-05 16:14:15 +01:00
# include <mach/hardware.h>
2005-11-10 14:26:51 +00:00
# include <asm/mach/irq.h>
2008-10-09 17:51:28 +03:00
/* selected INTC register offsets */
# define INTC_REVISION 0x0000
# define INTC_SYSCONFIG 0x0010
# define INTC_SYSSTATUS 0x0014
2008-12-10 17:36:52 -08:00
# define INTC_SIR 0x0040
2008-10-09 17:51:28 +03:00
# define INTC_CONTROL 0x0048
# define INTC_MIR_CLEAR0 0x0088
# define INTC_MIR_SET0 0x008c
# define INTC_PENDING_IRQ0 0x0098
/* Number of IRQ state bits in each MIR register */
# define IRQ_BITS_PER_REG 32
2005-11-10 14:26:51 +00:00
/*
* OMAP2 has a number of different interrupt controllers , each interrupt
* controller is identified as its own " bank " . Register definitions are
* fairly consistent for each bank , but not all registers are implemented
* for each bank . . when in doubt , consult the TRM .
*/
static struct omap_irq_bank {
2008-09-01 22:07:37 +01:00
void __iomem * base_reg ;
2005-11-10 14:26:51 +00:00
unsigned int nr_irqs ;
} __attribute__ ( ( aligned ( 4 ) ) ) irq_banks [ ] = {
{
/* MPU INTC */
2008-10-06 15:49:36 +03:00
. base_reg = 0 ,
2005-11-10 14:26:51 +00:00
. nr_irqs = 96 ,
2008-10-06 15:49:36 +03:00
} ,
2005-11-10 14:26:51 +00:00
} ;
2008-10-09 17:51:28 +03:00
/* INTC bank register get/set */
static void intc_bank_write_reg ( u32 val , struct omap_irq_bank * bank , u16 reg )
{
__raw_writel ( val , bank - > base_reg + reg ) ;
}
static u32 intc_bank_read_reg ( struct omap_irq_bank * bank , u16 reg )
{
return __raw_readl ( bank - > base_reg + reg ) ;
}
2008-12-10 17:36:52 -08:00
static int previous_irq ;
/*
* On 34 xx we can get occasional spurious interrupts if the ack from
* an interrupt handler does not get posted before we unmask . Warn about
* the interrupt handlers that need to flush posted writes .
*/
static int omap_check_spurious ( unsigned int irq )
{
u32 sir , spurious ;
sir = intc_bank_read_reg ( & irq_banks [ 0 ] , INTC_SIR ) ;
2009-04-23 11:10:50 -07:00
spurious = sir > > 7 ;
2008-12-10 17:36:52 -08:00
2009-04-23 11:10:50 -07:00
if ( spurious ) {
2008-12-10 17:36:52 -08:00
printk ( KERN_WARNING " Spurious irq %i: 0x%08x, please flush "
" posted write for irq %i \n " ,
irq , sir , previous_irq ) ;
return spurious ;
}
return 0 ;
}
2005-11-10 14:26:51 +00:00
/* XXX: FIQ and additional INTC support (only MPU at the moment) */
static void omap_ack_irq ( unsigned int irq )
{
2008-10-09 17:51:28 +03:00
intc_bank_write_reg ( 0x1 , & irq_banks [ 0 ] , INTC_CONTROL ) ;
2005-11-10 14:26:51 +00:00
}
static void omap_mask_irq ( unsigned int irq )
{
2008-10-09 17:51:28 +03:00
int offset = irq & ( ~ ( IRQ_BITS_PER_REG - 1 ) ) ;
2005-11-10 14:26:51 +00:00
2008-12-10 17:36:52 -08:00
if ( cpu_is_omap34xx ( ) ) {
int spurious = 0 ;
/*
* INT_34XX_GPT12_IRQ is also the spurious irq . Maybe because
* it is the highest irq number ?
*/
if ( irq = = INT_34XX_GPT12_IRQ )
spurious = omap_check_spurious ( irq ) ;
if ( ! spurious )
previous_irq = irq ;
}
2008-10-09 17:51:28 +03:00
irq & = ( IRQ_BITS_PER_REG - 1 ) ;
2005-11-10 14:26:51 +00:00
2008-10-09 17:51:28 +03:00
intc_bank_write_reg ( 1 < < irq , & irq_banks [ 0 ] , INTC_MIR_SET0 + offset ) ;
2005-11-10 14:26:51 +00:00
}
static void omap_unmask_irq ( unsigned int irq )
{
2008-10-09 17:51:28 +03:00
int offset = irq & ( ~ ( IRQ_BITS_PER_REG - 1 ) ) ;
2005-11-10 14:26:51 +00:00
2008-10-09 17:51:28 +03:00
irq & = ( IRQ_BITS_PER_REG - 1 ) ;
2005-11-10 14:26:51 +00:00
2008-10-09 17:51:28 +03:00
intc_bank_write_reg ( 1 < < irq , & irq_banks [ 0 ] , INTC_MIR_CLEAR0 + offset ) ;
2005-11-10 14:26:51 +00:00
}
static void omap_mask_ack_irq ( unsigned int irq )
{
omap_mask_irq ( irq ) ;
omap_ack_irq ( irq ) ;
}
2006-08-01 22:26:25 +01:00
static struct irq_chip omap_irq_chip = {
. name = " INTC " ,
2005-11-10 14:26:51 +00:00
. ack = omap_mask_ack_irq ,
. mask = omap_mask_irq ,
. unmask = omap_unmask_irq ,
2009-01-29 08:57:17 -08:00
. disable = omap_mask_irq ,
2005-11-10 14:26:51 +00:00
} ;
static void __init omap_irq_bank_init_one ( struct omap_irq_bank * bank )
{
unsigned long tmp ;
2008-10-09 17:51:28 +03:00
tmp = intc_bank_read_reg ( bank , INTC_REVISION ) & 0xff ;
2008-09-01 22:07:37 +01:00
printk ( KERN_INFO " IRQ: Found an INTC at 0x%p "
2005-11-10 14:26:51 +00:00
" (revision %ld.%ld) with %d interrupts \n " ,
bank - > base_reg , tmp > > 4 , tmp & 0xf , bank - > nr_irqs ) ;
2008-10-09 17:51:28 +03:00
tmp = intc_bank_read_reg ( bank , INTC_SYSCONFIG ) ;
2005-11-10 14:26:51 +00:00
tmp | = 1 < < 1 ; /* soft reset */
2008-10-09 17:51:28 +03:00
intc_bank_write_reg ( tmp , bank , INTC_SYSCONFIG ) ;
2005-11-10 14:26:51 +00:00
2008-10-09 17:51:28 +03:00
while ( ! ( intc_bank_read_reg ( bank , INTC_SYSSTATUS ) & 0x1 ) )
2005-11-10 14:26:51 +00:00
/* Wait for reset to complete */ ;
2006-12-06 17:13:50 -08:00
/* Enable autoidle */
2008-10-09 17:51:28 +03:00
intc_bank_write_reg ( 1 < < 0 , bank , INTC_SYSCONFIG ) ;
2005-11-10 14:26:51 +00:00
}
void __init omap_init_irq ( void )
{
2008-10-16 15:33:18 +02:00
unsigned long nr_of_irqs = 0 ;
2005-11-10 14:26:51 +00:00
unsigned int nr_banks = 0 ;
int i ;
for ( i = 0 ; i < ARRAY_SIZE ( irq_banks ) ; i + + ) {
struct omap_irq_bank * bank = irq_banks + i ;
2008-10-06 15:49:36 +03:00
if ( cpu_is_omap24xx ( ) )
2008-10-09 17:51:28 +03:00
bank - > base_reg = OMAP2_IO_ADDRESS ( OMAP24XX_IC_BASE ) ;
2008-10-09 17:51:41 +03:00
else if ( cpu_is_omap34xx ( ) )
bank - > base_reg = OMAP2_IO_ADDRESS ( OMAP34XX_IC_BASE ) ;
2008-10-09 17:51:28 +03:00
2005-11-10 14:26:51 +00:00
omap_irq_bank_init_one ( bank ) ;
2008-10-16 15:33:18 +02:00
nr_of_irqs + = bank - > nr_irqs ;
2005-11-10 14:26:51 +00:00
nr_banks + + ;
}
printk ( KERN_INFO " Total of %ld interrupts on %d active controller%s \n " ,
2008-10-16 15:33:18 +02:00
nr_of_irqs , nr_banks , nr_banks > 1 ? " s " : " " ) ;
2005-11-10 14:26:51 +00:00
2008-10-16 15:33:18 +02:00
for ( i = 0 ; i < nr_of_irqs ; i + + ) {
2005-11-10 14:26:51 +00:00
set_irq_chip ( i , & omap_irq_chip ) ;
2006-11-23 11:41:32 +00:00
set_irq_handler ( i , handle_level_irq ) ;
2005-11-10 14:26:51 +00:00
set_irq_flags ( i , IRQF_VALID ) ;
}
}