2009-03-26 11:06:08 +03:00
/*
* Interrupt routines for Gemini
*
* Copyright ( C ) 2001 - 2006 Storlink , Corp .
* Copyright ( C ) 2008 - 2009 Paulius Zaleckas < paulius . zaleckas @ teltonika . lt >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*/
# include <linux/init.h>
# include <linux/io.h>
# include <linux/ioport.h>
# include <linux/stddef.h>
# include <linux/list.h>
# include <linux/sched.h>
# include <asm/irq.h>
# include <asm/mach/irq.h>
# include <mach/hardware.h>
# define IRQ_SOURCE(base_addr) (base_addr + 0x00)
# define IRQ_MASK(base_addr) (base_addr + 0x04)
# define IRQ_CLEAR(base_addr) (base_addr + 0x08)
# define IRQ_TMODE(base_addr) (base_addr + 0x0C)
# define IRQ_TLEVEL(base_addr) (base_addr + 0x10)
# define IRQ_STATUS(base_addr) (base_addr + 0x14)
# define FIQ_SOURCE(base_addr) (base_addr + 0x20)
# define FIQ_MASK(base_addr) (base_addr + 0x24)
# define FIQ_CLEAR(base_addr) (base_addr + 0x28)
# define FIQ_TMODE(base_addr) (base_addr + 0x2C)
# define FIQ_LEVEL(base_addr) (base_addr + 0x30)
# define FIQ_STATUS(base_addr) (base_addr + 0x34)
2010-11-29 12:30:40 +03:00
static void gemini_ack_irq ( struct irq_data * d )
2009-03-26 11:06:08 +03:00
{
2010-11-29 12:30:40 +03:00
__raw_writel ( 1 < < d - > irq , IRQ_CLEAR ( IO_ADDRESS ( GEMINI_INTERRUPT_BASE ) ) ) ;
2009-03-26 11:06:08 +03:00
}
2010-11-29 12:30:40 +03:00
static void gemini_mask_irq ( struct irq_data * d )
2009-03-26 11:06:08 +03:00
{
unsigned int mask ;
mask = __raw_readl ( IRQ_MASK ( IO_ADDRESS ( GEMINI_INTERRUPT_BASE ) ) ) ;
2010-11-29 12:30:40 +03:00
mask & = ~ ( 1 < < d - > irq ) ;
2009-03-26 11:06:08 +03:00
__raw_writel ( mask , IRQ_MASK ( IO_ADDRESS ( GEMINI_INTERRUPT_BASE ) ) ) ;
}
2010-11-29 12:30:40 +03:00
static void gemini_unmask_irq ( struct irq_data * d )
2009-03-26 11:06:08 +03:00
{
unsigned int mask ;
mask = __raw_readl ( IRQ_MASK ( IO_ADDRESS ( GEMINI_INTERRUPT_BASE ) ) ) ;
2010-11-29 12:30:40 +03:00
mask | = ( 1 < < d - > irq ) ;
2009-03-26 11:06:08 +03:00
__raw_writel ( mask , IRQ_MASK ( IO_ADDRESS ( GEMINI_INTERRUPT_BASE ) ) ) ;
}
static struct irq_chip gemini_irq_chip = {
2010-11-29 12:30:40 +03:00
. name = " INTC " ,
. irq_ack = gemini_ack_irq ,
. irq_mask = gemini_mask_irq ,
. irq_unmask = gemini_unmask_irq ,
2009-03-26 11:06:08 +03:00
} ;
static struct resource irq_resource = {
. name = " irq_handler " ,
. start = IO_ADDRESS ( GEMINI_INTERRUPT_BASE ) ,
. end = IO_ADDRESS ( FIQ_STATUS ( GEMINI_INTERRUPT_BASE ) ) + 4 ,
} ;
void __init gemini_init_irq ( void )
{
unsigned int i , mode = 0 , level = 0 ;
/*
* Disable arch_idle ( ) by default since it is buggy
* For more info see arch / arm / mach - gemini / include / mach / system . h
*/
disable_hlt ( ) ;
request_resource ( & iomem_resource , & irq_resource ) ;
for ( i = 0 ; i < NR_IRQS ; i + + ) {
2011-03-24 15:25:22 +03:00
irq_set_chip ( i , & gemini_irq_chip ) ;
2009-03-26 11:06:08 +03:00
if ( ( i > = IRQ_TIMER1 & & i < = IRQ_TIMER3 ) | | ( i > = IRQ_SERIRQ0 & & i < = IRQ_SERIRQ1 ) ) {
2011-03-24 15:25:22 +03:00
irq_set_handler ( i , handle_edge_irq ) ;
2009-03-26 11:06:08 +03:00
mode | = 1 < < i ;
level | = 1 < < i ;
} else {
2011-03-24 15:25:22 +03:00
irq_set_handler ( i , handle_level_irq ) ;
2009-03-26 11:06:08 +03:00
}
set_irq_flags ( i , IRQF_VALID | IRQF_PROBE ) ;
}
/* Disable all interrupts */
__raw_writel ( 0 , IRQ_MASK ( IO_ADDRESS ( GEMINI_INTERRUPT_BASE ) ) ) ;
__raw_writel ( 0 , FIQ_MASK ( IO_ADDRESS ( GEMINI_INTERRUPT_BASE ) ) ) ;
/* Set interrupt mode */
__raw_writel ( mode , IRQ_TMODE ( IO_ADDRESS ( GEMINI_INTERRUPT_BASE ) ) ) ;
__raw_writel ( level , IRQ_TLEVEL ( IO_ADDRESS ( GEMINI_INTERRUPT_BASE ) ) ) ;
}