#define __INTR_PRIVATE
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: dec_3min.c,v 1.75 2024/06/02 12:11:35 andvar Exp $");
#include <sys/param.h>
#include <sys/cpu.h>
#include <sys/device.h>
#include <sys/intr.h>
#include <sys/lwp.h>
#include <sys/systm.h>
#include <sys/timetc.h>
#include <pmax/sysconf.h>
#include <mips/mips/mips_mcclock.h>
#include <dev/tc/tcvar.h>
#include <dev/tc/ioasicreg.h>
#include <dev/tc/ioasicvar.h>
#include <pmax/pmax/machdep.h>
#include <pmax/pmax/kmin.h>
#include <pmax/pmax/memc.h>
#include <pmax/pmax/cons.h>
#include <dev/ic/z8530sc.h>
#include <dev/tc/zs_ioasicvar.h>
#include "wsdisplay.h"
void dec_3min_init(void);
static void dec_3min_bus_reset(void);
static void dec_3min_cons_init(void);
static void dec_3min_intr(uint32_t, vaddr_t, uint32_t);
static void dec_3min_intr_establish(device_t, void *,
int, int (*)(void *), void *);
static void kn02ba_wbflush(void);
static void dec_3min_tc_init(void);
static uint32_t kmin_tc3_imask;
static const struct ipl_sr_map dec_3min_ipl_sr_map = {
.sr_bits = {
[IPL_NONE] = 0,
[IPL_SOFTCLOCK] = MIPS_SOFT_INT_MASK_0,
[IPL_SOFTNET] = MIPS_SOFT_INT_MASK,
[IPL_VM] = MIPS_SPLHIGH,
[IPL_SCHED] = MIPS_SPLHIGH,
[IPL_DDB] = MIPS_SPLHIGH,
[IPL_HIGH] = MIPS_SPLHIGH,
},
};
void
dec_3min_init(void)
{
platform.iobus = "tcbus";
platform.bus_reset = dec_3min_bus_reset;
platform.cons_init = dec_3min_cons_init;
platform.iointr = dec_3min_intr;
platform.intr_establish = dec_3min_intr_establish;
platform.memsize = memsize_bitmap;
platform.tc_init = dec_3min_tc_init;
*(volatile uint32_t *)MIPS_PHYS_TO_KSEG1(KMIN_REG_TIMEOUT) = 0;
kn02ba_wbflush();
ioasic_base = MIPS_PHYS_TO_KSEG1(KMIN_SYS_ASIC);
ipl_sr_map = dec_3min_ipl_sr_map;
*(volatile uint32_t *)(ioasic_base + IOASIC_IMSK) = KMIN_INTR_CLOCK;
mc_cpuspeed(ioasic_base+IOASIC_SLOT_8_START, MIPS_INT_MASK_3);
*(volatile uint32_t *)(ioasic_base + IOASIC_LANCE_DECODE) = 0x3;
*(volatile uint32_t *)(ioasic_base + IOASIC_SCSI_DECODE) = 0xe;
#if 0
*(volatile uint32_t *)(ioasic_base + IOASIC_SCC0_DECODE) = (0x10|4);
*(volatile uint32_t *)(ioasic_base + IOASIC_SCC1_DECODE) = (0x10|6);
*(volatile uint32_t *)(ioasic_base + IOASIC_CSR) = 0x00000f00;
#endif
kmin_tc3_imask = (KMIN_INTR_CLOCK|KMIN_INTR_PSWARN|KMIN_INTR_TIMEOUT);
*(volatile uint32_t *)(ioasic_base + IOASIC_INTR) = 0;
*(volatile uint32_t *)(ioasic_base + IOASIC_IMSK) = kmin_tc3_imask;
physmem_boardmax = KMIN_PHYS_MEMORY_END + 1;
if ((KMIN_MSR_SIZE_16Mb & *(int *)MIPS_PHYS_TO_KSEG1(KMIN_REG_MSR))
== 0)
physmem_boardmax = physmem_boardmax >> 2;
physmem_boardmax = MIPS_PHYS_TO_KSEG1(physmem_boardmax);
cpu_setmodel("DECstation 5000/1%d (3MIN)", mips_options.mips_cpu_mhz);
}
static void
dec_3min_bus_reset(void)
{
*(volatile uint32_t *)MIPS_PHYS_TO_KSEG1(KMIN_REG_TIMEOUT) = 0;
kn02ba_wbflush();
*(volatile uint32_t *)(ioasic_base + IOASIC_INTR) = 0;
kn02ba_wbflush();
}
static void
dec_3min_cons_init(void)
{
int kbd, crt, screen;
kbd = crt = screen = 0;
prom_findcons(&kbd, &crt, &screen);
if (screen > 0) {
#if NWSDISPLAY > 0
if (tcfb_cnattach(crt) > 0) {
zs_ioasic_lk201_cnattach(ioasic_base, 0x180000, 0);
return;
}
#endif
printf("No framebuffer device configured for slot %d: ", crt);
printf("using serial console\n");
}
DELAY(160000000 / 9600);
zs_ioasic_cnattach(ioasic_base, 0x180000, 1);
}
static void
dec_3min_intr_establish(device_t dev, void *cookie, int level,
int (*handler)(void *), void *arg)
{
uint32_t mask;
switch ((uintptr_t)cookie) {
case SYS_DEV_OPT0:
mask = MIPS_INT_MASK_0;
break;
case SYS_DEV_OPT1:
mask = MIPS_INT_MASK_1;
break;
case SYS_DEV_OPT2:
mask = MIPS_INT_MASK_2;
break;
case SYS_DEV_SCSI:
mask = (IOASIC_INTR_SCSI | IOASIC_INTR_SCSI_PTR_LOAD |
IOASIC_INTR_SCSI_OVRUN | IOASIC_INTR_SCSI_READ_E);
break;
case SYS_DEV_LANCE:
mask = KMIN_INTR_LANCE;
break;
case SYS_DEV_SCC0:
mask = KMIN_INTR_SCC_0;
break;
case SYS_DEV_SCC1:
mask = KMIN_INTR_SCC_1;
break;
default:
#ifdef DIAGNOSTIC
printf("warning: enabling unknown intr %p\n", cookie);
#endif
return;
}
#if defined(DEBUG)
printf("3MIN: imask %x, enabling slot %p, dev %p handler %p\n",
kmin_tc3_imask, cookie, dev, handler);
#endif
intrtab[(uintptr_t)cookie].ih_func = handler;
intrtab[(uintptr_t)cookie].ih_arg = arg;
switch ((uintptr_t)cookie) {
case SYS_DEV_OPT0:
case SYS_DEV_OPT1:
case SYS_DEV_OPT2:
break;
default:
kmin_tc3_imask |= mask;
break;
}
*(volatile uint32_t *)(ioasic_base + IOASIC_IMSK) = kmin_tc3_imask;
kn02ba_wbflush();
}
#define CHECKINTR(slot, bits) \
do { \
if (can_serve & (bits)) { \
intrtab[slot].ih_count.ev_count++; \
(*intrtab[slot].ih_func)(intrtab[slot].ih_arg); \
} \
} while (0)
static void
dec_3min_intr(uint32_t status, vaddr_t pc, uint32_t ipending)
{
static int user_warned = 0;
uint32_t old_mask;
old_mask = *(volatile uint32_t *)(ioasic_base + IOASIC_IMSK);
do {
if (ipending & MIPS_INT_MASK_4)
prom_haltbutton();
if (ipending & MIPS_INT_MASK_3) {
uint32_t intr, imsk, can_serve, turnoff;
turnoff = 0;
intr = *(volatile uint32_t *)(ioasic_base + IOASIC_INTR);
imsk = *(volatile uint32_t *)(ioasic_base + IOASIC_IMSK);
can_serve = intr & imsk;
if (intr & IOASIC_INTR_SCSI_PTR_LOAD) {
turnoff |= IOASIC_INTR_SCSI_PTR_LOAD;
#ifdef notdef
asc_dma_intr();
#endif
}
if (intr & (IOASIC_INTR_SCSI_OVRUN | IOASIC_INTR_SCSI_READ_E))
turnoff |= IOASIC_INTR_SCSI_OVRUN | IOASIC_INTR_SCSI_READ_E;
if (intr & IOASIC_INTR_LANCE_READ_E)
turnoff |= IOASIC_INTR_LANCE_READ_E;
if (turnoff)
*(volatile uint32_t *)(ioasic_base + IOASIC_INTR) = ~turnoff;
if (intr & KMIN_INTR_TIMEOUT) {
kn02ba_errintr();
pmax_memerr_evcnt.ev_count++;
}
if (intr & KMIN_INTR_CLOCK) {
struct clockframe cf;
__asm volatile("lbu $0,48(%0)" ::
"r"(ioasic_base + IOASIC_SLOT_8_START));
cf.pc = pc;
cf.sr = status;
cf.intr = (curcpu()->ci_idepth > 1);
hardclock(&cf);
pmax_clock_evcnt.ev_count++;
}
if (old_mask & KMIN_INTR_CLOCK) {
*(uint32_t *)(ioasic_base + IOASIC_IMSK) =
old_mask &
~(KMIN_INTR_SCC_0|KMIN_INTR_SCC_1
|IOASIC_INTR_LANCE|IOASIC_INTR_SCSI);
kn02ba_wbflush();
}
if (curcpu()->ci_idepth > 1)
break;
CHECKINTR(SYS_DEV_SCC0, IOASIC_INTR_SCC_0);
CHECKINTR(SYS_DEV_SCC1, IOASIC_INTR_SCC_1);
#ifdef notyet
if ((old_mask & (KMIN_INTR_SCC_1|KMIN_INTR_SCC_0)) ==
(KMIN_INTR_SCC_1|KMIN_INTR_SCC_0)) {
*imaskp = old_mask &
~(KMIN_INTR_SCC_0|KMIN_INTR_SCC_1 |
IOASIC_INTR_LANCE|IOASIC_INTR_SCSI);
kn02ba_wbflush();
}
if (curcpu()->ci_idepth > 1)
break;
#endif
CHECKINTR(SYS_DEV_LANCE, IOASIC_INTR_LANCE);
CHECKINTR(SYS_DEV_SCSI, IOASIC_INTR_SCSI);
if (user_warned && ((intr & KMIN_INTR_PSWARN) == 0)) {
printf("%s\n", "Power supply ok now.");
user_warned = 0;
}
if ((intr & KMIN_INTR_PSWARN) && (user_warned < 3)) {
user_warned++;
printf("%s\n", "Power supply overheating");
}
}
if ((ipending & MIPS_INT_MASK_0) && intrtab[SYS_DEV_OPT0].ih_func) {
(*intrtab[SYS_DEV_OPT0].ih_func)(intrtab[SYS_DEV_OPT0].ih_arg);
intrtab[SYS_DEV_OPT0].ih_count.ev_count++;
}
if ((ipending & MIPS_INT_MASK_1) && intrtab[SYS_DEV_OPT1].ih_func) {
(*intrtab[SYS_DEV_OPT1].ih_func)(intrtab[SYS_DEV_OPT1].ih_arg);
intrtab[SYS_DEV_OPT1].ih_count.ev_count++;
}
if ((ipending & MIPS_INT_MASK_2) && intrtab[SYS_DEV_OPT2].ih_func) {
(*intrtab[SYS_DEV_OPT2].ih_func)(intrtab[SYS_DEV_OPT2].ih_arg);
intrtab[SYS_DEV_OPT2].ih_count.ev_count++;
}
} while (0);
*(uint32_t *)(ioasic_base + IOASIC_IMSK) = old_mask;
}
static void
kn02ba_wbflush(void)
{
__asm volatile("lw $0,%0; lw $0,%0" ::
"i"(MIPS_PHYS_TO_KSEG1(KMIN_REG_IMSK)));
}
void
dec_3min_tc_init(void)
{
#if defined(MIPS3)
static struct timecounter tc = {
.tc_get_timecount = (timecounter_get_t *)mips3_cp0_count_read,
.tc_counter_mask = ~0u,
.tc_name = "mips3_cp0_counter",
.tc_quality = 100,
};
if (MIPS_HAS_CLOCK) {
tc.tc_frequency = mips_options.mips_cpu_mhz * 1000000;
tc_init(&tc);
}
#endif
}