#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: dec_kn8ae.c,v 1.45 2025/03/09 01:06:42 thorpej Exp $");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/lwp.h>
#include <machine/rpb.h>
#include <machine/autoconf.h>
#include <machine/cpuconf.h>
#include <machine/frame.h>
#include <machine/alpha.h>
#include <machine/logout.h>
#include <dev/pci/pcivar.h>
#include <alpha/tlsb/tlsbreg.h>
#include <alpha/tlsb/tlsbvar.h>
#include <alpha/tlsb/kftxxreg.h>
#include <alpha/tlsb/kftxxvar.h>
#define KV(_addr) ((void *)ALPHA_PHYS_TO_K0SEG((_addr)))
void dec_kn8ae_init(void);
void dec_kn8ae_cons_init(void);
static void dec_kn8ae_device_register(device_t, void *);
static void dec_kn8ae_mcheck_handler
(unsigned long, struct trapframe *, unsigned long, unsigned long);
const struct alpha_variation_table dec_kn8ae_variations[] = {
{ 0, "AlphaServer 8400" },
{ 0, NULL },
};
void
dec_kn8ae_init(void)
{
uint64_t variation;
platform.family = "AlphaServer 8400";
if ((platform.model = alpha_dsr_sysname()) == NULL) {
variation = hwrpb->rpb_variation & SV_ST_MASK;
if ((platform.model = alpha_variation_name(variation,
dec_kn8ae_variations)) == NULL)
platform.model = alpha_unknown_sysname();
}
platform.iobus = "tlsb";
platform.cons_init = dec_kn8ae_cons_init;
platform.device_register = dec_kn8ae_device_register;
platform.mcheck_handler = dec_kn8ae_mcheck_handler;
}
void
dec_kn8ae_cons_init(void)
{
}
static void
dec_kn8ae_device_register(device_t dev, void *aux)
{
static device_t primarydev;
struct bootdev_data *b = bootdev_data;
if (booted_device != NULL || b == NULL) {
return;
}
if (primarydev == NULL) {
if (device_is_a(dev, "dwlpx")) {
struct kft_dev_attach_args *ka = aux;
if (b->bus == ka->ka_hosenum) {
primarydev = dev;
#ifdef BDEBUG
printf("\nprimarydev = %s\n",
device_xname(dev));
#endif
}
}
return;
}
pci_find_bootdev(primarydev, dev, aux);
}
void kn8ae_harderr(unsigned long, unsigned long,
unsigned long, struct trapframe *);
static void kn8ae_softerr(unsigned long, unsigned long,
unsigned long, struct trapframe *);
void kn8ae_mcheck(unsigned long, unsigned long,
unsigned long, struct trapframe *);
static void clear_tlsb_ebits(int);
static void
clear_tlsb_ebits(int cpuonly)
{
int node;
uint32_t tldev;
for (node = 0; node <= TLSB_NODE_MAX; ++node) {
if ((tlsb_found & (1 << node)) == 0)
continue;
tldev = TLSB_GET_NODEREG(node, TLDEV);
if (tldev == 0) {
continue;
}
if (TLSB_GET_NODEREG(node, TLBER) &
(TLBER_UDE|TLBER_CWDE|TLBER_CRDE)) {
TLSB_PUT_NODEREG(node, TLESR0,
TLSB_GET_NODEREG(node, TLESR0));
TLSB_PUT_NODEREG(node, TLESR1,
TLSB_GET_NODEREG(node, TLESR1));
TLSB_PUT_NODEREG(node, TLESR2,
TLSB_GET_NODEREG(node, TLESR2));
TLSB_PUT_NODEREG(node, TLESR3,
TLSB_GET_NODEREG(node, TLESR3));
}
TLSB_PUT_NODEREG(node, TLBER,
TLSB_GET_NODEREG(node, TLBER));
TLSB_PUT_NODEREG(node, TLFADR0,
TLSB_GET_NODEREG(node, TLFADR0));
TLSB_PUT_NODEREG(node, TLFADR1,
TLSB_GET_NODEREG(node, TLFADR1));
if (TLDEV_ISCPU(tldev)) {
TLSB_PUT_NODEREG(node, TLEPAERR,
TLSB_GET_NODEREG(node, TLEPAERR));
TLSB_PUT_NODEREG(node, TLEPDERR,
TLSB_GET_NODEREG(node, TLEPDERR));
TLSB_PUT_NODEREG(node, TLEPMERR,
TLSB_GET_NODEREG(node, TLEPMERR));
continue;
}
if (cpuonly || TLDEV_ISMEM(tldev)) {
continue;
}
TLSB_PUT_NODEREG(node, KFT_ICCNSE,
TLSB_GET_NODEREG(node, KFT_ICCNSE));
TLSB_PUT_NODEREG(node, KFT_IDPNSE0,
TLSB_GET_NODEREG(node, KFT_IDPNSE0));
TLSB_PUT_NODEREG(node, KFT_IDPNSE1,
TLSB_GET_NODEREG(node, KFT_IDPNSE1));
if (TLDEV_DTYPE(tldev) == TLDEV_DTYPE_KFTHA) {
TLSB_PUT_NODEREG(node, KFT_IDPNSE2,
TLSB_GET_NODEREG(node, KFT_IDPNSE2));
TLSB_PUT_NODEREG(node, KFT_IDPNSE3,
TLSB_GET_NODEREG(node, KFT_IDPNSE3));
}
}
}
static const char *fmt1 = " %-25s = 0x%l016x\n";
void
kn8ae_harderr(unsigned long mces, unsigned long type, unsigned long logout, struct trapframe *framep)
{
int whami, cpuwerr, dof_cnt;
mc_hdr_ev5 *hdr;
mc_cc_ev5 *mptr;
struct tlsb_mchk_fatal *ptr;
hdr = (mc_hdr_ev5 *) logout;
mptr = (mc_cc_ev5 *) (logout + sizeof (*hdr));
ptr = (struct tlsb_mchk_fatal *)
(logout + sizeof (*hdr) + sizeof (*mptr));
whami = alpha_pal_whami();
printf("kn8ae: CPU ID %d system correctable error\n", whami);
printf(" Machine Check Code 0x%lx\n", hdr->mcheck_code);
printf(fmt1, "EI Status", mptr->ei_stat);
printf(fmt1, "EI Address", mptr->ei_addr);
printf(fmt1, "Fill Syndrome", mptr->fill_syndrome);
printf(fmt1, "Interrupt Status Reg.", mptr->isr);
printf("\n");
dof_cnt = (ptr->rsvdheader & 0xffffffff00000000) >> 32;
cpuwerr = ptr->rsvdheader & 0xffff;
printf(fmt1, "CPU W/Error.", cpuwerr);
printf(fmt1, "DOF Count.", dof_cnt);
printf(fmt1, "TLDEV", ptr->tldev);
printf(fmt1, "TLSB Bus Error", ptr->tlber);
printf(fmt1, "TLSB CNR", ptr->tlcnr);
printf(fmt1, "TLSB VID", ptr->tlvid);
printf(fmt1, "TLSB Error Syndrome 0", ptr->tlesr0);
printf(fmt1, "TLSB Error Syndrome 1", ptr->tlesr1);
printf(fmt1, "TLSB Error Syndrome 2", ptr->tlesr2);
printf(fmt1, "TLSB Error Syndrome 3", ptr->tlesr3);
printf(fmt1, "TLSB LEP_AERR", ptr->tlepaerr);
printf(fmt1, "TLSB MODCONF", ptr->tlmodconfig);
printf(fmt1, "TLSB LEP_MERR", ptr->tlepmerr);
printf(fmt1, "TLSB LEP_DERR", ptr->tlepderr);
printf(fmt1, "TLSB INTRMASK0", ptr->tlintrmask0);
printf(fmt1, "TLSB INTRMASK1", ptr->tlintrmask1);
printf(fmt1, "TLSB INTRSUM0", ptr->tlintrsum0);
printf(fmt1, "TLSB INTRSUM1", ptr->tlintrsum1);
printf(fmt1, "TLSB VMG", ptr->tlep_vmg);
printf("WARNING THIS IS NOT DONE YET YOU MAY GET DATA CORRUPTION");
clear_tlsb_ebits(0);
alpha_pal_wrmces(mces);
}
static void
kn8ae_softerr(unsigned long mces, unsigned long type, unsigned long logout, struct trapframe *framep)
{
int whami, cpuwerr, dof_cnt;
mc_hdr_ev5 *hdr;
mc_cc_ev5 *mptr;
struct tlsb_mchk_soft *ptr;
hdr = (mc_hdr_ev5 *) logout;
mptr = (mc_cc_ev5 *) (logout + sizeof (*hdr));
ptr = (struct tlsb_mchk_soft *)
(logout + sizeof (*hdr) + sizeof (*mptr));
whami = alpha_pal_whami();
printf("kn8ae: CPU ID %d processor correctable error\n", whami);
printf(" Machine Check Code 0x%lx\n", hdr->mcheck_code);
printf(fmt1, "EI Status", mptr->ei_stat);
printf(fmt1, "EI Address", mptr->ei_addr);
printf(fmt1, "Fill Syndrome", mptr->fill_syndrome);
printf(fmt1, "Interrupt Status Reg.", mptr->isr);
printf("\n");
dof_cnt = (ptr->rsvdheader & 0xffffffff00000000) >> 32;
cpuwerr = ptr->rsvdheader & 0xffff;
printf(fmt1, "CPU W/Error.", cpuwerr);
printf(fmt1, "DOF Count.", dof_cnt);
printf(fmt1, "TLDEV", ptr->tldev);
printf(fmt1, "TLSB Bus Error", ptr->tlber);
printf(fmt1, "TLSB Error Syndrome 0", ptr->tlesr0);
printf(fmt1, "TLSB Error Syndrome 1", ptr->tlesr1);
printf(fmt1, "TLSB Error Syndrome 2", ptr->tlesr2);
printf(fmt1, "TLSB Error Syndrome 3", ptr->tlesr3);
clear_tlsb_ebits(1);
alpha_pal_wrmces(mces);
}
void
kn8ae_mcheck(unsigned long mces, unsigned long type, unsigned long logout, struct trapframe *framep)
{
struct mchkinfo *mcp;
struct tlsb_mchk_fatal mcs[TLSB_NODE_MAX+1], *ptr;
mc_hdr_ev5 *hdr;
mc_uc_ev5 *mptr;
mcp = &curcpu()->ci_mcinfo;
if (mcp->mc_expected) {
machine_check(mces, framep, type, logout);
return;
}
ptr = NULL;
memset(mcs, 0, sizeof (mcs));
hdr = (mc_hdr_ev5 *) logout;
mptr = (mc_uc_ev5 *) (logout + sizeof (*hdr));
if (type == ALPHA_SYS_MCHECK) {
#if 0
int get_lsb_regs = 0;
int get_dwlpx_regs = 0;
#endif
ptr = (struct tlsb_mchk_fatal *)
(logout + sizeof (*hdr) + sizeof (*mptr));
#if 0
if (ptr->tlepaerr & TLEPAERR_WSPC_RD) {
get_dwlpx_regs++;
}
if ((ptr->tlepaerr & TLEPAERR_IBOX_TMO) &&
(mptr->ic_perr_stat & EV5_IC_PERR_IBOXTMO) &&
(ptr->tlepderr & TLEPDERR_GBTMO)) {
get_dwlpx_regs++;
}
#endif
} else {
}
ev5_logout_print(hdr, mptr);
if (type == ALPHA_SYS_MCHECK) {
if (ptr->tlepaerr & TLEPAERR_WSPC_RD) {
printf("\tWSPC READ error\n");
}
if ((ptr->tlepaerr & TLEPAERR_IBOX_TMO) &&
(mptr->ic_perr_stat & EV5_IC_PERR_IBOXTMO) &&
(ptr->tlepderr & TLEPDERR_GBTMO)) {
printf ("\tWSPC IBOX timeout detected\n");
}
#ifdef DIAGNOSTIC
printf(fmt1, "TLDEV", ptr->tldev);
printf(fmt1, "TLSB Bus Error", ptr->tlber);
printf(fmt1, "TLSB CNR", ptr->tlcnr);
printf(fmt1, "TLSB VID", ptr->tlvid);
printf(fmt1, "TLSB Error Syndrome 0", ptr->tlesr0);
printf(fmt1, "TLSB Error Syndrome 1", ptr->tlesr1);
printf(fmt1, "TLSB Error Syndrome 2", ptr->tlesr2);
printf(fmt1, "TLSB Error Syndrome 3", ptr->tlesr3);
printf(fmt1, "TLSB LEP_AERR", ptr->tlepaerr);
printf(fmt1, "TLSB MODCONF", ptr->tlmodconfig);
printf(fmt1, "TLSB LEP_MERR", ptr->tlepmerr);
printf(fmt1, "TLSB LEP_DERR", ptr->tlepderr);
printf(fmt1, "TLSB INTRMASK0", ptr->tlintrmask0);
printf(fmt1, "TLSB INTRMASK1", ptr->tlintrmask1);
printf(fmt1, "TLSB INTRSUM0", ptr->tlintrsum0);
printf(fmt1, "TLSB INTRSUM1", ptr->tlintrsum1);
printf(fmt1, "TLSB VMG", ptr->tlep_vmg);
#endif
} else {
}
clear_tlsb_ebits(0);
machine_check(mces, framep, type, logout);
}
static void
dec_kn8ae_mcheck_handler(unsigned long mces, struct trapframe *framep, unsigned long vector, unsigned long param)
{
switch (vector) {
case ALPHA_SYS_ERROR:
kn8ae_harderr(mces, vector, param, framep);
break;
case ALPHA_PROC_ERROR:
kn8ae_softerr(mces, vector, param, framep);
break;
case ALPHA_SYS_MCHECK:
case ALPHA_PROC_MCHECK:
kn8ae_mcheck(mces, vector, param, framep);
break;
default:
printf("KN8AE_MCHECK: unknown check vector 0x%lx\n", vector);
machine_check(mces, framep, vector, param);
break;
}
}