240 lines
5.4 KiB
C
240 lines
5.4 KiB
C
/*
|
|
* Copyright (C) 2001 MontaVista Software Inc.
|
|
* Author: Jun Sun, jsun@mvista.com or jsun@junsun.net
|
|
* Copyright (C) 2005 Ralf Baechle (ralf@linux-mips.org)
|
|
*
|
|
* linux/arch/mips/vr4181/common/irq.c
|
|
* Completely re-written to use the new irq.c
|
|
*
|
|
* Credits to Bradley D. LaRonde and Michael Klar for writing the original
|
|
* irq.c file which was derived from the common irq.c file.
|
|
*
|
|
* 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.
|
|
*/
|
|
#include <linux/types.h>
|
|
#include <linux/init.h>
|
|
#include <linux/kernel_stat.h>
|
|
#include <linux/signal.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/random.h>
|
|
|
|
#include <asm/irq.h>
|
|
#include <asm/mipsregs.h>
|
|
#include <asm/gdb-stub.h>
|
|
|
|
#include <asm/vr4181/vr4181.h>
|
|
|
|
/*
|
|
* Strategy:
|
|
*
|
|
* We essentially have three irq controllers, CPU, system, and gpio.
|
|
*
|
|
* CPU irq controller is taken care by arch/mips/kernel/irq_cpu.c and
|
|
* CONFIG_IRQ_CPU config option.
|
|
*
|
|
* We here provide sys_irq and gpio_irq controller code.
|
|
*/
|
|
|
|
static int sys_irq_base;
|
|
static int gpio_irq_base;
|
|
|
|
/* ---------------------- sys irq ------------------------ */
|
|
static void
|
|
sys_irq_enable(unsigned int irq)
|
|
{
|
|
irq -= sys_irq_base;
|
|
if (irq < 16) {
|
|
*VR4181_MSYSINT1REG |= (u16)(1 << irq);
|
|
} else {
|
|
irq -= 16;
|
|
*VR4181_MSYSINT2REG |= (u16)(1 << irq);
|
|
}
|
|
}
|
|
|
|
static void
|
|
sys_irq_disable(unsigned int irq)
|
|
{
|
|
irq -= sys_irq_base;
|
|
if (irq < 16) {
|
|
*VR4181_MSYSINT1REG &= ~((u16)(1 << irq));
|
|
} else {
|
|
irq -= 16;
|
|
*VR4181_MSYSINT2REG &= ~((u16)(1 << irq));
|
|
}
|
|
|
|
}
|
|
|
|
static unsigned int
|
|
sys_irq_startup(unsigned int irq)
|
|
{
|
|
sys_irq_enable(irq);
|
|
return 0;
|
|
}
|
|
|
|
#define sys_irq_shutdown sys_irq_disable
|
|
#define sys_irq_ack sys_irq_disable
|
|
|
|
static void
|
|
sys_irq_end(unsigned int irq)
|
|
{
|
|
if(!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS)))
|
|
sys_irq_enable(irq);
|
|
}
|
|
|
|
static hw_irq_controller sys_irq_controller = {
|
|
"vr4181_sys_irq",
|
|
sys_irq_startup,
|
|
sys_irq_shutdown,
|
|
sys_irq_enable,
|
|
sys_irq_disable,
|
|
sys_irq_ack,
|
|
sys_irq_end,
|
|
NULL /* no affinity stuff for UP */
|
|
};
|
|
|
|
/* ---------------------- gpio irq ------------------------ */
|
|
/* gpio irq lines use reverse logic */
|
|
static void
|
|
gpio_irq_enable(unsigned int irq)
|
|
{
|
|
irq -= gpio_irq_base;
|
|
*VR4181_GPINTMSK &= ~((u16)(1 << irq));
|
|
}
|
|
|
|
static void
|
|
gpio_irq_disable(unsigned int irq)
|
|
{
|
|
irq -= gpio_irq_base;
|
|
*VR4181_GPINTMSK |= (u16)(1 << irq);
|
|
}
|
|
|
|
static unsigned int
|
|
gpio_irq_startup(unsigned int irq)
|
|
{
|
|
gpio_irq_enable(irq);
|
|
|
|
irq -= gpio_irq_base;
|
|
*VR4181_GPINTEN |= (u16)(1 << irq );
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
gpio_irq_shutdown(unsigned int irq)
|
|
{
|
|
gpio_irq_disable(irq);
|
|
|
|
irq -= gpio_irq_base;
|
|
*VR4181_GPINTEN &= ~((u16)(1 << irq ));
|
|
}
|
|
|
|
static void
|
|
gpio_irq_ack(unsigned int irq)
|
|
{
|
|
u16 irqtype;
|
|
u16 irqshift;
|
|
|
|
gpio_irq_disable(irq);
|
|
|
|
/* we clear interrupt if it is edge triggered */
|
|
irq -= gpio_irq_base;
|
|
if (irq < 8) {
|
|
irqtype = *VR4181_GPINTTYPL;
|
|
irqshift = 2 << (irq*2);
|
|
} else {
|
|
irqtype = *VR4181_GPINTTYPH;
|
|
irqshift = 2 << ((irq-8)*2);
|
|
}
|
|
if ( ! (irqtype & irqshift) ) {
|
|
*VR4181_GPINTSTAT = (u16) (1 << irq);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gpio_irq_end(unsigned int irq)
|
|
{
|
|
if(!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS)))
|
|
gpio_irq_enable(irq);
|
|
}
|
|
|
|
static hw_irq_controller gpio_irq_controller = {
|
|
"vr4181_gpio_irq",
|
|
gpio_irq_startup,
|
|
gpio_irq_shutdown,
|
|
gpio_irq_enable,
|
|
gpio_irq_disable,
|
|
gpio_irq_ack,
|
|
gpio_irq_end,
|
|
NULL /* no affinity stuff for UP */
|
|
};
|
|
|
|
/* --------------------- IRQ init stuff ---------------------- */
|
|
|
|
extern asmlinkage void vr4181_handle_irq(void);
|
|
extern void breakpoint(void);
|
|
extern int setup_irq(unsigned int irq, struct irqaction *irqaction);
|
|
extern void mips_cpu_irq_init(u32 irq_base);
|
|
|
|
static struct irqaction cascade =
|
|
{ no_action, SA_INTERRUPT, CPU_MASK_NONE, "cascade", NULL, NULL };
|
|
static struct irqaction reserved =
|
|
{ no_action, SA_INTERRUPT, CPU_MASK_NONE, "cascade", NULL, NULL };
|
|
|
|
void __init arch_init_irq(void)
|
|
{
|
|
int i;
|
|
|
|
set_except_vector(0, vr4181_handle_irq);
|
|
|
|
/* init CPU irqs */
|
|
mips_cpu_irq_init(VR4181_CPU_IRQ_BASE);
|
|
|
|
/* init sys irqs */
|
|
sys_irq_base = VR4181_SYS_IRQ_BASE;
|
|
for (i=sys_irq_base; i < sys_irq_base + VR4181_NUM_SYS_IRQ; i++) {
|
|
irq_desc[i].status = IRQ_DISABLED;
|
|
irq_desc[i].action = NULL;
|
|
irq_desc[i].depth = 1;
|
|
irq_desc[i].handler = &sys_irq_controller;
|
|
}
|
|
|
|
/* init gpio irqs */
|
|
gpio_irq_base = VR4181_GPIO_IRQ_BASE;
|
|
for (i=gpio_irq_base; i < gpio_irq_base + VR4181_NUM_GPIO_IRQ; i++) {
|
|
irq_desc[i].status = IRQ_DISABLED;
|
|
irq_desc[i].action = NULL;
|
|
irq_desc[i].depth = 1;
|
|
irq_desc[i].handler = &gpio_irq_controller;
|
|
}
|
|
|
|
/* Default all ICU IRQs to off ... */
|
|
*VR4181_MSYSINT1REG = 0;
|
|
*VR4181_MSYSINT2REG = 0;
|
|
|
|
/* We initialize the level 2 ICU registers to all bits disabled. */
|
|
*VR4181_MPIUINTREG = 0;
|
|
*VR4181_MAIUINTREG = 0;
|
|
*VR4181_MKIUINTREG = 0;
|
|
|
|
/* disable all GPIO intrs */
|
|
*VR4181_GPINTMSK = 0xffff;
|
|
|
|
/* vector handler. What these do is register the IRQ as non-sharable */
|
|
setup_irq(VR4181_IRQ_INT0, &cascade);
|
|
setup_irq(VR4181_IRQ_GIU, &cascade);
|
|
|
|
/*
|
|
* RTC interrupts are interesting. They have two destinations.
|
|
* One is at sys irq controller, and the other is at CPU IP3 and IP4.
|
|
* RTC timer is used as system timer.
|
|
* We enable them here, but timer routine will register later
|
|
* with CPU IP3/IP4.
|
|
*/
|
|
setup_irq(VR4181_IRQ_RTCL1, &reserved);
|
|
setup_irq(VR4181_IRQ_RTCL2, &reserved);
|
|
}
|