2007-02-16 17:36:55 +03:00
/*
* arch / arm / mach - ns9xxx / irq . c
*
* Copyright ( C ) 2006 , 2007 by Digi International Inc .
* All rights reserved .
*
* 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/interrupt.h>
2008-02-29 15:27:53 +03:00
# include <linux/kernel_stat.h>
2008-09-06 15:10:45 +04:00
# include <linux/io.h>
2007-02-16 17:36:55 +03:00
# include <asm/mach/irq.h>
2008-08-05 19:14:15 +04:00
# include <mach/regs-sys-common.h>
# include <mach/irqs.h>
# include <mach/board.h>
2007-02-16 17:36:55 +03:00
# include "generic.h"
2007-12-11 18:52:50 +03:00
/* simple interrupt prio table: prio(x) < prio(y) <=> x < y */
# define irq2prio(i) (i)
# define prio2irq(p) (p)
2007-02-16 17:36:55 +03:00
static void ns9xxx_mask_irq ( unsigned int irq )
{
/* XXX: better use cpp symbols */
2007-12-11 18:52:50 +03:00
int prio = irq2prio ( irq ) ;
u32 ic = __raw_readl ( SYS_IC ( prio / 4 ) ) ;
ic & = ~ ( 1 < < ( 7 + 8 * ( 3 - ( prio & 3 ) ) ) ) ;
__raw_writel ( ic , SYS_IC ( prio / 4 ) ) ;
2007-02-16 17:36:55 +03:00
}
static void ns9xxx_ack_irq ( unsigned int irq )
{
2007-09-30 23:36:33 +04:00
__raw_writel ( 0 , SYS_ISRADDR ) ;
2007-02-16 17:36:55 +03:00
}
static void ns9xxx_maskack_irq ( unsigned int irq )
{
ns9xxx_mask_irq ( irq ) ;
ns9xxx_ack_irq ( irq ) ;
}
static void ns9xxx_unmask_irq ( unsigned int irq )
{
/* XXX: better use cpp symbols */
2007-12-11 18:52:50 +03:00
int prio = irq2prio ( irq ) ;
u32 ic = __raw_readl ( SYS_IC ( prio / 4 ) ) ;
ic | = 1 < < ( 7 + 8 * ( 3 - ( prio & 3 ) ) ) ;
__raw_writel ( ic , SYS_IC ( prio / 4 ) ) ;
2007-02-16 17:36:55 +03:00
}
static struct irq_chip ns9xxx_chip = {
. ack = ns9xxx_ack_irq ,
. mask = ns9xxx_mask_irq ,
. mask_ack = ns9xxx_maskack_irq ,
. unmask = ns9xxx_unmask_irq ,
} ;
2008-02-29 15:27:53 +03:00
#if 0
# define handle_irq handle_level_irq
# else
2008-04-25 17:24:59 +04:00
static void handle_prio_irq ( unsigned int irq , struct irq_desc * desc )
2008-02-29 15:27:53 +03:00
{
unsigned int cpu = smp_processor_id ( ) ;
struct irqaction * action ;
irqreturn_t action_ret ;
spin_lock ( & desc - > lock ) ;
2008-04-25 17:03:18 +04:00
BUG_ON ( desc - > status & IRQ_INPROGRESS ) ;
2008-02-29 15:27:53 +03:00
desc - > status & = ~ ( IRQ_REPLAY | IRQ_WAITING ) ;
kstat_cpu ( cpu ) . irqs [ irq ] + + ;
action = desc - > action ;
if ( unlikely ( ! action | | ( desc - > status & IRQ_DISABLED ) ) )
2008-04-25 17:03:18 +04:00
goto out_mask ;
2008-02-29 15:27:53 +03:00
desc - > status | = IRQ_INPROGRESS ;
spin_unlock ( & desc - > lock ) ;
action_ret = handle_IRQ_event ( irq , action ) ;
2008-04-25 17:16:17 +04:00
/* XXX: There is no direct way to access noirqdebug, so check
* unconditionally for spurious irqs . . .
* Maybe this function should go to kernel / irq / chip . c ? */
note_interrupt ( irq , desc , action_ret ) ;
2008-02-29 15:27:53 +03:00
spin_lock ( & desc - > lock ) ;
desc - > status & = ~ IRQ_INPROGRESS ;
2008-04-25 17:03:18 +04:00
if ( desc - > status & IRQ_DISABLED )
out_mask :
desc - > chip - > mask ( irq ) ;
/* ack unconditionally to unmask lower prio irqs */
desc - > chip - > ack ( irq ) ;
2008-02-29 15:27:53 +03:00
spin_unlock ( & desc - > lock ) ;
}
# define handle_irq handle_prio_irq
# endif
2007-02-16 17:36:55 +03:00
void __init ns9xxx_init_irq ( void )
{
int i ;
/* disable all IRQs */
for ( i = 0 ; i < 8 ; + + i )
2007-12-11 18:52:50 +03:00
__raw_writel ( prio2irq ( 4 * i ) < < 24 |
prio2irq ( 4 * i + 1 ) < < 16 |
prio2irq ( 4 * i + 2 ) < < 8 |
prio2irq ( 4 * i + 3 ) ,
SYS_IC ( i ) ) ;
2007-02-16 17:36:55 +03:00
for ( i = 0 ; i < 32 ; + + i )
2007-12-11 18:52:50 +03:00
__raw_writel ( prio2irq ( i ) , SYS_IVA ( i ) ) ;
2007-02-16 17:36:55 +03:00
2008-02-15 10:41:06 +03:00
for ( i = 0 ; i < = 31 ; + + i ) {
2007-02-16 17:36:55 +03:00
set_irq_chip ( i , & ns9xxx_chip ) ;
2008-02-29 15:27:53 +03:00
set_irq_handler ( i , handle_irq ) ;
2007-02-16 17:36:55 +03:00
set_irq_flags ( i , IRQF_VALID ) ;
}
}