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>
2015-07-08 00:11:46 +03:00
# include <linux/irqchip.h>
2011-11-30 22:21:07 +04:00
# include <linux/irqdomain.h>
# include <linux/of.h>
# include <linux/of_address.h>
2012-06-05 15:01:06 +04:00
# include <linux/of_irq.h>
2005-11-10 17:26:51 +03:00
2016-06-08 20:44:32 +03:00
# include <linux/irqchip/irq-omap-intc.h>
2014-09-16 01:15:02 +04:00
/* Define these here for now until we drop all board-files */
# define OMAP24XX_IC_BASE 0x480fe000
# define OMAP34XX_IC_BASE 0x48200000
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
2014-09-09 04:54:37 +04:00
# define INTC_PENDING_IRQ1 0x00b8
# define INTC_PENDING_IRQ2 0x00d8
# define INTC_PENDING_IRQ3 0x00f8
2014-09-09 04:54:32 +04:00
# define INTC_ILR0 0x0100
2005-11-10 17:26:51 +03:00
2011-09-06 12:56:17 +04:00
# define ACTIVEIRQ_MASK 0x7f /* omap2/3 active interrupt bits */
2015-12-15 17:26:12 +03:00
# define SPURIOUSIRQ_MASK (0x1ffffff << 7)
2014-09-09 04:54:35 +04:00
# define INTCPS_NR_ILR_REGS 128
2014-09-16 01:15:08 +04:00
# define INTCPS_NR_MIR_REGS 4
2011-09-06 12:56:17 +04:00
2014-09-16 01:15:07 +04:00
# define INTC_IDLE_FUNCIDLE (1 << 0)
# define INTC_IDLE_TURBO (1 << 1)
2014-09-16 01:15:06 +04:00
# define INTC_PROTECTION_ENABLE (1 << 0)
2014-09-09 04:54:38 +04:00
struct omap_intc_regs {
2008-09-26 16:18:20 +04:00
u32 sysconfig ;
u32 protection ;
u32 idle ;
u32 threshold ;
2014-09-09 04:54:35 +04:00
u32 ilr [ INTCPS_NR_ILR_REGS ] ;
2008-09-26 16:18:20 +04:00
u32 mir [ INTCPS_NR_MIR_REGS ] ;
} ;
2014-09-09 04:54:42 +04:00
static struct omap_intc_regs intc_context ;
static struct irq_domain * domain ;
static void __iomem * omap_irq_base ;
2014-09-09 04:54:57 +04:00
static int omap_nr_pending = 3 ;
2014-09-09 04:54:42 +04:00
static int omap_nr_irqs = 96 ;
2008-09-26 16:18:20 +04:00
2014-09-09 04:54:32 +04:00
static void intc_writel ( u32 reg , u32 val )
2008-10-09 18:51:28 +04:00
{
2014-09-09 04:54:32 +04:00
writel_relaxed ( val , omap_irq_base + reg ) ;
2008-10-09 18:51:28 +04:00
}
2014-09-09 04:54:32 +04:00
static u32 intc_readl ( u32 reg )
2008-10-09 18:51:28 +04:00
{
2014-09-09 04:54:32 +04:00
return readl_relaxed ( omap_irq_base + reg ) ;
2008-10-09 18:51:28 +04:00
}
2014-09-09 04:54:42 +04:00
void omap_intc_save_context ( void )
{
int i ;
intc_context . sysconfig =
intc_readl ( INTC_SYSCONFIG ) ;
intc_context . protection =
intc_readl ( INTC_PROTECTION ) ;
intc_context . idle =
intc_readl ( INTC_IDLE ) ;
intc_context . threshold =
intc_readl ( INTC_THRESHOLD ) ;
for ( i = 0 ; i < omap_nr_irqs ; i + + )
intc_context . ilr [ i ] =
intc_readl ( ( INTC_ILR0 + 0x4 * i ) ) ;
for ( i = 0 ; i < INTCPS_NR_MIR_REGS ; i + + )
intc_context . mir [ i ] =
intc_readl ( INTC_MIR0 + ( 0x20 * i ) ) ;
}
void omap_intc_restore_context ( void )
{
int i ;
intc_writel ( INTC_SYSCONFIG , intc_context . sysconfig ) ;
intc_writel ( INTC_PROTECTION , intc_context . protection ) ;
intc_writel ( INTC_IDLE , intc_context . idle ) ;
intc_writel ( INTC_THRESHOLD , intc_context . threshold ) ;
for ( i = 0 ; i < omap_nr_irqs ; i + + )
intc_writel ( INTC_ILR0 + 0x4 * i ,
intc_context . ilr [ i ] ) ;
for ( i = 0 ; i < INTCPS_NR_MIR_REGS ; i + + )
intc_writel ( INTC_MIR0 + 0x20 * i ,
intc_context . mir [ i ] ) ;
/* MIRs are saved and restore with other PRCM registers */
}
void omap3_intc_prepare_idle ( void )
{
/*
* Disable autoidle as it can stall interrupt controller ,
* cf . errata ID i540 for 3430 ( all revisions up to 3.1 . x )
*/
intc_writel ( INTC_SYSCONFIG , 0 ) ;
2014-09-16 01:15:07 +04:00
intc_writel ( INTC_IDLE , INTC_IDLE_TURBO ) ;
2014-09-09 04:54:42 +04:00
}
void omap3_intc_resume_idle ( void )
{
/* Re-enable autoidle */
intc_writel ( INTC_SYSCONFIG , 1 ) ;
2014-09-16 01:15:07 +04:00
intc_writel ( INTC_IDLE , 0 ) ;
2014-09-09 04:54:42 +04:00
}
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
{
2014-09-09 04:54:32 +04:00
intc_writel ( INTC_CONTROL , 0x1 ) ;
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
}
2014-09-09 04:54:35 +04:00
static void __init omap_irq_soft_reset ( void )
2005-11-10 17:26:51 +03:00
{
unsigned long tmp ;
2014-09-09 04:54:32 +04:00
tmp = intc_readl ( INTC_REVISION ) & 0xff ;
2014-09-09 04:54:35 +04:00
2012-07-26 10:54:26 +04:00
pr_info ( " IRQ: Found an INTC at 0x%p (revision %ld.%ld) with %d interrupts \n " ,
2014-09-09 04:54:35 +04:00
omap_irq_base , tmp > > 4 , tmp & 0xf , omap_nr_irqs ) ;
2005-11-10 17:26:51 +03:00
2014-09-09 04:54:32 +04:00
tmp = intc_readl ( INTC_SYSCONFIG ) ;
2005-11-10 17:26:51 +03:00
tmp | = 1 < < 1 ; /* soft reset */
2014-09-09 04:54:32 +04:00
intc_writel ( INTC_SYSCONFIG , tmp ) ;
2005-11-10 17:26:51 +03:00
2014-09-09 04:54:32 +04:00
while ( ! ( intc_readl ( 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 */
2014-09-09 04:54:32 +04:00
intc_writel ( INTC_SYSCONFIG , 1 < < 0 ) ;
2005-11-10 17:26:51 +03:00
}
2009-02-04 02:49:04 +03:00
int omap_irq_pending ( void )
{
2014-09-16 01:15:03 +04:00
int i ;
2009-02-04 02:49:04 +03:00
2014-09-16 01:15:03 +04:00
for ( i = 0 ; i < omap_nr_pending ; i + + )
if ( intc_readl ( INTC_PENDING_IRQ0 + ( 0x20 * i ) ) )
2014-09-09 04:54:35 +04:00
return 1 ;
2009-02-04 02:49:04 +03:00
return 0 ;
}
2014-09-09 04:54:42 +04:00
void omap3_intc_suspend ( void )
{
/* A pending interrupt would prevent OMAP from entering suspend */
omap_ack_irq ( NULL ) ;
}
2014-09-09 04:54:58 +04:00
static int __init omap_alloc_gc_of ( struct irq_domain * d , void __iomem * base )
{
int ret ;
int i ;
ret = irq_alloc_domain_generic_chips ( d , 32 , 1 , " INTC " ,
handle_level_irq , IRQ_NOREQUEST | IRQ_NOPROBE ,
IRQ_LEVEL , 0 ) ;
if ( ret ) {
pr_warn ( " Failed to allocate irq chips \n " ) ;
return ret ;
}
for ( i = 0 ; i < omap_nr_pending ; i + + ) {
struct irq_chip_generic * gc ;
struct irq_chip_type * ct ;
gc = irq_get_domain_generic_chip ( d , 32 * i ) ;
gc - > reg_base = base ;
ct = gc - > chip_types ;
ct - > type = IRQ_TYPE_LEVEL_MASK ;
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 - > chip . flags | = IRQCHIP_SKIP_SET_WAKE ;
ct - > regs . enable = INTC_MIR_CLEAR0 + 32 * i ;
ct - > regs . disable = INTC_MIR_SET0 + 32 * i ;
}
return 0 ;
}
static void __init omap_alloc_gc_legacy ( void __iomem * base ,
unsigned int irq_start , unsigned int num )
2011-05-16 13:07:38 +04:00
{
struct irq_chip_generic * gc ;
struct irq_chip_type * ct ;
gc = irq_alloc_generic_chip ( " INTC " , 1 , irq_start , base ,
2014-09-09 04:54:58 +04:00
handle_level_irq ) ;
2011-05-16 13:07:38 +04:00
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 ;
2012-04-25 07:05:24 +04:00
ct - > chip . flags | = IRQCHIP_SKIP_SET_WAKE ;
2011-05-16 13:07:38 +04:00
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 ,
2014-09-09 04:54:58 +04:00
IRQ_NOREQUEST | IRQ_NOPROBE , 0 ) ;
2011-05-16 13:07:38 +04:00
}
2014-09-09 04:54:58 +04:00
static int __init omap_init_irq_of ( struct device_node * node )
{
int ret ;
omap_irq_base = of_iomap ( node , 0 ) ;
if ( WARN_ON ( ! omap_irq_base ) )
return - ENOMEM ;
domain = irq_domain_add_linear ( node , omap_nr_irqs ,
& irq_generic_chip_ops , NULL ) ;
omap_irq_soft_reset ( ) ;
ret = omap_alloc_gc_of ( domain , omap_irq_base ) ;
if ( ret < 0 )
irq_domain_remove ( domain ) ;
return ret ;
}
2015-01-06 23:38:08 +03:00
static int __init omap_init_irq_legacy ( u32 base , struct device_node * node )
2005-11-10 17:26:51 +03:00
{
2014-09-09 04:54:35 +04:00
int 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 ) )
2014-09-09 04:54:58 +04:00
return - ENOMEM ;
2011-05-17 14:51:26 +04:00
2014-09-09 04:54:55 +04:00
irq_base = irq_alloc_descs ( - 1 , 0 , omap_nr_irqs , 0 ) ;
2011-11-30 22:21:07 +04:00
if ( irq_base < 0 ) {
pr_warn ( " Couldn't allocate IRQ numbers \n " ) ;
irq_base = 0 ;
}
2015-01-06 23:38:08 +03:00
domain = irq_domain_add_legacy ( node , omap_nr_irqs , irq_base , 0 ,
2014-09-09 04:54:35 +04:00
& irq_domain_simple_ops , NULL ) ;
2005-11-10 17:26:51 +03:00
2014-09-09 04:54:35 +04:00
omap_irq_soft_reset ( ) ;
2011-05-16 13:07:38 +04:00
2014-09-09 04:54:35 +04:00
for ( j = 0 ; j < omap_nr_irqs ; j + = 32 )
2014-09-09 04:54:58 +04:00
omap_alloc_gc_legacy ( omap_irq_base + j , j + irq_base , 32 ) ;
return 0 ;
}
2014-09-16 01:15:06 +04:00
static void __init omap_irq_enable_protection ( void )
{
u32 reg ;
reg = intc_readl ( INTC_PROTECTION ) ;
reg | = INTC_PROTECTION_ENABLE ;
intc_writel ( INTC_PROTECTION , reg ) ;
}
2014-09-09 04:54:58 +04:00
static int __init omap_init_irq ( u32 base , struct device_node * node )
{
2014-09-16 01:15:06 +04:00
int ret ;
2015-01-06 23:38:08 +03:00
/*
* FIXME legacy OMAP DMA driver sitting under arch / arm / plat - omap / dma . c
* depends is still not ready for linear IRQ domains ; because of that
* we need to temporarily " blacklist " OMAP2 and OMAP3 devices from using
* linear IRQ Domain until that driver is finally fixed .
*/
if ( of_device_is_compatible ( node , " ti,omap2-intc " ) | |
of_device_is_compatible ( node , " ti,omap3-intc " ) ) {
struct resource res ;
if ( of_address_to_resource ( node , 0 , & res ) )
return - ENOMEM ;
base = res . start ;
ret = omap_init_irq_legacy ( base , node ) ;
} else if ( node ) {
2014-09-16 01:15:06 +04:00
ret = omap_init_irq_of ( node ) ;
2015-01-06 23:38:08 +03:00
} else {
ret = omap_init_irq_legacy ( base , NULL ) ;
}
2014-09-16 01:15:06 +04:00
if ( ret = = 0 )
omap_irq_enable_protection ( ) ;
return ret ;
2005-11-10 17:26:51 +03:00
}
2014-09-09 04:54:52 +04:00
static asmlinkage void __exception_irq_entry
omap_intc_handle_irq ( struct pt_regs * regs )
2011-09-06 12:56:17 +04:00
{
2015-12-15 17:26:12 +03:00
extern unsigned long irq_err_count ;
2015-01-03 01:18:54 +03:00
u32 irqnr ;
2011-09-06 12:56:17 +04:00
2015-01-03 01:18:54 +03:00
irqnr = intc_readl ( INTC_SIR ) ;
2015-12-15 17:26:12 +03:00
/*
* A spurious IRQ can result if interrupt that triggered the
* sorting is no longer active during the sorting ( 10 INTC
* functional clock cycles after interrupt assertion ) . Or a
* change in interrupt mask affected the result during sorting
* time . There is no special handling required except ignoring
* the SIR register value just read and retrying .
* See section 6.2 .5 of AM335x TRM Literature Number : SPRUH73K
*
* Many a times , a spurious interrupt situation has been fixed
* by adding a flush for the posted write acking the IRQ in
* the device driver . Typically , this is going be the device
* driver whose interrupt was handled just before the spurious
* IRQ occurred . Pay attention to those device drivers if you
* run into hitting the spurious IRQ condition below .
*/
if ( unlikely ( ( irqnr & SPURIOUSIRQ_MASK ) = = SPURIOUSIRQ_MASK ) ) {
pr_err_once ( " %s: spurious irq! \n " , __func__ ) ;
irq_err_count + + ;
omap_ack_irq ( NULL ) ;
return ;
}
2015-01-03 01:18:54 +03:00
irqnr & = ACTIVEIRQ_MASK ;
handle_domain_irq ( domain , irqnr , regs ) ;
2011-09-06 12:56:17 +04:00
}
2014-09-09 04:54:51 +04:00
void __init omap3_init_irq ( void )
{
2014-09-09 04:54:55 +04:00
omap_nr_irqs = 96 ;
2014-09-09 04:54:57 +04:00
omap_nr_pending = 3 ;
2014-09-09 04:54:55 +04:00
omap_init_irq ( OMAP34XX_IC_BASE , NULL ) ;
2014-09-09 04:54:52 +04:00
set_handle_irq ( omap_intc_handle_irq ) ;
2014-09-09 04:54:51 +04:00
}
2014-09-09 04:54:43 +04:00
static int __init intc_of_init ( struct device_node * node ,
2011-11-30 22:21:07 +04:00
struct device_node * parent )
{
2014-09-09 04:54:58 +04:00
int ret ;
2014-09-09 04:54:55 +04:00
2014-09-09 04:54:57 +04:00
omap_nr_pending = 3 ;
2014-09-09 04:54:55 +04:00
omap_nr_irqs = 96 ;
2011-11-30 22:21:07 +04:00
if ( WARN_ON ( ! node ) )
return - ENODEV ;
2015-01-14 01:23:25 +03:00
if ( of_device_is_compatible ( node , " ti,dm814-intc " ) | |
of_device_is_compatible ( node , " ti,dm816-intc " ) | |
of_device_is_compatible ( node , " ti,am33xx-intc " ) ) {
2014-09-09 04:54:55 +04:00
omap_nr_irqs = 128 ;
2014-09-09 04:54:57 +04:00
omap_nr_pending = 4 ;
}
2014-09-09 04:54:47 +04:00
2014-09-09 04:54:58 +04:00
ret = omap_init_irq ( - 1 , of_node_get ( node ) ) ;
if ( ret < 0 )
return ret ;
2011-11-30 22:21:07 +04:00
2014-09-09 04:54:52 +04:00
set_handle_irq ( omap_intc_handle_irq ) ;
2014-09-09 04:54:43 +04:00
2011-11-30 22:21:07 +04:00
return 0 ;
}
2014-09-09 04:54:46 +04:00
IRQCHIP_DECLARE ( omap2_intc , " ti,omap2-intc " , intc_of_init ) ;
IRQCHIP_DECLARE ( omap3_intc , " ti,omap3-intc " , intc_of_init ) ;
2015-01-14 01:23:25 +03:00
IRQCHIP_DECLARE ( dm814x_intc , " ti,dm814-intc " , intc_of_init ) ;
IRQCHIP_DECLARE ( dm816x_intc , " ti,dm816-intc " , intc_of_init ) ;
2014-09-09 04:54:46 +04:00
IRQCHIP_DECLARE ( am33xx_intc , " ti,am33xx-intc " , intc_of_init ) ;