2005-04-17 02:20:36 +04:00
/*
* 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 .
2006-10-09 03:03:05 +04:00
*
* Copyright ( c ) 2004 MIPS Inc
* Author : chris @ mips . com
*
* Copyright ( C ) 2004 , 06 Ralf Baechle < ralf @ linux - mips . org >
2005-04-17 02:20:36 +04:00
*/
# include <linux/module.h>
# include <linux/interrupt.h>
# include <linux/kernel.h>
# include <linux/sched.h>
# include <linux/kernel_stat.h>
# include <asm/io.h>
# include <asm/irq.h>
# include <asm/msc01_ic.h>
2008-04-25 20:55:30 +04:00
# include <asm/traps.h>
2005-04-17 02:20:36 +04:00
static unsigned long _icctrl_msc ;
# define MSC01_IC_REG_BASE _icctrl_msc
# define MSCIC_WRITE(reg, data) do { *(volatile u32 *)(reg) = data; } while (0)
# define MSCIC_READ(reg, data) do { data = *(volatile u32 *)(reg); } while (0)
static unsigned int irq_base ;
/* mask off an interrupt */
static inline void mask_msc_irq ( unsigned int irq )
{
if ( irq < ( irq_base + 32 ) )
MSCIC_WRITE ( MSC01_IC_DISL , 1 < < ( irq - irq_base ) ) ;
else
MSCIC_WRITE ( MSC01_IC_DISH , 1 < < ( irq - irq_base - 32 ) ) ;
}
/* unmask an interrupt */
static inline void unmask_msc_irq ( unsigned int irq )
{
if ( irq < ( irq_base + 32 ) )
MSCIC_WRITE ( MSC01_IC_ENAL , 1 < < ( irq - irq_base ) ) ;
else
MSCIC_WRITE ( MSC01_IC_ENAH , 1 < < ( irq - irq_base - 32 ) ) ;
}
/*
* Masks and ACKs an IRQ
*/
static void level_mask_and_ack_msc_irq ( unsigned int irq )
{
mask_msc_irq ( irq ) ;
2005-07-14 19:57:16 +04:00
if ( ! cpu_has_veic )
2005-04-17 02:20:36 +04:00
MSCIC_WRITE ( MSC01_IC_EOI , 0 ) ;
2006-04-05 12:45:45 +04:00
/* This actually needs to be a call into platform code */
2007-09-21 20:13:55 +04:00
smtc_im_ack_irq ( irq ) ;
2005-04-17 02:20:36 +04:00
}
/*
* Masks and ACKs an IRQ
*/
static void edge_mask_and_ack_msc_irq ( unsigned int irq )
{
mask_msc_irq ( irq ) ;
2005-07-14 19:57:16 +04:00
if ( ! cpu_has_veic )
2005-04-17 02:20:36 +04:00
MSCIC_WRITE ( MSC01_IC_EOI , 0 ) ;
else {
u32 r ;
MSCIC_READ ( MSC01_IC_SUP + irq * 8 , r ) ;
MSCIC_WRITE ( MSC01_IC_SUP + irq * 8 , r | ~ MSC01_IC_SUP_EDGE_BIT ) ;
MSCIC_WRITE ( MSC01_IC_SUP + irq * 8 , r ) ;
}
2007-09-21 20:13:55 +04:00
smtc_im_ack_irq ( irq ) ;
2005-04-17 02:20:36 +04:00
}
/*
* End IRQ processing
*/
static void end_msc_irq ( unsigned int irq )
{
if ( ! ( irq_desc [ irq ] . status & ( IRQ_DISABLED | IRQ_INPROGRESS ) ) )
unmask_msc_irq ( irq ) ;
}
/*
* Interrupt handler for interrupts coming from SOC - it .
*/
2006-10-07 22:44:33 +04:00
void ll_msc_irq ( void )
2005-04-17 02:20:36 +04:00
{
unsigned int irq ;
/* read the interrupt vector register */
MSCIC_READ ( MSC01_IC_VEC , irq ) ;
if ( irq < 64 )
2006-10-07 22:44:33 +04:00
do_IRQ ( irq + irq_base ) ;
2005-04-17 02:20:36 +04:00
else {
/* Ignore spurious interrupt */
}
}
2008-04-25 20:55:30 +04:00
static void msc_bind_eic_interrupt ( int irq , int set )
2005-04-17 02:20:36 +04:00
{
MSCIC_WRITE ( MSC01_IC_RAMW ,
( irq < < MSC01_IC_RAMW_ADDR_SHF ) | ( set < < MSC01_IC_RAMW_DATA_SHF ) ) ;
}
2008-04-25 20:55:30 +04:00
static struct irq_chip msc_levelirq_type = {
2007-01-14 18:07:25 +03:00
. name = " SOC-it-Level " ,
2005-02-28 16:39:57 +03:00
. ack = level_mask_and_ack_msc_irq ,
2006-11-01 20:08:36 +03:00
. mask = mask_msc_irq ,
. mask_ack = level_mask_and_ack_msc_irq ,
. unmask = unmask_msc_irq ,
2006-11-13 19:13:18 +03:00
. eoi = unmask_msc_irq ,
2005-02-28 16:39:57 +03:00
. end = end_msc_irq ,
2005-04-17 02:20:36 +04:00
} ;
2008-04-25 20:55:30 +04:00
static struct irq_chip msc_edgeirq_type = {
2007-01-14 18:07:25 +03:00
. name = " SOC-it-Edge " ,
2005-02-28 16:39:57 +03:00
. ack = edge_mask_and_ack_msc_irq ,
2006-11-01 20:08:36 +03:00
. mask = mask_msc_irq ,
. mask_ack = edge_mask_and_ack_msc_irq ,
. unmask = unmask_msc_irq ,
2006-11-13 19:13:18 +03:00
. eoi = unmask_msc_irq ,
2005-02-28 16:39:57 +03:00
. end = end_msc_irq ,
2005-04-17 02:20:36 +04:00
} ;
2007-05-08 17:05:39 +04:00
void __init init_msc_irqs ( unsigned long icubase , unsigned int irqbase , msc_irqmap_t * imp , int nirq )
2005-04-17 02:20:36 +04:00
{
2007-10-12 02:46:15 +04:00
_icctrl_msc = ( unsigned long ) ioremap ( icubase , 0x40000 ) ;
2005-04-17 02:20:36 +04:00
/* Reset interrupt controller - initialises all registers to 0 */
MSCIC_WRITE ( MSC01_IC_RST , MSC01_IC_RST_RST_BIT ) ;
board_bind_eic_interrupt = & msc_bind_eic_interrupt ;
for ( ; nirq > = 0 ; nirq - - , imp + + ) {
int n = imp - > im_irq ;
switch ( imp - > im_type ) {
case MSC01_IRQ_EDGE :
2007-05-08 17:05:39 +04:00
set_irq_chip ( irqbase + n , & msc_edgeirq_type ) ;
2005-07-14 19:57:16 +04:00
if ( cpu_has_veic )
2005-04-17 02:20:36 +04:00
MSCIC_WRITE ( MSC01_IC_SUP + n * 8 , MSC01_IC_SUP_EDGE_BIT ) ;
else
MSCIC_WRITE ( MSC01_IC_SUP + n * 8 , MSC01_IC_SUP_EDGE_BIT | imp - > im_lvl ) ;
break ;
case MSC01_IRQ_LEVEL :
2007-05-08 17:05:39 +04:00
set_irq_chip ( irqbase + n , & msc_levelirq_type ) ;
2005-07-14 19:57:16 +04:00
if ( cpu_has_veic )
2005-04-17 02:20:36 +04:00
MSCIC_WRITE ( MSC01_IC_SUP + n * 8 , 0 ) ;
else
MSCIC_WRITE ( MSC01_IC_SUP + n * 8 , imp - > im_lvl ) ;
}
}
2007-05-08 17:05:39 +04:00
irq_base = irqbase ;
2005-04-17 02:20:36 +04:00
MSCIC_WRITE ( MSC01_IC_GENA , MSC01_IC_GENA_GENA_BIT ) ; /* Enable interrupt generation */
}