2005-11-10 17:26:51 +03:00
/*
2006-10-04 01:01:26 +04:00
* linux / arch / arm / mach - omap2 / irq . c
2005-11-10 17:26:51 +03: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>
2011-11-30 22:21:07 +04:00
# include <linux/module.h>
2005-11-10 17:26:51 +03:00
# include <linux/init.h>
# include <linux/interrupt.h>
2008-10-09 18:51:28 +04:00
# include <linux/io.h>
2012-02-24 22:34:35 +04:00
2011-09-06 12:56:17 +04:00
# include <asm/exception.h>
2005-11-10 17:26:51 +03:00
# include <asm/mach/irq.h>
2011-11-30 22:21:07 +04:00
# include <linux/irqdomain.h>
# include <linux/of.h>
# include <linux/of_address.h>
2005-11-10 17:26:51 +03:00
2012-02-24 22:34:35 +04:00
# include <mach/hardware.h>
# include "iomap.h"
2008-10-09 18:51:28 +04:00
/* selected INTC register offsets */
# define INTC_REVISION 0x0000
# define INTC_SYSCONFIG 0x0010
# define INTC_SYSSTATUS 0x0014
2008-12-11 04:36:52 +03:00
# define INTC_SIR 0x0040
2008-10-09 18:51:28 +04:00
# define INTC_CONTROL 0x0048
2008-09-26 16:18:20 +04:00
# define INTC_PROTECTION 0x004C
# define INTC_IDLE 0x0050
# define INTC_THRESHOLD 0x0068
# define INTC_MIR0 0x0084
2008-10-09 18:51:28 +04:00
# 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 17:26:51 +03:00
2011-09-06 12:56:17 +04:00
# define OMAP2_IRQ_BASE OMAP2_L4_IO_ADDRESS(OMAP24XX_IC_BASE)
# define OMAP3_IRQ_BASE OMAP2_L4_IO_ADDRESS(OMAP34XX_IC_BASE)
# define INTCPS_SIR_IRQ_OFFSET 0x0040 /* omap2/3 active interrupt offset */
# define ACTIVEIRQ_MASK 0x7f /* omap2/3 active interrupt bits */
2005-11-10 17:26:51 +03: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-02 01:07:37 +04:00
void __iomem * base_reg ;
2005-11-10 17:26:51 +03:00
unsigned int nr_irqs ;
} __attribute__ ( ( aligned ( 4 ) ) ) irq_banks [ ] = {
{
/* MPU INTC */
. nr_irqs = 96 ,
2008-10-06 16:49:36 +04:00
} ,
2005-11-10 17:26:51 +03:00
} ;
2011-11-30 22:21:07 +04:00
static struct irq_domain * domain ;
2008-09-26 16:18:20 +04:00
/* Structure to save interrupt controller context */
struct omap3_intc_regs {
u32 sysconfig ;
u32 protection ;
u32 idle ;
u32 threshold ;
u32 ilr [ INTCPS_NR_IRQS ] ;
u32 mir [ INTCPS_NR_MIR_REGS ] ;
} ;
2008-10-09 18:51:28 +04: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 ) ;
}
2005-11-10 17:26:51 +03:00
/* XXX: FIQ and additional INTC support (only MPU at the moment) */
2010-11-29 12:39:59 +03:00
static void omap_ack_irq ( struct irq_data * d )
2005-11-10 17:26:51 +03:00
{
2008-10-09 18:51:28 +04:00
intc_bank_write_reg ( 0x1 , & irq_banks [ 0 ] , INTC_CONTROL ) ;
2005-11-10 17:26:51 +03:00
}
2010-11-29 12:39:59 +03:00
static void omap_mask_ack_irq ( struct irq_data * d )
2005-11-10 17:26:51 +03:00
{
2011-05-16 13:07:38 +04:00
irq_gc_mask_disable_reg ( d ) ;
2010-11-29 12:39:59 +03:00
omap_ack_irq ( d ) ;
2005-11-10 17:26:51 +03:00
}
static void __init omap_irq_bank_init_one ( struct omap_irq_bank * bank )
{
unsigned long tmp ;
2008-10-09 18:51:28 +04:00
tmp = intc_bank_read_reg ( bank , INTC_REVISION ) & 0xff ;
2008-09-02 01:07:37 +04:00
printk ( KERN_INFO " IRQ: Found an INTC at 0x%p "
2005-11-10 17:26:51 +03:00
" (revision %ld.%ld) with %d interrupts \n " ,
bank - > base_reg , tmp > > 4 , tmp & 0xf , bank - > nr_irqs ) ;
2008-10-09 18:51:28 +04:00
tmp = intc_bank_read_reg ( bank , INTC_SYSCONFIG ) ;
2005-11-10 17:26:51 +03:00
tmp | = 1 < < 1 ; /* soft reset */
2008-10-09 18:51:28 +04:00
intc_bank_write_reg ( tmp , bank , INTC_SYSCONFIG ) ;
2005-11-10 17:26:51 +03:00
2008-10-09 18:51:28 +04:00
while ( ! ( intc_bank_read_reg ( bank , INTC_SYSSTATUS ) & 0x1 ) )
2005-11-10 17:26:51 +03:00
/* Wait for reset to complete */ ;
2006-12-07 04:13:50 +03:00
/* Enable autoidle */
2008-10-09 18:51:28 +04:00
intc_bank_write_reg ( 1 < < 0 , bank , INTC_SYSCONFIG ) ;
2005-11-10 17:26:51 +03:00
}
2009-02-04 02:49:04 +03:00
int omap_irq_pending ( void )
{
int i ;
for ( i = 0 ; i < ARRAY_SIZE ( irq_banks ) ; i + + ) {
struct omap_irq_bank * bank = irq_banks + i ;
int irq ;
for ( irq = 0 ; irq < bank - > nr_irqs ; irq + = 32 )
if ( intc_bank_read_reg ( bank , INTC_PENDING_IRQ0 +
( ( irq > > 5 ) < < 5 ) ) )
return 1 ;
}
return 0 ;
}
2011-05-16 13:07:38 +04:00
static __init void
omap_alloc_gc ( void __iomem * base , unsigned int irq_start , unsigned int num )
{
struct irq_chip_generic * gc ;
struct irq_chip_type * ct ;
gc = irq_alloc_generic_chip ( " INTC " , 1 , irq_start , base ,
handle_level_irq ) ;
ct = gc - > chip_types ;
ct - > chip . irq_ack = omap_mask_ack_irq ;
ct - > chip . irq_mask = irq_gc_mask_disable_reg ;
ct - > chip . irq_unmask = irq_gc_unmask_enable_reg ;
ct - > regs . ack = INTC_CONTROL ;
ct - > regs . enable = INTC_MIR_CLEAR0 ;
ct - > regs . disable = INTC_MIR_SET0 ;
irq_setup_generic_chip ( gc , IRQ_MSK ( num ) , IRQ_GC_INIT_MASK_CACHE ,
IRQ_NOREQUEST | IRQ_NOPROBE , 0 ) ;
}
2011-11-30 22:21:07 +04:00
static void __init omap_init_irq ( u32 base , int nr_irqs ,
struct device_node * node )
2005-11-10 17:26:51 +03:00
{
2011-11-15 21:22:45 +04:00
void __iomem * omap_irq_base ;
2008-10-16 17:33:18 +04:00
unsigned long nr_of_irqs = 0 ;
2005-11-10 17:26:51 +03:00
unsigned int nr_banks = 0 ;
2011-11-30 22:21:07 +04:00
int i , j , irq_base ;
2005-11-10 17:26:51 +03:00
2011-05-17 14:51:26 +04:00
omap_irq_base = ioremap ( base , SZ_4K ) ;
if ( WARN_ON ( ! omap_irq_base ) )
return ;
2011-11-30 22:21:07 +04:00
irq_base = irq_alloc_descs ( - 1 , 0 , nr_irqs , 0 ) ;
if ( irq_base < 0 ) {
pr_warn ( " Couldn't allocate IRQ numbers \n " ) ;
irq_base = 0 ;
}
domain = irq_domain_add_legacy ( node , nr_irqs , irq_base , 0 ,
& irq_domain_simple_ops , NULL ) ;
2005-11-10 17:26:51 +03:00
for ( i = 0 ; i < ARRAY_SIZE ( irq_banks ) ; i + + ) {
struct omap_irq_bank * bank = irq_banks + i ;
2011-05-17 14:51:26 +04:00
bank - > nr_irqs = nr_irqs ;
2011-02-16 19:31:39 +03:00
2009-10-20 02:25:13 +04:00
/* Static mapping, never released */
bank - > base_reg = ioremap ( base , SZ_4K ) ;
if ( ! bank - > base_reg ) {
2011-11-30 22:21:07 +04:00
pr_err ( " Could not ioremap irq bank%i \n " , i ) ;
2009-10-20 02:25:13 +04:00
continue ;
}
2008-10-09 18:51:28 +04:00
2005-11-10 17:26:51 +03:00
omap_irq_bank_init_one ( bank ) ;
2011-09-30 22:05:56 +04:00
for ( j = 0 ; j < bank - > nr_irqs ; j + = 32 )
2011-11-30 22:21:07 +04:00
omap_alloc_gc ( bank - > base_reg + j , j + irq_base , 32 ) ;
2011-05-16 13:07:38 +04:00
2008-10-16 17:33:18 +04:00
nr_of_irqs + = bank - > nr_irqs ;
2005-11-10 17:26:51 +03:00
nr_banks + + ;
}
2011-11-30 22:21:07 +04:00
pr_info ( " Total of %ld interrupts on %d active controller%s \n " ,
nr_of_irqs , nr_banks , nr_banks > 1 ? " s " : " " ) ;
2005-11-10 17:26:51 +03:00
}
2011-05-17 14:51:26 +04:00
void __init omap2_init_irq ( void )
{
2011-11-30 22:21:07 +04:00
omap_init_irq ( OMAP24XX_IC_BASE , 96 , NULL ) ;
2011-05-17 14:51:26 +04:00
}
void __init omap3_init_irq ( void )
{
2011-11-30 22:21:07 +04:00
omap_init_irq ( OMAP34XX_IC_BASE , 96 , NULL ) ;
2011-05-17 14:51:26 +04:00
}
2011-12-13 22:46:44 +04:00
void __init ti81xx_init_irq ( void )
2011-05-17 14:51:26 +04:00
{
2011-11-30 22:21:07 +04:00
omap_init_irq ( OMAP34XX_IC_BASE , 128 , NULL ) ;
2011-05-17 14:51:26 +04:00
}
2011-09-06 12:56:17 +04:00
static inline void omap_intc_handle_irq ( void __iomem * base_addr , struct pt_regs * regs )
{
u32 irqnr ;
do {
irqnr = readl_relaxed ( base_addr + 0x98 ) ;
if ( irqnr )
goto out ;
irqnr = readl_relaxed ( base_addr + 0xb8 ) ;
if ( irqnr )
goto out ;
irqnr = readl_relaxed ( base_addr + 0xd8 ) ;
# ifdef CONFIG_SOC_OMAPTI816X
if ( irqnr )
goto out ;
irqnr = readl_relaxed ( base_addr + 0xf8 ) ;
# endif
out :
if ( ! irqnr )
break ;
irqnr = readl_relaxed ( base_addr + INTCPS_SIR_IRQ_OFFSET ) ;
irqnr & = ACTIVEIRQ_MASK ;
2011-11-30 22:21:07 +04:00
if ( irqnr ) {
irqnr = irq_find_mapping ( domain , irqnr ) ;
2011-09-06 12:56:17 +04:00
handle_IRQ ( irqnr , regs ) ;
2011-11-30 22:21:07 +04:00
}
2011-09-06 12:56:17 +04:00
} while ( irqnr ) ;
}
asmlinkage void __exception_irq_entry omap2_intc_handle_irq ( struct pt_regs * regs )
{
void __iomem * base_addr = OMAP2_IRQ_BASE ;
omap_intc_handle_irq ( base_addr , regs ) ;
}
2011-11-30 22:21:07 +04:00
int __init omap_intc_of_init ( struct device_node * node ,
struct device_node * parent )
{
struct resource res ;
u32 nr_irqs = 96 ;
if ( WARN_ON ( ! node ) )
return - ENODEV ;
if ( of_address_to_resource ( node , 0 , & res ) ) {
WARN ( 1 , " unable to get intc registers \n " ) ;
return - EINVAL ;
}
if ( of_property_read_u32 ( node , " ti,intc-size " , & nr_irqs ) )
pr_warn ( " unable to get intc-size, default to %d \n " , nr_irqs ) ;
omap_init_irq ( res . start , nr_irqs , of_node_get ( node ) ) ;
return 0 ;
}
2008-09-26 16:18:20 +04:00
# ifdef CONFIG_ARCH_OMAP3
2011-01-28 03:39:43 +03:00
static struct omap3_intc_regs intc_context [ ARRAY_SIZE ( irq_banks ) ] ;
2008-09-26 16:18:20 +04:00
void omap_intc_save_context ( void )
{
int ind = 0 , i = 0 ;
for ( ind = 0 ; ind < ARRAY_SIZE ( irq_banks ) ; ind + + ) {
struct omap_irq_bank * bank = irq_banks + ind ;
intc_context [ ind ] . sysconfig =
intc_bank_read_reg ( bank , INTC_SYSCONFIG ) ;
intc_context [ ind ] . protection =
intc_bank_read_reg ( bank , INTC_PROTECTION ) ;
intc_context [ ind ] . idle =
intc_bank_read_reg ( bank , INTC_IDLE ) ;
intc_context [ ind ] . threshold =
intc_bank_read_reg ( bank , INTC_THRESHOLD ) ;
for ( i = 0 ; i < INTCPS_NR_IRQS ; i + + )
intc_context [ ind ] . ilr [ i ] =
2009-03-12 19:12:29 +03:00
intc_bank_read_reg ( bank , ( 0x100 + 0x4 * i ) ) ;
2008-09-26 16:18:20 +04:00
for ( i = 0 ; i < INTCPS_NR_MIR_REGS ; i + + )
intc_context [ ind ] . mir [ i ] =
intc_bank_read_reg ( & irq_banks [ 0 ] , INTC_MIR0 +
( 0x20 * i ) ) ;
}
}
void omap_intc_restore_context ( void )
{
int ind = 0 , i = 0 ;
for ( ind = 0 ; ind < ARRAY_SIZE ( irq_banks ) ; ind + + ) {
struct omap_irq_bank * bank = irq_banks + ind ;
intc_bank_write_reg ( intc_context [ ind ] . sysconfig ,
bank , INTC_SYSCONFIG ) ;
intc_bank_write_reg ( intc_context [ ind ] . sysconfig ,
bank , INTC_SYSCONFIG ) ;
intc_bank_write_reg ( intc_context [ ind ] . protection ,
bank , INTC_PROTECTION ) ;
intc_bank_write_reg ( intc_context [ ind ] . idle ,
bank , INTC_IDLE ) ;
intc_bank_write_reg ( intc_context [ ind ] . threshold ,
bank , INTC_THRESHOLD ) ;
for ( i = 0 ; i < INTCPS_NR_IRQS ; i + + )
intc_bank_write_reg ( intc_context [ ind ] . ilr [ i ] ,
2009-03-12 19:12:29 +03:00
bank , ( 0x100 + 0x4 * i ) ) ;
2008-09-26 16:18:20 +04:00
for ( i = 0 ; i < INTCPS_NR_MIR_REGS ; i + + )
intc_bank_write_reg ( intc_context [ ind ] . mir [ i ] ,
& irq_banks [ 0 ] , INTC_MIR0 + ( 0x20 * i ) ) ;
}
/* MIRs are saved and restore with other PRCM registers */
}
2009-10-23 20:03:48 +04:00
void omap3_intc_suspend ( void )
{
/* A pending interrupt would prevent OMAP from entering suspend */
omap_ack_irq ( 0 ) ;
}
2009-10-23 20:03:50 +04:00
void omap3_intc_prepare_idle ( void )
{
2010-11-17 20:52:11 +03:00
/*
* Disable autoidle as it can stall interrupt controller ,
* cf . errata ID i540 for 3430 ( all revisions up to 3.1 . x )
*/
2009-10-23 20:03:50 +04:00
intc_bank_write_reg ( 0 , & irq_banks [ 0 ] , INTC_SYSCONFIG ) ;
}
void omap3_intc_resume_idle ( void )
{
/* Re-enable autoidle */
intc_bank_write_reg ( 1 , & irq_banks [ 0 ] , INTC_SYSCONFIG ) ;
}
2011-09-06 12:56:17 +04:00
asmlinkage void __exception_irq_entry omap3_intc_handle_irq ( struct pt_regs * regs )
{
void __iomem * base_addr = OMAP3_IRQ_BASE ;
omap_intc_handle_irq ( base_addr , regs ) ;
}
2008-09-26 16:18:20 +04:00
# endif /* CONFIG_ARCH_OMAP3 */