2005-04-17 02:20:36 +04:00
/*
2013-01-22 15:59:30 +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
2005-04-17 02:20:36 +04:00
* 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/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 */
2011-03-24 00:08:59 +03:00
static inline void mask_msc_irq ( struct irq_data * d )
2005-04-17 02:20:36 +04:00
{
2011-03-24 00:08:59 +03:00
unsigned int irq = d - > irq ;
2005-04-17 02:20:36 +04:00
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 */
2011-03-24 00:08:59 +03:00
static inline void unmask_msc_irq ( struct irq_data * d )
2005-04-17 02:20:36 +04:00
{
2011-03-24 00:08:59 +03:00
unsigned int irq = d - > irq ;
2005-04-17 02:20:36 +04:00
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
*/
2011-03-24 00:08:59 +03:00
static void level_mask_and_ack_msc_irq ( struct irq_data * d )
2005-04-17 02:20:36 +04:00
{
2011-03-24 00:08:59 +03:00
mask_msc_irq ( d ) ;
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 ) ;
}
/*
* Masks and ACKs an IRQ
*/
2011-03-24 00:08:59 +03:00
static void edge_mask_and_ack_msc_irq ( struct irq_data * d )
2005-04-17 02:20:36 +04:00
{
2011-03-24 00:08:59 +03:00
unsigned int irq = d - > irq ;
mask_msc_irq ( d ) ;
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 ) ;
}
}
/*
* 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
{
2013-01-22 15:59:30 +04:00
unsigned int irq ;
2005-04-17 02:20:36 +04:00
/* 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 " ,
2011-03-24 00:08:59 +03:00
. irq_ack = level_mask_and_ack_msc_irq ,
. irq_mask = mask_msc_irq ,
. irq_mask_ack = level_mask_and_ack_msc_irq ,
. irq_unmask = unmask_msc_irq ,
. irq_eoi = unmask_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 " ,
2011-03-24 00:08:59 +03:00
. irq_ack = edge_mask_and_ack_msc_irq ,
. irq_mask = mask_msc_irq ,
. irq_mask_ack = edge_mask_and_ack_msc_irq ,
. irq_unmask = unmask_msc_irq ,
. irq_eoi = unmask_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 ;
2014-06-23 12:48:51 +04:00
for ( ; nirq > 0 ; nirq - - , imp + + ) {
2005-04-17 02:20:36 +04:00
int n = imp - > im_irq ;
switch ( imp - > im_type ) {
case MSC01_IRQ_EDGE :
2011-03-27 17:19:28 +04:00
irq_set_chip_and_handler_name ( irqbase + n ,
& msc_edgeirq_type ,
handle_edge_irq ,
" edge " ) ;
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 :
2011-03-27 17:19:28 +04:00
irq_set_chip_and_handler_name ( irqbase + n ,
& msc_levelirq_type ,
handle_level_irq ,
" level " ) ;
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 */
}