2008-07-16 19:12:25 +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 .
*
* THIS SOFTWARE IS PROVIDED ` ` AS IS ' ' AND ANY EXPRESS OR IMPLIED
* WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED . IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT , INDIRECT ,
* INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL DAMAGES ( INCLUDING , BUT
* NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ; LOSS OF
* USE , DATA , OR PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY , OR TORT
* ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
*
* 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 . ,
* 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*
* Copyright 2002 MontaVista Software Inc .
* Author : MontaVista Software , Inc .
* stevel @ mvista . com or source @ mvista . com
*/
# include <linux/bitops.h>
# include <linux/errno.h>
# include <linux/init.h>
# include <linux/io.h>
# include <linux/kernel_stat.h>
# include <linux/module.h>
# include <linux/signal.h>
# include <linux/sched.h>
# include <linux/types.h>
# include <linux/interrupt.h>
# include <linux/ioport.h>
# include <linux/timex.h>
# include <linux/slab.h>
# include <linux/random.h>
# include <linux/delay.h>
# include <asm/bootinfo.h>
# include <asm/time.h>
# include <asm/mipsregs.h>
# include <asm/system.h>
2008-08-23 20:53:50 +04:00
# include <asm/mach-rc32434/irq.h>
2008-07-16 19:12:25 +04:00
struct intr_group {
u32 mask ; /* mask of valid bits in pending/mask registers */
volatile u32 * base_addr ;
} ;
# define RC32434_NR_IRQS (GROUP4_IRQ_BASE + 32)
# if (NR_IRQS < RC32434_NR_IRQS)
# error Too little irqs defined. Did you override <asm / irq.h> ?
# endif
static const struct intr_group intr_group [ NUM_INTR_GROUPS ] = {
{
. mask = 0x0000efff ,
. base_addr = ( u32 * ) KSEG1ADDR ( IC_GROUP0_PEND + 0 * IC_GROUP_OFFSET ) } ,
{
. mask = 0x00001fff ,
. base_addr = ( u32 * ) KSEG1ADDR ( IC_GROUP0_PEND + 1 * IC_GROUP_OFFSET ) } ,
{
. mask = 0x00000007 ,
. base_addr = ( u32 * ) KSEG1ADDR ( IC_GROUP0_PEND + 2 * IC_GROUP_OFFSET ) } ,
{
. mask = 0x0003ffff ,
. base_addr = ( u32 * ) KSEG1ADDR ( IC_GROUP0_PEND + 3 * IC_GROUP_OFFSET ) } ,
{
. mask = 0xffffffff ,
. base_addr = ( u32 * ) KSEG1ADDR ( IC_GROUP0_PEND + 4 * IC_GROUP_OFFSET ) }
} ;
# define READ_PEND(base) (*(base))
# define READ_MASK(base) (*(base + 2))
# define WRITE_MASK(base, val) (*(base + 2) = (val))
static inline int irq_to_group ( unsigned int irq_nr )
{
return ( irq_nr - GROUP0_IRQ_BASE ) > > 5 ;
}
static inline int group_to_ip ( unsigned int group )
{
return group + 2 ;
}
static inline void enable_local_irq ( unsigned int ip )
{
int ipnum = 0x100 < < ip ;
set_c0_status ( ipnum ) ;
}
static inline void disable_local_irq ( unsigned int ip )
{
int ipnum = 0x100 < < ip ;
clear_c0_status ( ipnum ) ;
}
static inline void ack_local_irq ( unsigned int ip )
{
int ipnum = 0x100 < < ip ;
clear_c0_cause ( ipnum ) ;
}
static void rb532_enable_irq ( unsigned int irq_nr )
{
int ip = irq_nr - GROUP0_IRQ_BASE ;
unsigned int group , intr_bit ;
volatile unsigned int * addr ;
if ( ip < 0 )
enable_local_irq ( irq_nr ) ;
else {
group = ip > > 5 ;
ip & = ( 1 < < 5 ) - 1 ;
intr_bit = 1 < < ip ;
enable_local_irq ( group_to_ip ( group ) ) ;
addr = intr_group [ group ] . base_addr ;
WRITE_MASK ( addr , READ_MASK ( addr ) & ~ intr_bit ) ;
}
}
static void rb532_disable_irq ( unsigned int irq_nr )
{
int ip = irq_nr - GROUP0_IRQ_BASE ;
unsigned int group , intr_bit , mask ;
volatile unsigned int * addr ;
if ( ip < 0 ) {
disable_local_irq ( irq_nr ) ;
} else {
group = ip > > 5 ;
ip & = ( 1 < < 5 ) - 1 ;
intr_bit = 1 < < ip ;
addr = intr_group [ group ] . base_addr ;
mask = READ_MASK ( addr ) ;
mask | = intr_bit ;
WRITE_MASK ( addr , mask ) ;
/*
* if there are no more interrupts enabled in this
* group , disable corresponding IP
*/
if ( mask = = intr_group [ group ] . mask )
disable_local_irq ( group_to_ip ( group ) ) ;
}
}
static void rb532_mask_and_ack_irq ( unsigned int irq_nr )
{
rb532_disable_irq ( irq_nr ) ;
ack_local_irq ( group_to_ip ( irq_to_group ( irq_nr ) ) ) ;
}
static struct irq_chip rc32434_irq_type = {
. name = " RB532 " ,
. ack = rb532_disable_irq ,
. mask = rb532_disable_irq ,
. mask_ack = rb532_mask_and_ack_irq ,
. unmask = rb532_enable_irq ,
} ;
void __init arch_init_irq ( void )
{
int i ;
pr_info ( " Initializing IRQ's: %d out of %d \n " , RC32434_NR_IRQS , NR_IRQS ) ;
for ( i = 0 ; i < RC32434_NR_IRQS ; i + + )
set_irq_chip_and_handler ( i , & rc32434_irq_type ,
handle_level_irq ) ;
}
/* Main Interrupt dispatcher */
asmlinkage void plat_irq_dispatch ( void )
{
unsigned int ip , pend , group ;
volatile unsigned int * addr ;
unsigned int cp0_cause = read_c0_cause ( ) & read_c0_status ( ) ;
if ( cp0_cause & CAUSEF_IP7 ) {
do_IRQ ( 7 ) ;
} else {
ip = ( cp0_cause & 0x7c00 ) ;
if ( ip ) {
group = 21 + ( fls ( ip ) - 32 ) ;
addr = intr_group [ group ] . base_addr ;
pend = READ_PEND ( addr ) ;
pend & = ~ READ_MASK ( addr ) ; /* only unmasked interrupts */
pend = 39 + ( fls ( pend ) - 32 ) ;
do_IRQ ( ( group < < 5 ) + pend ) ;
}
}
}