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 .
*/
2015-02-15 00:41:56 +03:00
# include <linux/bitops.h>
2005-04-17 02:20:36 +04:00
# 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>
2012-07-22 21:50:22 +04:00
# include <linux/of_address.h>
# include <linux/of_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"
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)
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 .
*/
2012-07-22 21:50:22 +04:00
static void __iomem * pxa_irq_base ;
2008-03-04 08:53:05 +03:00
static int pxa_internal_irq_nr ;
2012-07-22 21:50:22 +04:00
static bool cpu_has_ipr ;
2015-02-15 00:41:56 +03:00
static struct irq_domain * pxa_irq_domain ;
2010-11-17 14:03:36 +03:00
2011-01-13 01:42:24 +03:00
static inline void __iomem * irq_base ( int i )
{
2012-07-22 21:50:22 +04:00
static unsigned long phys_base_offset [ ] = {
0x0 ,
0x9c ,
0x130 ,
2011-01-13 01:42:24 +03:00
} ;
2012-07-22 21:50:22 +04:00
return pxa_irq_base + phys_base_offset [ 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 ) ;
2015-02-15 00:41:56 +03:00
irq_hw_number_t irq = irqd_to_hwirq ( d ) ;
2010-11-24 06:54:22 +03:00
uint32_t icmr = __raw_readl ( base + ICMR ) ;
2015-02-15 00:41:56 +03:00
icmr & = ~ BIT ( irq & 0x1f ) ;
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 ) ;
2015-02-15 00:41:56 +03:00
irq_hw_number_t irq = irqd_to_hwirq ( d ) ;
2010-11-24 06:54:22 +03:00
uint32_t icmr = __raw_readl ( base + ICMR ) ;
2015-02-15 00:41:56 +03:00
icmr | = BIT ( irq & 0x1f ) ;
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 {
2012-07-22 21:50:22 +04:00
icip = __raw_readl ( pxa_irq_base + ICIP ) ;
icmr = __raw_readl ( pxa_irq_base + ICMR ) ;
2011-04-27 18:48:05 +04:00
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 ) ;
}
2015-02-15 00:41:56 +03:00
static int pxa_irq_map ( struct irq_domain * h , unsigned int virq ,
irq_hw_number_t hw )
2007-06-06 09:36:04 +04:00
{
2015-02-15 00:41:56 +03:00
void __iomem * base = irq_base ( hw / 32 ) ;
2007-06-06 09:36:04 +04:00
2015-02-15 00:41:56 +03:00
/* initialize interrupt priority */
if ( cpu_has_ipr )
__raw_writel ( hw | IPR_VALID , pxa_irq_base + IPR ( hw ) ) ;
irq_set_chip_and_handler ( virq , & pxa_internal_irq_chip ,
handle_level_irq ) ;
irq_set_chip_data ( virq , base ) ;
return 0 ;
}
2015-04-27 15:50:46 +03:00
static const struct irq_domain_ops pxa_irq_ops = {
2015-02-15 00:41:56 +03:00
. map = pxa_irq_map ,
. xlate = irq_domain_xlate_onecell ,
} ;
static __init void
pxa_init_irq_common ( struct device_node * node , int irq_nr ,
int ( * fn ) ( struct irq_data * , unsigned int ) )
{
int n ;
2009-11-02 22:02:21 +03:00
2008-03-04 08:53:05 +03:00
pxa_internal_irq_nr = irq_nr ;
2015-02-15 00:41:56 +03:00
pxa_irq_domain = irq_domain_add_legacy ( node , irq_nr ,
PXA_IRQ ( 0 ) , 0 ,
& pxa_irq_ops , NULL ) ;
if ( ! pxa_irq_domain )
panic ( " Unable to add PXA IRQ domain \n " ) ;
irq_set_default_host ( pxa_irq_domain ) ;
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 */
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
2015-02-15 00:41:56 +03:00
void __init pxa_init_irq ( int irq_nr , int ( * fn ) ( struct irq_data * , unsigned int ) )
{
BUG_ON ( irq_nr > MAX_INTERNAL_IRQS ) ;
pxa_irq_base = io_p2v ( 0x40d00000 ) ;
cpu_has_ipr = ! cpu_is_pxa25x ( ) ;
pxa_init_irq_common ( NULL , irq_nr , fn ) ;
}
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
2012-07-22 21:50:22 +04:00
if ( cpu_has_ipr ) {
2010-01-11 15:39:37 +03:00
for ( i = 0 ; i < pxa_internal_irq_nr ; i + + )
2012-07-22 21:50:22 +04:00
saved_ipr [ i ] = __raw_readl ( pxa_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
}
2012-07-22 21:50:22 +04:00
if ( cpu_has_ipr )
2010-11-24 06:54:22 +03:00
for ( i = 0 ; i < pxa_internal_irq_nr ; i + + )
2012-07-22 21:50:22 +04:00
__raw_writel ( saved_ipr [ i ] , pxa_irq_base + IPR ( i ) ) ;
2010-11-24 06:54:22 +03:00
2012-07-22 21:50:22 +04:00
__raw_writel ( 1 , pxa_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 ,
} ;
2012-07-22 21:50:22 +04:00
# ifdef CONFIG_OF
static const struct of_device_id intc_ids [ ] __initconst = {
{ . compatible = " marvell,pxa-intc " , } ,
{ }
} ;
void __init pxa_dt_irq_init ( int ( * fn ) ( struct irq_data * , unsigned int ) )
{
struct device_node * node ;
struct resource res ;
2015-02-15 00:41:56 +03:00
int ret ;
2012-07-22 21:50:22 +04:00
node = of_find_matching_node ( NULL , intc_ids ) ;
if ( ! node ) {
pr_err ( " Failed to find interrupt controller in arch-pxa \n " ) ;
return ;
}
ret = of_property_read_u32 ( node , " marvell,intc-nr-irqs " ,
& pxa_internal_irq_nr ) ;
if ( ret ) {
pr_err ( " Not found marvell,intc-nr-irqs property \n " ) ;
return ;
}
ret = of_address_to_resource ( node , 0 , & res ) ;
if ( ret < 0 ) {
pr_err ( " No registers defined for node \n " ) ;
return ;
}
pxa_irq_base = io_p2v ( res . start ) ;
if ( of_find_property ( node , " marvell,intc-priority " , NULL ) )
cpu_has_ipr = 1 ;
ret = irq_alloc_descs ( - 1 , 0 , pxa_internal_irq_nr , 0 ) ;
if ( ret < 0 ) {
pr_err ( " Failed to allocate IRQ numbers \n " ) ;
return ;
}
2015-02-15 00:41:56 +03:00
pxa_init_irq_common ( node , pxa_internal_irq_nr , fn ) ;
2012-07-22 21:50:22 +04:00
}
# endif /* CONFIG_OF */