2007-10-23 23:14:42 +04:00
/*
2008-03-27 21:51:41 +03:00
* arch / arm / mach - orion5x / irq . c
2007-10-23 23:14:42 +04:00
*
* Core IRQ functions for Marvell Orion System On Chip
*
* Maintainer : Tzachi Perelstein < tzachi @ marvell . com >
*
* This file is licensed under the terms of the GNU General Public
2008-03-27 21:51:41 +03:00
* License version 2. This program is licensed " as is " without any
2007-10-23 23:14:42 +04:00
* warranty of any kind , whether express or implied .
*/
# include <linux/kernel.h>
# include <linux/init.h>
# include <linux/irq.h>
2008-09-06 15:10:45 +04:00
# include <linux/io.h>
2007-10-23 23:14:42 +04:00
# include <asm/gpio.h>
2008-08-05 19:14:15 +04:00
# include <mach/orion5x.h>
2008-08-09 15:44:58 +04:00
# include <plat/irq.h>
2007-10-23 23:14:42 +04:00
# include "common.h"
/*****************************************************************************
* Orion GPIO IRQ
2007-11-15 11:57:48 +03:00
*
* GPIO_IN_POL register controlls whether GPIO_DATA_IN will hold the same
* value of the line or the opposite value .
*
* Level IRQ handlers : DATA_IN is used directly as cause register .
* Interrupt are masked by LEVEL_MASK registers .
* Edge IRQ handlers : Change in DATA_IN are latched in EDGE_CAUSE .
* Interrupt are masked by EDGE_MASK registers .
* Both - edge handlers : Similar to regular Edge handlers , but also swaps
* the polarity to catch the next line transaction .
* This is a race condition that might not perfectly
* work on some use cases .
*
* Every eight GPIO lines are grouped ( OR ' ed ) before going up to main
* cause register .
*
* EDGE cause mask
* data - in / - - - - - - - - | | - - - - - | | - - - - \
* - - - - - | | - - - - - - - - - to main cause reg
* X \ - - - - - - - - - - - - - - - - | | - - - - /
* polarity LEVEL mask
*
2007-10-23 23:14:42 +04:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2008-03-27 21:51:41 +03:00
static void orion5x_gpio_irq_ack ( u32 irq )
2007-11-15 11:57:48 +03:00
{
int pin = irq_to_gpio ( irq ) ;
if ( irq_desc [ irq ] . status & IRQ_LEVEL )
/*
* Mask bit for level interrupt
*/
2008-03-27 21:51:41 +03:00
orion5x_clrbits ( GPIO_LEVEL_MASK , 1 < < pin ) ;
2007-11-15 11:57:48 +03:00
else
/*
* Clear casue bit for egde interrupt
*/
2008-03-27 21:51:41 +03:00
orion5x_clrbits ( GPIO_EDGE_CAUSE , 1 < < pin ) ;
2007-11-15 11:57:48 +03:00
}
2008-03-27 21:51:41 +03:00
static void orion5x_gpio_irq_mask ( u32 irq )
2007-10-23 23:14:42 +04:00
{
int pin = irq_to_gpio ( irq ) ;
2007-11-15 11:57:48 +03:00
if ( irq_desc [ irq ] . status & IRQ_LEVEL )
2008-03-27 21:51:41 +03:00
orion5x_clrbits ( GPIO_LEVEL_MASK , 1 < < pin ) ;
2007-11-15 11:57:48 +03:00
else
2008-03-27 21:51:41 +03:00
orion5x_clrbits ( GPIO_EDGE_MASK , 1 < < pin ) ;
2007-10-23 23:14:42 +04:00
}
2008-03-27 21:51:41 +03:00
static void orion5x_gpio_irq_unmask ( u32 irq )
2007-10-23 23:14:42 +04:00
{
int pin = irq_to_gpio ( irq ) ;
2007-11-15 11:57:48 +03:00
if ( irq_desc [ irq ] . status & IRQ_LEVEL )
2008-03-27 21:51:41 +03:00
orion5x_setbits ( GPIO_LEVEL_MASK , 1 < < pin ) ;
2007-11-15 11:57:48 +03:00
else
2008-03-27 21:51:41 +03:00
orion5x_setbits ( GPIO_EDGE_MASK , 1 < < pin ) ;
2007-10-23 23:14:42 +04:00
}
2008-03-27 21:51:41 +03:00
static int orion5x_gpio_set_irq_type ( u32 irq , u32 type )
2007-10-23 23:14:42 +04:00
{
int pin = irq_to_gpio ( irq ) ;
2007-11-15 11:57:48 +03:00
struct irq_desc * desc ;
2007-10-23 23:14:42 +04:00
2008-05-28 18:43:48 +04:00
if ( ( readl ( GPIO_IO_CONF ) & ( 1 < < pin ) ) = = 0 ) {
2008-03-27 21:51:41 +03:00
printk ( KERN_ERR " orion5x_gpio_set_irq_type failed "
2007-10-23 23:14:42 +04:00
" (irq %d, pin %d). \n " , irq , pin ) ;
return - EINVAL ;
}
2007-11-15 11:57:48 +03:00
desc = irq_desc + irq ;
2007-10-23 23:14:42 +04:00
switch ( type ) {
2008-07-27 07:23:31 +04:00
case IRQ_TYPE_LEVEL_HIGH :
2007-11-15 11:57:48 +03:00
desc - > handle_irq = handle_level_irq ;
desc - > status | = IRQ_LEVEL ;
2008-03-27 21:51:41 +03:00
orion5x_clrbits ( GPIO_IN_POL , ( 1 < < pin ) ) ;
2007-10-23 23:14:42 +04:00
break ;
2008-07-27 07:23:31 +04:00
case IRQ_TYPE_LEVEL_LOW :
2007-11-15 11:57:48 +03:00
desc - > handle_irq = handle_level_irq ;
desc - > status | = IRQ_LEVEL ;
2008-03-27 21:51:41 +03:00
orion5x_setbits ( GPIO_IN_POL , ( 1 < < pin ) ) ;
2007-11-15 11:57:48 +03:00
break ;
2008-07-27 07:23:31 +04:00
case IRQ_TYPE_EDGE_RISING :
2007-11-15 11:57:48 +03:00
desc - > handle_irq = handle_edge_irq ;
desc - > status & = ~ IRQ_LEVEL ;
2008-03-27 21:51:41 +03:00
orion5x_clrbits ( GPIO_IN_POL , ( 1 < < pin ) ) ;
2007-11-15 11:57:48 +03:00
break ;
2008-07-27 07:23:31 +04:00
case IRQ_TYPE_EDGE_FALLING :
2007-11-15 11:57:48 +03:00
desc - > handle_irq = handle_edge_irq ;
desc - > status & = ~ IRQ_LEVEL ;
2008-03-27 21:51:41 +03:00
orion5x_setbits ( GPIO_IN_POL , ( 1 < < pin ) ) ;
2007-11-15 11:57:48 +03:00
break ;
2008-07-27 07:23:31 +04:00
case IRQ_TYPE_EDGE_BOTH :
2007-11-15 11:57:48 +03:00
desc - > handle_irq = handle_edge_irq ;
desc - > status & = ~ IRQ_LEVEL ;
/*
* set initial polarity based on current input level
*/
2008-05-28 18:43:48 +04:00
if ( ( readl ( GPIO_IN_POL ) ^ readl ( GPIO_DATA_IN ) )
2007-11-15 11:57:48 +03:00
& ( 1 < < pin ) )
2008-03-27 21:51:41 +03:00
orion5x_setbits ( GPIO_IN_POL , ( 1 < < pin ) ) ; /* falling */
2007-11-15 11:57:48 +03:00
else
2008-03-27 21:51:41 +03:00
orion5x_clrbits ( GPIO_IN_POL , ( 1 < < pin ) ) ; /* rising */
2007-11-15 11:57:48 +03:00
2007-10-23 23:14:42 +04:00
break ;
default :
printk ( KERN_ERR " failed to set irq=%d (type=%d) \n " , irq , type ) ;
return - EINVAL ;
}
2007-11-15 11:57:48 +03:00
desc - > status & = ~ IRQ_TYPE_SENSE_MASK ;
desc - > status | = type & IRQ_TYPE_SENSE_MASK ;
2007-10-23 23:14:42 +04:00
return 0 ;
}
2008-03-27 21:51:41 +03:00
static struct irq_chip orion5x_gpio_irq_chip = {
2007-10-23 23:14:42 +04:00
. name = " Orion-IRQ-GPIO " ,
2008-03-27 21:51:41 +03:00
. ack = orion5x_gpio_irq_ack ,
. mask = orion5x_gpio_irq_mask ,
. unmask = orion5x_gpio_irq_unmask ,
. set_type = orion5x_gpio_set_irq_type ,
2007-10-23 23:14:42 +04:00
} ;
2008-03-27 21:51:41 +03:00
static void orion5x_gpio_irq_handler ( unsigned int irq , struct irq_desc * desc )
2007-10-23 23:14:42 +04:00
{
2007-11-15 11:57:48 +03:00
u32 cause , offs , pin ;
2007-10-23 23:14:42 +04:00
2008-03-27 21:51:41 +03:00
BUG_ON ( irq < IRQ_ORION5X_GPIO_0_7 | | irq > IRQ_ORION5X_GPIO_24_31 ) ;
offs = ( irq - IRQ_ORION5X_GPIO_0_7 ) * 8 ;
2008-05-28 18:43:48 +04:00
cause = ( readl ( GPIO_DATA_IN ) & readl ( GPIO_LEVEL_MASK ) ) |
( readl ( GPIO_EDGE_CAUSE ) & readl ( GPIO_EDGE_MASK ) ) ;
2007-11-15 11:57:48 +03:00
for ( pin = offs ; pin < offs + 8 ; pin + + ) {
if ( cause & ( 1 < < pin ) ) {
irq = gpio_to_irq ( pin ) ;
desc = irq_desc + irq ;
2008-07-27 07:23:31 +04:00
if ( ( desc - > status & IRQ_TYPE_SENSE_MASK ) = = IRQ_TYPE_EDGE_BOTH ) {
2007-11-15 11:57:48 +03:00
/* Swap polarity (race with GPIO line) */
2008-05-28 18:43:48 +04:00
u32 polarity = readl ( GPIO_IN_POL ) ;
2007-11-15 11:57:48 +03:00
polarity ^ = 1 < < pin ;
2008-05-28 18:43:48 +04:00
writel ( polarity , GPIO_IN_POL ) ;
2007-10-23 23:14:42 +04:00
}
2008-10-09 16:36:24 +04:00
generic_handle_irq ( irq ) ;
2007-10-23 23:14:42 +04:00
}
}
}
2008-03-27 21:51:41 +03:00
static void __init orion5x_init_gpio_irq ( void )
2007-10-23 23:14:42 +04:00
{
int i ;
2007-11-15 11:57:48 +03:00
struct irq_desc * desc ;
2007-10-23 23:14:42 +04:00
/*
* Mask and clear GPIO IRQ interrupts
*/
2008-05-28 18:43:48 +04:00
writel ( 0x0 , GPIO_LEVEL_MASK ) ;
writel ( 0x0 , GPIO_EDGE_MASK ) ;
writel ( 0x0 , GPIO_EDGE_CAUSE ) ;
2007-10-23 23:14:42 +04:00
/*
2007-11-15 11:57:48 +03:00
* Register chained level handlers for GPIO IRQs by default .
* User can use set_type ( ) if he wants to use edge types handlers .
2007-10-23 23:14:42 +04:00
*/
2008-03-27 21:51:41 +03:00
for ( i = IRQ_ORION5X_GPIO_START ; i < NR_IRQS ; i + + ) {
set_irq_chip ( i , & orion5x_gpio_irq_chip ) ;
2007-10-23 23:14:42 +04:00
set_irq_handler ( i , handle_level_irq ) ;
2007-11-15 11:57:48 +03:00
desc = irq_desc + i ;
desc - > status | = IRQ_LEVEL ;
2007-10-23 23:14:42 +04:00
set_irq_flags ( i , IRQF_VALID ) ;
}
2008-03-27 21:51:41 +03:00
set_irq_chained_handler ( IRQ_ORION5X_GPIO_0_7 , orion5x_gpio_irq_handler ) ;
set_irq_chained_handler ( IRQ_ORION5X_GPIO_8_15 , orion5x_gpio_irq_handler ) ;
set_irq_chained_handler ( IRQ_ORION5X_GPIO_16_23 , orion5x_gpio_irq_handler ) ;
set_irq_chained_handler ( IRQ_ORION5X_GPIO_24_31 , orion5x_gpio_irq_handler ) ;
2007-10-23 23:14:42 +04:00
}
/*****************************************************************************
* Orion Main IRQ
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2008-03-27 21:51:41 +03:00
static void __init orion5x_init_main_irq ( void )
2007-10-23 23:14:42 +04:00
{
2008-03-27 21:51:40 +03:00
orion_irq_init ( 0 , ( void __iomem * ) MAIN_IRQ_MASK ) ;
2007-10-23 23:14:42 +04:00
}
2008-03-27 21:51:41 +03:00
void __init orion5x_init_irq ( void )
2007-10-23 23:14:42 +04:00
{
2008-03-27 21:51:41 +03:00
orion5x_init_main_irq ( ) ;
orion5x_init_gpio_irq ( ) ;
2007-10-23 23:14:42 +04:00
}