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