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>
2010-11-24 06:54:22 +03:00
# include <linux/io.h>
# include <linux/irq.h>
2005-04-17 02:20:36 +04:00
2008-08-05 19:14:15 +04:00
# include <mach/hardware.h>
2010-11-24 06:54:22 +03:00
# include <mach/irqs.h>
2009-01-06 12:37:37 +03:00
# include <mach/gpio.h>
2005-04-17 02:20:36 +04:00
# include "generic.h"
2010-11-24 06:54:22 +03:00
# define IRQ_BASE (void __iomem *)io_p2v(0x40d00000)
# 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 ) ) )
# 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 ( ) ;
}
2008-03-04 08:53:05 +03:00
static void pxa_mask_irq ( unsigned int irq )
2005-04-17 02:20:36 +04:00
{
2010-11-24 06:54:22 +03:00
void __iomem * base = get_irq_chip_data ( irq ) ;
uint32_t icmr = __raw_readl ( base + ICMR ) ;
icmr & = ~ ( 1 < < IRQ_BIT ( irq ) ) ;
__raw_writel ( icmr , base + ICMR ) ;
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
{
2010-11-24 06:54:22 +03:00
void __iomem * base = get_irq_chip_data ( irq ) ;
uint32_t icmr = __raw_readl ( base + ICMR ) ;
icmr | = 1 < < IRQ_BIT ( irq ) ;
__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 " ,
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 )
{
2010-11-24 06:54:22 +03:00
struct irq_desc * desc = irq_to_desc ( irq ) ;
desc - > chip - > mask ( irq ) ;
2009-01-06 12:37:37 +03:00
}
static void pxa_unmask_low_gpio ( unsigned int irq )
{
2010-11-24 06:54:22 +03:00
struct irq_desc * desc = irq_to_desc ( irq ) ;
desc - > chip - > unmask ( irq ) ;
2009-01-06 12:37:37 +03:00
}
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 ;
}
2010-11-24 06:54:22 +03:00
static inline void __iomem * irq_base ( int i )
{
static unsigned long phys_base [ ] = {
0x40d00000 ,
0x40d0009c ,
0x40d00130 ,
} ;
return ( void __iomem * ) io_p2v ( phys_base [ i > > 5 ] ) ;
}
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
{
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 ) {
void __iomem * base = irq_base ( n ) ;
__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 ) ;
set_irq_chip ( irq , & pxa_internal_irq_chip ) ;
set_irq_chip_data ( irq , base ) ;
set_irq_handler ( irq , handle_level_irq ) ;
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
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 )
{
2010-11-24 06:54:22 +03:00
int i ;
for ( i = 0 ; i < pxa_internal_irq_nr ; i + = 32 ) {
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 ;
}
static int pxa_irq_resume ( struct sys_device * dev )
{
2010-11-24 06:54:22 +03:00
int i ;
2008-03-04 08:53:05 +03:00
2010-11-24 06:54:22 +03:00
for ( i = 0 ; i < pxa_internal_irq_nr ; i + = 32 ) {
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
}
2010-11-24 06:54:22 +03:00
if ( ! cpu_is_pxa25x ( ) )
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
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 ) ;