2009-08-06 15:12:43 +03:00
/*
* arch / arm / mach - dove / irq . c
*
* Dove IRQ handling .
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed " as is " without any
* warranty of any kind , whether express or implied .
*/
# include <linux/kernel.h>
# include <linux/init.h>
# include <linux/irq.h>
# include <linux/gpio.h>
# include <linux/io.h>
# include <asm/mach/arch.h>
# include <plat/irq.h>
# include <asm/mach/irq.h>
# include <mach/pm.h>
# include <mach/bridge-regs.h>
2012-08-29 10:16:55 -05:00
# include <plat/orion-gpio.h>
2009-08-06 15:12:43 +03:00
# include "common.h"
2010-11-29 10:27:47 +01:00
static void pmu_irq_mask ( struct irq_data * d )
2009-08-06 15:12:43 +03:00
{
2010-11-29 10:27:47 +01:00
int pin = irq_to_pmu ( d - > irq ) ;
2009-08-06 15:12:43 +03:00
u32 u ;
u = readl ( PMU_INTERRUPT_MASK ) ;
u & = ~ ( 1 < < ( pin & 31 ) ) ;
writel ( u , PMU_INTERRUPT_MASK ) ;
}
2010-11-29 10:27:47 +01:00
static void pmu_irq_unmask ( struct irq_data * d )
2009-08-06 15:12:43 +03:00
{
2010-11-29 10:27:47 +01:00
int pin = irq_to_pmu ( d - > irq ) ;
2009-08-06 15:12:43 +03:00
u32 u ;
u = readl ( PMU_INTERRUPT_MASK ) ;
u | = 1 < < ( pin & 31 ) ;
writel ( u , PMU_INTERRUPT_MASK ) ;
}
2010-11-29 10:27:47 +01:00
static void pmu_irq_ack ( struct irq_data * d )
2009-08-06 15:12:43 +03:00
{
2010-11-29 10:27:47 +01:00
int pin = irq_to_pmu ( d - > irq ) ;
2009-08-06 15:12:43 +03:00
u32 u ;
2012-11-18 16:29:44 +00:00
/*
* The PMU mask register is not RW0C : it is RW . This means that
* the bits take whatever value is written to them ; if you write
* a ' 1 ' , you will set the interrupt .
*
* Unfortunately this means there is NO race free way to clear
* these interrupts .
*
* So , let ' s structure the code so that the window is as small as
* possible .
*/
2009-08-06 15:12:43 +03:00
u = ~ ( 1 < < ( pin & 31 ) ) ;
2012-11-18 16:29:44 +00:00
u & = readl_relaxed ( PMU_INTERRUPT_CAUSE ) ;
writel_relaxed ( u , PMU_INTERRUPT_CAUSE ) ;
2009-08-06 15:12:43 +03:00
}
static struct irq_chip pmu_irq_chip = {
. name = " pmu_irq " ,
2010-11-29 10:27:47 +01:00
. irq_mask = pmu_irq_mask ,
. irq_unmask = pmu_irq_unmask ,
. irq_ack = pmu_irq_ack ,
2009-08-06 15:12:43 +03:00
} ;
static void pmu_irq_handler ( unsigned int irq , struct irq_desc * desc )
{
unsigned long cause = readl ( PMU_INTERRUPT_CAUSE ) ;
cause & = readl ( PMU_INTERRUPT_MASK ) ;
if ( cause = = 0 ) {
do_bad_IRQ ( irq , desc ) ;
return ;
}
for ( irq = 0 ; irq < NR_PMU_IRQS ; irq + + ) {
if ( ! ( cause & ( 1 < < irq ) ) )
continue ;
irq = pmu_to_irq ( irq ) ;
2011-03-24 12:33:40 +01:00
generic_handle_irq ( irq ) ;
2009-08-06 15:12:43 +03:00
}
}
2012-06-27 13:40:04 +02:00
static int __initdata gpio0_irqs [ 4 ] = {
IRQ_DOVE_GPIO_0_7 ,
IRQ_DOVE_GPIO_8_15 ,
IRQ_DOVE_GPIO_16_23 ,
IRQ_DOVE_GPIO_24_31 ,
} ;
static int __initdata gpio1_irqs [ 4 ] = {
IRQ_DOVE_HIGH_GPIO ,
0 ,
0 ,
0 ,
} ;
static int __initdata gpio2_irqs [ 4 ] = {
0 ,
0 ,
0 ,
0 ,
} ;
2014-04-22 23:26:26 +02:00
# ifdef CONFIG_MULTI_IRQ_HANDLER
/*
* Compiling with both non - DT and DT support enabled , will
* break asm irq handler used by non - DT boards . Therefore ,
* we provide a C - style irq handler even for non - DT boards ,
* if MULTI_IRQ_HANDLER is set .
*/
static void __iomem * dove_irq_base = IRQ_VIRT_BASE ;
static asmlinkage void
__exception_irq_entry dove_legacy_handle_irq ( struct pt_regs * regs )
{
u32 stat ;
stat = readl_relaxed ( dove_irq_base + IRQ_CAUSE_LOW_OFF ) ;
stat & = readl_relaxed ( dove_irq_base + IRQ_MASK_LOW_OFF ) ;
if ( stat ) {
unsigned int hwirq = __fls ( stat ) ;
handle_IRQ ( hwirq , regs ) ;
return ;
}
stat = readl_relaxed ( dove_irq_base + IRQ_CAUSE_HIGH_OFF ) ;
stat & = readl_relaxed ( dove_irq_base + IRQ_MASK_HIGH_OFF ) ;
if ( stat ) {
unsigned int hwirq = 32 + __fls ( stat ) ;
handle_IRQ ( hwirq , regs ) ;
return ;
}
}
# endif
2009-08-06 15:12:43 +03:00
void __init dove_init_irq ( void )
{
int i ;
2012-09-11 14:27:18 +02:00
orion_irq_init ( 0 , IRQ_VIRT_BASE + IRQ_MASK_LOW_OFF ) ;
orion_irq_init ( 32 , IRQ_VIRT_BASE + IRQ_MASK_HIGH_OFF ) ;
2009-08-06 15:12:43 +03:00
2014-04-22 23:26:26 +02:00
# ifdef CONFIG_MULTI_IRQ_HANDLER
set_handle_irq ( dove_legacy_handle_irq ) ;
# endif
2009-08-06 15:12:43 +03:00
/*
2010-12-14 12:54:03 +01:00
* Initialize gpiolib for GPIOs 0 - 71.
2009-08-06 15:12:43 +03:00
*/
2012-09-11 14:27:18 +02:00
orion_gpio_init ( NULL , 0 , 32 , DOVE_GPIO_LO_VIRT_BASE , 0 ,
2012-06-27 13:40:04 +02:00
IRQ_DOVE_GPIO_START , gpio0_irqs ) ;
2012-09-11 14:27:18 +02:00
orion_gpio_init ( NULL , 32 , 32 , DOVE_GPIO_HI_VIRT_BASE , 0 ,
2012-06-27 13:40:04 +02:00
IRQ_DOVE_GPIO_START + 32 , gpio1_irqs ) ;
2012-09-11 14:27:18 +02:00
orion_gpio_init ( NULL , 64 , 8 , DOVE_GPIO2_VIRT_BASE , 0 ,
2012-06-27 13:40:04 +02:00
IRQ_DOVE_GPIO_START + 64 , gpio2_irqs ) ;
2009-08-06 15:12:43 +03:00
/*
* Mask and clear PMU interrupts
*/
writel ( 0 , PMU_INTERRUPT_MASK ) ;
writel ( 0 , PMU_INTERRUPT_CAUSE ) ;
for ( i = IRQ_DOVE_PMU_START ; i < NR_IRQS ; i + + ) {
2011-03-24 13:35:09 +01:00
irq_set_chip_and_handler ( i , & pmu_irq_chip , handle_level_irq ) ;
2011-03-24 12:33:40 +01:00
irq_set_status_flags ( i , IRQ_LEVEL ) ;
2009-08-06 15:12:43 +03:00
set_irq_flags ( i , IRQF_VALID ) ;
}
2011-03-24 13:25:22 +01:00
irq_set_chained_handler ( IRQ_DOVE_PMU , pmu_irq_handler ) ;
2009-08-06 15:12:43 +03:00
}