#define __INTR_PRIVATE
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: dec_3max.c,v 1.56 2014/03/24 19:31:39 christos Exp $");
#include "dzkbd.h"
#include <sys/param.h>
#include <sys/bus.h>
#include <sys/cpu.h>
#include <sys/device.h>
#include <sys/intr.h>
#include <sys/lwp.h>
#include <sys/systm.h>
#include <mips/locore.h>
#include <mips/mips/mips_mcclock.h>
#include <dev/tc/tcvar.h>
#include <pmax/sysconf.h>
#include <pmax/pmax/machdep.h>
#include <pmax/pmax/kn02.h>
#include <pmax/pmax/memc.h>
#include <dev/dec/dzreg.h>
#include <dev/dec/dzvar.h>
#include <dev/dec/dzkbdvar.h>
#include <pmax/pmax/cons.h>
#include "wsdisplay.h"
void dec_3max_init(void);
static void dec_3max_bus_reset(void);
static void dec_3max_cons_init(void);
static void dec_3max_errintr(void);
static void dec_3max_intr(uint32_t, vaddr_t, uint32_t);
static void dec_3max_intr_establish(device_t, void *,
int, int (*)(void *), void *);
#define kn02_wbflush() wbflush()
static const struct ipl_sr_map dec_3max_ipl_sr_map = {
.sr_bits = {
[IPL_NONE] = 0,
[IPL_SOFTCLOCK] = MIPS_SOFT_INT_MASK_0,
[IPL_SOFTNET] = MIPS_SOFT_INT_MASK,
[IPL_VM] = MIPS_SPL0,
[IPL_SCHED] = MIPS_SPLHIGH,
[IPL_DDB] = MIPS_SPLHIGH,
[IPL_HIGH] = MIPS_SPLHIGH,
},
};
void
dec_3max_init(void)
{
uint32_t csr;
platform.iobus = "tcbus";
platform.bus_reset = dec_3max_bus_reset;
platform.cons_init = dec_3max_cons_init;
platform.iointr = dec_3max_intr;
platform.intr_establish = dec_3max_intr_establish;
platform.memsize = memsize_bitmap;
*(volatile uint32_t *)MIPS_PHYS_TO_KSEG1(KN02_SYS_ERRADR) = 0;
kn02_wbflush();
ipl_sr_map = dec_3max_ipl_sr_map;
mc_cpuspeed(MIPS_PHYS_TO_KSEG1(KN02_SYS_CLOCK), MIPS_INT_MASK_1);
csr = *(volatile uint32_t *)MIPS_PHYS_TO_KSEG1(KN02_SYS_CSR);
csr &= ~(KN02_CSR_WRESERVED|KN02_CSR_IOINTEN|KN02_CSR_CORRECT|0xff);
*(volatile uint32_t *)MIPS_PHYS_TO_KSEG1(KN02_SYS_CSR) = csr;
kn02_wbflush();
cpu_setmodel("DECstation 5000/200 (3MAX)");
}
static void
dec_3max_bus_reset(void)
{
*(volatile uint32_t *)MIPS_PHYS_TO_KSEG1(KN02_SYS_ERRADR) = 0;
kn02_wbflush();
*(volatile uint32_t *)MIPS_PHYS_TO_KSEG1(KN02_SYS_CHKSYN) = 0;
kn02_wbflush();
}
static void
dec_3max_cons_init(void)
{
int kbd, crt, screen;
extern int tcfb_cnattach(int);
kbd = crt = screen = 0;
prom_findcons(&kbd, &crt, &screen);
if (screen > 0) {
#if NWSDISPLAY > 0
if (kbd == 7 && tcfb_cnattach(crt) > 0) {
dz_ibus_cnsetup(KN02_SYS_DZ);
dzkbd_cnattach(NULL);
return;
}
#endif
printf("No framebuffer device configured for slot %d: ", crt);
printf("using serial console\n");
}
DELAY(160000000 / 9600);
dz_ibus_cnsetup(KN02_SYS_DZ);
dz_ibus_cnattach(kbd);
}
static const struct {
int cookie;
int intrbit;
} kn02intrs[] = {
{ SYS_DEV_OPT0, KN02_IP_SLOT0 },
{ SYS_DEV_OPT1, KN02_IP_SLOT1 },
{ SYS_DEV_OPT2, KN02_IP_SLOT2 },
{ SYS_DEV_SCSI, KN02_IP_SCSI },
{ SYS_DEV_LANCE, KN02_IP_LANCE },
{ SYS_DEV_SCC0, KN02_IP_DZ },
};
static void
dec_3max_intr_establish(device_t dev, void *cookie, int level,
int (*handler)(void *), void *arg)
{
int i;
uint32_t csr;
for (i = 0; i < __arraycount(kn02intrs); i++) {
if (kn02intrs[i].cookie == (intptr_t)cookie)
goto found;
}
panic("intr_establish: invalid cookie %p", cookie);
found:
intrtab[(intptr_t)cookie].ih_func = handler;
intrtab[(intptr_t)cookie].ih_arg = arg;
csr = *(volatile uint32_t *)MIPS_PHYS_TO_KSEG1(KN02_SYS_CSR) &
0x00ffff00;
csr |= (kn02intrs[i].intrbit << 16);
*(volatile uint32_t *)MIPS_PHYS_TO_KSEG1(KN02_SYS_CSR) = csr;
}
#define CALLINTR(vvv) \
do { \
intrtab[vvv].ih_count.ev_count++; \
(*intrtab[vvv].ih_func)(intrtab[vvv].ih_arg); \
} while (0)
static void
dec_3max_intr(uint32_t status, vaddr_t pc, uint32_t ipending)
{
static bool warned = false;
uint32_t csr;
if (ipending & MIPS_INT_MASK_1) {
struct clockframe cf;
csr = *(volatile uint32_t *)MIPS_PHYS_TO_KSEG1(KN02_SYS_CSR);
if ((csr & KN02_CSR_PSWARN) && !warned) {
warned = true;
printf("WARNING: power supply is overheating!\n");
} else if (warned && !(csr & KN02_CSR_PSWARN)) {
warned = false;
printf("WARNING: power supply is OK again\n");
}
__asm volatile("lbu $0,48(%0)" ::
"r"(MIPS_PHYS_TO_KSEG1(KN02_SYS_CLOCK)));
cf.pc = pc;
cf.sr = status;
cf.intr = (curcpu()->ci_idepth > 1);
hardclock(&cf);
pmax_clock_evcnt.ev_count++;
}
if (ipending & MIPS_INT_MASK_0) {
csr = *(volatile uint32_t *)MIPS_PHYS_TO_KSEG1(KN02_SYS_CSR);
csr &= (csr >> KN02_CSR_IOINTEN_SHIFT);
if (csr & (KN02_IP_DZ | KN02_IP_LANCE | KN02_IP_SCSI)) {
if (csr & KN02_IP_DZ)
CALLINTR(SYS_DEV_SCC0);
if (csr & KN02_IP_LANCE)
CALLINTR(SYS_DEV_LANCE);
if (csr & KN02_IP_SCSI)
CALLINTR(SYS_DEV_SCSI);
}
if (csr & (KN02_IP_SLOT2 | KN02_IP_SLOT1 | KN02_IP_SLOT0)) {
if (csr & KN02_IP_SLOT2)
CALLINTR(SYS_DEV_OPT2);
if (csr & KN02_IP_SLOT1)
CALLINTR(SYS_DEV_OPT1);
if (csr & KN02_IP_SLOT0)
CALLINTR(SYS_DEV_OPT0);
}
}
if (ipending & MIPS_INT_MASK_3) {
dec_3max_errintr();
pmax_memerr_evcnt.ev_count++;
}
}
static void
dec_3max_errintr(void)
{
uint32_t erradr, csr;
vaddr_t errsyn;
erradr = *(volatile uint32_t *)MIPS_PHYS_TO_KSEG1(KN02_SYS_ERRADR);
errsyn = MIPS_PHYS_TO_KSEG1(KN02_SYS_CHKSYN);
*(volatile uint32_t *)MIPS_PHYS_TO_KSEG1(KN02_SYS_ERRADR) = 0;
kn02_wbflush();
csr = *(volatile uint32_t *)MIPS_PHYS_TO_KSEG1(KN02_SYS_CSR);
dec_mtasic_err(erradr, errsyn, csr & KN02_CSR_BNK32M);
}