#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: news5000.c,v 1.23 2024/06/02 13:28:44 andvar Exp $");
#define __INTR_PRIVATE
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/timetc.h>
#include <sys/cpu.h>
#include <sys/intr.h>
#include <mips/locore.h>
#include <machine/adrsmap.h>
#include <newsmips/apbus/apbusvar.h>
#include <newsmips/newsmips/machid.h>
static void news5000_level1_intr(void);
static void news5000_level0_intr(void);
static void news5000_enable_intr(void);
static void news5000_disable_intr(void);
static void news5000_enable_timer(void);
static void news5000_readidrom(uint8_t *);
static void news5000_tc_init(void);
static uint32_t news5000_getfreerun(struct timecounter *);
static const struct ipl_sr_map news5000_ipl_sr_map = {
.sr_bits = {
[IPL_NONE] = 0,
[IPL_SOFTCLOCK] = MIPS_SOFT_INT_MASK_0,
[IPL_SOFTNET] = MIPS_SOFT_INT_MASK,
[IPL_VM] = MIPS_SOFT_INT_MASK
| MIPS_INT_MASK_0
| MIPS_INT_MASK_1,
[IPL_SCHED] = MIPS_SOFT_INT_MASK
| MIPS_INT_MASK_0
| MIPS_INT_MASK_1
| MIPS_INT_MASK_2,
[IPL_DDB] = MIPS_INT_MASK,
[IPL_HIGH] = MIPS_INT_MASK,
},
};
void
news5000_intr(int ppl, vaddr_t pc, uint32_t status)
{
uint32_t ipending;
int ipl;
while (ppl < (ipl = splintr(&ipending))) {
if (ipending & MIPS_INT_MASK_2) {
#ifdef DEBUG
static int l2cnt = 0;
#endif
uint32_t int2stat;
int2stat = *(volatile uint32_t *)NEWS5000_INTST2;
#ifdef DEBUG
l2cnt++;
if (l2cnt == 50) {
*(volatile uint32_t *)NEWS5000_LED_SEC = 1;
}
if (l2cnt == 100) {
*(volatile uint32_t *)NEWS5000_LED_SEC = 0;
l2cnt = 0;
}
#endif
if (int2stat & NEWS5000_INT2_TIMER0) {
struct clockframe cf = {
.pc = pc,
.sr = status,
.intr = (curcpu()->ci_idepth > 1),
};
*(volatile uint32_t *)NEWS5000_TIMER0 = 1;
hardclock(&cf);
intrcnt[HARDCLOCK_INTR]++;
}
apbus_wbflush();
}
if (ipending & MIPS_INT_MASK_5) {
uint32_t int5stat;
int5stat = *(volatile u_int *)NEWS5000_INTST5;
printf("level5 interrupt (%08x)\n", int5stat);
apbus_wbflush();
}
if (ipending & MIPS_INT_MASK_4) {
uint32_t int4stat;
int4stat = *(volatile uint32_t *)NEWS5000_INTST4;
printf("level4 interrupt (%08x)\n", int4stat);
if (int4stat & NEWS5000_INT4_APBUS) {
uint32_t stat;
stat = *(volatile uint32_t *)NEWS5000_APBUS_INTST;
printf("APbus error 0x%04x\n", stat & 0xffff);
if (stat & NEWS5000_APBUS_INT_DMAADDR) {
printf("DMA Address Error: "
"slot=%x, addr=0x%08x\n",
*(volatile uint32_t *)NEWS5000_APBUS_DER_S,
*(volatile uint32_t *)NEWS5000_APBUS_DER_A);
}
if (stat & NEWS5000_APBUS_INT_RDTIMEO)
printf("IO Read Timeout: addr=0x%08x\n",
*(volatile uint32_t *)NEWS5000_APBUS_BER_A);
if (stat & NEWS5000_APBUS_INT_WRTIMEO)
printf("IO Write Timeout: addr=0x%08x\n",
*(volatile uint32_t *)NEWS5000_APBUS_BER_A);
*(volatile uint32_t *)0xb4c00014 = stat;
}
apbus_wbflush();
}
if (ipending & MIPS_INT_MASK_3) {
uint32_t int3stat;
int3stat = *(volatile uint32_t *)NEWS5000_INTST3;
printf("level3 interrupt (%08x)\n", int3stat);
apbus_wbflush();
}
if (ipending & MIPS_INT_MASK_1) {
news5000_level1_intr();
apbus_wbflush();
}
if (ipending & MIPS_INT_MASK_0) {
news5000_level0_intr();
apbus_wbflush();
}
}
}
static void
news5000_level1_intr(void)
{
uint32_t int1stat;
int1stat = *(volatile uint32_t *)NEWS5000_INTST1;
if (int1stat) {
if (apbus_intr_dispatch(1, int1stat) == 0)
printf("level1_intr: no handler (mask 0x%04x)\n",
int1stat);
} else
printf("level1 stray interrupt?\n");
}
static void
news5000_level0_intr(void)
{
uint32_t int0stat;
int0stat = *(volatile uint32_t *)NEWS5000_INTST0;
if (int0stat) {
if (apbus_intr_dispatch(0, int0stat) == 0)
printf("level0_intr: no handler (mask 0x%04x)\n",
int0stat);
} else
printf("level0 stray interrupt?\n");
}
static void
news5000_enable_intr(void)
{
*(volatile uint32_t *)NEWS5000_INTEN4 = NEWS5000_INT4_APBUS;
*(volatile uint32_t *)NEWS5000_APBUS_INTMSK = 0xffff;
*(volatile uint32_t *)NEWS5000_INTEN5 = 0;
}
static void
news5000_disable_intr(void)
{
*(volatile uint32_t *)NEWS5000_INTEN0 = 0;
*(volatile uint32_t *)NEWS5000_INTEN1 = 0;
*(volatile uint32_t *)NEWS5000_INTEN2 = 0;
*(volatile uint32_t *)NEWS5000_INTEN3 = 0;
*(volatile uint32_t *)NEWS5000_INTEN4 = 0;
*(volatile uint32_t *)NEWS5000_INTEN5 = 0;
}
static void
news5000_enable_timer(void)
{
news5000_tc_init();
*(volatile uint32_t *)NEWS5000_INTEN2 = NEWS5000_INT2_TIMER0;
}
static uint32_t
news5000_getfreerun(struct timecounter *tc)
{
return *(volatile uint32_t *)NEWS5000_FREERUN;
}
static void
news5000_tc_init(void)
{
static struct timecounter tc = {
.tc_get_timecount = news5000_getfreerun,
.tc_frequency = 1000000,
.tc_counter_mask = ~0,
.tc_name = "news5000_freerun",
.tc_quality = 100,
};
tc_init(&tc);
}
static void
news5000_readidrom(uint8_t *rom)
{
uint32_t *p = (void *)NEWS5000_IDROM;
int i;
for (i = 0; i < sizeof(struct idrom); i++, p += 2)
*rom++ = ((*p & 0x0f) << 4) + (*(p + 1) & 0x0f);
}
extern struct idrom idrom;
void
news5000_init(void)
{
ipl_sr_map = news5000_ipl_sr_map;
enable_intr = news5000_enable_intr;
disable_intr = news5000_disable_intr;
enable_timer = news5000_enable_timer;
news_wbflush = (uint32_t *)NEWS5000_WBFLUSH;
news5000_readidrom((uint8_t *)&idrom);
hostid = idrom.id_serial;
#define NEWS5000_FDC_FDOUT 0xbed20000
#define FDO_FRST 0x04
*(volatile uint8_t *)NEWS5000_FDC_FDOUT = FDO_FRST;
}