1*2874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
269c75fb4SMaciej W. Rozycki /*
369c75fb4SMaciej W. Rozycki * Bus error event handling code for DECstation/DECsystem 3100
469c75fb4SMaciej W. Rozycki * and 2100 (KN01) systems equipped with parity error detection
569c75fb4SMaciej W. Rozycki * logic.
669c75fb4SMaciej W. Rozycki *
769c75fb4SMaciej W. Rozycki * Copyright (c) 2005 Maciej W. Rozycki
869c75fb4SMaciej W. Rozycki */
969c75fb4SMaciej W. Rozycki
1069c75fb4SMaciej W. Rozycki #include <linux/init.h>
1169c75fb4SMaciej W. Rozycki #include <linux/interrupt.h>
1269c75fb4SMaciej W. Rozycki #include <linux/kernel.h>
1369c75fb4SMaciej W. Rozycki #include <linux/spinlock.h>
1469c75fb4SMaciej W. Rozycki #include <linux/types.h>
1569c75fb4SMaciej W. Rozycki
1669c75fb4SMaciej W. Rozycki #include <asm/inst.h>
1749afb1f6SMaciej W. Rozycki #include <asm/irq_regs.h>
1869c75fb4SMaciej W. Rozycki #include <asm/mipsregs.h>
1969c75fb4SMaciej W. Rozycki #include <asm/page.h>
2049afb1f6SMaciej W. Rozycki #include <asm/ptrace.h>
2169c75fb4SMaciej W. Rozycki #include <asm/traps.h>
227c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
2369c75fb4SMaciej W. Rozycki
2469c75fb4SMaciej W. Rozycki #include <asm/dec/kn01.h>
2569c75fb4SMaciej W. Rozycki
2669c75fb4SMaciej W. Rozycki
2769c75fb4SMaciej W. Rozycki /* CP0 hazard avoidance. */
2869c75fb4SMaciej W. Rozycki #define BARRIER \
2969c75fb4SMaciej W. Rozycki __asm__ __volatile__( \
3069c75fb4SMaciej W. Rozycki ".set push\n\t" \
3169c75fb4SMaciej W. Rozycki ".set noreorder\n\t" \
3269c75fb4SMaciej W. Rozycki "nop\n\t" \
3369c75fb4SMaciej W. Rozycki ".set pop\n\t")
3469c75fb4SMaciej W. Rozycki
3569c75fb4SMaciej W. Rozycki /*
3669c75fb4SMaciej W. Rozycki * Bits 7:0 of the Control Register are write-only -- the
3769c75fb4SMaciej W. Rozycki * corresponding bits of the Status Register have a different
3869c75fb4SMaciej W. Rozycki * meaning. Hence we use a cache. It speeds up things a bit
3969c75fb4SMaciej W. Rozycki * as well.
4069c75fb4SMaciej W. Rozycki *
4169c75fb4SMaciej W. Rozycki * There is no default value -- it has to be initialized.
4269c75fb4SMaciej W. Rozycki */
4369c75fb4SMaciej W. Rozycki u16 cached_kn01_csr;
4432baba2fSRalf Baechle static DEFINE_RAW_SPINLOCK(kn01_lock);
4569c75fb4SMaciej W. Rozycki
4669c75fb4SMaciej W. Rozycki
dec_kn01_be_ack(void)4769c75fb4SMaciej W. Rozycki static inline void dec_kn01_be_ack(void)
4869c75fb4SMaciej W. Rozycki {
49a5fc9c0bSMaciej W. Rozycki volatile u16 *csr = (void *)CKSEG1ADDR(KN01_SLOT_BASE + KN01_CSR);
5069c75fb4SMaciej W. Rozycki unsigned long flags;
5169c75fb4SMaciej W. Rozycki
5232baba2fSRalf Baechle raw_spin_lock_irqsave(&kn01_lock, flags);
5369c75fb4SMaciej W. Rozycki
5469c75fb4SMaciej W. Rozycki *csr = cached_kn01_csr | KN01_CSR_MEMERR; /* Clear bus IRQ. */
5569c75fb4SMaciej W. Rozycki iob();
5669c75fb4SMaciej W. Rozycki
5732baba2fSRalf Baechle raw_spin_unlock_irqrestore(&kn01_lock, flags);
5869c75fb4SMaciej W. Rozycki }
5969c75fb4SMaciej W. Rozycki
dec_kn01_be_backend(struct pt_regs * regs,int is_fixup,int invoker)6069c75fb4SMaciej W. Rozycki static int dec_kn01_be_backend(struct pt_regs *regs, int is_fixup, int invoker)
6169c75fb4SMaciej W. Rozycki {
62a5fc9c0bSMaciej W. Rozycki volatile u32 *kn01_erraddr = (void *)CKSEG1ADDR(KN01_SLOT_BASE +
63a5fc9c0bSMaciej W. Rozycki KN01_ERRADDR);
6469c75fb4SMaciej W. Rozycki
6569c75fb4SMaciej W. Rozycki static const char excstr[] = "exception";
6669c75fb4SMaciej W. Rozycki static const char intstr[] = "interrupt";
6769c75fb4SMaciej W. Rozycki static const char cpustr[] = "CPU";
6869c75fb4SMaciej W. Rozycki static const char mreadstr[] = "memory read";
6969c75fb4SMaciej W. Rozycki static const char readstr[] = "read";
7069c75fb4SMaciej W. Rozycki static const char writestr[] = "write";
7169c75fb4SMaciej W. Rozycki static const char timestr[] = "timeout";
7269c75fb4SMaciej W. Rozycki static const char paritystr[] = "parity error";
7369c75fb4SMaciej W. Rozycki
7469c75fb4SMaciej W. Rozycki int data = regs->cp0_cause & 4;
7569c75fb4SMaciej W. Rozycki unsigned int __user *pc = (unsigned int __user *)regs->cp0_epc +
7669c75fb4SMaciej W. Rozycki ((regs->cp0_cause & CAUSEF_BD) != 0);
7769c75fb4SMaciej W. Rozycki union mips_instruction insn;
7869c75fb4SMaciej W. Rozycki unsigned long entrylo, offset;
7969c75fb4SMaciej W. Rozycki long asid, entryhi, vaddr;
8069c75fb4SMaciej W. Rozycki
8169c75fb4SMaciej W. Rozycki const char *kind, *agent, *cycle, *event;
8269c75fb4SMaciej W. Rozycki unsigned long address;
8369c75fb4SMaciej W. Rozycki
8469c75fb4SMaciej W. Rozycki u32 erraddr = *kn01_erraddr;
8569c75fb4SMaciej W. Rozycki int action = MIPS_BE_FATAL;
8669c75fb4SMaciej W. Rozycki
8769c75fb4SMaciej W. Rozycki /* Ack ASAP, so that any subsequent errors get caught. */
8869c75fb4SMaciej W. Rozycki dec_kn01_be_ack();
8969c75fb4SMaciej W. Rozycki
9069c75fb4SMaciej W. Rozycki kind = invoker ? intstr : excstr;
9169c75fb4SMaciej W. Rozycki
9269c75fb4SMaciej W. Rozycki agent = cpustr;
9369c75fb4SMaciej W. Rozycki
9469c75fb4SMaciej W. Rozycki if (invoker)
9569c75fb4SMaciej W. Rozycki address = erraddr;
9669c75fb4SMaciej W. Rozycki else {
9769c75fb4SMaciej W. Rozycki /* Bloody hardware doesn't record the address for reads... */
9869c75fb4SMaciej W. Rozycki if (data) {
9969c75fb4SMaciej W. Rozycki /* This never faults. */
10069c75fb4SMaciej W. Rozycki __get_user(insn.word, pc);
10169c75fb4SMaciej W. Rozycki vaddr = regs->regs[insn.i_format.rs] +
10269c75fb4SMaciej W. Rozycki insn.i_format.simmediate;
10369c75fb4SMaciej W. Rozycki } else
10469c75fb4SMaciej W. Rozycki vaddr = (long)pc;
10569c75fb4SMaciej W. Rozycki if (KSEGX(vaddr) == CKSEG0 || KSEGX(vaddr) == CKSEG1)
10669c75fb4SMaciej W. Rozycki address = CPHYSADDR(vaddr);
10769c75fb4SMaciej W. Rozycki else {
10869c75fb4SMaciej W. Rozycki /* Peek at what physical address the CPU used. */
10969c75fb4SMaciej W. Rozycki asid = read_c0_entryhi();
11069c75fb4SMaciej W. Rozycki entryhi = asid & (PAGE_SIZE - 1);
11169c75fb4SMaciej W. Rozycki entryhi |= vaddr & ~(PAGE_SIZE - 1);
11269c75fb4SMaciej W. Rozycki write_c0_entryhi(entryhi);
11369c75fb4SMaciej W. Rozycki BARRIER;
11469c75fb4SMaciej W. Rozycki tlb_probe();
11569c75fb4SMaciej W. Rozycki /* No need to check for presence. */
11669c75fb4SMaciej W. Rozycki tlb_read();
11769c75fb4SMaciej W. Rozycki entrylo = read_c0_entrylo0();
11869c75fb4SMaciej W. Rozycki write_c0_entryhi(asid);
11969c75fb4SMaciej W. Rozycki offset = vaddr & (PAGE_SIZE - 1);
12069c75fb4SMaciej W. Rozycki address = (entrylo & ~(PAGE_SIZE - 1)) | offset;
12169c75fb4SMaciej W. Rozycki }
12269c75fb4SMaciej W. Rozycki }
12369c75fb4SMaciej W. Rozycki
12469c75fb4SMaciej W. Rozycki /* Treat low 256MB as memory, high -- as I/O. */
12569c75fb4SMaciej W. Rozycki if (address < 0x10000000) {
12669c75fb4SMaciej W. Rozycki cycle = mreadstr;
12769c75fb4SMaciej W. Rozycki event = paritystr;
12869c75fb4SMaciej W. Rozycki } else {
12969c75fb4SMaciej W. Rozycki cycle = invoker ? writestr : readstr;
13069c75fb4SMaciej W. Rozycki event = timestr;
13169c75fb4SMaciej W. Rozycki }
13269c75fb4SMaciej W. Rozycki
13369c75fb4SMaciej W. Rozycki if (is_fixup)
13469c75fb4SMaciej W. Rozycki action = MIPS_BE_FIXUP;
13569c75fb4SMaciej W. Rozycki
13669c75fb4SMaciej W. Rozycki if (action != MIPS_BE_FIXUP)
13769c75fb4SMaciej W. Rozycki printk(KERN_ALERT "Bus error %s: %s %s %s at %#010lx\n",
13869c75fb4SMaciej W. Rozycki kind, agent, cycle, event, address);
13969c75fb4SMaciej W. Rozycki
14069c75fb4SMaciej W. Rozycki return action;
14169c75fb4SMaciej W. Rozycki }
14269c75fb4SMaciej W. Rozycki
dec_kn01_be_handler(struct pt_regs * regs,int is_fixup)14369c75fb4SMaciej W. Rozycki int dec_kn01_be_handler(struct pt_regs *regs, int is_fixup)
14469c75fb4SMaciej W. Rozycki {
14569c75fb4SMaciej W. Rozycki return dec_kn01_be_backend(regs, is_fixup, 0);
14669c75fb4SMaciej W. Rozycki }
14769c75fb4SMaciej W. Rozycki
dec_kn01_be_interrupt(int irq,void * dev_id)1486dab2f45SRalf Baechle irqreturn_t dec_kn01_be_interrupt(int irq, void *dev_id)
14969c75fb4SMaciej W. Rozycki {
150a5fc9c0bSMaciej W. Rozycki volatile u16 *csr = (void *)CKSEG1ADDR(KN01_SLOT_BASE + KN01_CSR);
1516dab2f45SRalf Baechle struct pt_regs *regs = get_irq_regs();
15269c75fb4SMaciej W. Rozycki int action;
15369c75fb4SMaciej W. Rozycki
15469c75fb4SMaciej W. Rozycki if (!(*csr & KN01_CSR_MEMERR))
15569c75fb4SMaciej W. Rozycki return IRQ_NONE; /* Must have been video. */
15669c75fb4SMaciej W. Rozycki
15769c75fb4SMaciej W. Rozycki action = dec_kn01_be_backend(regs, 0, 1);
15869c75fb4SMaciej W. Rozycki
15969c75fb4SMaciej W. Rozycki if (action == MIPS_BE_DISCARD)
16069c75fb4SMaciej W. Rozycki return IRQ_HANDLED;
16169c75fb4SMaciej W. Rozycki
16269c75fb4SMaciej W. Rozycki /*
16369c75fb4SMaciej W. Rozycki * FIXME: Find the affected processes and kill them, otherwise
16469c75fb4SMaciej W. Rozycki * we must die.
16569c75fb4SMaciej W. Rozycki *
16669c75fb4SMaciej W. Rozycki * The interrupt is asynchronously delivered thus EPC and RA
16769c75fb4SMaciej W. Rozycki * may be irrelevant, but are printed for a reference.
16869c75fb4SMaciej W. Rozycki */
16969c75fb4SMaciej W. Rozycki printk(KERN_ALERT "Fatal bus interrupt, epc == %08lx, ra == %08lx\n",
17069c75fb4SMaciej W. Rozycki regs->cp0_epc, regs->regs[31]);
17169c75fb4SMaciej W. Rozycki die("Unrecoverable bus error", regs);
17269c75fb4SMaciej W. Rozycki }
17369c75fb4SMaciej W. Rozycki
17469c75fb4SMaciej W. Rozycki
dec_kn01_be_init(void)17569c75fb4SMaciej W. Rozycki void __init dec_kn01_be_init(void)
17669c75fb4SMaciej W. Rozycki {
177a5fc9c0bSMaciej W. Rozycki volatile u16 *csr = (void *)CKSEG1ADDR(KN01_SLOT_BASE + KN01_CSR);
17869c75fb4SMaciej W. Rozycki unsigned long flags;
17969c75fb4SMaciej W. Rozycki
18032baba2fSRalf Baechle raw_spin_lock_irqsave(&kn01_lock, flags);
18169c75fb4SMaciej W. Rozycki
18269c75fb4SMaciej W. Rozycki /* Preset write-only bits of the Control Register cache. */
18369c75fb4SMaciej W. Rozycki cached_kn01_csr = *csr;
18469c75fb4SMaciej W. Rozycki cached_kn01_csr &= KN01_CSR_STATUS | KN01_CSR_PARDIS | KN01_CSR_TXDIS;
18569c75fb4SMaciej W. Rozycki cached_kn01_csr |= KN01_CSR_LEDS;
18669c75fb4SMaciej W. Rozycki
18769c75fb4SMaciej W. Rozycki /* Enable parity error detection. */
18869c75fb4SMaciej W. Rozycki cached_kn01_csr &= ~KN01_CSR_PARDIS;
18969c75fb4SMaciej W. Rozycki *csr = cached_kn01_csr;
19069c75fb4SMaciej W. Rozycki iob();
19169c75fb4SMaciej W. Rozycki
19232baba2fSRalf Baechle raw_spin_unlock_irqrestore(&kn01_lock, flags);
19369c75fb4SMaciej W. Rozycki
19469c75fb4SMaciej W. Rozycki /* Clear any leftover errors from the firmware. */
19569c75fb4SMaciej W. Rozycki dec_kn01_be_ack();
19669c75fb4SMaciej W. Rozycki }
197