NetBSD-5.0.2/sys/arch/ews4800mips/ews4800mips/tr2_intr.c
/* $NetBSD: tr2_intr.c,v 1.10 2008/04/28 20:23:18 martin Exp $ */
/*-
* Copyright (c) 2004, 2005 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by UCHIYAMA Yasushi.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``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 FOUNDATION OR CONTRIBUTORS
* 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.
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: tr2_intr.c,v 1.10 2008/04/28 20:23:18 martin Exp $");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/device.h>
#include <sys/intr.h>
#include <machine/locore.h> /* mips3_cp0* */
#include <machine/sbdvar.h>
#define _SBD_TR2_PRIVATE
#include <machine/sbd_tr2.h>
#include <mips/cpuregs.h>
SBD_DECL(tr2);
const uint32_t tr2_sr_bits[_IPL_N] = {
[IPL_NONE] = 0,
[IPL_SOFTCLOCK] = MIPS_SOFT_INT_MASK_0,
[IPL_SOFTNET] =
MIPS_SOFT_INT_MASK_0 | MIPS_SOFT_INT_MASK_1,
[IPL_VM] =
MIPS_SOFT_INT_MASK_0 | MIPS_SOFT_INT_MASK_1 |
MIPS_INT_MASK_0 |
MIPS_INT_MASK_2 |
MIPS_INT_MASK_4,
[IPL_SCHED] =
MIPS_SOFT_INT_MASK_0 | MIPS_SOFT_INT_MASK_1 |
MIPS_INT_MASK_0 |
MIPS_INT_MASK_2 |
MIPS_INT_MASK_4 |
MIPS_INT_MASK_5,
/* !!! TEST !!! VME INTERRUPT IS NOT MASKED */
};
#define NIRQ 8
struct tr2_intr_handler {
int (*func)(void *);
void *arg;
volatile uint8_t *picnic_reg;
uint8_t picnic_mask;
struct evcnt evcnt;
char evname[32];
} tr2_intr_handler[NIRQ] = {
[0] = { NULL, NULL, PICNIC_INT4_MASK_REG, PICNIC_INT_KBMS },
[2] = { NULL, NULL, PICNIC_INT4_MASK_REG, PICNIC_INT_SERIAL },
[5] = { NULL, NULL, PICNIC_INT2_MASK_REG, PICNIC_INT_SCSI },
[6] = { NULL, NULL, PICNIC_INT2_MASK_REG, PICNIC_INT_ETHER },
[7] = { NULL, NULL, PICNIC_INT0_MASK_REG, PICNIC_INT_FDDLPT },
};
struct evcnt timer_tr2_ev =
EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, "picnic", "timer");
void
tr2_intr_init(void)
{
uint32_t a = (uint32_t)ews4800mips_nmi_vec;
/* Install dump button handler address to NVSRAM (jumped from ROM) */
*(NVSRAM_CDUMP_ADDR + 0) = (a >> 24) & 0xff;
*(NVSRAM_CDUMP_ADDR + 4) = (a >> 16) & 0xff;
*(NVSRAM_CDUMP_ADDR + 8) = (a >> 8) & 0xff;
*(NVSRAM_CDUMP_ADDR +12) = a & 0xff;
/* Disable external interrupts */
*PICNIC_INT0_MASK_REG = 0;
*PICNIC_INT2_MASK_REG = 0;
*PICNIC_INT4_MASK_REG = 0;
*PICNIC_INT5_MASK_REG = 0;
evcnt_attach_static(&timer_tr2_ev);
}
void *
tr2_intr_establish(int irq, int (*func)(void *), void *arg)
{
struct tr2_intr_handler *ih = &tr2_intr_handler[irq];
int s;
s = splhigh();
ih->func = func;
ih->arg = arg;
snprintf(ih->evname, sizeof(ih->evname), "irq %d", irq);
evcnt_attach_dynamic(&ih->evcnt, EVCNT_TYPE_INTR, NULL,
"picnic", ih->evname);
*ih->picnic_reg |= ih->picnic_mask;
splx(s);
return (void *)irq;
}
void
tr2_intr_disestablish(void *arg)
{
int s, irq = (int)arg;
struct tr2_intr_handler *ih = &tr2_intr_handler[irq];
s = splhigh();
*ih->picnic_reg &= ~ih->picnic_mask;
ih->func = NULL;
ih->arg = NULL;
evcnt_detach(&ih->evcnt);
splx(s);
}
void
tr2_intr(uint32_t status, uint32_t cause, uint32_t pc, uint32_t ipending)
{
struct tr2_intr_handler *ih;
struct clockframe cf;
uint32_t r, handled;
handled = 0;
if (ipending & MIPS_INT_MASK_5) { /* CLOCK */
cf.pc = pc;
cf.sr = status;
*PICNIC_INT5_STATUS_REG = 0;
r = *PICNIC_INT5_STATUS_REG;
hardclock(&cf);
timer_tr2_ev.ev_count++;
handled |= MIPS_INT_MASK_5;
}
_splset((status & handled) | MIPS_SR_INT_IE);
if (ipending & MIPS_INT_MASK_4) { /* KBD, MOUSE, SERIAL */
r = *PICNIC_INT4_STATUS_REG;
if (r & PICNIC_INT_KBMS) {
ih = &tr2_intr_handler[0];
if (ih->func) {
ih->func(ih->arg);
ih->evcnt.ev_count++;
}
r &= ~PICNIC_INT_KBMS;
}
if (r & PICNIC_INT_SERIAL) {
#if 0
printf("SIO interrupt\n");
#endif
ih = &tr2_intr_handler[2];
if (ih->func) {
ih->func(ih->arg);
ih->evcnt.ev_count++;
}
r &= ~PICNIC_INT_SERIAL;
}
handled |= MIPS_INT_MASK_4;
}
_splset((status & handled) | MIPS_SR_INT_IE);
if (ipending & MIPS_INT_MASK_3) { /* VME */
printf("VME interrupt\n");
r = *(volatile uint32_t *)0xbfb00018; /* NABI? */
if ((r & 0x10) != 0) {
/* vme high interrupt */
} else if ((r & 0x4) != 0) {
/* vme lo interrupt */
} else {
/* error */
}
}
if (ipending & MIPS_INT_MASK_2) { /* ETHER, SCSI */
r = *PICNIC_INT2_STATUS_REG;
if (r & PICNIC_INT_ETHER) {
ih = &tr2_intr_handler[6];
if (ih->func) {
ih->func(ih->arg);
ih->evcnt.ev_count++;
}
r &= ~PICNIC_INT_ETHER;
}
if (r & PICNIC_INT_SCSI) {
ih = &tr2_intr_handler[5];
if (ih->func) {
ih->func(ih->arg);
ih->evcnt.ev_count++;
}
r &= ~PICNIC_INT_SCSI;
}
if ((r & PICNIC_INT_FDDLPT) &&
((cause & status) & MIPS_INT_MASK_5)) {
#ifdef DEBUG
printf("FDD LPT interrupt\n");
#endif
ih = &tr2_intr_handler[7];
if (ih->func) {
ih->func(ih->arg);
ih->evcnt.ev_count++;
}
r &= ~PICNIC_INT_FDDLPT;
}
handled |= MIPS_INT_MASK_2;
}
_splset((status & handled) | MIPS_SR_INT_IE);
if (ipending & MIPS_INT_MASK_1)
panic("unknown interrupt INT1\n");
if (ipending & MIPS_INT_MASK_0) { /* FDD, PRINTER */
printf("printer, printer interrupt\n");
r = *PICNIC_INT0_STATUS_REG;
if (r & PICNIC_INT_FDDLPT) {
printf("FDD, Printer interrupt.\n");
} else {
printf("unknown interrupt INT0\n");
}
handled |= MIPS_INT_MASK_0;
}
cause &= ~handled;
_splset(((status & ~cause) & MIPS_HARD_INT_MASK) | MIPS_SR_INT_IE);
}
void
tr2_initclocks(void)
{
/* Enable clock interrupt */
*PICNIC_INT5_MASK_REG |= PICNIC_INT_CLOCK;
}