2010-05-01 08:59:38 +04:00
/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved.
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston , MA
* 02110 - 1301 , USA .
*
*/
# include <linux/io.h>
# include <linux/irq.h>
# include <linux/interrupt.h>
# include <asm/irq.h>
static unsigned int int_enable ;
static unsigned int wake_enable ;
static struct sirc_regs_t sirc_regs = {
. int_enable = SPSS_SIRC_INT_ENABLE ,
. int_enable_clear = SPSS_SIRC_INT_ENABLE_CLEAR ,
. int_enable_set = SPSS_SIRC_INT_ENABLE_SET ,
. int_type = SPSS_SIRC_INT_TYPE ,
. int_polarity = SPSS_SIRC_INT_POLARITY ,
. int_clear = SPSS_SIRC_INT_CLEAR ,
} ;
static struct sirc_cascade_regs sirc_reg_table [ ] = {
{
. int_status = SPSS_SIRC_IRQ_STATUS ,
. cascade_irq = INT_SIRC_0 ,
}
} ;
/* Mask off the given interrupt. Keep the int_enable mask in sync with
the enable reg , so it can be restored after power collapse . */
2010-11-29 12:37:34 +03:00
static void sirc_irq_mask ( struct irq_data * d )
2010-05-01 08:59:38 +04:00
{
unsigned int mask ;
2010-11-29 12:37:34 +03:00
mask = 1 < < ( d - > irq - FIRST_SIRC_IRQ ) ;
2010-05-01 08:59:38 +04:00
writel ( mask , sirc_regs . int_enable_clear ) ;
int_enable & = ~ mask ;
return ;
}
/* Unmask the given interrupt. Keep the int_enable mask in sync with
the enable reg , so it can be restored after power collapse . */
2010-11-29 12:37:34 +03:00
static void sirc_irq_unmask ( struct irq_data * d )
2010-05-01 08:59:38 +04:00
{
unsigned int mask ;
2010-11-29 12:37:34 +03:00
mask = 1 < < ( d - > irq - FIRST_SIRC_IRQ ) ;
2010-05-01 08:59:38 +04:00
writel ( mask , sirc_regs . int_enable_set ) ;
int_enable | = mask ;
return ;
}
2010-11-29 12:37:34 +03:00
static void sirc_irq_ack ( struct irq_data * d )
2010-05-01 08:59:38 +04:00
{
unsigned int mask ;
2010-11-29 12:37:34 +03:00
mask = 1 < < ( d - > irq - FIRST_SIRC_IRQ ) ;
2010-05-01 08:59:38 +04:00
writel ( mask , sirc_regs . int_clear ) ;
return ;
}
2010-11-29 12:37:34 +03:00
static int sirc_irq_set_wake ( struct irq_data * d , unsigned int on )
2010-05-01 08:59:38 +04:00
{
unsigned int mask ;
/* Used to set the interrupt enable mask during power collapse. */
2010-11-29 12:37:34 +03:00
mask = 1 < < ( d - > irq - FIRST_SIRC_IRQ ) ;
2010-05-01 08:59:38 +04:00
if ( on )
wake_enable | = mask ;
else
wake_enable & = ~ mask ;
return 0 ;
}
2010-11-29 12:37:34 +03:00
static int sirc_irq_set_type ( struct irq_data * d , unsigned int flow_type )
2010-05-01 08:59:38 +04:00
{
unsigned int mask ;
unsigned int val ;
2010-11-29 12:37:34 +03:00
mask = 1 < < ( d - > irq - FIRST_SIRC_IRQ ) ;
2010-05-01 08:59:38 +04:00
val = readl ( sirc_regs . int_polarity ) ;
if ( flow_type & ( IRQF_TRIGGER_LOW | IRQF_TRIGGER_FALLING ) )
val | = mask ;
else
val & = ~ mask ;
writel ( val , sirc_regs . int_polarity ) ;
val = readl ( sirc_regs . int_type ) ;
if ( flow_type & ( IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING ) ) {
val | = mask ;
2010-11-29 12:37:34 +03:00
irq_desc [ d - > irq ] . handle_irq = handle_edge_irq ;
2010-05-01 08:59:38 +04:00
} else {
val & = ~ mask ;
2010-11-29 12:37:34 +03:00
irq_desc [ d - > irq ] . handle_irq = handle_level_irq ;
2010-05-01 08:59:38 +04:00
}
writel ( val , sirc_regs . int_type ) ;
return 0 ;
}
/* Finds the pending interrupt on the passed cascade irq and redrives it */
static void sirc_irq_handler ( unsigned int irq , struct irq_desc * desc )
{
unsigned int reg = 0 ;
unsigned int sirq ;
unsigned int status ;
while ( ( reg < ARRAY_SIZE ( sirc_reg_table ) ) & &
( sirc_reg_table [ reg ] . cascade_irq ! = irq ) )
reg + + ;
status = readl ( sirc_reg_table [ reg ] . int_status ) ;
status & = SIRC_MASK ;
if ( status = = 0 )
return ;
for ( sirq = 0 ;
( sirq < NR_SIRC_IRQS ) & & ( ( status & ( 1U < < sirq ) ) = = 0 ) ;
sirq + + )
;
generic_handle_irq ( sirq + FIRST_SIRC_IRQ ) ;
2010-11-29 12:37:34 +03:00
desc - > irq_data . chip - > irq_ack ( & desc - > irq_data ) ;
2010-05-01 08:59:38 +04:00
}
static struct irq_chip sirc_irq_chip = {
2010-11-29 12:37:34 +03:00
. name = " sirc " ,
. irq_ack = sirc_irq_ack ,
. irq_mask = sirc_irq_mask ,
. irq_unmask = sirc_irq_unmask ,
. irq_set_wake = sirc_irq_set_wake ,
. irq_set_type = sirc_irq_set_type ,
2010-05-01 08:59:38 +04:00
} ;
void __init msm_init_sirc ( void )
{
int i ;
int_enable = 0 ;
wake_enable = 0 ;
for ( i = FIRST_SIRC_IRQ ; i < LAST_SIRC_IRQ ; i + + ) {
set_irq_chip ( i , & sirc_irq_chip ) ;
set_irq_handler ( i , handle_edge_irq ) ;
set_irq_flags ( i , IRQF_VALID ) ;
}
for ( i = 0 ; i < ARRAY_SIZE ( sirc_reg_table ) ; i + + ) {
set_irq_chained_handler ( sirc_reg_table [ i ] . cascade_irq ,
sirc_irq_handler ) ;
set_irq_wake ( sirc_reg_table [ i ] . cascade_irq , 1 ) ;
}
return ;
}