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>
2008-01-29 02:00:02 +03:00
# include <linux/sysdev.h>
2005-04-17 02:20:36 +04:00
2008-08-05 19:14:15 +04:00
# include <mach/hardware.h>
2005-04-17 02:20:36 +04:00
# include <asm/irq.h>
# include <asm/mach/irq.h>
2009-01-06 12:37:37 +03:00
# include <mach/gpio.h>
2009-01-20 06:04:16 +03:00
# include <mach/regs-intc.h>
2005-04-17 02:20:36 +04:00
# include "generic.h"
2009-11-02 22:02:21 +03:00
# define MAX_INTERNAL_IRQS 128
2008-03-04 08:53:05 +03:00
# define IRQ_BIT(n) (((n) - PXA_IRQ(0)) & 0x1f)
# define _ICMR(n) (*((((n) - PXA_IRQ(0)) & ~0x1f) ? &ICMR2 : &ICMR))
# define _ICLR(n) (*((((n) - PXA_IRQ(0)) & ~0x1f) ? &ICLR2 : &ICLR))
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 ( ) ;
}
2008-03-04 08:53:05 +03:00
static void pxa_mask_irq ( unsigned int irq )
2005-04-17 02:20:36 +04:00
{
2008-03-04 08:53:05 +03:00
_ICMR ( irq ) & = ~ ( 1 < < IRQ_BIT ( irq ) ) ;
2005-04-17 02:20:36 +04:00
}
2008-03-04 08:53:05 +03:00
static void pxa_unmask_irq ( unsigned int irq )
2005-04-17 02:20:36 +04:00
{
2008-03-04 08:53:05 +03:00
_ICMR ( irq ) | = 1 < < IRQ_BIT ( irq ) ;
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 " ,
2008-03-04 08:53:05 +03:00
. ack = pxa_mask_irq ,
. mask = pxa_mask_irq ,
. unmask = pxa_unmask_irq ,
2005-04-17 02:20:36 +04:00
} ;
2009-01-06 12:37:37 +03:00
/*
* GPIO IRQs for GPIO 0 and 1
*/
static int pxa_set_low_gpio_type ( unsigned int irq , unsigned int type )
{
int gpio = irq - IRQ_GPIO0 ;
if ( __gpio_is_occupied ( gpio ) ) {
pr_err ( " %s failed: GPIO is configured \n " , __func__ ) ;
return - EINVAL ;
}
if ( type & IRQ_TYPE_EDGE_RISING )
GRER0 | = GPIO_bit ( gpio ) ;
else
GRER0 & = ~ GPIO_bit ( gpio ) ;
if ( type & IRQ_TYPE_EDGE_FALLING )
GFER0 | = GPIO_bit ( gpio ) ;
else
GFER0 & = ~ GPIO_bit ( gpio ) ;
return 0 ;
}
static void pxa_ack_low_gpio ( unsigned int irq )
{
GEDR0 = ( 1 < < ( irq - IRQ_GPIO0 ) ) ;
}
static void pxa_mask_low_gpio ( unsigned int irq )
{
ICMR & = ~ ( 1 < < ( irq - PXA_IRQ ( 0 ) ) ) ;
}
static void pxa_unmask_low_gpio ( unsigned int irq )
{
ICMR | = 1 < < ( irq - PXA_IRQ ( 0 ) ) ;
}
static struct irq_chip pxa_low_gpio_chip = {
. name = " GPIO-l " ,
. ack = pxa_ack_low_gpio ,
. mask = pxa_mask_low_gpio ,
. unmask = pxa_unmask_low_gpio ,
. set_type = pxa_set_low_gpio_type ,
} ;
static void __init pxa_init_low_gpio_irq ( set_wake_t fn )
{
int irq ;
/* clear edge detection on GPIO 0 and 1 */
GFER0 & = ~ 0x3 ;
GRER0 & = ~ 0x3 ;
GEDR0 = 0x3 ;
for ( irq = IRQ_GPIO0 ; irq < = IRQ_GPIO1 ; irq + + ) {
set_irq_chip ( irq , & pxa_low_gpio_chip ) ;
set_irq_handler ( irq , handle_edge_irq ) ;
set_irq_flags ( irq , IRQF_VALID ) ;
}
pxa_low_gpio_chip . set_wake = fn ;
}
2008-03-04 09:19:58 +03:00
void __init pxa_init_irq ( int irq_nr , set_wake_t fn )
2007-06-06 09:36:04 +04:00
{
2009-08-19 15:49:31 +04:00
int irq , i ;
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
2008-09-01 16:03:32 +04:00
for ( irq = PXA_IRQ ( 0 ) ; irq < PXA_IRQ ( irq_nr ) ; irq + = 32 ) {
2008-03-04 08:53:05 +03:00
_ICMR ( irq ) = 0 ; /* disable all IRQs */
_ICLR ( irq ) = 0 ; /* all IRQs are IRQ, not FIQ */
}
2007-06-06 09:36:04 +04:00
2009-08-19 15:49:31 +04:00
/* initialize interrupt priority */
2010-11-17 14:03:36 +03:00
if ( cpu_has_ipr ( ) ) {
2009-08-19 15:49:31 +04:00
for ( i = 0 ; i < irq_nr ; i + + )
IPR ( i ) = i | ( 1 < < 31 ) ;
}
2007-06-06 09:36:04 +04:00
/* only unmasked interrupts kick us out of idle */
ICCR = 1 ;
2008-03-04 08:53:05 +03:00
for ( irq = PXA_IRQ ( 0 ) ; irq < PXA_IRQ ( irq_nr ) ; irq + + ) {
set_irq_chip ( irq , & pxa_internal_irq_chip ) ;
2007-06-06 09:32:38 +04:00
set_irq_handler ( irq , handle_level_irq ) ;
set_irq_flags ( irq , IRQF_VALID ) ;
}
2005-04-17 02:20:36 +04:00
2008-03-04 09:19:58 +03:00
pxa_internal_irq_chip . set_wake = fn ;
2009-01-06 12:37:37 +03:00
pxa_init_low_gpio_irq ( 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
static int pxa_irq_suspend ( struct sys_device * dev , pm_message_t state )
{
2008-03-04 08:53:05 +03:00
int i , irq = PXA_IRQ ( 0 ) ;
for ( i = 0 ; irq < PXA_IRQ ( pxa_internal_irq_nr ) ; i + + , irq + = 32 ) {
saved_icmr [ i ] = _ICMR ( irq ) ;
_ICMR ( irq ) = 0 ;
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 + + )
saved_ipr [ i ] = IPR ( i ) ;
}
2008-01-29 02:00:02 +03:00
return 0 ;
}
static int pxa_irq_resume ( struct sys_device * dev )
{
2008-03-04 08:53:05 +03:00
int i , irq = PXA_IRQ ( 0 ) ;
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 + + )
IPR ( i ) = saved_ipr [ i ] ;
}
2008-03-04 08:53:05 +03:00
for ( i = 0 ; irq < PXA_IRQ ( pxa_internal_irq_nr ) ; i + + , irq + = 32 ) {
_ICMR ( irq ) = saved_icmr [ i ] ;
_ICLR ( irq ) = 0 ;
2008-01-29 02:00:02 +03:00
}
2008-03-04 08:53:05 +03:00
ICCR = 1 ;
2008-01-29 02:00:02 +03:00
return 0 ;
}
# else
# define pxa_irq_suspend NULL
# define pxa_irq_resume NULL
# endif
struct sysdev_class pxa_irq_sysclass = {
. name = " irq " ,
. suspend = pxa_irq_suspend ,
. resume = pxa_irq_resume ,
} ;
static int __init pxa_irq_init ( void )
{
return sysdev_class_register ( & pxa_irq_sysclass ) ;
}
core_initcall ( pxa_irq_init ) ;