162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Bus error event handling code for 5000-series systems equipped 462306a36Sopenharmony_ci * with parity error detection logic, i.e. DECstation/DECsystem 562306a36Sopenharmony_ci * 5000/120, /125, /133 (KN02-BA), 5000/150 (KN04-BA) and Personal 662306a36Sopenharmony_ci * DECstation/DECsystem 5000/20, /25, /33 (KN02-CA), 5000/50 762306a36Sopenharmony_ci * (KN04-CA) systems. 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Copyright (c) 2005 Maciej W. Rozycki 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/init.h> 1362306a36Sopenharmony_ci#include <linux/interrupt.h> 1462306a36Sopenharmony_ci#include <linux/kernel.h> 1562306a36Sopenharmony_ci#include <linux/types.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include <asm/addrspace.h> 1862306a36Sopenharmony_ci#include <asm/cpu-type.h> 1962306a36Sopenharmony_ci#include <asm/irq_regs.h> 2062306a36Sopenharmony_ci#include <asm/ptrace.h> 2162306a36Sopenharmony_ci#include <asm/traps.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#include <asm/dec/kn02ca.h> 2462306a36Sopenharmony_ci#include <asm/dec/kn02xa.h> 2562306a36Sopenharmony_ci#include <asm/dec/kn05.h> 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistatic inline void dec_kn02xa_be_ack(void) 2862306a36Sopenharmony_ci{ 2962306a36Sopenharmony_ci volatile u32 *mer = (void *)CKSEG1ADDR(KN02XA_MER); 3062306a36Sopenharmony_ci volatile u32 *mem_intr = (void *)CKSEG1ADDR(KN02XA_MEM_INTR); 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci *mer = KN02CA_MER_INTR; /* Clear errors; keep the ARC IRQ. */ 3362306a36Sopenharmony_ci *mem_intr = 0; /* Any write clears the bus IRQ. */ 3462306a36Sopenharmony_ci iob(); 3562306a36Sopenharmony_ci} 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic int dec_kn02xa_be_backend(struct pt_regs *regs, int is_fixup, 3862306a36Sopenharmony_ci int invoker) 3962306a36Sopenharmony_ci{ 4062306a36Sopenharmony_ci volatile u32 *kn02xa_mer = (void *)CKSEG1ADDR(KN02XA_MER); 4162306a36Sopenharmony_ci volatile u32 *kn02xa_ear = (void *)CKSEG1ADDR(KN02XA_EAR); 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci static const char excstr[] = "exception"; 4462306a36Sopenharmony_ci static const char intstr[] = "interrupt"; 4562306a36Sopenharmony_ci static const char cpustr[] = "CPU"; 4662306a36Sopenharmony_ci static const char mreadstr[] = "memory read"; 4762306a36Sopenharmony_ci static const char readstr[] = "read"; 4862306a36Sopenharmony_ci static const char writestr[] = "write"; 4962306a36Sopenharmony_ci static const char timestr[] = "timeout"; 5062306a36Sopenharmony_ci static const char paritystr[] = "parity error"; 5162306a36Sopenharmony_ci static const char lanestat[][4] = { " OK", "BAD" }; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci const char *kind, *agent, *cycle, *event; 5462306a36Sopenharmony_ci unsigned long address; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci u32 mer = *kn02xa_mer; 5762306a36Sopenharmony_ci u32 ear = *kn02xa_ear; 5862306a36Sopenharmony_ci int action = MIPS_BE_FATAL; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci /* Ack ASAP, so that any subsequent errors get caught. */ 6162306a36Sopenharmony_ci dec_kn02xa_be_ack(); 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci kind = invoker ? intstr : excstr; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci /* No DMA errors? */ 6662306a36Sopenharmony_ci agent = cpustr; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci address = ear & KN02XA_EAR_ADDRESS; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci /* Low 256MB is decoded as memory, high -- as TC. */ 7162306a36Sopenharmony_ci if (address < 0x10000000) { 7262306a36Sopenharmony_ci cycle = mreadstr; 7362306a36Sopenharmony_ci event = paritystr; 7462306a36Sopenharmony_ci } else { 7562306a36Sopenharmony_ci cycle = invoker ? writestr : readstr; 7662306a36Sopenharmony_ci event = timestr; 7762306a36Sopenharmony_ci } 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci if (is_fixup) 8062306a36Sopenharmony_ci action = MIPS_BE_FIXUP; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci if (action != MIPS_BE_FIXUP) 8362306a36Sopenharmony_ci printk(KERN_ALERT "Bus error %s: %s %s %s at %#010lx\n", 8462306a36Sopenharmony_ci kind, agent, cycle, event, address); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci if (action != MIPS_BE_FIXUP && address < 0x10000000) 8762306a36Sopenharmony_ci printk(KERN_ALERT " Byte lane status %#3x -- " 8862306a36Sopenharmony_ci "#3: %s, #2: %s, #1: %s, #0: %s\n", 8962306a36Sopenharmony_ci (mer & KN02XA_MER_BYTERR) >> 8, 9062306a36Sopenharmony_ci lanestat[(mer & KN02XA_MER_BYTERR_3) != 0], 9162306a36Sopenharmony_ci lanestat[(mer & KN02XA_MER_BYTERR_2) != 0], 9262306a36Sopenharmony_ci lanestat[(mer & KN02XA_MER_BYTERR_1) != 0], 9362306a36Sopenharmony_ci lanestat[(mer & KN02XA_MER_BYTERR_0) != 0]); 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci return action; 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ciint dec_kn02xa_be_handler(struct pt_regs *regs, int is_fixup) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci return dec_kn02xa_be_backend(regs, is_fixup, 0); 10162306a36Sopenharmony_ci} 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ciirqreturn_t dec_kn02xa_be_interrupt(int irq, void *dev_id) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci struct pt_regs *regs = get_irq_regs(); 10662306a36Sopenharmony_ci int action = dec_kn02xa_be_backend(regs, 0, 1); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci if (action == MIPS_BE_DISCARD) 10962306a36Sopenharmony_ci return IRQ_HANDLED; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci /* 11262306a36Sopenharmony_ci * FIXME: Find the affected processes and kill them, otherwise 11362306a36Sopenharmony_ci * we must die. 11462306a36Sopenharmony_ci * 11562306a36Sopenharmony_ci * The interrupt is asynchronously delivered thus EPC and RA 11662306a36Sopenharmony_ci * may be irrelevant, but are printed for a reference. 11762306a36Sopenharmony_ci */ 11862306a36Sopenharmony_ci printk(KERN_ALERT "Fatal bus interrupt, epc == %08lx, ra == %08lx\n", 11962306a36Sopenharmony_ci regs->cp0_epc, regs->regs[31]); 12062306a36Sopenharmony_ci die("Unrecoverable bus error", regs); 12162306a36Sopenharmony_ci} 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_civoid __init dec_kn02xa_be_init(void) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci volatile u32 *mbcs = (void *)CKSEG1ADDR(KN4K_SLOT_BASE + KN4K_MB_CSR); 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci /* For KN04 we need to make sure EE (?) is enabled in the MB. */ 12962306a36Sopenharmony_ci if (current_cpu_type() == CPU_R4000SC) 13062306a36Sopenharmony_ci *mbcs |= KN4K_MB_CSR_EE; 13162306a36Sopenharmony_ci fast_iob(); 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci /* Clear any leftover errors from the firmware. */ 13462306a36Sopenharmony_ci dec_kn02xa_be_ack(); 13562306a36Sopenharmony_ci} 136