2005-04-17 02:20:36 +04:00
/*
* linux / arch / arm / mach - pxa / irq . c
*
2008-03-04 06:42:26 +03:00
* Generic PXA IRQ handling
2005-04-17 02:20:36 +04:00
*
* Author : Nicolas Pitre
* Created : Jun 15 , 2001
* Copyright : MontaVista Software Inc .
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*/
# include <linux/init.h>
# include <linux/module.h>
# include <linux/interrupt.h>
2011-04-23 00:03:11 +04:00
# include <linux/syscore_ops.h>
2010-11-24 06:54:22 +03:00
# include <linux/io.h>
# include <linux/irq.h>
2005-04-17 02:20:36 +04:00
2011-10-08 14:20:42 +04:00
# include <asm/exception.h>
2008-08-05 19:14:15 +04:00
# include <mach/hardware.h>
2010-11-24 06:54:22 +03:00
# include <mach/irqs.h>
2005-04-17 02:20:36 +04:00
# include "generic.h"
2011-10-02 00:03:45 +04:00
# define IRQ_BASE io_p2v(0x40d00000)
2010-11-24 06:54:22 +03:00
# define ICIP (0x000)
# define ICMR (0x004)
# define ICLR (0x008)
# define ICFR (0x00c)
# define ICPR (0x010)
# define ICCR (0x014)
# define ICHP (0x018)
# define IPR(i) (((i) < 32) ? (0x01c + ((i) << 2)) : \
( ( i ) < 64 ) ? ( 0x0b0 + ( ( ( i ) - 32 ) < < 2 ) ) : \
( 0x144 + ( ( ( i ) - 64 ) < < 2 ) ) )
2011-04-27 18:48:05 +04:00
# define ICHP_VAL_IRQ (1 << 31)
# define ICHP_IRQ(i) (((i) >> 16) & 0x7fff)
2010-11-24 06:54:22 +03:00
# define IPR_VALID (1 << 31)
# define IRQ_BIT(n) (((n) - PXA_IRQ(0)) & 0x1f)
2009-11-02 22:02:21 +03:00
2010-11-24 06:54:22 +03:00
# define MAX_INTERNAL_IRQS 128
2005-04-17 02:20:36 +04:00
/*
* This is for peripheral IRQs internal to the PXA chip .
*/
2008-03-04 08:53:05 +03:00
static int pxa_internal_irq_nr ;
2010-11-17 14:03:36 +03:00
static inline int cpu_has_ipr ( void )
{
return ! cpu_is_pxa25x ( ) ;
}
2011-01-13 01:42:24 +03:00
static inline void __iomem * irq_base ( int i )
{
static unsigned long phys_base [ ] = {
0x40d00000 ,
0x40d0009c ,
0x40d00130 ,
} ;
2011-10-02 00:03:45 +04:00
return io_p2v ( phys_base [ i ] ) ;
2011-01-13 01:42:24 +03:00
}
2011-04-27 18:48:04 +04:00
void pxa_mask_irq ( struct irq_data * d )
2005-04-17 02:20:36 +04:00
{
2010-11-29 13:18:26 +03:00
void __iomem * base = irq_data_get_irq_chip_data ( d ) ;
2010-11-24 06:54:22 +03:00
uint32_t icmr = __raw_readl ( base + ICMR ) ;
2010-11-29 13:18:26 +03:00
icmr & = ~ ( 1 < < IRQ_BIT ( d - > irq ) ) ;
2010-11-24 06:54:22 +03:00
__raw_writel ( icmr , base + ICMR ) ;
2005-04-17 02:20:36 +04:00
}
2011-04-27 18:48:04 +04:00
void pxa_unmask_irq ( struct irq_data * d )
2005-04-17 02:20:36 +04:00
{
2010-11-29 13:18:26 +03:00
void __iomem * base = irq_data_get_irq_chip_data ( d ) ;
2010-11-24 06:54:22 +03:00
uint32_t icmr = __raw_readl ( base + ICMR ) ;
2010-11-29 13:18:26 +03:00
icmr | = 1 < < IRQ_BIT ( d - > irq ) ;
2010-11-24 06:54:22 +03:00
__raw_writel ( icmr , base + ICMR ) ;
2005-04-17 02:20:36 +04:00
}
2008-03-04 08:53:05 +03:00
static struct irq_chip pxa_internal_irq_chip = {
2006-08-02 01:26:25 +04:00
. name = " SC " ,
2010-11-29 13:18:26 +03:00
. irq_ack = pxa_mask_irq ,
. irq_mask = pxa_mask_irq ,
. irq_unmask = pxa_unmask_irq ,
2005-04-17 02:20:36 +04:00
} ;
2011-04-27 18:48:05 +04:00
asmlinkage void __exception_irq_entry icip_handle_irq ( struct pt_regs * regs )
{
uint32_t icip , icmr , mask ;
do {
icip = __raw_readl ( IRQ_BASE + ICIP ) ;
icmr = __raw_readl ( IRQ_BASE + ICMR ) ;
mask = icip & icmr ;
if ( mask = = 0 )
break ;
handle_IRQ ( PXA_IRQ ( fls ( mask ) - 1 ) , regs ) ;
} while ( 1 ) ;
}
asmlinkage void __exception_irq_entry ichp_handle_irq ( struct pt_regs * regs )
{
uint32_t ichp ;
do {
__asm__ __volatile__ ( " mrc p6, 0, %0, c5, c0, 0 \n " : " =r " ( ichp ) ) ;
if ( ( ichp & ICHP_VAL_IRQ ) = = 0 )
break ;
handle_IRQ ( PXA_IRQ ( ICHP_IRQ ( ichp ) ) , regs ) ;
} while ( 1 ) ;
}
2011-10-17 16:37:52 +04:00
void __init pxa_init_irq ( int irq_nr , int ( * fn ) ( struct irq_data * , unsigned int ) )
2007-06-06 09:36:04 +04:00
{
2010-11-24 06:54:22 +03:00
int irq , i , n ;
2007-06-06 09:36:04 +04:00
2009-11-02 22:02:21 +03:00
BUG_ON ( irq_nr > MAX_INTERNAL_IRQS ) ;
2008-03-04 08:53:05 +03:00
pxa_internal_irq_nr = irq_nr ;
2007-06-06 09:36:04 +04:00
2010-11-24 06:54:22 +03:00
for ( n = 0 ; n < irq_nr ; n + = 32 ) {
2011-01-11 01:53:12 +03:00
void __iomem * base = irq_base ( n > > 5 ) ;
2010-11-24 06:54:22 +03:00
__raw_writel ( 0 , base + ICMR ) ; /* disable all IRQs */
__raw_writel ( 0 , base + ICLR ) ; /* all IRQs are IRQ, not FIQ */
for ( i = n ; ( i < ( n + 32 ) ) & & ( i < irq_nr ) ; i + + ) {
/* initialize interrupt priority */
if ( cpu_has_ipr ( ) )
__raw_writel ( i | IPR_VALID , IRQ_BASE + IPR ( i ) ) ;
irq = PXA_IRQ ( i ) ;
2011-03-24 15:35:09 +03:00
irq_set_chip_and_handler ( irq , & pxa_internal_irq_chip ,
handle_level_irq ) ;
2011-03-24 15:29:39 +03:00
irq_set_chip_data ( irq , base ) ;
2010-11-24 06:54:22 +03:00
set_irq_flags ( irq , IRQF_VALID ) ;
}
2009-08-19 15:49:31 +04:00
}
2007-06-06 09:36:04 +04:00
/* only unmasked interrupts kick us out of idle */
2010-11-24 06:54:22 +03:00
__raw_writel ( 1 , irq_base ( 0 ) + ICCR ) ;
2005-04-17 02:20:36 +04:00
2010-11-29 13:18:26 +03:00
pxa_internal_irq_chip . irq_set_wake = fn ;
2007-08-29 13:22:17 +04:00
}
2008-01-29 02:00:02 +03:00
# ifdef CONFIG_PM
2009-11-02 22:02:21 +03:00
static unsigned long saved_icmr [ MAX_INTERNAL_IRQS / 32 ] ;
static unsigned long saved_ipr [ MAX_INTERNAL_IRQS ] ;
2008-01-29 02:00:02 +03:00
2011-04-23 00:03:11 +04:00
static int pxa_irq_suspend ( void )
2008-01-29 02:00:02 +03:00
{
2010-11-24 06:54:22 +03:00
int i ;
2011-01-11 01:53:12 +03:00
for ( i = 0 ; i < pxa_internal_irq_nr / 32 ; i + + ) {
2010-11-24 06:54:22 +03:00
void __iomem * base = irq_base ( i ) ;
2008-03-04 08:53:05 +03:00
2010-11-24 06:54:22 +03:00
saved_icmr [ i ] = __raw_readl ( base + ICMR ) ;
__raw_writel ( 0 , base + ICMR ) ;
2008-01-29 02:00:02 +03:00
}
2010-01-11 15:39:37 +03:00
2010-11-17 14:03:36 +03:00
if ( cpu_has_ipr ( ) ) {
2010-01-11 15:39:37 +03:00
for ( i = 0 ; i < pxa_internal_irq_nr ; i + + )
2010-11-24 06:54:22 +03:00
saved_ipr [ i ] = __raw_readl ( IRQ_BASE + IPR ( i ) ) ;
2010-01-11 15:39:37 +03:00
}
2008-01-29 02:00:02 +03:00
return 0 ;
}
2011-04-23 00:03:11 +04:00
static void pxa_irq_resume ( void )
2008-01-29 02:00:02 +03:00
{
2010-11-24 06:54:22 +03:00
int i ;
2008-03-04 08:53:05 +03:00
2011-01-11 01:53:12 +03:00
for ( i = 0 ; i < pxa_internal_irq_nr / 32 ; i + + ) {
2010-11-24 06:54:22 +03:00
void __iomem * base = irq_base ( i ) ;
2010-01-11 15:39:37 +03:00
2010-11-24 06:54:22 +03:00
__raw_writel ( saved_icmr [ i ] , base + ICMR ) ;
__raw_writel ( 0 , base + ICLR ) ;
2008-01-29 02:00:02 +03:00
}
2011-01-10 02:29:04 +03:00
if ( cpu_has_ipr ( ) )
2010-11-24 06:54:22 +03:00
for ( i = 0 ; i < pxa_internal_irq_nr ; i + + )
__raw_writel ( saved_ipr [ i ] , IRQ_BASE + IPR ( i ) ) ;
__raw_writel ( 1 , IRQ_BASE + ICCR ) ;
2008-01-29 02:00:02 +03:00
}
# else
# define pxa_irq_suspend NULL
# define pxa_irq_resume NULL
# endif
2011-04-23 00:03:11 +04:00
struct syscore_ops pxa_irq_syscore_ops = {
2008-01-29 02:00:02 +03:00
. suspend = pxa_irq_suspend ,
. resume = pxa_irq_resume ,
} ;