sys/arch/ews4800mips/ews4800mips/tr2_intr.c
author martin <martin@NetBSD.org>
Mon, 28 Apr 2008 20:22:51 +0000
branchtrunk
changeset 169261 0fba8181c5ff
parent 167819 c07d67218ee5
child 198152 d35cad0013d3
child 280589 deaee8015174
child 280595 2dce56295d95
child 280621 6545b7475815
child 282742 969444890837
permissions -rw-r--r--
Remove clause 3 and 4 from TNF licenses

/*	$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;
}