NetBSD/sys/arch/alpha/tc/tc_3000_300.c
thorpej 0fb6b9a8f8 Rework the interrupt code, shaving some cycles off in the process.
Rather than an "iointr" routine that decomposes a vector into an
IRQ, we maintain a vector table directly, hooking up each "iointr"
routine at the correct vector.  This also allows us to hook device
interrupts up to specific vectors (c.f. Jensen).

We can shave even more cycles off, here, and I will, but it requires
some changes to the alpha_shared_intr stuff.
2001-07-27 00:25:18 +00:00

320 lines
8.3 KiB
C

/* $NetBSD: tc_3000_300.c,v 1.26 2001/07/27 00:25:21 thorpej Exp $ */
/*
* Copyright (c) 1994, 1995, 1996 Carnegie-Mellon University.
* All rights reserved.
*
* Author: Chris G. Demetriou
*
* Permission to use, copy, modify and distribute this software and
* its documentation is hereby granted, provided that both the copyright
* notice and this permission notice appear in all copies of the
* software, derivative works or modified versions, and any portions
* thereof, and that both notices appear in supporting documentation.
*
* CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
* CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND
* FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
*
* Carnegie Mellon requests users of this software to return to
*
* Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
* School of Computer Science
* Carnegie Mellon University
* Pittsburgh PA 15213-3890
*
* any improvements or extensions that they make and grant Carnegie the
* rights to redistribute these changes.
*/
#include <sys/cdefs.h> /* RCS ID & Copyright macro defns */
__KERNEL_RCSID(0, "$NetBSD: tc_3000_300.c,v 1.26 2001/07/27 00:25:21 thorpej Exp $");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/malloc.h>
#include <machine/autoconf.h>
#include <machine/pte.h>
#include <dev/tc/tcvar.h>
#include <dev/tc/ioasicreg.h>
#include <alpha/tc/tc_conf.h>
#include <alpha/tc/tc_3000_300.h>
#include "wsdisplay.h"
#include "sfb.h"
#if NSFB > 0
extern int sfb_cnattach __P((tc_addr_t));
#endif
int tc_3000_300_intrnull __P((void *));
#define C(x) ((void *)(u_long)x)
#define KV(x) (ALPHA_PHYS_TO_K0SEG(x))
/*
* We have to read and modify the IOASIC registers directly, because
* the TC option slot interrupt request and mask bits are stored there,
* and the ioasic code isn't initted when we need to frob some interrupt
* bits.
*/
#define DEC_3000_300_IOASIC_ADDR KV(0x1a0000000)
struct tc_slotdesc tc_3000_300_slots[] = {
{ KV(0x100000000), C(TC_3000_300_DEV_OPT0), }, /* 0 - opt slot 0 */
{ KV(0x120000000), C(TC_3000_300_DEV_OPT1), }, /* 1 - opt slot 1 */
{ KV(0x140000000), C(TC_3000_300_DEV_BOGUS), }, /* 2 - unused */
{ KV(0x160000000), C(TC_3000_300_DEV_BOGUS), }, /* 3 - unused */
{ KV(0x180000000), C(TC_3000_300_DEV_BOGUS), }, /* 4 - TCDS ASIC */
{ KV(0x1a0000000), C(TC_3000_300_DEV_BOGUS), }, /* 5 - IOCTL ASIC */
{ KV(0x1c0000000), C(TC_3000_300_DEV_BOGUS), }, /* 6 - CXTurbo */
};
int tc_3000_300_nslots =
sizeof(tc_3000_300_slots) / sizeof(tc_3000_300_slots[0]);
struct tc_builtin tc_3000_300_builtins[] = {
{ "PMAGB-BA", 6, 0x02000000, C(TC_3000_300_DEV_CXTURBO), },
{ "FLAMG-IO", 5, 0x00000000, C(TC_3000_300_DEV_IOASIC), },
{ "PMAZ-DS ", 4, 0x00000000, C(TC_3000_300_DEV_TCDS), },
};
int tc_3000_300_nbuiltins =
sizeof(tc_3000_300_builtins) / sizeof(tc_3000_300_builtins[0]);
struct tcintr {
int (*tci_func) __P((void *));
void *tci_arg;
struct evcnt tci_evcnt;
} tc_3000_300_intr[TC_3000_300_NCOOKIES];
void
tc_3000_300_intr_setup()
{
volatile u_int32_t *imskp;
char *cp;
u_long i;
/*
* Disable all interrupts that we can (can't disable builtins).
*/
imskp = (volatile u_int32_t *)(DEC_3000_300_IOASIC_ADDR + IOASIC_IMSK);
*imskp &= ~(IOASIC_INTR_300_OPT0 | IOASIC_INTR_300_OPT1);
/*
* Set up interrupt handlers.
*/
for (i = 0; i < TC_3000_300_NCOOKIES; i++) {
tc_3000_300_intr[i].tci_func = tc_3000_300_intrnull;
tc_3000_300_intr[i].tci_arg = (void *)i;
cp = malloc(12, M_DEVBUF, M_NOWAIT);
if (cp == NULL)
panic("tc_3000_300_intr_setup");
sprintf(cp, "slot %lu", i);
evcnt_attach_dynamic(&tc_3000_300_intr[i].tci_evcnt,
EVCNT_TYPE_INTR, NULL, "tc", cp);
}
}
const struct evcnt *
tc_3000_300_intr_evcnt(tcadev, cookie)
struct device *tcadev;
void *cookie;
{
u_long dev = (u_long)cookie;
#ifdef DIAGNOSTIC
/* XXX bounds-check cookie. */
#endif
return (&tc_3000_300_intr[dev].tci_evcnt);
}
void
tc_3000_300_intr_establish(tcadev, cookie, level, func, arg)
struct device *tcadev;
void *cookie, *arg;
tc_intrlevel_t level;
int (*func) __P((void *));
{
volatile u_int32_t *imskp;
u_long dev = (u_long)cookie;
#ifdef DIAGNOSTIC
/* XXX bounds-check cookie. */
#endif
if (tc_3000_300_intr[dev].tci_func != tc_3000_300_intrnull)
panic("tc_3000_300_intr_establish: cookie %lu twice", dev);
tc_3000_300_intr[dev].tci_func = func;
tc_3000_300_intr[dev].tci_arg = arg;
imskp = (volatile u_int32_t *)(DEC_3000_300_IOASIC_ADDR + IOASIC_IMSK);
switch (dev) {
case TC_3000_300_DEV_OPT0:
*imskp |= IOASIC_INTR_300_OPT0;
break;
case TC_3000_300_DEV_OPT1:
*imskp |= IOASIC_INTR_300_OPT1;
break;
default:
/* interrupts for builtins always enabled */
break;
}
}
void
tc_3000_300_intr_disestablish(tcadev, cookie)
struct device *tcadev;
void *cookie;
{
volatile u_int32_t *imskp;
u_long dev = (u_long)cookie;
#ifdef DIAGNOSTIC
/* XXX bounds-check cookie. */
#endif
if (tc_3000_300_intr[dev].tci_func == tc_3000_300_intrnull)
panic("tc_3000_300_intr_disestablish: cookie %lu bad intr",
dev);
imskp = (volatile u_int32_t *)(DEC_3000_300_IOASIC_ADDR + IOASIC_IMSK);
switch (dev) {
case TC_3000_300_DEV_OPT0:
*imskp &= ~IOASIC_INTR_300_OPT0;
break;
case TC_3000_300_DEV_OPT1:
*imskp &= ~IOASIC_INTR_300_OPT1;
break;
default:
/* interrupts for builtins always enabled */
break;
}
tc_3000_300_intr[dev].tci_func = tc_3000_300_intrnull;
tc_3000_300_intr[dev].tci_arg = (void *)dev;
}
int
tc_3000_300_intrnull(val)
void *val;
{
panic("tc_3000_300_intrnull: uncaught TC intr for cookie %ld\n",
(u_long)val);
}
void
tc_3000_300_iointr(arg, vec)
void *arg;
unsigned long vec;
{
u_int32_t tcir, ioasicir, ioasicimr;
int ifound;
#ifdef DIAGNOSTIC
int s;
if (vec != 0x800)
panic("INVALID ASSUMPTION: vec 0x%lx, not 0x800", vec);
s = splhigh();
if (s != ALPHA_PSL_IPL_IO)
panic("INVALID ASSUMPTION: IPL %d, not %d", s,
ALPHA_PSL_IPL_IO);
splx(s);
#endif
do {
tc_syncbus();
/* find out what interrupts/errors occurred */
tcir = *(volatile u_int32_t *)TC_3000_300_IR;
ioasicir = *(volatile u_int32_t *)
(DEC_3000_300_IOASIC_ADDR + IOASIC_INTR);
ioasicimr = *(volatile u_int32_t *)
(DEC_3000_300_IOASIC_ADDR + IOASIC_IMSK);
tc_mb();
/* Ignore interrupts that aren't enabled out. */
ioasicir &= ioasicimr;
/* clear the interrupts/errors we found. */
*(volatile u_int32_t *)TC_3000_300_IR = tcir;
/* XXX can't clear TC option slot interrupts here? */
tc_wmb();
ifound = 0;
#define INCRINTRCNT(slot) tc_3000_300_intr[slot].tci_evcnt.ev_count++
#define CHECKINTR(slot, flag) \
if (flag) { \
ifound = 1; \
INCRINTRCNT(slot); \
(*tc_3000_300_intr[slot].tci_func) \
(tc_3000_300_intr[slot].tci_arg); \
}
/* Do them in order of priority; highest slot # first. */
CHECKINTR(TC_3000_300_DEV_CXTURBO,
tcir & TC_3000_300_IR_CXTURBO);
CHECKINTR(TC_3000_300_DEV_IOASIC,
(tcir & TC_3000_300_IR_IOASIC) &&
(ioasicir & ~(IOASIC_INTR_300_OPT1|IOASIC_INTR_300_OPT0)));
CHECKINTR(TC_3000_300_DEV_TCDS, tcir & TC_3000_300_IR_TCDS);
CHECKINTR(TC_3000_300_DEV_OPT1,
ioasicir & IOASIC_INTR_300_OPT1);
CHECKINTR(TC_3000_300_DEV_OPT0,
ioasicir & IOASIC_INTR_300_OPT0);
#undef CHECKINTR
#ifdef DIAGNOSTIC
#define PRINTINTR(msg, bits) \
if (tcir & bits) \
printf(msg);
PRINTINTR("BCache tag parity error\n",
TC_3000_300_IR_BCTAGPARITY);
PRINTINTR("TC overrun error\n", TC_3000_300_IR_TCOVERRUN);
PRINTINTR("TC I/O timeout\n", TC_3000_300_IR_TCTIMEOUT);
PRINTINTR("Bcache parity error\n",
TC_3000_300_IR_BCACHEPARITY);
PRINTINTR("Memory parity error\n", TC_3000_300_IR_MEMPARITY);
#undef PRINTINTR
#endif
} while (ifound);
}
#if NWSDISPLAY > 0
/*
* tc_3000_300_fb_cnattach --
* Attempt to map the CTB output device to a slot and attach the
* framebuffer as the output side of the console.
*/
int
tc_3000_300_fb_cnattach(turbo_slot)
u_int64_t turbo_slot;
{
u_int32_t output_slot;
output_slot = turbo_slot & 0xffffffff;
if (output_slot >= tc_3000_300_nslots) {
return EINVAL;
}
if (output_slot == 0) {
#if NSFB > 0
sfb_cnattach(KV(0x1c0000000) + 0x02000000);
return 0;
#else
return ENXIO;
#endif
}
return tc_fb_cnattach(tc_3000_300_slots[output_slot-1].tcs_addr);
}
#endif /* NWSDISPLAY */