2008-09-01 17:22:39 +04:00
/*
* TX4939 irq routines
* Based on linux / arch / mips / kernel / irq_txx9 . c ,
* and RBTX49xx patch from CELF patch archive .
*
* Copyright 2001 , 2003 - 2005 MontaVista Software Inc .
* Author : MontaVista Software , Inc .
2013-01-22 15:59:30 +04:00
* ahennessy @ mvista . com
* source @ mvista . com
2008-09-01 17:22:39 +04:00
* Copyright ( C ) 2000 - 2001 , 2005 - 2007 Toshiba Corporation
*
* This file is subject to the terms and conditions of the GNU General Public
* License . See the file " COPYING " in the main directory of this archive
* for more details .
*/
/*
* TX4939 defines 64 IRQs .
* Similer to irq_txx9 . c but different register layouts .
*/
# include <linux/init.h>
# include <linux/interrupt.h>
2010-10-07 17:08:54 +04:00
# include <linux/irq.h>
2008-09-01 17:22:39 +04:00
# include <linux/types.h>
# include <asm/irq_cpu.h>
# include <asm/txx9irq.h>
# include <asm/txx9/tx4939.h>
/* IRCER : Int. Control Enable */
# define TXx9_IRCER_ICE 0x00000001
/* IRCR : Int. Control */
# define TXx9_IRCR_LOW 0x00000000
# define TXx9_IRCR_HIGH 0x00000001
# define TXx9_IRCR_DOWN 0x00000002
# define TXx9_IRCR_UP 0x00000003
# define TXx9_IRCR_EDGE(cr) ((cr) & 0x00000002)
/* IRSCR : Int. Status Control */
# define TXx9_IRSCR_EIClrE 0x00000100
# define TXx9_IRSCR_EIClr_MASK 0x0000000f
/* IRCSR : Int. Current Status */
# define TXx9_IRCSR_IF 0x00010000
# define irc_dlevel 0
# define irc_elevel 1
static struct {
unsigned char level ;
unsigned char mode ;
} tx4939irq [ TX4939_NUM_IR ] __read_mostly ;
2011-03-24 00:09:16 +03:00
static void tx4939_irq_unmask ( struct irq_data * d )
2008-09-01 17:22:39 +04:00
{
2011-03-24 00:09:16 +03:00
unsigned int irq_nr = d - > irq - TXX9_IRQ_BASE ;
2008-09-01 17:22:39 +04:00
u32 __iomem * lvlp ;
int ofs ;
if ( irq_nr < 32 ) {
irq_nr - - ;
lvlp = & tx4939_ircptr - > lvl [ ( irq_nr % 16 ) / 2 ] . r ;
} else {
irq_nr - = 32 ;
lvlp = & tx4939_ircptr - > lvl [ 8 + ( irq_nr % 16 ) / 2 ] . r ;
}
ofs = ( irq_nr & 16 ) + ( irq_nr & 1 ) * 8 ;
__raw_writel ( ( __raw_readl ( lvlp ) & ~ ( 0xff < < ofs ) )
| ( tx4939irq [ irq_nr ] . level < < ofs ) ,
lvlp ) ;
}
2011-03-24 00:09:16 +03:00
static inline void tx4939_irq_mask ( struct irq_data * d )
2008-09-01 17:22:39 +04:00
{
2011-03-24 00:09:16 +03:00
unsigned int irq_nr = d - > irq - TXX9_IRQ_BASE ;
2008-09-01 17:22:39 +04:00
u32 __iomem * lvlp ;
int ofs ;
if ( irq_nr < 32 ) {
irq_nr - - ;
lvlp = & tx4939_ircptr - > lvl [ ( irq_nr % 16 ) / 2 ] . r ;
} else {
irq_nr - = 32 ;
lvlp = & tx4939_ircptr - > lvl [ 8 + ( irq_nr % 16 ) / 2 ] . r ;
}
ofs = ( irq_nr & 16 ) + ( irq_nr & 1 ) * 8 ;
__raw_writel ( ( __raw_readl ( lvlp ) & ~ ( 0xff < < ofs ) )
| ( irc_dlevel < < ofs ) ,
lvlp ) ;
mmiowb ( ) ;
}
2011-03-24 00:09:16 +03:00
static void tx4939_irq_mask_ack ( struct irq_data * d )
2008-09-01 17:22:39 +04:00
{
2011-03-24 00:09:16 +03:00
unsigned int irq_nr = d - > irq - TXX9_IRQ_BASE ;
2008-09-01 17:22:39 +04:00
2011-03-24 00:09:16 +03:00
tx4939_irq_mask ( d ) ;
2008-09-01 17:22:39 +04:00
if ( TXx9_IRCR_EDGE ( tx4939irq [ irq_nr ] . mode ) ) {
irq_nr - - ;
/* clear edge detection */
__raw_writel ( ( TXx9_IRSCR_EIClrE | ( irq_nr & 0xf ) )
< < ( irq_nr & 0x10 ) ,
& tx4939_ircptr - > edc . r ) ;
}
}
2011-03-24 00:09:16 +03:00
static int tx4939_irq_set_type ( struct irq_data * d , unsigned int flow_type )
2008-09-01 17:22:39 +04:00
{
2011-03-24 00:09:16 +03:00
unsigned int irq_nr = d - > irq - TXX9_IRQ_BASE ;
2008-09-01 17:22:39 +04:00
u32 cr ;
u32 __iomem * crp ;
int ofs ;
int mode ;
if ( flow_type & IRQF_TRIGGER_PROBE )
return 0 ;
switch ( flow_type & IRQF_TRIGGER_MASK ) {
case IRQF_TRIGGER_RISING :
mode = TXx9_IRCR_UP ;
break ;
case IRQF_TRIGGER_FALLING :
mode = TXx9_IRCR_DOWN ;
break ;
case IRQF_TRIGGER_HIGH :
mode = TXx9_IRCR_HIGH ;
break ;
case IRQF_TRIGGER_LOW :
mode = TXx9_IRCR_LOW ;
break ;
default :
return - EINVAL ;
}
if ( irq_nr < 32 ) {
irq_nr - - ;
crp = & tx4939_ircptr - > dm [ ( irq_nr & 8 ) > > 3 ] . r ;
} else {
irq_nr - = 32 ;
crp = & tx4939_ircptr - > dm2 [ ( ( irq_nr & 8 ) > > 3 ) ] . r ;
}
ofs = ( ( ( irq_nr & 16 ) > > 1 ) | ( irq_nr & ( 8 - 1 ) ) ) * 2 ;
cr = __raw_readl ( crp ) ;
cr & = ~ ( 0x3 < < ofs ) ;
cr | = ( mode & 0x3 ) < < ofs ;
__raw_writel ( cr , crp ) ;
tx4939irq [ irq_nr ] . mode = mode ;
return 0 ;
}
static struct irq_chip tx4939_irq_chip = {
. name = " TX4939 " ,
2011-03-24 00:09:16 +03:00
. irq_ack = tx4939_irq_mask_ack ,
. irq_mask = tx4939_irq_mask ,
. irq_mask_ack = tx4939_irq_mask_ack ,
. irq_unmask = tx4939_irq_unmask ,
. irq_set_type = tx4939_irq_set_type ,
2008-09-01 17:22:39 +04:00
} ;
static int tx4939_irq_set_pri ( int irc_irq , int new_pri )
{
int old_pri ;
if ( ( unsigned int ) irc_irq > = TX4939_NUM_IR )
return 0 ;
old_pri = tx4939irq [ irc_irq ] . level ;
tx4939irq [ irc_irq ] . level = new_pri ;
return old_pri ;
}
void __init tx4939_irq_init ( void )
{
int i ;
mips_cpu_irq_init ( ) ;
/* disable interrupt control */
__raw_writel ( 0 , & tx4939_ircptr - > den . r ) ;
__raw_writel ( 0 , & tx4939_ircptr - > maskint . r ) ;
__raw_writel ( 0 , & tx4939_ircptr - > maskext . r ) ;
/* irq_base + 0 is not used */
for ( i = 1 ; i < TX4939_NUM_IR ; i + + ) {
tx4939irq [ i ] . level = 4 ; /* middle level */
tx4939irq [ i ] . mode = TXx9_IRCR_LOW ;
2011-03-27 17:19:28 +04:00
irq_set_chip_and_handler ( TXX9_IRQ_BASE + i , & tx4939_irq_chip ,
handle_level_irq ) ;
2008-09-01 17:22:39 +04:00
}
/* mask all IRC interrupts */
__raw_writel ( 0 , & tx4939_ircptr - > msk . r ) ;
for ( i = 0 ; i < 16 ; i + + )
__raw_writel ( 0 , & tx4939_ircptr - > lvl [ i ] . r ) ;
/* setup IRC interrupt mode (Low Active) */
for ( i = 0 ; i < 2 ; i + + )
__raw_writel ( 0 , & tx4939_ircptr - > dm [ i ] . r ) ;
for ( i = 0 ; i < 2 ; i + + )
__raw_writel ( 0 , & tx4939_ircptr - > dm2 [ i ] . r ) ;
/* enable interrupt control */
__raw_writel ( TXx9_IRCER_ICE , & tx4939_ircptr - > den . r ) ;
__raw_writel ( irc_elevel , & tx4939_ircptr - > msk . r ) ;
2011-03-27 17:19:28 +04:00
irq_set_chained_handler ( MIPS_CPU_IRQ_BASE + TX4939_IRC_INT ,
2008-09-01 17:22:39 +04:00
handle_simple_irq ) ;
/* raise priority for errors, timers, sio */
tx4939_irq_set_pri ( TX4939_IR_WTOERR , 7 ) ;
tx4939_irq_set_pri ( TX4939_IR_PCIERR , 7 ) ;
tx4939_irq_set_pri ( TX4939_IR_PCIPME , 7 ) ;
for ( i = 0 ; i < TX4939_NUM_IR_TMR ; i + + )
tx4939_irq_set_pri ( TX4939_IR_TMR ( i ) , 6 ) ;
for ( i = 0 ; i < TX4939_NUM_IR_SIO ; i + + )
tx4939_irq_set_pri ( TX4939_IR_SIO ( i ) , 5 ) ;
}
int tx4939_irq ( void )
{
u32 csr = __raw_readl ( & tx4939_ircptr - > cs . r ) ;
if ( likely ( ! ( csr & TXx9_IRCSR_IF ) ) )
return TXX9_IRQ_BASE + ( csr & ( TX4939_NUM_IR - 1 ) ) ;
return - 1 ;
}