162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * ip28-berr.c: Bus error handling. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2002, 2003 Ladislav Michl (ladis@linux-mips.org) 662306a36Sopenharmony_ci * Copyright (C) 2005 Peter Fuerst (pf@net.alphadv.de) - IP28 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/init.h> 1062306a36Sopenharmony_ci#include <linux/kernel.h> 1162306a36Sopenharmony_ci#include <linux/mm.h> 1262306a36Sopenharmony_ci#include <linux/sched.h> 1362306a36Sopenharmony_ci#include <linux/sched/debug.h> 1462306a36Sopenharmony_ci#include <linux/sched/signal.h> 1562306a36Sopenharmony_ci#include <linux/seq_file.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include <asm/addrspace.h> 1862306a36Sopenharmony_ci#include <asm/traps.h> 1962306a36Sopenharmony_ci#include <asm/branch.h> 2062306a36Sopenharmony_ci#include <asm/irq_regs.h> 2162306a36Sopenharmony_ci#include <asm/sgi/mc.h> 2262306a36Sopenharmony_ci#include <asm/sgi/hpc3.h> 2362306a36Sopenharmony_ci#include <asm/sgi/ioc.h> 2462306a36Sopenharmony_ci#include <asm/sgi/ip22.h> 2562306a36Sopenharmony_ci#include <asm/r4kcache.h> 2662306a36Sopenharmony_ci#include <linux/uaccess.h> 2762306a36Sopenharmony_ci#include <asm/bootinfo.h> 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_cistatic unsigned int count_be_is_fixup; 3062306a36Sopenharmony_cistatic unsigned int count_be_handler; 3162306a36Sopenharmony_cistatic unsigned int count_be_interrupt; 3262306a36Sopenharmony_cistatic int debug_be_interrupt; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_cistatic unsigned int cpu_err_stat; /* Status reg for CPU */ 3562306a36Sopenharmony_cistatic unsigned int gio_err_stat; /* Status reg for GIO */ 3662306a36Sopenharmony_cistatic unsigned int cpu_err_addr; /* Error address reg for CPU */ 3762306a36Sopenharmony_cistatic unsigned int gio_err_addr; /* Error address reg for GIO */ 3862306a36Sopenharmony_cistatic unsigned int extio_stat; 3962306a36Sopenharmony_cistatic unsigned int hpc3_berr_stat; /* Bus error interrupt status */ 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistruct hpc3_stat { 4262306a36Sopenharmony_ci unsigned long addr; 4362306a36Sopenharmony_ci unsigned int ctrl; 4462306a36Sopenharmony_ci unsigned int cbp; 4562306a36Sopenharmony_ci unsigned int ndptr; 4662306a36Sopenharmony_ci}; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistatic struct { 4962306a36Sopenharmony_ci struct hpc3_stat pbdma[8]; 5062306a36Sopenharmony_ci struct hpc3_stat scsi[2]; 5162306a36Sopenharmony_ci struct hpc3_stat ethrx, ethtx; 5262306a36Sopenharmony_ci} hpc3; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cistatic struct { 5562306a36Sopenharmony_ci unsigned long err_addr; 5662306a36Sopenharmony_ci struct { 5762306a36Sopenharmony_ci u32 lo; 5862306a36Sopenharmony_ci u32 hi; 5962306a36Sopenharmony_ci } tags[1][2], tagd[4][2], tagi[4][2]; /* Way 0/1 */ 6062306a36Sopenharmony_ci} cache_tags; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_cistatic inline void save_cache_tags(unsigned busaddr) 6362306a36Sopenharmony_ci{ 6462306a36Sopenharmony_ci unsigned long addr = CAC_BASE | busaddr; 6562306a36Sopenharmony_ci int i; 6662306a36Sopenharmony_ci cache_tags.err_addr = addr; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci /* 6962306a36Sopenharmony_ci * Starting with a bus-address, save secondary cache (indexed by 7062306a36Sopenharmony_ci * PA[23..18:7..6]) tags first. 7162306a36Sopenharmony_ci */ 7262306a36Sopenharmony_ci addr &= ~1L; 7362306a36Sopenharmony_ci#define tag cache_tags.tags[0] 7462306a36Sopenharmony_ci cache_op(Index_Load_Tag_S, addr); 7562306a36Sopenharmony_ci tag[0].lo = read_c0_taglo(); /* PA[35:18], VA[13:12] */ 7662306a36Sopenharmony_ci tag[0].hi = read_c0_taghi(); /* PA[39:36] */ 7762306a36Sopenharmony_ci cache_op(Index_Load_Tag_S, addr | 1L); 7862306a36Sopenharmony_ci tag[1].lo = read_c0_taglo(); /* PA[35:18], VA[13:12] */ 7962306a36Sopenharmony_ci tag[1].hi = read_c0_taghi(); /* PA[39:36] */ 8062306a36Sopenharmony_ci#undef tag 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci /* 8362306a36Sopenharmony_ci * Save all primary data cache (indexed by VA[13:5]) tags which 8462306a36Sopenharmony_ci * might fit to this bus-address, knowing that VA[11:0] == PA[11:0]. 8562306a36Sopenharmony_ci * Saving all tags and evaluating them later is easier and safer 8662306a36Sopenharmony_ci * than relying on VA[13:12] from the secondary cache tags to pick 8762306a36Sopenharmony_ci * matching primary tags here already. 8862306a36Sopenharmony_ci */ 8962306a36Sopenharmony_ci addr &= (0xffL << 56) | ((1 << 12) - 1); 9062306a36Sopenharmony_ci#define tag cache_tags.tagd[i] 9162306a36Sopenharmony_ci for (i = 0; i < 4; ++i, addr += (1 << 12)) { 9262306a36Sopenharmony_ci cache_op(Index_Load_Tag_D, addr); 9362306a36Sopenharmony_ci tag[0].lo = read_c0_taglo(); /* PA[35:12] */ 9462306a36Sopenharmony_ci tag[0].hi = read_c0_taghi(); /* PA[39:36] */ 9562306a36Sopenharmony_ci cache_op(Index_Load_Tag_D, addr | 1L); 9662306a36Sopenharmony_ci tag[1].lo = read_c0_taglo(); /* PA[35:12] */ 9762306a36Sopenharmony_ci tag[1].hi = read_c0_taghi(); /* PA[39:36] */ 9862306a36Sopenharmony_ci } 9962306a36Sopenharmony_ci#undef tag 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci /* 10262306a36Sopenharmony_ci * Save primary instruction cache (indexed by VA[13:6]) tags 10362306a36Sopenharmony_ci * the same way. 10462306a36Sopenharmony_ci */ 10562306a36Sopenharmony_ci addr &= (0xffL << 56) | ((1 << 12) - 1); 10662306a36Sopenharmony_ci#define tag cache_tags.tagi[i] 10762306a36Sopenharmony_ci for (i = 0; i < 4; ++i, addr += (1 << 12)) { 10862306a36Sopenharmony_ci cache_op(Index_Load_Tag_I, addr); 10962306a36Sopenharmony_ci tag[0].lo = read_c0_taglo(); /* PA[35:12] */ 11062306a36Sopenharmony_ci tag[0].hi = read_c0_taghi(); /* PA[39:36] */ 11162306a36Sopenharmony_ci cache_op(Index_Load_Tag_I, addr | 1L); 11262306a36Sopenharmony_ci tag[1].lo = read_c0_taglo(); /* PA[35:12] */ 11362306a36Sopenharmony_ci tag[1].hi = read_c0_taghi(); /* PA[39:36] */ 11462306a36Sopenharmony_ci } 11562306a36Sopenharmony_ci#undef tag 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci#define GIO_ERRMASK 0xff00 11962306a36Sopenharmony_ci#define CPU_ERRMASK 0x3f00 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_cistatic void save_and_clear_buserr(void) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci int i; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci /* save status registers */ 12662306a36Sopenharmony_ci cpu_err_addr = sgimc->cerr; 12762306a36Sopenharmony_ci cpu_err_stat = sgimc->cstat; 12862306a36Sopenharmony_ci gio_err_addr = sgimc->gerr; 12962306a36Sopenharmony_ci gio_err_stat = sgimc->gstat; 13062306a36Sopenharmony_ci extio_stat = sgioc->extio; 13162306a36Sopenharmony_ci hpc3_berr_stat = hpc3c0->bestat; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci hpc3.scsi[0].addr = (unsigned long)&hpc3c0->scsi_chan0; 13462306a36Sopenharmony_ci hpc3.scsi[0].ctrl = hpc3c0->scsi_chan0.ctrl; /* HPC3_SCTRL_ACTIVE ? */ 13562306a36Sopenharmony_ci hpc3.scsi[0].cbp = hpc3c0->scsi_chan0.cbptr; 13662306a36Sopenharmony_ci hpc3.scsi[0].ndptr = hpc3c0->scsi_chan0.ndptr; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci hpc3.scsi[1].addr = (unsigned long)&hpc3c0->scsi_chan1; 13962306a36Sopenharmony_ci hpc3.scsi[1].ctrl = hpc3c0->scsi_chan1.ctrl; /* HPC3_SCTRL_ACTIVE ? */ 14062306a36Sopenharmony_ci hpc3.scsi[1].cbp = hpc3c0->scsi_chan1.cbptr; 14162306a36Sopenharmony_ci hpc3.scsi[1].ndptr = hpc3c0->scsi_chan1.ndptr; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci hpc3.ethrx.addr = (unsigned long)&hpc3c0->ethregs.rx_cbptr; 14462306a36Sopenharmony_ci hpc3.ethrx.ctrl = hpc3c0->ethregs.rx_ctrl; /* HPC3_ERXCTRL_ACTIVE ? */ 14562306a36Sopenharmony_ci hpc3.ethrx.cbp = hpc3c0->ethregs.rx_cbptr; 14662306a36Sopenharmony_ci hpc3.ethrx.ndptr = hpc3c0->ethregs.rx_ndptr; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci hpc3.ethtx.addr = (unsigned long)&hpc3c0->ethregs.tx_cbptr; 14962306a36Sopenharmony_ci hpc3.ethtx.ctrl = hpc3c0->ethregs.tx_ctrl; /* HPC3_ETXCTRL_ACTIVE ? */ 15062306a36Sopenharmony_ci hpc3.ethtx.cbp = hpc3c0->ethregs.tx_cbptr; 15162306a36Sopenharmony_ci hpc3.ethtx.ndptr = hpc3c0->ethregs.tx_ndptr; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci for (i = 0; i < 8; ++i) { 15462306a36Sopenharmony_ci /* HPC3_PDMACTRL_ISACT ? */ 15562306a36Sopenharmony_ci hpc3.pbdma[i].addr = (unsigned long)&hpc3c0->pbdma[i]; 15662306a36Sopenharmony_ci hpc3.pbdma[i].ctrl = hpc3c0->pbdma[i].pbdma_ctrl; 15762306a36Sopenharmony_ci hpc3.pbdma[i].cbp = hpc3c0->pbdma[i].pbdma_bptr; 15862306a36Sopenharmony_ci hpc3.pbdma[i].ndptr = hpc3c0->pbdma[i].pbdma_dptr; 15962306a36Sopenharmony_ci } 16062306a36Sopenharmony_ci i = 0; 16162306a36Sopenharmony_ci if (gio_err_stat & CPU_ERRMASK) 16262306a36Sopenharmony_ci i = gio_err_addr; 16362306a36Sopenharmony_ci if (cpu_err_stat & CPU_ERRMASK) 16462306a36Sopenharmony_ci i = cpu_err_addr; 16562306a36Sopenharmony_ci save_cache_tags(i); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci sgimc->cstat = sgimc->gstat = 0; 16862306a36Sopenharmony_ci} 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_cistatic void print_cache_tags(void) 17162306a36Sopenharmony_ci{ 17262306a36Sopenharmony_ci u32 scb, scw; 17362306a36Sopenharmony_ci int i; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci printk(KERN_ERR "Cache tags @ %08x:\n", (unsigned)cache_tags.err_addr); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci /* PA[31:12] shifted to PTag0 (PA[35:12]) format */ 17862306a36Sopenharmony_ci scw = (cache_tags.err_addr >> 4) & 0x0fffff00; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci scb = cache_tags.err_addr & ((1 << 12) - 1) & ~((1 << 5) - 1); 18162306a36Sopenharmony_ci for (i = 0; i < 4; ++i) { /* for each possible VA[13:12] value */ 18262306a36Sopenharmony_ci if ((cache_tags.tagd[i][0].lo & 0x0fffff00) != scw && 18362306a36Sopenharmony_ci (cache_tags.tagd[i][1].lo & 0x0fffff00) != scw) 18462306a36Sopenharmony_ci continue; 18562306a36Sopenharmony_ci printk(KERN_ERR 18662306a36Sopenharmony_ci "D: 0: %08x %08x, 1: %08x %08x (VA[13:5] %04x)\n", 18762306a36Sopenharmony_ci cache_tags.tagd[i][0].hi, cache_tags.tagd[i][0].lo, 18862306a36Sopenharmony_ci cache_tags.tagd[i][1].hi, cache_tags.tagd[i][1].lo, 18962306a36Sopenharmony_ci scb | (1 << 12)*i); 19062306a36Sopenharmony_ci } 19162306a36Sopenharmony_ci scb = cache_tags.err_addr & ((1 << 12) - 1) & ~((1 << 6) - 1); 19262306a36Sopenharmony_ci for (i = 0; i < 4; ++i) { /* for each possible VA[13:12] value */ 19362306a36Sopenharmony_ci if ((cache_tags.tagi[i][0].lo & 0x0fffff00) != scw && 19462306a36Sopenharmony_ci (cache_tags.tagi[i][1].lo & 0x0fffff00) != scw) 19562306a36Sopenharmony_ci continue; 19662306a36Sopenharmony_ci printk(KERN_ERR 19762306a36Sopenharmony_ci "I: 0: %08x %08x, 1: %08x %08x (VA[13:6] %04x)\n", 19862306a36Sopenharmony_ci cache_tags.tagi[i][0].hi, cache_tags.tagi[i][0].lo, 19962306a36Sopenharmony_ci cache_tags.tagi[i][1].hi, cache_tags.tagi[i][1].lo, 20062306a36Sopenharmony_ci scb | (1 << 12)*i); 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci i = read_c0_config(); 20362306a36Sopenharmony_ci scb = i & (1 << 13) ? 7:6; /* scblksize = 2^[7..6] */ 20462306a36Sopenharmony_ci scw = ((i >> 16) & 7) + 19 - 1; /* scwaysize = 2^[24..19] / 2 */ 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci i = ((1 << scw) - 1) & ~((1 << scb) - 1); 20762306a36Sopenharmony_ci printk(KERN_ERR "S: 0: %08x %08x, 1: %08x %08x (PA[%u:%u] %05x)\n", 20862306a36Sopenharmony_ci cache_tags.tags[0][0].hi, cache_tags.tags[0][0].lo, 20962306a36Sopenharmony_ci cache_tags.tags[0][1].hi, cache_tags.tags[0][1].lo, 21062306a36Sopenharmony_ci scw-1, scb, i & (unsigned)cache_tags.err_addr); 21162306a36Sopenharmony_ci} 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_cistatic inline const char *cause_excode_text(int cause) 21462306a36Sopenharmony_ci{ 21562306a36Sopenharmony_ci static const char *txt[32] = 21662306a36Sopenharmony_ci { "Interrupt", 21762306a36Sopenharmony_ci "TLB modification", 21862306a36Sopenharmony_ci "TLB (load or instruction fetch)", 21962306a36Sopenharmony_ci "TLB (store)", 22062306a36Sopenharmony_ci "Address error (load or instruction fetch)", 22162306a36Sopenharmony_ci "Address error (store)", 22262306a36Sopenharmony_ci "Bus error (instruction fetch)", 22362306a36Sopenharmony_ci "Bus error (data: load or store)", 22462306a36Sopenharmony_ci "Syscall", 22562306a36Sopenharmony_ci "Breakpoint", 22662306a36Sopenharmony_ci "Reserved instruction", 22762306a36Sopenharmony_ci "Coprocessor unusable", 22862306a36Sopenharmony_ci "Arithmetic Overflow", 22962306a36Sopenharmony_ci "Trap", 23062306a36Sopenharmony_ci "14", 23162306a36Sopenharmony_ci "Floating-Point", 23262306a36Sopenharmony_ci "16", "17", "18", "19", "20", "21", "22", 23362306a36Sopenharmony_ci "Watch Hi/Lo", 23462306a36Sopenharmony_ci "24", "25", "26", "27", "28", "29", "30", "31", 23562306a36Sopenharmony_ci }; 23662306a36Sopenharmony_ci return txt[(cause & 0x7c) >> 2]; 23762306a36Sopenharmony_ci} 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_cistatic void print_buserr(const struct pt_regs *regs) 24062306a36Sopenharmony_ci{ 24162306a36Sopenharmony_ci const int field = 2 * sizeof(unsigned long); 24262306a36Sopenharmony_ci int error = 0; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci if (extio_stat & EXTIO_MC_BUSERR) { 24562306a36Sopenharmony_ci printk(KERN_ERR "MC Bus Error\n"); 24662306a36Sopenharmony_ci error |= 1; 24762306a36Sopenharmony_ci } 24862306a36Sopenharmony_ci if (extio_stat & EXTIO_HPC3_BUSERR) { 24962306a36Sopenharmony_ci printk(KERN_ERR "HPC3 Bus Error 0x%x:<id=0x%x,%s,lane=0x%x>\n", 25062306a36Sopenharmony_ci hpc3_berr_stat, 25162306a36Sopenharmony_ci (hpc3_berr_stat & HPC3_BESTAT_PIDMASK) >> 25262306a36Sopenharmony_ci HPC3_BESTAT_PIDSHIFT, 25362306a36Sopenharmony_ci (hpc3_berr_stat & HPC3_BESTAT_CTYPE) ? "PIO" : "DMA", 25462306a36Sopenharmony_ci hpc3_berr_stat & HPC3_BESTAT_BLMASK); 25562306a36Sopenharmony_ci error |= 2; 25662306a36Sopenharmony_ci } 25762306a36Sopenharmony_ci if (extio_stat & EXTIO_EISA_BUSERR) { 25862306a36Sopenharmony_ci printk(KERN_ERR "EISA Bus Error\n"); 25962306a36Sopenharmony_ci error |= 4; 26062306a36Sopenharmony_ci } 26162306a36Sopenharmony_ci if (cpu_err_stat & CPU_ERRMASK) { 26262306a36Sopenharmony_ci printk(KERN_ERR "CPU error 0x%x<%s%s%s%s%s%s> @ 0x%08x\n", 26362306a36Sopenharmony_ci cpu_err_stat, 26462306a36Sopenharmony_ci cpu_err_stat & SGIMC_CSTAT_RD ? "RD " : "", 26562306a36Sopenharmony_ci cpu_err_stat & SGIMC_CSTAT_PAR ? "PAR " : "", 26662306a36Sopenharmony_ci cpu_err_stat & SGIMC_CSTAT_ADDR ? "ADDR " : "", 26762306a36Sopenharmony_ci cpu_err_stat & SGIMC_CSTAT_SYSAD_PAR ? "SYSAD " : "", 26862306a36Sopenharmony_ci cpu_err_stat & SGIMC_CSTAT_SYSCMD_PAR ? "SYSCMD " : "", 26962306a36Sopenharmony_ci cpu_err_stat & SGIMC_CSTAT_BAD_DATA ? "BAD_DATA " : "", 27062306a36Sopenharmony_ci cpu_err_addr); 27162306a36Sopenharmony_ci error |= 8; 27262306a36Sopenharmony_ci } 27362306a36Sopenharmony_ci if (gio_err_stat & GIO_ERRMASK) { 27462306a36Sopenharmony_ci printk(KERN_ERR "GIO error 0x%x:<%s%s%s%s%s%s%s%s> @ 0x%08x\n", 27562306a36Sopenharmony_ci gio_err_stat, 27662306a36Sopenharmony_ci gio_err_stat & SGIMC_GSTAT_RD ? "RD " : "", 27762306a36Sopenharmony_ci gio_err_stat & SGIMC_GSTAT_WR ? "WR " : "", 27862306a36Sopenharmony_ci gio_err_stat & SGIMC_GSTAT_TIME ? "TIME " : "", 27962306a36Sopenharmony_ci gio_err_stat & SGIMC_GSTAT_PROM ? "PROM " : "", 28062306a36Sopenharmony_ci gio_err_stat & SGIMC_GSTAT_ADDR ? "ADDR " : "", 28162306a36Sopenharmony_ci gio_err_stat & SGIMC_GSTAT_BC ? "BC " : "", 28262306a36Sopenharmony_ci gio_err_stat & SGIMC_GSTAT_PIO_RD ? "PIO_RD " : "", 28362306a36Sopenharmony_ci gio_err_stat & SGIMC_GSTAT_PIO_WR ? "PIO_WR " : "", 28462306a36Sopenharmony_ci gio_err_addr); 28562306a36Sopenharmony_ci error |= 16; 28662306a36Sopenharmony_ci } 28762306a36Sopenharmony_ci if (!error) 28862306a36Sopenharmony_ci printk(KERN_ERR "MC: Hmm, didn't find any error condition.\n"); 28962306a36Sopenharmony_ci else { 29062306a36Sopenharmony_ci printk(KERN_ERR "CP0: config %08x, " 29162306a36Sopenharmony_ci "MC: cpuctrl0/1: %08x/%05x, giopar: %04x\n" 29262306a36Sopenharmony_ci "MC: cpu/gio_memacc: %08x/%05x, memcfg0/1: %08x/%08x\n", 29362306a36Sopenharmony_ci read_c0_config(), 29462306a36Sopenharmony_ci sgimc->cpuctrl0, sgimc->cpuctrl0, sgimc->giopar, 29562306a36Sopenharmony_ci sgimc->cmacc, sgimc->gmacc, 29662306a36Sopenharmony_ci sgimc->mconfig0, sgimc->mconfig1); 29762306a36Sopenharmony_ci print_cache_tags(); 29862306a36Sopenharmony_ci } 29962306a36Sopenharmony_ci printk(KERN_ALERT "%s, epc == %0*lx, ra == %0*lx\n", 30062306a36Sopenharmony_ci cause_excode_text(regs->cp0_cause), 30162306a36Sopenharmony_ci field, regs->cp0_epc, field, regs->regs[31]); 30262306a36Sopenharmony_ci} 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_cistatic int check_microtlb(u32 hi, u32 lo, unsigned long vaddr) 30562306a36Sopenharmony_ci{ 30662306a36Sopenharmony_ci /* This is likely rather similar to correct code ;-) */ 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci vaddr &= 0x7fffffff; /* Doc. states that top bit is ignored */ 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci /* If tlb-entry is valid and VPN-high (bits [30:21] ?) matches... */ 31162306a36Sopenharmony_ci if ((lo & 2) && (vaddr >> 21) == ((hi<<1) >> 22)) { 31262306a36Sopenharmony_ci u32 ctl = sgimc->dma_ctrl; 31362306a36Sopenharmony_ci if (ctl & 1) { 31462306a36Sopenharmony_ci unsigned int pgsz = (ctl & 2) ? 14:12; /* 16k:4k */ 31562306a36Sopenharmony_ci /* PTEIndex is VPN-low (bits [22:14]/[20:12] ?) */ 31662306a36Sopenharmony_ci unsigned long pte = (lo >> 6) << 12; /* PTEBase */ 31762306a36Sopenharmony_ci pte += 8*((vaddr >> pgsz) & 0x1ff); 31862306a36Sopenharmony_ci if (page_is_ram(PFN_DOWN(pte))) { 31962306a36Sopenharmony_ci /* 32062306a36Sopenharmony_ci * Note: Since DMA hardware does look up 32162306a36Sopenharmony_ci * translation on its own, this PTE *must* 32262306a36Sopenharmony_ci * match the TLB/EntryLo-register format ! 32362306a36Sopenharmony_ci */ 32462306a36Sopenharmony_ci unsigned long a = *(unsigned long *) 32562306a36Sopenharmony_ci PHYS_TO_XKSEG_UNCACHED(pte); 32662306a36Sopenharmony_ci a = (a & 0x3f) << 6; /* PFN */ 32762306a36Sopenharmony_ci a += vaddr & ((1 << pgsz) - 1); 32862306a36Sopenharmony_ci return cpu_err_addr == a; 32962306a36Sopenharmony_ci } 33062306a36Sopenharmony_ci } 33162306a36Sopenharmony_ci } 33262306a36Sopenharmony_ci return 0; 33362306a36Sopenharmony_ci} 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_cistatic int check_vdma_memaddr(void) 33662306a36Sopenharmony_ci{ 33762306a36Sopenharmony_ci if (cpu_err_stat & CPU_ERRMASK) { 33862306a36Sopenharmony_ci u32 a = sgimc->maddronly; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci if (!(sgimc->dma_ctrl & 0x100)) /* Xlate-bit clear ? */ 34162306a36Sopenharmony_ci return cpu_err_addr == a; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci if (check_microtlb(sgimc->dtlb_hi0, sgimc->dtlb_lo0, a) || 34462306a36Sopenharmony_ci check_microtlb(sgimc->dtlb_hi1, sgimc->dtlb_lo1, a) || 34562306a36Sopenharmony_ci check_microtlb(sgimc->dtlb_hi2, sgimc->dtlb_lo2, a) || 34662306a36Sopenharmony_ci check_microtlb(sgimc->dtlb_hi3, sgimc->dtlb_lo3, a)) 34762306a36Sopenharmony_ci return 1; 34862306a36Sopenharmony_ci } 34962306a36Sopenharmony_ci return 0; 35062306a36Sopenharmony_ci} 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_cistatic int check_vdma_gioaddr(void) 35362306a36Sopenharmony_ci{ 35462306a36Sopenharmony_ci if (gio_err_stat & GIO_ERRMASK) { 35562306a36Sopenharmony_ci u32 a = sgimc->gio_dma_trans; 35662306a36Sopenharmony_ci a = (sgimc->gmaddronly & ~a) | (sgimc->gio_dma_sbits & a); 35762306a36Sopenharmony_ci return gio_err_addr == a; 35862306a36Sopenharmony_ci } 35962306a36Sopenharmony_ci return 0; 36062306a36Sopenharmony_ci} 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci/* 36362306a36Sopenharmony_ci * MC sends an interrupt whenever bus or parity errors occur. In addition, 36462306a36Sopenharmony_ci * if the error happened during a CPU read, it also asserts the bus error 36562306a36Sopenharmony_ci * pin on the R4K. Code in bus error handler save the MC bus error registers 36662306a36Sopenharmony_ci * and then clear the interrupt when this happens. 36762306a36Sopenharmony_ci */ 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_cistatic int ip28_be_interrupt(const struct pt_regs *regs) 37062306a36Sopenharmony_ci{ 37162306a36Sopenharmony_ci int i; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci save_and_clear_buserr(); 37462306a36Sopenharmony_ci /* 37562306a36Sopenharmony_ci * Try to find out, whether we got here by a mispredicted speculative 37662306a36Sopenharmony_ci * load/store operation. If so, it's not fatal, we can go on. 37762306a36Sopenharmony_ci */ 37862306a36Sopenharmony_ci /* Any cause other than "Interrupt" (ExcCode 0) is fatal. */ 37962306a36Sopenharmony_ci if (regs->cp0_cause & CAUSEF_EXCCODE) 38062306a36Sopenharmony_ci goto mips_be_fatal; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci /* Any cause other than "Bus error interrupt" (IP6) is weird. */ 38362306a36Sopenharmony_ci if ((regs->cp0_cause & CAUSEF_IP6) != CAUSEF_IP6) 38462306a36Sopenharmony_ci goto mips_be_fatal; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci if (extio_stat & (EXTIO_HPC3_BUSERR | EXTIO_EISA_BUSERR)) 38762306a36Sopenharmony_ci goto mips_be_fatal; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci /* Any state other than "Memory bus error" is fatal. */ 39062306a36Sopenharmony_ci if (cpu_err_stat & CPU_ERRMASK & ~SGIMC_CSTAT_ADDR) 39162306a36Sopenharmony_ci goto mips_be_fatal; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci /* GIO errors other than timeouts are fatal */ 39462306a36Sopenharmony_ci if (gio_err_stat & GIO_ERRMASK & ~SGIMC_GSTAT_TIME) 39562306a36Sopenharmony_ci goto mips_be_fatal; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci /* 39862306a36Sopenharmony_ci * Now we have an asynchronous bus error, speculatively or DMA caused. 39962306a36Sopenharmony_ci * Need to search all DMA descriptors for the error address. 40062306a36Sopenharmony_ci */ 40162306a36Sopenharmony_ci for (i = 0; i < sizeof(hpc3)/sizeof(struct hpc3_stat); ++i) { 40262306a36Sopenharmony_ci struct hpc3_stat *hp = (struct hpc3_stat *)&hpc3 + i; 40362306a36Sopenharmony_ci if ((cpu_err_stat & CPU_ERRMASK) && 40462306a36Sopenharmony_ci (cpu_err_addr == hp->ndptr || cpu_err_addr == hp->cbp)) 40562306a36Sopenharmony_ci break; 40662306a36Sopenharmony_ci if ((gio_err_stat & GIO_ERRMASK) && 40762306a36Sopenharmony_ci (gio_err_addr == hp->ndptr || gio_err_addr == hp->cbp)) 40862306a36Sopenharmony_ci break; 40962306a36Sopenharmony_ci } 41062306a36Sopenharmony_ci if (i < sizeof(hpc3)/sizeof(struct hpc3_stat)) { 41162306a36Sopenharmony_ci struct hpc3_stat *hp = (struct hpc3_stat *)&hpc3 + i; 41262306a36Sopenharmony_ci printk(KERN_ERR "at DMA addresses: HPC3 @ %08lx:" 41362306a36Sopenharmony_ci " ctl %08x, ndp %08x, cbp %08x\n", 41462306a36Sopenharmony_ci CPHYSADDR(hp->addr), hp->ctrl, hp->ndptr, hp->cbp); 41562306a36Sopenharmony_ci goto mips_be_fatal; 41662306a36Sopenharmony_ci } 41762306a36Sopenharmony_ci /* Check MC's virtual DMA stuff. */ 41862306a36Sopenharmony_ci if (check_vdma_memaddr()) { 41962306a36Sopenharmony_ci printk(KERN_ERR "at GIO DMA: mem address 0x%08x.\n", 42062306a36Sopenharmony_ci sgimc->maddronly); 42162306a36Sopenharmony_ci goto mips_be_fatal; 42262306a36Sopenharmony_ci } 42362306a36Sopenharmony_ci if (check_vdma_gioaddr()) { 42462306a36Sopenharmony_ci printk(KERN_ERR "at GIO DMA: gio address 0x%08x.\n", 42562306a36Sopenharmony_ci sgimc->gmaddronly); 42662306a36Sopenharmony_ci goto mips_be_fatal; 42762306a36Sopenharmony_ci } 42862306a36Sopenharmony_ci /* A speculative bus error... */ 42962306a36Sopenharmony_ci if (debug_be_interrupt) { 43062306a36Sopenharmony_ci print_buserr(regs); 43162306a36Sopenharmony_ci printk(KERN_ERR "discarded!\n"); 43262306a36Sopenharmony_ci } 43362306a36Sopenharmony_ci return MIPS_BE_DISCARD; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_cimips_be_fatal: 43662306a36Sopenharmony_ci print_buserr(regs); 43762306a36Sopenharmony_ci return MIPS_BE_FATAL; 43862306a36Sopenharmony_ci} 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_civoid ip22_be_interrupt(int irq) 44162306a36Sopenharmony_ci{ 44262306a36Sopenharmony_ci struct pt_regs *regs = get_irq_regs(); 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci count_be_interrupt++; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci if (ip28_be_interrupt(regs) != MIPS_BE_DISCARD) { 44762306a36Sopenharmony_ci /* Assume it would be too dangerous to continue ... */ 44862306a36Sopenharmony_ci die_if_kernel("Oops", regs); 44962306a36Sopenharmony_ci force_sig(SIGBUS); 45062306a36Sopenharmony_ci } else if (debug_be_interrupt) 45162306a36Sopenharmony_ci show_regs(regs); 45262306a36Sopenharmony_ci} 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_cistatic int ip28_be_handler(struct pt_regs *regs, int is_fixup) 45562306a36Sopenharmony_ci{ 45662306a36Sopenharmony_ci /* 45762306a36Sopenharmony_ci * We arrive here only in the unusual case of do_be() invocation, 45862306a36Sopenharmony_ci * i.e. by a bus error exception without a bus error interrupt. 45962306a36Sopenharmony_ci */ 46062306a36Sopenharmony_ci if (is_fixup) { 46162306a36Sopenharmony_ci count_be_is_fixup++; 46262306a36Sopenharmony_ci save_and_clear_buserr(); 46362306a36Sopenharmony_ci return MIPS_BE_FIXUP; 46462306a36Sopenharmony_ci } 46562306a36Sopenharmony_ci count_be_handler++; 46662306a36Sopenharmony_ci return ip28_be_interrupt(regs); 46762306a36Sopenharmony_ci} 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_civoid __init ip22_be_init(void) 47062306a36Sopenharmony_ci{ 47162306a36Sopenharmony_ci mips_set_be_handler(ip28_be_handler); 47262306a36Sopenharmony_ci} 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ciint ip28_show_be_info(struct seq_file *m) 47562306a36Sopenharmony_ci{ 47662306a36Sopenharmony_ci seq_printf(m, "IP28 be fixups\t\t: %u\n", count_be_is_fixup); 47762306a36Sopenharmony_ci seq_printf(m, "IP28 be interrupts\t: %u\n", count_be_interrupt); 47862306a36Sopenharmony_ci seq_printf(m, "IP28 be handler\t\t: %u\n", count_be_handler); 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci return 0; 48162306a36Sopenharmony_ci} 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_cistatic int __init debug_be_setup(char *str) 48462306a36Sopenharmony_ci{ 48562306a36Sopenharmony_ci debug_be_interrupt++; 48662306a36Sopenharmony_ci return 1; 48762306a36Sopenharmony_ci} 48862306a36Sopenharmony_ci__setup("ip28_debug_be", debug_be_setup); 489