2009-12-04 17:41:28 +03:00
/*
* linux / arch / arm / mach - mmp / irq - mmp2 . c
*
* Generic IRQ handling , GPIO IRQ demultiplexing , etc .
*
* Author : Haojian Zhuang < haojian . zhuang @ marvell . com >
* Copyright : Marvell International Ltd .
*
* 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 .
*/
# include <linux/init.h>
# include <linux/irq.h>
# include <linux/io.h>
# include <mach/regs-icu.h>
2010-07-15 18:22:33 +04:00
# include <mach/mmp2.h>
2009-12-04 17:41:28 +03:00
# include "common.h"
2010-11-29 12:36:07 +03:00
static void icu_mask_irq ( struct irq_data * d )
2009-12-04 17:41:28 +03:00
{
2010-11-29 12:36:07 +03:00
uint32_t r = __raw_readl ( ICU_INT_CONF ( d - > irq ) ) ;
2009-12-04 17:41:28 +03:00
r & = ~ ICU_INT_ROUTE_PJ4_IRQ ;
2010-11-29 12:36:07 +03:00
__raw_writel ( r , ICU_INT_CONF ( d - > irq ) ) ;
2009-12-04 17:41:28 +03:00
}
2010-11-29 12:36:07 +03:00
static void icu_unmask_irq ( struct irq_data * d )
2009-12-04 17:41:28 +03:00
{
2010-11-29 12:36:07 +03:00
uint32_t r = __raw_readl ( ICU_INT_CONF ( d - > irq ) ) ;
2009-12-04 17:41:28 +03:00
r | = ICU_INT_ROUTE_PJ4_IRQ ;
2010-11-29 12:36:07 +03:00
__raw_writel ( r , ICU_INT_CONF ( d - > irq ) ) ;
2009-12-04 17:41:28 +03:00
}
static struct irq_chip icu_irq_chip = {
. name = " icu_irq " ,
2010-11-29 12:36:07 +03:00
. irq_mask = icu_mask_irq ,
. irq_mask_ack = icu_mask_irq ,
. irq_unmask = icu_unmask_irq ,
2009-12-04 17:41:28 +03:00
} ;
2010-11-29 12:36:07 +03:00
static void pmic_irq_ack ( struct irq_data * d )
2010-02-03 18:01:18 +03:00
{
2010-11-29 12:36:07 +03:00
if ( d - > irq = = IRQ_MMP2_PMIC )
2010-02-03 18:01:18 +03:00
mmp2_clear_pmic_int ( ) ;
}
2009-12-04 17:41:28 +03:00
# define SECOND_IRQ_MASK(_name_, irq_base, prefix) \
2010-11-29 12:36:07 +03:00
static void _name_ # # _mask_irq ( struct irq_data * d ) \
2009-12-04 17:41:28 +03:00
{ \
uint32_t r ; \
2010-11-29 12:36:07 +03:00
r = __raw_readl ( prefix # # _MASK ) | ( 1 < < ( d - > irq - irq_base ) ) ; \
2009-12-04 17:41:28 +03:00
__raw_writel ( r , prefix # # _MASK ) ; \
}
# define SECOND_IRQ_UNMASK(_name_, irq_base, prefix) \
2010-11-29 12:36:07 +03:00
static void _name_ # # _unmask_irq ( struct irq_data * d ) \
2009-12-04 17:41:28 +03:00
{ \
uint32_t r ; \
2010-11-29 12:36:07 +03:00
r = __raw_readl ( prefix # # _MASK ) & ~ ( 1 < < ( d - > irq - irq_base ) ) ; \
2009-12-04 17:41:28 +03:00
__raw_writel ( r , prefix # # _MASK ) ; \
}
# define SECOND_IRQ_DEMUX(_name_, irq_base, prefix) \
static void _name_ # # _irq_demux ( unsigned int irq , struct irq_desc * desc ) \
{ \
unsigned long status , mask , n ; \
mask = __raw_readl ( prefix # # _MASK ) ; \
while ( 1 ) { \
status = __raw_readl ( prefix # # _STATUS ) & ~ mask ; \
if ( status = = 0 ) \
break ; \
n = find_first_bit ( & status , BITS_PER_LONG ) ; \
while ( n < BITS_PER_LONG ) { \
generic_handle_irq ( irq_base + n ) ; \
n = find_next_bit ( & status , BITS_PER_LONG , n + 1 ) ; \
} \
} \
}
# define SECOND_IRQ_CHIP(_name_, irq_base, prefix) \
SECOND_IRQ_MASK ( _name_ , irq_base , prefix ) \
SECOND_IRQ_UNMASK ( _name_ , irq_base , prefix ) \
SECOND_IRQ_DEMUX ( _name_ , irq_base , prefix ) \
static struct irq_chip _name_ # # _irq_chip = { \
. name = # _name_ , \
2010-11-29 12:36:07 +03:00
. irq_mask = _name_ # # _mask_irq , \
. irq_unmask = _name_ # # _unmask_irq , \
2009-12-04 17:41:28 +03:00
}
SECOND_IRQ_CHIP ( pmic , IRQ_MMP2_PMIC_BASE , MMP2_ICU_INT4 ) ;
SECOND_IRQ_CHIP ( rtc , IRQ_MMP2_RTC_BASE , MMP2_ICU_INT5 ) ;
SECOND_IRQ_CHIP ( twsi , IRQ_MMP2_TWSI_BASE , MMP2_ICU_INT17 ) ;
SECOND_IRQ_CHIP ( misc , IRQ_MMP2_MISC_BASE , MMP2_ICU_INT35 ) ;
SECOND_IRQ_CHIP ( ssp , IRQ_MMP2_SSP_BASE , MMP2_ICU_INT51 ) ;
static void init_mux_irq ( struct irq_chip * chip , int start , int num )
{
int irq ;
for ( irq = start ; num > 0 ; irq + + , num - - ) {
2010-11-29 12:36:07 +03:00
struct irq_data * d = irq_get_irq_data ( irq ) ;
2010-02-03 10:39:35 +03:00
/* mask and clear the IRQ */
2010-11-29 12:36:07 +03:00
chip - > irq_mask ( d ) ;
if ( chip - > irq_ack )
chip - > irq_ack ( d ) ;
2010-02-03 10:39:35 +03:00
2009-12-04 17:41:28 +03:00
set_irq_chip ( irq , chip ) ;
set_irq_flags ( irq , IRQF_VALID ) ;
set_irq_handler ( irq , handle_level_irq ) ;
}
}
2010-01-25 14:03:54 +03:00
void __init mmp2_init_icu ( void )
2009-12-04 17:41:28 +03:00
{
int irq ;
for ( irq = 0 ; irq < IRQ_MMP2_MUX_BASE ; irq + + ) {
2010-11-29 12:36:07 +03:00
icu_mask_irq ( irq_get_irq_data ( irq ) ) ;
2009-12-04 17:41:28 +03:00
set_irq_chip ( irq , & icu_irq_chip ) ;
set_irq_flags ( irq , IRQF_VALID ) ;
switch ( irq ) {
case IRQ_MMP2_PMIC_MUX :
case IRQ_MMP2_RTC_MUX :
case IRQ_MMP2_TWSI_MUX :
case IRQ_MMP2_MISC_MUX :
case IRQ_MMP2_SSP_MUX :
break ;
default :
set_irq_handler ( irq , handle_level_irq ) ;
break ;
}
}
2010-02-03 18:01:18 +03:00
/* NOTE: IRQ_MMP2_PMIC requires the PMIC MFPR register
* to be written to clear the interrupt
*/
2010-11-29 12:36:07 +03:00
pmic_irq_chip . irq_ack = pmic_irq_ack ;
2010-02-03 18:01:18 +03:00
2009-12-04 17:41:28 +03:00
init_mux_irq ( & pmic_irq_chip , IRQ_MMP2_PMIC_BASE , 2 ) ;
init_mux_irq ( & rtc_irq_chip , IRQ_MMP2_RTC_BASE , 2 ) ;
init_mux_irq ( & twsi_irq_chip , IRQ_MMP2_TWSI_BASE , 5 ) ;
init_mux_irq ( & misc_irq_chip , IRQ_MMP2_MISC_BASE , 15 ) ;
init_mux_irq ( & ssp_irq_chip , IRQ_MMP2_SSP_BASE , 2 ) ;
set_irq_chained_handler ( IRQ_MMP2_PMIC_MUX , pmic_irq_demux ) ;
set_irq_chained_handler ( IRQ_MMP2_RTC_MUX , rtc_irq_demux ) ;
set_irq_chained_handler ( IRQ_MMP2_TWSI_MUX , twsi_irq_demux ) ;
set_irq_chained_handler ( IRQ_MMP2_MISC_MUX , misc_irq_demux ) ;
set_irq_chained_handler ( IRQ_MMP2_SSP_MUX , ssp_irq_demux ) ;
}