diff options
Diffstat (limited to 'arch/mips/dec')
-rw-r--r-- | arch/mips/dec/Makefile | 10 | ||||
-rw-r--r-- | arch/mips/dec/Platform | 7 | ||||
-rw-r--r-- | arch/mips/dec/ecc-berr.c | 274 | ||||
-rw-r--r-- | arch/mips/dec/int-handler.S | 311 | ||||
-rw-r--r-- | arch/mips/dec/ioasic-irq.c | 112 | ||||
-rw-r--r-- | arch/mips/dec/kn01-berr.c | 196 | ||||
-rw-r--r-- | arch/mips/dec/kn02-irq.c | 75 | ||||
-rw-r--r-- | arch/mips/dec/kn02xa-berr.c | 135 | ||||
-rw-r--r-- | arch/mips/dec/platform.c | 40 | ||||
-rw-r--r-- | arch/mips/dec/prom/Makefile | 9 | ||||
-rw-r--r-- | arch/mips/dec/prom/cmdline.c | 40 | ||||
-rw-r--r-- | arch/mips/dec/prom/console.c | 41 | ||||
-rw-r--r-- | arch/mips/dec/prom/dectypes.h | 15 | ||||
-rw-r--r-- | arch/mips/dec/prom/identify.c | 187 | ||||
-rw-r--r-- | arch/mips/dec/prom/init.c | 137 | ||||
-rw-r--r-- | arch/mips/dec/prom/locore.S | 30 | ||||
-rw-r--r-- | arch/mips/dec/prom/memory.c | 117 | ||||
-rw-r--r-- | arch/mips/dec/reset.c | 41 | ||||
-rw-r--r-- | arch/mips/dec/setup.c | 784 | ||||
-rw-r--r-- | arch/mips/dec/tc.c | 95 | ||||
-rw-r--r-- | arch/mips/dec/time.c | 172 | ||||
-rw-r--r-- | arch/mips/dec/wbflush.c | 92 |
22 files changed, 2920 insertions, 0 deletions
diff --git a/arch/mips/dec/Makefile b/arch/mips/dec/Makefile new file mode 100644 index 000000000..c9f62f1da --- /dev/null +++ b/arch/mips/dec/Makefile @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Makefile for the DECstation family specific parts of the kernel +# + +obj-y := ecc-berr.o int-handler.o ioasic-irq.o kn01-berr.o \ + kn02-irq.o kn02xa-berr.o platform.o reset.o setup.o time.o + +obj-$(CONFIG_TC) += tc.o +obj-$(CONFIG_CPU_HAS_WB) += wbflush.o diff --git a/arch/mips/dec/Platform b/arch/mips/dec/Platform new file mode 100644 index 000000000..c82391e83 --- /dev/null +++ b/arch/mips/dec/Platform @@ -0,0 +1,7 @@ +# +# DECstation family +# +cflags-$(CONFIG_MACH_DECSTATION) += \ + -I$(srctree)/arch/mips/include/asm/mach-dec +libs-$(CONFIG_MACH_DECSTATION) += arch/mips/dec/prom/ +load-$(CONFIG_MACH_DECSTATION) += 0xffffffff80040000 diff --git a/arch/mips/dec/ecc-berr.c b/arch/mips/dec/ecc-berr.c new file mode 100644 index 000000000..1eb356fdd --- /dev/null +++ b/arch/mips/dec/ecc-berr.c @@ -0,0 +1,274 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Bus error event handling code for systems equipped with ECC + * handling logic, i.e. DECstation/DECsystem 5000/200 (KN02), + * 5000/240 (KN03), 5000/260 (KN05) and DECsystem 5900 (KN03), + * 5900/260 (KN05) systems. + * + * Copyright (c) 2003, 2005 Maciej W. Rozycki + */ + +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/types.h> + +#include <asm/addrspace.h> +#include <asm/bootinfo.h> +#include <asm/cpu.h> +#include <asm/cpu-type.h> +#include <asm/irq_regs.h> +#include <asm/processor.h> +#include <asm/ptrace.h> +#include <asm/traps.h> + +#include <asm/dec/ecc.h> +#include <asm/dec/kn02.h> +#include <asm/dec/kn03.h> +#include <asm/dec/kn05.h> + +static volatile u32 *kn0x_erraddr; +static volatile u32 *kn0x_chksyn; + +static inline void dec_ecc_be_ack(void) +{ + *kn0x_erraddr = 0; /* any write clears the IRQ */ + iob(); +} + +static int dec_ecc_be_backend(struct pt_regs *regs, int is_fixup, int invoker) +{ + static const char excstr[] = "exception"; + static const char intstr[] = "interrupt"; + static const char cpustr[] = "CPU"; + static const char dmastr[] = "DMA"; + static const char readstr[] = "read"; + static const char mreadstr[] = "memory read"; + static const char writestr[] = "write"; + static const char mwritstr[] = "partial memory write"; + static const char timestr[] = "timeout"; + static const char overstr[] = "overrun"; + static const char eccstr[] = "ECC error"; + + const char *kind, *agent, *cycle, *event; + const char *status = "", *xbit = "", *fmt = ""; + unsigned long address; + u16 syn = 0, sngl; + + int i = 0; + + u32 erraddr = *kn0x_erraddr; + u32 chksyn = *kn0x_chksyn; + int action = MIPS_BE_FATAL; + + /* For non-ECC ack ASAP, so that any subsequent errors get caught. */ + if ((erraddr & (KN0X_EAR_VALID | KN0X_EAR_ECCERR)) == KN0X_EAR_VALID) + dec_ecc_be_ack(); + + kind = invoker ? intstr : excstr; + + if (!(erraddr & KN0X_EAR_VALID)) { + /* No idea what happened. */ + printk(KERN_ALERT "Unidentified bus error %s\n", kind); + return action; + } + + agent = (erraddr & KN0X_EAR_CPU) ? cpustr : dmastr; + + if (erraddr & KN0X_EAR_ECCERR) { + /* An ECC error on a CPU or DMA transaction. */ + cycle = (erraddr & KN0X_EAR_WRITE) ? mwritstr : mreadstr; + event = eccstr; + } else { + /* A CPU timeout or a DMA overrun. */ + cycle = (erraddr & KN0X_EAR_WRITE) ? writestr : readstr; + event = (erraddr & KN0X_EAR_CPU) ? timestr : overstr; + } + + address = erraddr & KN0X_EAR_ADDRESS; + /* For ECC errors on reads adjust for MT pipelining. */ + if ((erraddr & (KN0X_EAR_WRITE | KN0X_EAR_ECCERR)) == KN0X_EAR_ECCERR) + address = (address & ~0xfffLL) | ((address - 5) & 0xfffLL); + address <<= 2; + + /* Only CPU errors are fixable. */ + if (erraddr & KN0X_EAR_CPU && is_fixup) + action = MIPS_BE_FIXUP; + + if (erraddr & KN0X_EAR_ECCERR) { + static const u8 data_sbit[32] = { + 0x4f, 0x4a, 0x52, 0x54, 0x57, 0x58, 0x5b, 0x5d, + 0x23, 0x25, 0x26, 0x29, 0x2a, 0x2c, 0x31, 0x34, + 0x0e, 0x0b, 0x13, 0x15, 0x16, 0x19, 0x1a, 0x1c, + 0x62, 0x64, 0x67, 0x68, 0x6b, 0x6d, 0x70, 0x75, + }; + static const u8 data_mbit[25] = { + 0x07, 0x0d, 0x1f, + 0x2f, 0x32, 0x37, 0x38, 0x3b, 0x3d, 0x3e, + 0x43, 0x45, 0x46, 0x49, 0x4c, 0x51, 0x5e, + 0x61, 0x6e, 0x73, 0x76, 0x79, 0x7a, 0x7c, 0x7f, + }; + static const char sbestr[] = "corrected single"; + static const char dbestr[] = "uncorrectable double"; + static const char mbestr[] = "uncorrectable multiple"; + + if (!(address & 0x4)) + syn = chksyn; /* Low bank. */ + else + syn = chksyn >> 16; /* High bank. */ + + if (!(syn & KN0X_ESR_VLDLO)) { + /* Ack now, no rewrite will happen. */ + dec_ecc_be_ack(); + + fmt = KERN_ALERT "%s" "invalid\n"; + } else { + sngl = syn & KN0X_ESR_SNGLO; + syn &= KN0X_ESR_SYNLO; + + /* + * Multibit errors may be tagged incorrectly; + * check the syndrome explicitly. + */ + for (i = 0; i < 25; i++) + if (syn == data_mbit[i]) + break; + + if (i < 25) { + status = mbestr; + } else if (!sngl) { + status = dbestr; + } else { + volatile u32 *ptr = + (void *)CKSEG1ADDR(address); + + *ptr = *ptr; /* Rewrite. */ + iob(); + + status = sbestr; + action = MIPS_BE_DISCARD; + } + + /* Ack now, now we've rewritten (or not). */ + dec_ecc_be_ack(); + + if (syn && syn == (syn & -syn)) { + if (syn == 0x01) { + fmt = KERN_ALERT "%s" + "%#04x -- %s bit error " + "at check bit C%s\n"; + xbit = "X"; + } else { + fmt = KERN_ALERT "%s" + "%#04x -- %s bit error " + "at check bit C%s%u\n"; + } + i = syn >> 2; + } else { + for (i = 0; i < 32; i++) + if (syn == data_sbit[i]) + break; + if (i < 32) + fmt = KERN_ALERT "%s" + "%#04x -- %s bit error " + "at data bit D%s%u\n"; + else + fmt = KERN_ALERT "%s" + "%#04x -- %s bit error\n"; + } + } + } + + if (action != MIPS_BE_FIXUP) + printk(KERN_ALERT "Bus error %s: %s %s %s at %#010lx\n", + kind, agent, cycle, event, address); + + if (action != MIPS_BE_FIXUP && erraddr & KN0X_EAR_ECCERR) + printk(fmt, " ECC syndrome ", syn, status, xbit, i); + + return action; +} + +int dec_ecc_be_handler(struct pt_regs *regs, int is_fixup) +{ + return dec_ecc_be_backend(regs, is_fixup, 0); +} + +irqreturn_t dec_ecc_be_interrupt(int irq, void *dev_id) +{ + struct pt_regs *regs = get_irq_regs(); + + int action = dec_ecc_be_backend(regs, 0, 1); + + if (action == MIPS_BE_DISCARD) + return IRQ_HANDLED; + + /* + * FIXME: Find the affected processes and kill them, otherwise + * we must die. + * + * The interrupt is asynchronously delivered thus EPC and RA + * may be irrelevant, but are printed for a reference. + */ + printk(KERN_ALERT "Fatal bus interrupt, epc == %08lx, ra == %08lx\n", + regs->cp0_epc, regs->regs[31]); + die("Unrecoverable bus error", regs); +} + + +/* + * Initialization differs a bit between KN02 and KN03/KN05, so we + * need two variants. Once set up, all systems can be handled the + * same way. + */ +static inline void dec_kn02_be_init(void) +{ + volatile u32 *csr = (void *)CKSEG1ADDR(KN02_SLOT_BASE + KN02_CSR); + + kn0x_erraddr = (void *)CKSEG1ADDR(KN02_SLOT_BASE + KN02_ERRADDR); + kn0x_chksyn = (void *)CKSEG1ADDR(KN02_SLOT_BASE + KN02_CHKSYN); + + /* Preset write-only bits of the Control Register cache. */ + cached_kn02_csr = *csr | KN02_CSR_LEDS; + + /* Set normal ECC detection and generation. */ + cached_kn02_csr &= ~(KN02_CSR_DIAGCHK | KN02_CSR_DIAGGEN); + /* Enable ECC correction. */ + cached_kn02_csr |= KN02_CSR_CORRECT; + *csr = cached_kn02_csr; + iob(); +} + +static inline void dec_kn03_be_init(void) +{ + volatile u32 *mcr = (void *)CKSEG1ADDR(KN03_SLOT_BASE + IOASIC_MCR); + volatile u32 *mbcs = (void *)CKSEG1ADDR(KN4K_SLOT_BASE + KN4K_MB_CSR); + + kn0x_erraddr = (void *)CKSEG1ADDR(KN03_SLOT_BASE + IOASIC_ERRADDR); + kn0x_chksyn = (void *)CKSEG1ADDR(KN03_SLOT_BASE + IOASIC_CHKSYN); + + /* + * Set normal ECC detection and generation, enable ECC correction. + * For KN05 we also need to make sure EE (?) is enabled in the MB. + * Otherwise DBE/IBE exceptions would be masked but bus error + * interrupts would still arrive, resulting in an inevitable crash + * if get_dbe() triggers one. + */ + *mcr = (*mcr & ~(KN03_MCR_DIAGCHK | KN03_MCR_DIAGGEN)) | + KN03_MCR_CORRECT; + if (current_cpu_type() == CPU_R4400SC) + *mbcs |= KN4K_MB_CSR_EE; + fast_iob(); +} + +void __init dec_ecc_be_init(void) +{ + if (mips_machtype == MACH_DS5000_200) + dec_kn02_be_init(); + else + dec_kn03_be_init(); + + /* Clear any leftover errors from the firmware. */ + dec_ecc_be_ack(); +} diff --git a/arch/mips/dec/int-handler.S b/arch/mips/dec/int-handler.S new file mode 100644 index 000000000..011d1d678 --- /dev/null +++ b/arch/mips/dec/int-handler.S @@ -0,0 +1,311 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 1995, 1996, 1997 Paul M. Antoine and Harald Koerfgen + * Copyright (C) 2000, 2001, 2002, 2003, 2005 Maciej W. Rozycki + * + * Written by Ralf Baechle and Andreas Busse, modified for DECstation + * support by Paul Antoine and Harald Koerfgen. + * + * completely rewritten: + * Copyright (C) 1998 Harald Koerfgen + * + * Rewritten extensively for controller-driven IRQ support + * by Maciej W. Rozycki. + */ + +#include <asm/addrspace.h> +#include <asm/asm.h> +#include <asm/mipsregs.h> +#include <asm/regdef.h> +#include <asm/stackframe.h> + +#include <asm/dec/interrupts.h> +#include <asm/dec/ioasic_addrs.h> +#include <asm/dec/ioasic_ints.h> +#include <asm/dec/kn01.h> +#include <asm/dec/kn02.h> +#include <asm/dec/kn02xa.h> +#include <asm/dec/kn03.h> + +#define KN02_CSR_BASE CKSEG1ADDR(KN02_SLOT_BASE + KN02_CSR) +#define KN02XA_IOASIC_BASE CKSEG1ADDR(KN02XA_SLOT_BASE + IOASIC_IOCTL) +#define KN03_IOASIC_BASE CKSEG1ADDR(KN03_SLOT_BASE + IOASIC_IOCTL) + + .text + .set noreorder +/* + * plat_irq_dispatch: Interrupt handler for DECstations + * + * We follow the model in the Indy interrupt code by David Miller, where he + * says: a lot of complication here is taken away because: + * + * 1) We handle one interrupt and return, sitting in a loop + * and moving across all the pending IRQ bits in the cause + * register is _NOT_ the answer, the common case is one + * pending IRQ so optimize in that direction. + * + * 2) We need not check against bits in the status register + * IRQ mask, that would make this routine slow as hell. + * + * 3) Linux only thinks in terms of all IRQs on or all IRQs + * off, nothing in between like BSD spl() brain-damage. + * + * Furthermore, the IRQs on the DECstations look basically (barring + * software IRQs which we don't use at all) like... + * + * DS2100/3100's, aka kn01, aka Pmax: + * + * MIPS IRQ Source + * -------- ------ + * 0 Software (ignored) + * 1 Software (ignored) + * 2 SCSI + * 3 Lance Ethernet + * 4 DZ11 serial + * 5 RTC + * 6 Memory Controller & Video + * 7 FPU + * + * DS5000/200, aka kn02, aka 3max: + * + * MIPS IRQ Source + * -------- ------ + * 0 Software (ignored) + * 1 Software (ignored) + * 2 TurboChannel + * 3 RTC + * 4 Reserved + * 5 Memory Controller + * 6 Reserved + * 7 FPU + * + * DS5000/1xx's, aka kn02ba, aka 3min: + * + * MIPS IRQ Source + * -------- ------ + * 0 Software (ignored) + * 1 Software (ignored) + * 2 TurboChannel Slot 0 + * 3 TurboChannel Slot 1 + * 4 TurboChannel Slot 2 + * 5 TurboChannel Slot 3 (ASIC) + * 6 Halt button + * 7 FPU/R4k timer + * + * DS5000/2x's, aka kn02ca, aka maxine: + * + * MIPS IRQ Source + * -------- ------ + * 0 Software (ignored) + * 1 Software (ignored) + * 2 Periodic Interrupt (100usec) + * 3 RTC + * 4 I/O write timeout + * 5 TurboChannel (ASIC) + * 6 Halt Keycode from Access.Bus keyboard (CTRL-ALT-ENTER) + * 7 FPU/R4k timer + * + * DS5000/2xx's, aka kn03, aka 3maxplus: + * + * MIPS IRQ Source + * -------- ------ + * 0 Software (ignored) + * 1 Software (ignored) + * 2 System Board (ASIC) + * 3 RTC + * 4 Reserved + * 5 Memory + * 6 Halt Button + * 7 FPU/R4k timer + * + * We handle the IRQ according to _our_ priority (see setup.c), + * then we just return. If multiple IRQs are pending then we will + * just take another exception, big deal. + */ + .align 5 + NESTED(plat_irq_dispatch, PT_SIZE, ra) + .set noreorder + + /* + * Get pending Interrupts + */ + mfc0 t0,CP0_CAUSE # get pending interrupts + mfc0 t1,CP0_STATUS +#if defined(CONFIG_32BIT) && defined(CONFIG_MIPS_FP_SUPPORT) + lw t2,cpu_fpu_mask +#endif + andi t0,ST0_IM # CAUSE.CE may be non-zero! + and t0,t1 # isolate allowed ones + + beqz t0,spurious + +#if defined(CONFIG_32BIT) && defined(CONFIG_MIPS_FP_SUPPORT) + and t2,t0 + bnez t2,fpu # handle FPU immediately +#endif + + /* + * Find irq with highest priority + */ + # open coded PTR_LA t1, cpu_mask_nr_tbl +#if defined(CONFIG_32BIT) || defined(KBUILD_64BIT_SYM32) + # open coded la t1, cpu_mask_nr_tbl + lui t1, %hi(cpu_mask_nr_tbl) + addiu t1, %lo(cpu_mask_nr_tbl) +#else +#error GCC `-msym32' option required for 64-bit DECstation builds +#endif +1: lw t2,(t1) + nop + and t2,t0 + beqz t2,1b + addu t1,2*PTRSIZE # delay slot + + /* + * Do the low-level stuff + */ + lw a0,(-PTRSIZE)(t1) + nop + bgez a0,handle_it # irq_nr >= 0? + # irq_nr < 0: it is an address + nop + jr a0 + # a trick to save a branch: + lui t2,(KN03_IOASIC_BASE>>16)&0xffff + # upper part of IOASIC Address + +/* + * Handle "IRQ Controller" Interrupts + * Masked Interrupts are still visible and have to be masked "by hand". + */ + FEXPORT(kn02_io_int) # 3max + lui t0,(KN02_CSR_BASE>>16)&0xffff + # get interrupt status and mask + lw t0,(t0) + nop + andi t1,t0,KN02_IRQ_ALL + b 1f + srl t0,16 # shift interrupt mask + + FEXPORT(kn02xa_io_int) # 3min/maxine + lui t2,(KN02XA_IOASIC_BASE>>16)&0xffff + # upper part of IOASIC Address + + FEXPORT(kn03_io_int) # 3max+ (t2 loaded earlier) + lw t0,IO_REG_SIR(t2) # get status: IOASIC sir + lw t1,IO_REG_SIMR(t2) # get mask: IOASIC simr + nop + +1: and t0,t1 # mask out allowed ones + + beqz t0,spurious + + /* + * Find irq with highest priority + */ + # open coded PTR_LA t1,asic_mask_nr_tbl +#if defined(CONFIG_32BIT) || defined(KBUILD_64BIT_SYM32) + # open coded la t1, asic_mask_nr_tbl + lui t1, %hi(asic_mask_nr_tbl) + addiu t1, %lo(asic_mask_nr_tbl) +#else +#error GCC `-msym32' option required for 64-bit DECstation builds +#endif +2: lw t2,(t1) + nop + and t2,t0 + beq zero,t2,2b + addu t1,2*PTRSIZE # delay slot + + /* + * Do the low-level stuff + */ + lw a0,%lo(-PTRSIZE)(t1) + nop + bgez a0,handle_it # irq_nr >= 0? + # irq_nr < 0: it is an address + nop + jr a0 + nop # delay slot + +/* + * Dispatch low-priority interrupts. We reconsider all status + * bits again, which looks like a lose, but it makes the code + * simple and O(log n), so it gets compensated. + */ + FEXPORT(cpu_all_int) # HALT, timers, software junk + li a0,DEC_CPU_IRQ_BASE + srl t0,CAUSEB_IP + li t1,CAUSEF_IP>>CAUSEB_IP # mask + b 1f + li t2,4 # nr of bits / 2 + + FEXPORT(kn02_all_int) # impossible ? + li a0,KN02_IRQ_BASE + li t1,KN02_IRQ_ALL # mask + b 1f + li t2,4 # nr of bits / 2 + + FEXPORT(asic_all_int) # various I/O ASIC junk + li a0,IO_IRQ_BASE + li t1,IO_IRQ_ALL # mask + b 1f + li t2,8 # nr of bits / 2 + +/* + * Dispatch DMA interrupts -- O(log n). + */ + FEXPORT(asic_dma_int) # I/O ASIC DMA events + li a0,IO_IRQ_BASE+IO_INR_DMA + srl t0,IO_INR_DMA + li t1,IO_IRQ_DMA>>IO_INR_DMA # mask + li t2,8 # nr of bits / 2 + + /* + * Find irq with highest priority. + * Highest irq number takes precedence. + */ +1: srlv t3,t1,t2 +2: xor t1,t3 + and t3,t0,t1 + beqz t3,3f + nop + move t0,t3 + addu a0,t2 +3: srl t2,1 + bnez t2,2b + srlv t3,t1,t2 + +handle_it: + j dec_irq_dispatch + nop + +#if defined(CONFIG_32BIT) && defined(CONFIG_MIPS_FP_SUPPORT) +fpu: + lw t0,fpu_kstat_irq + nop + lw t1,(t0) + nop + addu t1,1 + j handle_fpe_int + sw t1,(t0) +#endif + +spurious: + j spurious_interrupt + nop + END(plat_irq_dispatch) + +/* + * Generic unimplemented interrupt routines -- cpu_mask_nr_tbl + * and asic_mask_nr_tbl are initialized to point all interrupts here. + * The tables are then filled in by machine-specific initialisation + * in dec_setup(). + */ + FEXPORT(dec_intr_unimplemented) + move a1,t0 # cheats way of printing an arg! + ASM_PANIC("Unimplemented cpu interrupt! CP0_CAUSE: 0x%08x"); + + FEXPORT(asic_intr_unimplemented) + move a1,t0 # cheats way of printing an arg! + ASM_PANIC("Unimplemented asic interrupt! ASIC ISR: 0x%08x"); diff --git a/arch/mips/dec/ioasic-irq.c b/arch/mips/dec/ioasic-irq.c new file mode 100644 index 000000000..971f7b467 --- /dev/null +++ b/arch/mips/dec/ioasic-irq.c @@ -0,0 +1,112 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * DEC I/O ASIC interrupts. + * + * Copyright (c) 2002, 2003, 2013 Maciej W. Rozycki + */ + +#include <linux/init.h> +#include <linux/irq.h> +#include <linux/types.h> + +#include <asm/dec/ioasic.h> +#include <asm/dec/ioasic_addrs.h> +#include <asm/dec/ioasic_ints.h> + +static int ioasic_irq_base; + +static void unmask_ioasic_irq(struct irq_data *d) +{ + u32 simr; + + simr = ioasic_read(IO_REG_SIMR); + simr |= (1 << (d->irq - ioasic_irq_base)); + ioasic_write(IO_REG_SIMR, simr); +} + +static void mask_ioasic_irq(struct irq_data *d) +{ + u32 simr; + + simr = ioasic_read(IO_REG_SIMR); + simr &= ~(1 << (d->irq - ioasic_irq_base)); + ioasic_write(IO_REG_SIMR, simr); +} + +static void ack_ioasic_irq(struct irq_data *d) +{ + mask_ioasic_irq(d); + fast_iob(); +} + +static struct irq_chip ioasic_irq_type = { + .name = "IO-ASIC", + .irq_ack = ack_ioasic_irq, + .irq_mask = mask_ioasic_irq, + .irq_mask_ack = ack_ioasic_irq, + .irq_unmask = unmask_ioasic_irq, +}; + +static void clear_ioasic_dma_irq(struct irq_data *d) +{ + u32 sir; + + sir = ~(1 << (d->irq - ioasic_irq_base)); + ioasic_write(IO_REG_SIR, sir); + fast_iob(); +} + +static struct irq_chip ioasic_dma_irq_type = { + .name = "IO-ASIC-DMA", + .irq_ack = clear_ioasic_dma_irq, + .irq_mask = mask_ioasic_irq, + .irq_unmask = unmask_ioasic_irq, + .irq_eoi = clear_ioasic_dma_irq, +}; + +/* + * I/O ASIC implements two kinds of DMA interrupts, informational and + * error interrupts. + * + * The former do not stop DMA and should be cleared as soon as possible + * so that if they retrigger before the handler has completed, usually as + * a side effect of actions taken by the handler, then they are reissued. + * These use the `handle_edge_irq' handler that clears the request right + * away. + * + * The latter stop DMA and do not resume it until the interrupt has been + * cleared. This cannot be done until after a corrective action has been + * taken and this also means they will not retrigger. Therefore they use + * the `handle_fasteoi_irq' handler that only clears the request on the + * way out. Because MIPS processor interrupt inputs, one of which the I/O + * ASIC is cascaded to, are level-triggered it is recommended that error + * DMA interrupt action handlers are registered with the IRQF_ONESHOT flag + * set so that they are run with the interrupt line masked. + * + * This mask has `1' bits in the positions of informational interrupts. + */ +#define IO_IRQ_DMA_INFO \ + (IO_IRQ_MASK(IO_INR_SCC0A_RXDMA) | \ + IO_IRQ_MASK(IO_INR_SCC1A_RXDMA) | \ + IO_IRQ_MASK(IO_INR_ISDN_TXDMA) | \ + IO_IRQ_MASK(IO_INR_ISDN_RXDMA) | \ + IO_IRQ_MASK(IO_INR_ASC_DMA)) + +void __init init_ioasic_irqs(int base) +{ + int i; + + /* Mask interrupts. */ + ioasic_write(IO_REG_SIMR, 0); + fast_iob(); + + for (i = base; i < base + IO_INR_DMA; i++) + irq_set_chip_and_handler(i, &ioasic_irq_type, + handle_level_irq); + for (; i < base + IO_IRQ_LINES; i++) + irq_set_chip_and_handler(i, &ioasic_dma_irq_type, + 1 << (i - base) & IO_IRQ_DMA_INFO ? + handle_edge_irq : handle_fasteoi_irq); + + ioasic_irq_base = base; +} diff --git a/arch/mips/dec/kn01-berr.c b/arch/mips/dec/kn01-berr.c new file mode 100644 index 000000000..76efed7bc --- /dev/null +++ b/arch/mips/dec/kn01-berr.c @@ -0,0 +1,196 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Bus error event handling code for DECstation/DECsystem 3100 + * and 2100 (KN01) systems equipped with parity error detection + * logic. + * + * Copyright (c) 2005 Maciej W. Rozycki + */ + +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/spinlock.h> +#include <linux/types.h> + +#include <asm/inst.h> +#include <asm/irq_regs.h> +#include <asm/mipsregs.h> +#include <asm/page.h> +#include <asm/ptrace.h> +#include <asm/traps.h> +#include <linux/uaccess.h> + +#include <asm/dec/kn01.h> + + +/* CP0 hazard avoidance. */ +#define BARRIER \ + __asm__ __volatile__( \ + ".set push\n\t" \ + ".set noreorder\n\t" \ + "nop\n\t" \ + ".set pop\n\t") + +/* + * Bits 7:0 of the Control Register are write-only -- the + * corresponding bits of the Status Register have a different + * meaning. Hence we use a cache. It speeds up things a bit + * as well. + * + * There is no default value -- it has to be initialized. + */ +u16 cached_kn01_csr; +static DEFINE_RAW_SPINLOCK(kn01_lock); + + +static inline void dec_kn01_be_ack(void) +{ + volatile u16 *csr = (void *)CKSEG1ADDR(KN01_SLOT_BASE + KN01_CSR); + unsigned long flags; + + raw_spin_lock_irqsave(&kn01_lock, flags); + + *csr = cached_kn01_csr | KN01_CSR_MEMERR; /* Clear bus IRQ. */ + iob(); + + raw_spin_unlock_irqrestore(&kn01_lock, flags); +} + +static int dec_kn01_be_backend(struct pt_regs *regs, int is_fixup, int invoker) +{ + volatile u32 *kn01_erraddr = (void *)CKSEG1ADDR(KN01_SLOT_BASE + + KN01_ERRADDR); + + static const char excstr[] = "exception"; + static const char intstr[] = "interrupt"; + static const char cpustr[] = "CPU"; + static const char mreadstr[] = "memory read"; + static const char readstr[] = "read"; + static const char writestr[] = "write"; + static const char timestr[] = "timeout"; + static const char paritystr[] = "parity error"; + + int data = regs->cp0_cause & 4; + unsigned int __user *pc = (unsigned int __user *)regs->cp0_epc + + ((regs->cp0_cause & CAUSEF_BD) != 0); + union mips_instruction insn; + unsigned long entrylo, offset; + long asid, entryhi, vaddr; + + const char *kind, *agent, *cycle, *event; + unsigned long address; + + u32 erraddr = *kn01_erraddr; + int action = MIPS_BE_FATAL; + + /* Ack ASAP, so that any subsequent errors get caught. */ + dec_kn01_be_ack(); + + kind = invoker ? intstr : excstr; + + agent = cpustr; + + if (invoker) + address = erraddr; + else { + /* Bloody hardware doesn't record the address for reads... */ + if (data) { + /* This never faults. */ + __get_user(insn.word, pc); + vaddr = regs->regs[insn.i_format.rs] + + insn.i_format.simmediate; + } else + vaddr = (long)pc; + if (KSEGX(vaddr) == CKSEG0 || KSEGX(vaddr) == CKSEG1) + address = CPHYSADDR(vaddr); + else { + /* Peek at what physical address the CPU used. */ + asid = read_c0_entryhi(); + entryhi = asid & (PAGE_SIZE - 1); + entryhi |= vaddr & ~(PAGE_SIZE - 1); + write_c0_entryhi(entryhi); + BARRIER; + tlb_probe(); + /* No need to check for presence. */ + tlb_read(); + entrylo = read_c0_entrylo0(); + write_c0_entryhi(asid); + offset = vaddr & (PAGE_SIZE - 1); + address = (entrylo & ~(PAGE_SIZE - 1)) | offset; + } + } + + /* Treat low 256MB as memory, high -- as I/O. */ + if (address < 0x10000000) { + cycle = mreadstr; + event = paritystr; + } else { + cycle = invoker ? writestr : readstr; + event = timestr; + } + + if (is_fixup) + action = MIPS_BE_FIXUP; + + if (action != MIPS_BE_FIXUP) + printk(KERN_ALERT "Bus error %s: %s %s %s at %#010lx\n", + kind, agent, cycle, event, address); + + return action; +} + +int dec_kn01_be_handler(struct pt_regs *regs, int is_fixup) +{ + return dec_kn01_be_backend(regs, is_fixup, 0); +} + +irqreturn_t dec_kn01_be_interrupt(int irq, void *dev_id) +{ + volatile u16 *csr = (void *)CKSEG1ADDR(KN01_SLOT_BASE + KN01_CSR); + struct pt_regs *regs = get_irq_regs(); + int action; + + if (!(*csr & KN01_CSR_MEMERR)) + return IRQ_NONE; /* Must have been video. */ + + action = dec_kn01_be_backend(regs, 0, 1); + + if (action == MIPS_BE_DISCARD) + return IRQ_HANDLED; + + /* + * FIXME: Find the affected processes and kill them, otherwise + * we must die. + * + * The interrupt is asynchronously delivered thus EPC and RA + * may be irrelevant, but are printed for a reference. + */ + printk(KERN_ALERT "Fatal bus interrupt, epc == %08lx, ra == %08lx\n", + regs->cp0_epc, regs->regs[31]); + die("Unrecoverable bus error", regs); +} + + +void __init dec_kn01_be_init(void) +{ + volatile u16 *csr = (void *)CKSEG1ADDR(KN01_SLOT_BASE + KN01_CSR); + unsigned long flags; + + raw_spin_lock_irqsave(&kn01_lock, flags); + + /* Preset write-only bits of the Control Register cache. */ + cached_kn01_csr = *csr; + cached_kn01_csr &= KN01_CSR_STATUS | KN01_CSR_PARDIS | KN01_CSR_TXDIS; + cached_kn01_csr |= KN01_CSR_LEDS; + + /* Enable parity error detection. */ + cached_kn01_csr &= ~KN01_CSR_PARDIS; + *csr = cached_kn01_csr; + iob(); + + raw_spin_unlock_irqrestore(&kn01_lock, flags); + + /* Clear any leftover errors from the firmware. */ + dec_kn01_be_ack(); +} diff --git a/arch/mips/dec/kn02-irq.c b/arch/mips/dec/kn02-irq.c new file mode 100644 index 000000000..7e18de574 --- /dev/null +++ b/arch/mips/dec/kn02-irq.c @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * DECstation 5000/200 (KN02) Control and Status Register + * interrupts. + * + * Copyright (c) 2002, 2003, 2005 Maciej W. Rozycki + */ + +#include <linux/init.h> +#include <linux/irq.h> +#include <linux/types.h> + +#include <asm/dec/kn02.h> + + +/* + * Bits 7:0 of the Control Register are write-only -- the + * corresponding bits of the Status Register have a different + * meaning. Hence we use a cache. It speeds up things a bit + * as well. + * + * There is no default value -- it has to be initialized. + */ +u32 cached_kn02_csr; + +static int kn02_irq_base; + +static void unmask_kn02_irq(struct irq_data *d) +{ + volatile u32 *csr = (volatile u32 *)CKSEG1ADDR(KN02_SLOT_BASE + + KN02_CSR); + + cached_kn02_csr |= (1 << (d->irq - kn02_irq_base + 16)); + *csr = cached_kn02_csr; +} + +static void mask_kn02_irq(struct irq_data *d) +{ + volatile u32 *csr = (volatile u32 *)CKSEG1ADDR(KN02_SLOT_BASE + + KN02_CSR); + + cached_kn02_csr &= ~(1 << (d->irq - kn02_irq_base + 16)); + *csr = cached_kn02_csr; +} + +static void ack_kn02_irq(struct irq_data *d) +{ + mask_kn02_irq(d); + iob(); +} + +static struct irq_chip kn02_irq_type = { + .name = "KN02-CSR", + .irq_ack = ack_kn02_irq, + .irq_mask = mask_kn02_irq, + .irq_mask_ack = ack_kn02_irq, + .irq_unmask = unmask_kn02_irq, +}; + +void __init init_kn02_irqs(int base) +{ + volatile u32 *csr = (volatile u32 *)CKSEG1ADDR(KN02_SLOT_BASE + + KN02_CSR); + int i; + + /* Mask interrupts. */ + cached_kn02_csr &= ~KN02_CSR_IOINTEN; + *csr = cached_kn02_csr; + iob(); + + for (i = base; i < base + KN02_IRQ_LINES; i++) + irq_set_chip_and_handler(i, &kn02_irq_type, handle_level_irq); + + kn02_irq_base = base; +} diff --git a/arch/mips/dec/kn02xa-berr.c b/arch/mips/dec/kn02xa-berr.c new file mode 100644 index 000000000..9699fc4e6 --- /dev/null +++ b/arch/mips/dec/kn02xa-berr.c @@ -0,0 +1,135 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Bus error event handling code for 5000-series systems equipped + * with parity error detection logic, i.e. DECstation/DECsystem + * 5000/120, /125, /133 (KN02-BA), 5000/150 (KN04-BA) and Personal + * DECstation/DECsystem 5000/20, /25, /33 (KN02-CA), 5000/50 + * (KN04-CA) systems. + * + * Copyright (c) 2005 Maciej W. Rozycki + */ + +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/types.h> + +#include <asm/addrspace.h> +#include <asm/cpu-type.h> +#include <asm/irq_regs.h> +#include <asm/ptrace.h> +#include <asm/traps.h> + +#include <asm/dec/kn02ca.h> +#include <asm/dec/kn02xa.h> +#include <asm/dec/kn05.h> + +static inline void dec_kn02xa_be_ack(void) +{ + volatile u32 *mer = (void *)CKSEG1ADDR(KN02XA_MER); + volatile u32 *mem_intr = (void *)CKSEG1ADDR(KN02XA_MEM_INTR); + + *mer = KN02CA_MER_INTR; /* Clear errors; keep the ARC IRQ. */ + *mem_intr = 0; /* Any write clears the bus IRQ. */ + iob(); +} + +static int dec_kn02xa_be_backend(struct pt_regs *regs, int is_fixup, + int invoker) +{ + volatile u32 *kn02xa_mer = (void *)CKSEG1ADDR(KN02XA_MER); + volatile u32 *kn02xa_ear = (void *)CKSEG1ADDR(KN02XA_EAR); + + static const char excstr[] = "exception"; + static const char intstr[] = "interrupt"; + static const char cpustr[] = "CPU"; + static const char mreadstr[] = "memory read"; + static const char readstr[] = "read"; + static const char writestr[] = "write"; + static const char timestr[] = "timeout"; + static const char paritystr[] = "parity error"; + static const char lanestat[][4] = { " OK", "BAD" }; + + const char *kind, *agent, *cycle, *event; + unsigned long address; + + u32 mer = *kn02xa_mer; + u32 ear = *kn02xa_ear; + int action = MIPS_BE_FATAL; + + /* Ack ASAP, so that any subsequent errors get caught. */ + dec_kn02xa_be_ack(); + + kind = invoker ? intstr : excstr; + + /* No DMA errors? */ + agent = cpustr; + + address = ear & KN02XA_EAR_ADDRESS; + + /* Low 256MB is decoded as memory, high -- as TC. */ + if (address < 0x10000000) { + cycle = mreadstr; + event = paritystr; + } else { + cycle = invoker ? writestr : readstr; + event = timestr; + } + + if (is_fixup) + action = MIPS_BE_FIXUP; + + if (action != MIPS_BE_FIXUP) + printk(KERN_ALERT "Bus error %s: %s %s %s at %#010lx\n", + kind, agent, cycle, event, address); + + if (action != MIPS_BE_FIXUP && address < 0x10000000) + printk(KERN_ALERT " Byte lane status %#3x -- " + "#3: %s, #2: %s, #1: %s, #0: %s\n", + (mer & KN02XA_MER_BYTERR) >> 8, + lanestat[(mer & KN02XA_MER_BYTERR_3) != 0], + lanestat[(mer & KN02XA_MER_BYTERR_2) != 0], + lanestat[(mer & KN02XA_MER_BYTERR_1) != 0], + lanestat[(mer & KN02XA_MER_BYTERR_0) != 0]); + + return action; +} + +int dec_kn02xa_be_handler(struct pt_regs *regs, int is_fixup) +{ + return dec_kn02xa_be_backend(regs, is_fixup, 0); +} + +irqreturn_t dec_kn02xa_be_interrupt(int irq, void *dev_id) +{ + struct pt_regs *regs = get_irq_regs(); + int action = dec_kn02xa_be_backend(regs, 0, 1); + + if (action == MIPS_BE_DISCARD) + return IRQ_HANDLED; + + /* + * FIXME: Find the affected processes and kill them, otherwise + * we must die. + * + * The interrupt is asynchronously delivered thus EPC and RA + * may be irrelevant, but are printed for a reference. + */ + printk(KERN_ALERT "Fatal bus interrupt, epc == %08lx, ra == %08lx\n", + regs->cp0_epc, regs->regs[31]); + die("Unrecoverable bus error", regs); +} + + +void __init dec_kn02xa_be_init(void) +{ + volatile u32 *mbcs = (void *)CKSEG1ADDR(KN4K_SLOT_BASE + KN4K_MB_CSR); + + /* For KN04 we need to make sure EE (?) is enabled in the MB. */ + if (current_cpu_type() == CPU_R4000SC) + *mbcs |= KN4K_MB_CSR_EE; + fast_iob(); + + /* Clear any leftover errors from the firmware. */ + dec_kn02xa_be_ack(); +} diff --git a/arch/mips/dec/platform.c b/arch/mips/dec/platform.c new file mode 100644 index 000000000..c4fcb8c58 --- /dev/null +++ b/arch/mips/dec/platform.c @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * DEC platform devices. + * + * Copyright (c) 2014 Maciej W. Rozycki + */ + +#include <linux/ioport.h> +#include <linux/kernel.h> +#include <linux/mc146818rtc.h> +#include <linux/platform_device.h> + +static struct resource dec_rtc_resources[] = { + { + .name = "rtc", + .flags = IORESOURCE_MEM, + }, +}; + +static struct cmos_rtc_board_info dec_rtc_info = { + .flags = CMOS_RTC_FLAGS_NOFREQ, + .address_space = 64, +}; + +static struct platform_device dec_rtc_device = { + .name = "rtc_cmos", + .id = PLATFORM_DEVID_NONE, + .dev.platform_data = &dec_rtc_info, + .resource = dec_rtc_resources, + .num_resources = ARRAY_SIZE(dec_rtc_resources), +}; + +static int __init dec_add_devices(void) +{ + dec_rtc_resources[0].start = RTC_PORT(0); + dec_rtc_resources[0].end = RTC_PORT(0) + dec_kn_slot_size - 1; + return platform_device_register(&dec_rtc_device); +} + +device_initcall(dec_add_devices); diff --git a/arch/mips/dec/prom/Makefile b/arch/mips/dec/prom/Makefile new file mode 100644 index 000000000..2bad87551 --- /dev/null +++ b/arch/mips/dec/prom/Makefile @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Makefile for the DECstation prom monitor library routines +# under Linux. +# + +lib-y += init.o memory.o cmdline.o identify.o console.o + +lib-$(CONFIG_CPU_R3000) += locore.o diff --git a/arch/mips/dec/prom/cmdline.c b/arch/mips/dec/prom/cmdline.c new file mode 100644 index 000000000..3ed63280a --- /dev/null +++ b/arch/mips/dec/prom/cmdline.c @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * cmdline.c: read the command line passed to us by the PROM. + * + * Copyright (C) 1998 Harald Koerfgen + * Copyright (C) 2002, 2004 Maciej W. Rozycki + */ +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/types.h> + +#include <asm/bootinfo.h> +#include <asm/dec/prom.h> + +#undef PROM_DEBUG + +void __init prom_init_cmdline(s32 argc, s32 *argv, u32 magic) +{ + char *arg; + int start_arg, i; + + /* + * collect args and prepare cmd_line + */ + if (!prom_is_rex(magic)) + start_arg = 1; + else + start_arg = 2; + for (i = start_arg; i < argc; i++) { + arg = (void *)(long)(argv[i]); + strcat(arcs_cmdline, arg); + if (i < (argc - 1)) + strcat(arcs_cmdline, " "); + } + +#ifdef PROM_DEBUG + printk("arcs_cmdline: %s\n", &(arcs_cmdline[0])); +#endif +} diff --git a/arch/mips/dec/prom/console.c b/arch/mips/dec/prom/console.c new file mode 100644 index 000000000..31a8441d8 --- /dev/null +++ b/arch/mips/dec/prom/console.c @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * DECstation PROM-based early console support. + * + * Copyright (C) 2004, 2007 Maciej W. Rozycki + */ +#include <linux/console.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/string.h> + +#include <asm/dec/prom.h> + +static void __init prom_console_write(struct console *con, const char *s, + unsigned int c) +{ + char buf[81]; + unsigned int chunk = sizeof(buf) - 1; + + while (c > 0) { + if (chunk > c) + chunk = c; + memcpy(buf, s, chunk); + buf[chunk] = '\0'; + prom_printf("%s", buf); + s += chunk; + c -= chunk; + } +} + +static struct console promcons __initdata = { + .name = "prom", + .write = prom_console_write, + .flags = CON_BOOT | CON_PRINTBUFFER, + .index = -1, +}; + +void __init register_prom_console(void) +{ + register_console(&promcons); +} diff --git a/arch/mips/dec/prom/dectypes.h b/arch/mips/dec/prom/dectypes.h new file mode 100644 index 000000000..9fcbcc7cd --- /dev/null +++ b/arch/mips/dec/prom/dectypes.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef DECTYPES +#define DECTYPES + +#define DS2100_3100 1 /* DS2100/3100 Pmax */ +#define DS5000_200 2 /* DS5000/200 3max */ +#define DS5000_1XX 3 /* DS5000/1xx kmin */ +#define DS5000_2X0 4 /* DS5000/2x0 3max+ */ +#define DS5800 5 /* DS5800 Isis */ +#define DS5400 6 /* DS5400 MIPSfair */ +#define DS5000_XX 7 /* DS5000/xx maxine */ +#define DS5500 11 /* DS5500 MIPSfair-2 */ +#define DS5100 12 /* DS5100 MIPSmate */ + +#endif diff --git a/arch/mips/dec/prom/identify.c b/arch/mips/dec/prom/identify.c new file mode 100644 index 000000000..80cd14cd1 --- /dev/null +++ b/arch/mips/dec/prom/identify.c @@ -0,0 +1,187 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * identify.c: machine identification code. + * + * Copyright (C) 1998 Harald Koerfgen and Paul M. Antoine + * Copyright (C) 2002, 2003, 2004, 2005 Maciej W. Rozycki + */ +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/mc146818rtc.h> +#include <linux/export.h> +#include <linux/string.h> +#include <linux/types.h> + +#include <asm/bootinfo.h> + +#include <asm/dec/ioasic.h> +#include <asm/dec/ioasic_addrs.h> +#include <asm/dec/kn01.h> +#include <asm/dec/kn02.h> +#include <asm/dec/kn02ba.h> +#include <asm/dec/kn02ca.h> +#include <asm/dec/kn03.h> +#include <asm/dec/kn230.h> +#include <asm/dec/prom.h> +#include <asm/dec/system.h> + +#include "dectypes.h" + +static const char *dec_system_strings[] = { + [MACH_DSUNKNOWN] "unknown DECstation", + [MACH_DS23100] "DECstation 2100/3100", + [MACH_DS5100] "DECsystem 5100", + [MACH_DS5000_200] "DECstation 5000/200", + [MACH_DS5000_1XX] "DECstation 5000/1xx", + [MACH_DS5000_XX] "Personal DECstation 5000/xx", + [MACH_DS5000_2X0] "DECstation 5000/2x0", + [MACH_DS5400] "DECsystem 5400", + [MACH_DS5500] "DECsystem 5500", + [MACH_DS5800] "DECsystem 5800", + [MACH_DS5900] "DECsystem 5900", +}; + +const char *get_system_type(void) +{ +#define STR_BUF_LEN 64 + static char system[STR_BUF_LEN]; + static int called = 0; + + if (called == 0) { + called = 1; + snprintf(system, STR_BUF_LEN, "Digital %s", + dec_system_strings[mips_machtype]); + } + + return system; +} + + +/* + * Setup essential system-specific memory addresses. We need them + * early. Semantically the functions belong to prom/init.c, but they + * are compact enough we want them inlined. --macro + */ +volatile u8 *dec_rtc_base; + +EXPORT_SYMBOL(dec_rtc_base); + +static inline void prom_init_kn01(void) +{ + dec_kn_slot_base = KN01_SLOT_BASE; + dec_kn_slot_size = KN01_SLOT_SIZE; + + dec_rtc_base = (void *)CKSEG1ADDR(dec_kn_slot_base + KN01_RTC); +} + +static inline void prom_init_kn230(void) +{ + dec_kn_slot_base = KN01_SLOT_BASE; + dec_kn_slot_size = KN01_SLOT_SIZE; + + dec_rtc_base = (void *)CKSEG1ADDR(dec_kn_slot_base + KN01_RTC); +} + +static inline void prom_init_kn02(void) +{ + dec_kn_slot_base = KN02_SLOT_BASE; + dec_kn_slot_size = KN02_SLOT_SIZE; + dec_tc_bus = 1; + + dec_rtc_base = (void *)CKSEG1ADDR(dec_kn_slot_base + KN02_RTC); +} + +static inline void prom_init_kn02xa(void) +{ + dec_kn_slot_base = KN02XA_SLOT_BASE; + dec_kn_slot_size = IOASIC_SLOT_SIZE; + dec_tc_bus = 1; + + ioasic_base = (void *)CKSEG1ADDR(dec_kn_slot_base + IOASIC_IOCTL); + dec_rtc_base = (void *)CKSEG1ADDR(dec_kn_slot_base + IOASIC_TOY); +} + +static inline void prom_init_kn03(void) +{ + dec_kn_slot_base = KN03_SLOT_BASE; + dec_kn_slot_size = IOASIC_SLOT_SIZE; + dec_tc_bus = 1; + + ioasic_base = (void *)CKSEG1ADDR(dec_kn_slot_base + IOASIC_IOCTL); + dec_rtc_base = (void *)CKSEG1ADDR(dec_kn_slot_base + IOASIC_TOY); +} + + +void __init prom_identify_arch(u32 magic) +{ + unsigned char dec_cpunum, dec_firmrev, dec_etc, dec_systype; + u32 dec_sysid; + + if (!prom_is_rex(magic)) { + dec_sysid = simple_strtoul(prom_getenv("systype"), + (char **)0, 0); + } else { + dec_sysid = rex_getsysid(); + if (dec_sysid == 0) { + printk("Zero sysid returned from PROM! " + "Assuming a PMAX-like machine.\n"); + dec_sysid = 1; + } + } + + dec_cpunum = (dec_sysid & 0xff000000) >> 24; + dec_systype = (dec_sysid & 0xff0000) >> 16; + dec_firmrev = (dec_sysid & 0xff00) >> 8; + dec_etc = dec_sysid & 0xff; + + /* + * FIXME: This may not be an exhaustive list of DECStations/Servers! + * Put all model-specific initialisation calls here. + */ + switch (dec_systype) { + case DS2100_3100: + mips_machtype = MACH_DS23100; + prom_init_kn01(); + break; + case DS5100: /* DS5100 MIPSMATE */ + mips_machtype = MACH_DS5100; + prom_init_kn230(); + break; + case DS5000_200: /* DS5000 3max */ + mips_machtype = MACH_DS5000_200; + prom_init_kn02(); + break; + case DS5000_1XX: /* DS5000/100 3min */ + mips_machtype = MACH_DS5000_1XX; + prom_init_kn02xa(); + break; + case DS5000_2X0: /* DS5000/240 3max+ or DS5900 bigmax */ + mips_machtype = MACH_DS5000_2X0; + prom_init_kn03(); + if (!(ioasic_read(IO_REG_SIR) & KN03_IO_INR_3MAXP)) + mips_machtype = MACH_DS5900; + break; + case DS5000_XX: /* Personal DS5000/xx maxine */ + mips_machtype = MACH_DS5000_XX; + prom_init_kn02xa(); + break; + case DS5800: /* DS5800 Isis */ + mips_machtype = MACH_DS5800; + break; + case DS5400: /* DS5400 MIPSfair */ + mips_machtype = MACH_DS5400; + break; + case DS5500: /* DS5500 MIPSfair-2 */ + mips_machtype = MACH_DS5500; + break; + default: + mips_machtype = MACH_DSUNKNOWN; + break; + } + + if (mips_machtype == MACH_DSUNKNOWN) + printk("This is an %s, id is %x\n", + dec_system_strings[mips_machtype], dec_systype); + else + printk("This is a %s\n", dec_system_strings[mips_machtype]); +} diff --git a/arch/mips/dec/prom/init.c b/arch/mips/dec/prom/init.c new file mode 100644 index 000000000..cb12eb211 --- /dev/null +++ b/arch/mips/dec/prom/init.c @@ -0,0 +1,137 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * init.c: PROM library initialisation code. + * + * Copyright (C) 1998 Harald Koerfgen + * Copyright (C) 2002, 2004 Maciej W. Rozycki + */ +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/linkage.h> +#include <linux/smp.h> +#include <linux/string.h> +#include <linux/types.h> + +#include <asm/bootinfo.h> +#include <asm/cpu.h> +#include <asm/cpu-type.h> +#include <asm/processor.h> + +#include <asm/dec/prom.h> + + +int (*__rex_bootinit)(void); +int (*__rex_bootread)(void); +int (*__rex_getbitmap)(memmap *); +unsigned long *(*__rex_slot_address)(int); +void *(*__rex_gettcinfo)(void); +int (*__rex_getsysid)(void); +void (*__rex_clear_cache)(void); + +int (*__prom_getchar)(void); +char *(*__prom_getenv)(char *); +int (*__prom_printf)(char *, ...); + +int (*__pmax_open)(char*, int); +int (*__pmax_lseek)(int, long, int); +int (*__pmax_read)(int, void *, int); +int (*__pmax_close)(int); + + +/* + * Detect which PROM the DECSTATION has, and set the callback vectors + * appropriately. + */ +void __init which_prom(s32 magic, s32 *prom_vec) +{ + /* + * No sign of the REX PROM's magic number means we assume a non-REX + * machine (i.e. we're on a DS2100/3100, DS5100 or DS5000/2xx) + */ + if (prom_is_rex(magic)) { + /* + * Set up prom abstraction structure with REX entry points. + */ + __rex_bootinit = + (void *)(long)*(prom_vec + REX_PROM_BOOTINIT); + __rex_bootread = + (void *)(long)*(prom_vec + REX_PROM_BOOTREAD); + __rex_getbitmap = + (void *)(long)*(prom_vec + REX_PROM_GETBITMAP); + __prom_getchar = + (void *)(long)*(prom_vec + REX_PROM_GETCHAR); + __prom_getenv = + (void *)(long)*(prom_vec + REX_PROM_GETENV); + __rex_getsysid = + (void *)(long)*(prom_vec + REX_PROM_GETSYSID); + __rex_gettcinfo = + (void *)(long)*(prom_vec + REX_PROM_GETTCINFO); + __prom_printf = + (void *)(long)*(prom_vec + REX_PROM_PRINTF); + __rex_slot_address = + (void *)(long)*(prom_vec + REX_PROM_SLOTADDR); + __rex_clear_cache = + (void *)(long)*(prom_vec + REX_PROM_CLEARCACHE); + } else { + /* + * Set up prom abstraction structure with non-REX entry points. + */ + __prom_getchar = (void *)PMAX_PROM_GETCHAR; + __prom_getenv = (void *)PMAX_PROM_GETENV; + __prom_printf = (void *)PMAX_PROM_PRINTF; + __pmax_open = (void *)PMAX_PROM_OPEN; + __pmax_lseek = (void *)PMAX_PROM_LSEEK; + __pmax_read = (void *)PMAX_PROM_READ; + __pmax_close = (void *)PMAX_PROM_CLOSE; + } +} + +void __init prom_init(void) +{ + extern void dec_machine_halt(void); + static const char cpu_msg[] __initconst = + "Sorry, this kernel is compiled for a wrong CPU type!\n"; + s32 argc = fw_arg0; + s32 *argv = (void *)fw_arg1; + u32 magic = fw_arg2; + s32 *prom_vec = (void *)fw_arg3; + + /* + * Determine which PROM we have + * (and therefore which machine we're on!) + */ + which_prom(magic, prom_vec); + + if (prom_is_rex(magic)) + rex_clear_cache(); + + /* Register the early console. */ + register_prom_console(); + + /* Were we compiled with the right CPU option? */ +#if defined(CONFIG_CPU_R3000) + if ((current_cpu_type() == CPU_R4000SC) || + (current_cpu_type() == CPU_R4400SC)) { + static const char r4k_msg[] __initconst = + "Please recompile with \"CONFIG_CPU_R4X00 = y\".\n"; + printk(cpu_msg); + printk(r4k_msg); + dec_machine_halt(); + } +#endif + +#if defined(CONFIG_CPU_R4X00) + if ((current_cpu_type() == CPU_R3000) || + (current_cpu_type() == CPU_R3000A)) { + static const char r3k_msg[] __initconst = + "Please recompile with \"CONFIG_CPU_R3000 = y\".\n"; + printk(cpu_msg); + printk(r3k_msg); + dec_machine_halt(); + } +#endif + + prom_meminit(magic); + prom_identify_arch(magic); + prom_init_cmdline(argc, argv, magic); +} diff --git a/arch/mips/dec/prom/locore.S b/arch/mips/dec/prom/locore.S new file mode 100644 index 000000000..0eb8fab62 --- /dev/null +++ b/arch/mips/dec/prom/locore.S @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * locore.S + */ +#include <asm/asm.h> +#include <asm/regdef.h> +#include <asm/mipsregs.h> + + .text + +/* + * Simple general exception handling routine. This one is used for the + * Memory sizing routine for pmax machines. HK + */ + +NESTED(genexcept_early, 0, sp) + .set noat + .set noreorder + + mfc0 k0, CP0_STATUS + la k1, mem_err + + sw k0, 0(k1) + + mfc0 k0, CP0_EPC + nop + addiu k0, 4 # skip the causing instruction + jr k0 + rfe +END(genexcept_early) diff --git a/arch/mips/dec/prom/memory.c b/arch/mips/dec/prom/memory.c new file mode 100644 index 000000000..44490c30d --- /dev/null +++ b/arch/mips/dec/prom/memory.c @@ -0,0 +1,117 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * memory.c: memory initialisation code. + * + * Copyright (C) 1998 Harald Koerfgen, Frieder Streffer and Paul M. Antoine + * Copyright (C) 2000, 2002 Maciej W. Rozycki + */ +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/memblock.h> +#include <linux/types.h> + +#include <asm/addrspace.h> +#include <asm/dec/machtype.h> +#include <asm/dec/prom.h> +#include <asm/page.h> +#include <asm/sections.h> + + +volatile unsigned long mem_err; /* So we know an error occurred */ + +/* + * Probe memory in 4MB chunks, waiting for an error to tell us we've fallen + * off the end of real memory. Only suitable for the 2100/3100's (PMAX). + */ + +#define CHUNK_SIZE 0x400000 + +static __init void pmax_setup_memory_region(void) +{ + volatile unsigned char *memory_page, dummy; + char old_handler[0x80]; + extern char genexcept_early; + + /* Install exception handler */ + memcpy(&old_handler, (void *)(CKSEG0 + 0x80), 0x80); + memcpy((void *)(CKSEG0 + 0x80), &genexcept_early, 0x80); + + /* read unmapped and uncached (KSEG1) + * DECstations have at least 4MB RAM + * Assume less than 480MB of RAM, as this is max for 5000/2xx + * FIXME this should be replaced by the first free page! + */ + for (memory_page = (unsigned char *)CKSEG1 + CHUNK_SIZE; + mem_err == 0 && memory_page < (unsigned char *)CKSEG1 + 0x1e00000; + memory_page += CHUNK_SIZE) { + dummy = *memory_page; + } + memcpy((void *)(CKSEG0 + 0x80), &old_handler, 0x80); + + memblock_add(0, (unsigned long)memory_page - CKSEG1 - CHUNK_SIZE); +} + +/* + * Use the REX prom calls to get hold of the memory bitmap, and thence + * determine memory size. + */ +static __init void rex_setup_memory_region(void) +{ + int i, bitmap_size; + unsigned long mem_start = 0, mem_size = 0; + memmap *bm; + + /* some free 64k */ + bm = (memmap *)CKSEG0ADDR(0x28000); + + bitmap_size = rex_getbitmap(bm); + + for (i = 0; i < bitmap_size; i++) { + /* FIXME: very simplistically only add full sets of pages */ + if (bm->bitmap[i] == 0xff) + mem_size += (8 * bm->pagesize); + else if (!mem_size) + mem_start += (8 * bm->pagesize); + else { + memblock_add(mem_start, mem_size); + mem_start += mem_size + (8 * bm->pagesize); + mem_size = 0; + } + } + if (mem_size) + memblock_add(mem_start, mem_size); +} + +void __init prom_meminit(u32 magic) +{ + if (!prom_is_rex(magic)) + pmax_setup_memory_region(); + else + rex_setup_memory_region(); +} + +void __init prom_free_prom_memory(void) +{ + unsigned long end; + + /* + * Free everything below the kernel itself but leave + * the first page reserved for the exception handlers. + */ + +#if IS_ENABLED(CONFIG_DECLANCE) + /* + * Leave 128 KB reserved for Lance memory for + * IOASIC DECstations. + * + * XXX: save this address for use in dec_lance.c? + */ + if (IOASIC) + end = __pa(&_text) - 0x00020000; + else +#endif + end = __pa(&_text); + + free_init_pages("unused PROM memory", PAGE_SIZE, end); +} diff --git a/arch/mips/dec/reset.c b/arch/mips/dec/reset.c new file mode 100644 index 000000000..3df01f1da --- /dev/null +++ b/arch/mips/dec/reset.c @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Reset a DECstation machine. + * + * Copyright (C) 199x the Anonymous + * Copyright (C) 2001, 2002, 2003 Maciej W. Rozycki + */ +#include <linux/interrupt.h> +#include <linux/linkage.h> + +#include <asm/addrspace.h> + +typedef void __noreturn (* noret_func_t)(void); + +static inline void __noreturn back_to_prom(void) +{ + noret_func_t func = (void *)CKSEG1ADDR(0x1fc00000); + + func(); +} + +void __noreturn dec_machine_restart(char *command) +{ + back_to_prom(); +} + +void __noreturn dec_machine_halt(void) +{ + back_to_prom(); +} + +void __noreturn dec_machine_power_off(void) +{ + /* DECstations don't have a software power switch */ + back_to_prom(); +} + +irqreturn_t dec_intr_halt(int irq, void *dev_id) +{ + dec_machine_halt(); +} diff --git a/arch/mips/dec/setup.c b/arch/mips/dec/setup.c new file mode 100644 index 000000000..6c3704f51 --- /dev/null +++ b/arch/mips/dec/setup.c @@ -0,0 +1,784 @@ +/* + * System-specific setup, especially interrupts. + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 1998 Harald Koerfgen + * Copyright (C) 2000, 2001, 2002, 2003, 2005, 2020 Maciej W. Rozycki + */ +#include <linux/console.h> +#include <linux/export.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/ioport.h> +#include <linux/irq.h> +#include <linux/irqnr.h> +#include <linux/memblock.h> +#include <linux/param.h> +#include <linux/percpu-defs.h> +#include <linux/sched.h> +#include <linux/spinlock.h> +#include <linux/types.h> +#include <linux/pm.h> + +#include <asm/addrspace.h> +#include <asm/bootinfo.h> +#include <asm/cpu.h> +#include <asm/cpu-features.h> +#include <asm/cpu-type.h> +#include <asm/irq.h> +#include <asm/irq_cpu.h> +#include <asm/mipsregs.h> +#include <asm/page.h> +#include <asm/reboot.h> +#include <asm/sections.h> +#include <asm/time.h> +#include <asm/traps.h> +#include <asm/wbflush.h> + +#include <asm/dec/interrupts.h> +#include <asm/dec/ioasic.h> +#include <asm/dec/ioasic_addrs.h> +#include <asm/dec/ioasic_ints.h> +#include <asm/dec/kn01.h> +#include <asm/dec/kn02.h> +#include <asm/dec/kn02ba.h> +#include <asm/dec/kn02ca.h> +#include <asm/dec/kn03.h> +#include <asm/dec/kn230.h> +#include <asm/dec/system.h> + + +extern void dec_machine_restart(char *command); +extern void dec_machine_halt(void); +extern void dec_machine_power_off(void); +extern irqreturn_t dec_intr_halt(int irq, void *dev_id); + +unsigned long dec_kn_slot_base, dec_kn_slot_size; + +EXPORT_SYMBOL(dec_kn_slot_base); +EXPORT_SYMBOL(dec_kn_slot_size); + +int dec_tc_bus; + +DEFINE_SPINLOCK(ioasic_ssr_lock); +EXPORT_SYMBOL(ioasic_ssr_lock); + +volatile u32 *ioasic_base; + +EXPORT_SYMBOL(ioasic_base); + +/* + * IRQ routing and priority tables. Priorities are set as follows: + * + * KN01 KN230 KN02 KN02-BA KN02-CA KN03 + * + * MEMORY CPU CPU CPU ASIC CPU CPU + * RTC CPU CPU CPU ASIC CPU CPU + * DMA - - - ASIC ASIC ASIC + * SERIAL0 CPU CPU CSR ASIC ASIC ASIC + * SERIAL1 - - - ASIC - ASIC + * SCSI CPU CPU CSR ASIC ASIC ASIC + * ETHERNET CPU * CSR ASIC ASIC ASIC + * other - - - ASIC - - + * TC2 - - CSR CPU ASIC ASIC + * TC1 - - CSR CPU ASIC ASIC + * TC0 - - CSR CPU ASIC ASIC + * other - CPU - CPU ASIC ASIC + * other - - - - CPU CPU + * + * * -- shared with SCSI + */ + +int dec_interrupt[DEC_NR_INTS] = { + [0 ... DEC_NR_INTS - 1] = -1 +}; + +EXPORT_SYMBOL(dec_interrupt); + +int_ptr cpu_mask_nr_tbl[DEC_MAX_CPU_INTS][2] = { + { { .i = ~0 }, { .p = dec_intr_unimplemented } }, +}; +int_ptr asic_mask_nr_tbl[DEC_MAX_ASIC_INTS][2] = { + { { .i = ~0 }, { .p = asic_intr_unimplemented } }, +}; +int cpu_fpu_mask = DEC_CPU_IRQ_MASK(DEC_CPU_INR_FPU); +int *fpu_kstat_irq; + +static irq_handler_t busirq_handler; +static unsigned int busirq_flags = IRQF_NO_THREAD; + +/* + * Bus error (DBE/IBE exceptions and bus interrupts) handling setup. + */ +static void __init dec_be_init(void) +{ + switch (mips_machtype) { + case MACH_DS23100: /* DS2100/DS3100 Pmin/Pmax */ + mips_set_be_handler(dec_kn01_be_handler); + busirq_handler = dec_kn01_be_interrupt; + busirq_flags |= IRQF_SHARED; + dec_kn01_be_init(); + break; + case MACH_DS5000_1XX: /* DS5000/1xx 3min */ + case MACH_DS5000_XX: /* DS5000/xx Maxine */ + mips_set_be_handler(dec_kn02xa_be_handler); + busirq_handler = dec_kn02xa_be_interrupt; + dec_kn02xa_be_init(); + break; + case MACH_DS5000_200: /* DS5000/200 3max */ + case MACH_DS5000_2X0: /* DS5000/240 3max+ */ + case MACH_DS5900: /* DS5900 bigmax */ + mips_set_be_handler(dec_ecc_be_handler); + busirq_handler = dec_ecc_be_interrupt; + dec_ecc_be_init(); + break; + } +} + +void __init plat_mem_setup(void) +{ + board_be_init = dec_be_init; + + wbflush_setup(); + + _machine_restart = dec_machine_restart; + _machine_halt = dec_machine_halt; + pm_power_off = dec_machine_power_off; + + ioport_resource.start = ~0UL; + ioport_resource.end = 0UL; + + /* Stay away from the firmware working memory area for now. */ + memblock_reserve(PHYS_OFFSET, __pa_symbol(&_text) - PHYS_OFFSET); +} + +/* + * Machine-specific initialisation for KN01, aka DS2100 (aka Pmin) + * or DS3100 (aka Pmax). + */ +static int kn01_interrupt[DEC_NR_INTS] __initdata = { + [DEC_IRQ_CASCADE] = -1, + [DEC_IRQ_AB_RECV] = -1, + [DEC_IRQ_AB_XMIT] = -1, + [DEC_IRQ_DZ11] = DEC_CPU_IRQ_NR(KN01_CPU_INR_DZ11), + [DEC_IRQ_ASC] = -1, + [DEC_IRQ_FLOPPY] = -1, + [DEC_IRQ_FPU] = DEC_CPU_IRQ_NR(DEC_CPU_INR_FPU), + [DEC_IRQ_HALT] = -1, + [DEC_IRQ_ISDN] = -1, + [DEC_IRQ_LANCE] = DEC_CPU_IRQ_NR(KN01_CPU_INR_LANCE), + [DEC_IRQ_BUS] = DEC_CPU_IRQ_NR(KN01_CPU_INR_BUS), + [DEC_IRQ_PSU] = -1, + [DEC_IRQ_RTC] = DEC_CPU_IRQ_NR(KN01_CPU_INR_RTC), + [DEC_IRQ_SCC0] = -1, + [DEC_IRQ_SCC1] = -1, + [DEC_IRQ_SII] = DEC_CPU_IRQ_NR(KN01_CPU_INR_SII), + [DEC_IRQ_TC0] = -1, + [DEC_IRQ_TC1] = -1, + [DEC_IRQ_TC2] = -1, + [DEC_IRQ_TIMER] = -1, + [DEC_IRQ_VIDEO] = DEC_CPU_IRQ_NR(KN01_CPU_INR_VIDEO), + [DEC_IRQ_ASC_MERR] = -1, + [DEC_IRQ_ASC_ERR] = -1, + [DEC_IRQ_ASC_DMA] = -1, + [DEC_IRQ_FLOPPY_ERR] = -1, + [DEC_IRQ_ISDN_ERR] = -1, + [DEC_IRQ_ISDN_RXDMA] = -1, + [DEC_IRQ_ISDN_TXDMA] = -1, + [DEC_IRQ_LANCE_MERR] = -1, + [DEC_IRQ_SCC0A_RXERR] = -1, + [DEC_IRQ_SCC0A_RXDMA] = -1, + [DEC_IRQ_SCC0A_TXERR] = -1, + [DEC_IRQ_SCC0A_TXDMA] = -1, + [DEC_IRQ_AB_RXERR] = -1, + [DEC_IRQ_AB_RXDMA] = -1, + [DEC_IRQ_AB_TXERR] = -1, + [DEC_IRQ_AB_TXDMA] = -1, + [DEC_IRQ_SCC1A_RXERR] = -1, + [DEC_IRQ_SCC1A_RXDMA] = -1, + [DEC_IRQ_SCC1A_TXERR] = -1, + [DEC_IRQ_SCC1A_TXDMA] = -1, +}; + +static int_ptr kn01_cpu_mask_nr_tbl[][2] __initdata = { + { { .i = DEC_CPU_IRQ_MASK(KN01_CPU_INR_BUS) }, + { .i = DEC_CPU_IRQ_NR(KN01_CPU_INR_BUS) } }, + { { .i = DEC_CPU_IRQ_MASK(KN01_CPU_INR_RTC) }, + { .i = DEC_CPU_IRQ_NR(KN01_CPU_INR_RTC) } }, + { { .i = DEC_CPU_IRQ_MASK(KN01_CPU_INR_DZ11) }, + { .i = DEC_CPU_IRQ_NR(KN01_CPU_INR_DZ11) } }, + { { .i = DEC_CPU_IRQ_MASK(KN01_CPU_INR_SII) }, + { .i = DEC_CPU_IRQ_NR(KN01_CPU_INR_SII) } }, + { { .i = DEC_CPU_IRQ_MASK(KN01_CPU_INR_LANCE) }, + { .i = DEC_CPU_IRQ_NR(KN01_CPU_INR_LANCE) } }, + { { .i = DEC_CPU_IRQ_ALL }, + { .p = cpu_all_int } }, +}; + +static void __init dec_init_kn01(void) +{ + /* IRQ routing. */ + memcpy(&dec_interrupt, &kn01_interrupt, + sizeof(kn01_interrupt)); + + /* CPU IRQ priorities. */ + memcpy(&cpu_mask_nr_tbl, &kn01_cpu_mask_nr_tbl, + sizeof(kn01_cpu_mask_nr_tbl)); + + mips_cpu_irq_init(); + +} /* dec_init_kn01 */ + + +/* + * Machine-specific initialisation for KN230, aka DS5100, aka MIPSmate. + */ +static int kn230_interrupt[DEC_NR_INTS] __initdata = { + [DEC_IRQ_CASCADE] = -1, + [DEC_IRQ_AB_RECV] = -1, + [DEC_IRQ_AB_XMIT] = -1, + [DEC_IRQ_DZ11] = DEC_CPU_IRQ_NR(KN230_CPU_INR_DZ11), + [DEC_IRQ_ASC] = -1, + [DEC_IRQ_FLOPPY] = -1, + [DEC_IRQ_FPU] = DEC_CPU_IRQ_NR(DEC_CPU_INR_FPU), + [DEC_IRQ_HALT] = DEC_CPU_IRQ_NR(KN230_CPU_INR_HALT), + [DEC_IRQ_ISDN] = -1, + [DEC_IRQ_LANCE] = DEC_CPU_IRQ_NR(KN230_CPU_INR_LANCE), + [DEC_IRQ_BUS] = DEC_CPU_IRQ_NR(KN230_CPU_INR_BUS), + [DEC_IRQ_PSU] = -1, + [DEC_IRQ_RTC] = DEC_CPU_IRQ_NR(KN230_CPU_INR_RTC), + [DEC_IRQ_SCC0] = -1, + [DEC_IRQ_SCC1] = -1, + [DEC_IRQ_SII] = DEC_CPU_IRQ_NR(KN230_CPU_INR_SII), + [DEC_IRQ_TC0] = -1, + [DEC_IRQ_TC1] = -1, + [DEC_IRQ_TC2] = -1, + [DEC_IRQ_TIMER] = -1, + [DEC_IRQ_VIDEO] = -1, + [DEC_IRQ_ASC_MERR] = -1, + [DEC_IRQ_ASC_ERR] = -1, + [DEC_IRQ_ASC_DMA] = -1, + [DEC_IRQ_FLOPPY_ERR] = -1, + [DEC_IRQ_ISDN_ERR] = -1, + [DEC_IRQ_ISDN_RXDMA] = -1, + [DEC_IRQ_ISDN_TXDMA] = -1, + [DEC_IRQ_LANCE_MERR] = -1, + [DEC_IRQ_SCC0A_RXERR] = -1, + [DEC_IRQ_SCC0A_RXDMA] = -1, + [DEC_IRQ_SCC0A_TXERR] = -1, + [DEC_IRQ_SCC0A_TXDMA] = -1, + [DEC_IRQ_AB_RXERR] = -1, + [DEC_IRQ_AB_RXDMA] = -1, + [DEC_IRQ_AB_TXERR] = -1, + [DEC_IRQ_AB_TXDMA] = -1, + [DEC_IRQ_SCC1A_RXERR] = -1, + [DEC_IRQ_SCC1A_RXDMA] = -1, + [DEC_IRQ_SCC1A_TXERR] = -1, + [DEC_IRQ_SCC1A_TXDMA] = -1, +}; + +static int_ptr kn230_cpu_mask_nr_tbl[][2] __initdata = { + { { .i = DEC_CPU_IRQ_MASK(KN230_CPU_INR_BUS) }, + { .i = DEC_CPU_IRQ_NR(KN230_CPU_INR_BUS) } }, + { { .i = DEC_CPU_IRQ_MASK(KN230_CPU_INR_RTC) }, + { .i = DEC_CPU_IRQ_NR(KN230_CPU_INR_RTC) } }, + { { .i = DEC_CPU_IRQ_MASK(KN230_CPU_INR_DZ11) }, + { .i = DEC_CPU_IRQ_NR(KN230_CPU_INR_DZ11) } }, + { { .i = DEC_CPU_IRQ_MASK(KN230_CPU_INR_SII) }, + { .i = DEC_CPU_IRQ_NR(KN230_CPU_INR_SII) } }, + { { .i = DEC_CPU_IRQ_ALL }, + { .p = cpu_all_int } }, +}; + +static void __init dec_init_kn230(void) +{ + /* IRQ routing. */ + memcpy(&dec_interrupt, &kn230_interrupt, + sizeof(kn230_interrupt)); + + /* CPU IRQ priorities. */ + memcpy(&cpu_mask_nr_tbl, &kn230_cpu_mask_nr_tbl, + sizeof(kn230_cpu_mask_nr_tbl)); + + mips_cpu_irq_init(); + +} /* dec_init_kn230 */ + + +/* + * Machine-specific initialisation for KN02, aka DS5000/200, aka 3max. + */ +static int kn02_interrupt[DEC_NR_INTS] __initdata = { + [DEC_IRQ_CASCADE] = DEC_CPU_IRQ_NR(KN02_CPU_INR_CASCADE), + [DEC_IRQ_AB_RECV] = -1, + [DEC_IRQ_AB_XMIT] = -1, + [DEC_IRQ_DZ11] = KN02_IRQ_NR(KN02_CSR_INR_DZ11), + [DEC_IRQ_ASC] = KN02_IRQ_NR(KN02_CSR_INR_ASC), + [DEC_IRQ_FLOPPY] = -1, + [DEC_IRQ_FPU] = DEC_CPU_IRQ_NR(DEC_CPU_INR_FPU), + [DEC_IRQ_HALT] = -1, + [DEC_IRQ_ISDN] = -1, + [DEC_IRQ_LANCE] = KN02_IRQ_NR(KN02_CSR_INR_LANCE), + [DEC_IRQ_BUS] = DEC_CPU_IRQ_NR(KN02_CPU_INR_BUS), + [DEC_IRQ_PSU] = -1, + [DEC_IRQ_RTC] = DEC_CPU_IRQ_NR(KN02_CPU_INR_RTC), + [DEC_IRQ_SCC0] = -1, + [DEC_IRQ_SCC1] = -1, + [DEC_IRQ_SII] = -1, + [DEC_IRQ_TC0] = KN02_IRQ_NR(KN02_CSR_INR_TC0), + [DEC_IRQ_TC1] = KN02_IRQ_NR(KN02_CSR_INR_TC1), + [DEC_IRQ_TC2] = KN02_IRQ_NR(KN02_CSR_INR_TC2), + [DEC_IRQ_TIMER] = -1, + [DEC_IRQ_VIDEO] = -1, + [DEC_IRQ_ASC_MERR] = -1, + [DEC_IRQ_ASC_ERR] = -1, + [DEC_IRQ_ASC_DMA] = -1, + [DEC_IRQ_FLOPPY_ERR] = -1, + [DEC_IRQ_ISDN_ERR] = -1, + [DEC_IRQ_ISDN_RXDMA] = -1, + [DEC_IRQ_ISDN_TXDMA] = -1, + [DEC_IRQ_LANCE_MERR] = -1, + [DEC_IRQ_SCC0A_RXERR] = -1, + [DEC_IRQ_SCC0A_RXDMA] = -1, + [DEC_IRQ_SCC0A_TXERR] = -1, + [DEC_IRQ_SCC0A_TXDMA] = -1, + [DEC_IRQ_AB_RXERR] = -1, + [DEC_IRQ_AB_RXDMA] = -1, + [DEC_IRQ_AB_TXERR] = -1, + [DEC_IRQ_AB_TXDMA] = -1, + [DEC_IRQ_SCC1A_RXERR] = -1, + [DEC_IRQ_SCC1A_RXDMA] = -1, + [DEC_IRQ_SCC1A_TXERR] = -1, + [DEC_IRQ_SCC1A_TXDMA] = -1, +}; + +static int_ptr kn02_cpu_mask_nr_tbl[][2] __initdata = { + { { .i = DEC_CPU_IRQ_MASK(KN02_CPU_INR_BUS) }, + { .i = DEC_CPU_IRQ_NR(KN02_CPU_INR_BUS) } }, + { { .i = DEC_CPU_IRQ_MASK(KN02_CPU_INR_RTC) }, + { .i = DEC_CPU_IRQ_NR(KN02_CPU_INR_RTC) } }, + { { .i = DEC_CPU_IRQ_MASK(KN02_CPU_INR_CASCADE) }, + { .p = kn02_io_int } }, + { { .i = DEC_CPU_IRQ_ALL }, + { .p = cpu_all_int } }, +}; + +static int_ptr kn02_asic_mask_nr_tbl[][2] __initdata = { + { { .i = KN02_IRQ_MASK(KN02_CSR_INR_DZ11) }, + { .i = KN02_IRQ_NR(KN02_CSR_INR_DZ11) } }, + { { .i = KN02_IRQ_MASK(KN02_CSR_INR_ASC) }, + { .i = KN02_IRQ_NR(KN02_CSR_INR_ASC) } }, + { { .i = KN02_IRQ_MASK(KN02_CSR_INR_LANCE) }, + { .i = KN02_IRQ_NR(KN02_CSR_INR_LANCE) } }, + { { .i = KN02_IRQ_MASK(KN02_CSR_INR_TC2) }, + { .i = KN02_IRQ_NR(KN02_CSR_INR_TC2) } }, + { { .i = KN02_IRQ_MASK(KN02_CSR_INR_TC1) }, + { .i = KN02_IRQ_NR(KN02_CSR_INR_TC1) } }, + { { .i = KN02_IRQ_MASK(KN02_CSR_INR_TC0) }, + { .i = KN02_IRQ_NR(KN02_CSR_INR_TC0) } }, + { { .i = KN02_IRQ_ALL }, + { .p = kn02_all_int } }, +}; + +static void __init dec_init_kn02(void) +{ + /* IRQ routing. */ + memcpy(&dec_interrupt, &kn02_interrupt, + sizeof(kn02_interrupt)); + + /* CPU IRQ priorities. */ + memcpy(&cpu_mask_nr_tbl, &kn02_cpu_mask_nr_tbl, + sizeof(kn02_cpu_mask_nr_tbl)); + + /* KN02 CSR IRQ priorities. */ + memcpy(&asic_mask_nr_tbl, &kn02_asic_mask_nr_tbl, + sizeof(kn02_asic_mask_nr_tbl)); + + mips_cpu_irq_init(); + init_kn02_irqs(KN02_IRQ_BASE); + +} /* dec_init_kn02 */ + + +/* + * Machine-specific initialisation for KN02-BA, aka DS5000/1xx + * (xx = 20, 25, 33), aka 3min. Also applies to KN04(-BA), aka + * DS5000/150, aka 4min. + */ +static int kn02ba_interrupt[DEC_NR_INTS] __initdata = { + [DEC_IRQ_CASCADE] = DEC_CPU_IRQ_NR(KN02BA_CPU_INR_CASCADE), + [DEC_IRQ_AB_RECV] = -1, + [DEC_IRQ_AB_XMIT] = -1, + [DEC_IRQ_DZ11] = -1, + [DEC_IRQ_ASC] = IO_IRQ_NR(KN02BA_IO_INR_ASC), + [DEC_IRQ_FLOPPY] = -1, + [DEC_IRQ_FPU] = DEC_CPU_IRQ_NR(DEC_CPU_INR_FPU), + [DEC_IRQ_HALT] = DEC_CPU_IRQ_NR(KN02BA_CPU_INR_HALT), + [DEC_IRQ_ISDN] = -1, + [DEC_IRQ_LANCE] = IO_IRQ_NR(KN02BA_IO_INR_LANCE), + [DEC_IRQ_BUS] = IO_IRQ_NR(KN02BA_IO_INR_BUS), + [DEC_IRQ_PSU] = IO_IRQ_NR(KN02BA_IO_INR_PSU), + [DEC_IRQ_RTC] = IO_IRQ_NR(KN02BA_IO_INR_RTC), + [DEC_IRQ_SCC0] = IO_IRQ_NR(KN02BA_IO_INR_SCC0), + [DEC_IRQ_SCC1] = IO_IRQ_NR(KN02BA_IO_INR_SCC1), + [DEC_IRQ_SII] = -1, + [DEC_IRQ_TC0] = DEC_CPU_IRQ_NR(KN02BA_CPU_INR_TC0), + [DEC_IRQ_TC1] = DEC_CPU_IRQ_NR(KN02BA_CPU_INR_TC1), + [DEC_IRQ_TC2] = DEC_CPU_IRQ_NR(KN02BA_CPU_INR_TC2), + [DEC_IRQ_TIMER] = -1, + [DEC_IRQ_VIDEO] = -1, + [DEC_IRQ_ASC_MERR] = IO_IRQ_NR(IO_INR_ASC_MERR), + [DEC_IRQ_ASC_ERR] = IO_IRQ_NR(IO_INR_ASC_ERR), + [DEC_IRQ_ASC_DMA] = IO_IRQ_NR(IO_INR_ASC_DMA), + [DEC_IRQ_FLOPPY_ERR] = -1, + [DEC_IRQ_ISDN_ERR] = -1, + [DEC_IRQ_ISDN_RXDMA] = -1, + [DEC_IRQ_ISDN_TXDMA] = -1, + [DEC_IRQ_LANCE_MERR] = IO_IRQ_NR(IO_INR_LANCE_MERR), + [DEC_IRQ_SCC0A_RXERR] = IO_IRQ_NR(IO_INR_SCC0A_RXERR), + [DEC_IRQ_SCC0A_RXDMA] = IO_IRQ_NR(IO_INR_SCC0A_RXDMA), + [DEC_IRQ_SCC0A_TXERR] = IO_IRQ_NR(IO_INR_SCC0A_TXERR), + [DEC_IRQ_SCC0A_TXDMA] = IO_IRQ_NR(IO_INR_SCC0A_TXDMA), + [DEC_IRQ_AB_RXERR] = -1, + [DEC_IRQ_AB_RXDMA] = -1, + [DEC_IRQ_AB_TXERR] = -1, + [DEC_IRQ_AB_TXDMA] = -1, + [DEC_IRQ_SCC1A_RXERR] = IO_IRQ_NR(IO_INR_SCC1A_RXERR), + [DEC_IRQ_SCC1A_RXDMA] = IO_IRQ_NR(IO_INR_SCC1A_RXDMA), + [DEC_IRQ_SCC1A_TXERR] = IO_IRQ_NR(IO_INR_SCC1A_TXERR), + [DEC_IRQ_SCC1A_TXDMA] = IO_IRQ_NR(IO_INR_SCC1A_TXDMA), +}; + +static int_ptr kn02ba_cpu_mask_nr_tbl[][2] __initdata = { + { { .i = DEC_CPU_IRQ_MASK(KN02BA_CPU_INR_CASCADE) }, + { .p = kn02xa_io_int } }, + { { .i = DEC_CPU_IRQ_MASK(KN02BA_CPU_INR_TC2) }, + { .i = DEC_CPU_IRQ_NR(KN02BA_CPU_INR_TC2) } }, + { { .i = DEC_CPU_IRQ_MASK(KN02BA_CPU_INR_TC1) }, + { .i = DEC_CPU_IRQ_NR(KN02BA_CPU_INR_TC1) } }, + { { .i = DEC_CPU_IRQ_MASK(KN02BA_CPU_INR_TC0) }, + { .i = DEC_CPU_IRQ_NR(KN02BA_CPU_INR_TC0) } }, + { { .i = DEC_CPU_IRQ_ALL }, + { .p = cpu_all_int } }, +}; + +static int_ptr kn02ba_asic_mask_nr_tbl[][2] __initdata = { + { { .i = IO_IRQ_MASK(KN02BA_IO_INR_BUS) }, + { .i = IO_IRQ_NR(KN02BA_IO_INR_BUS) } }, + { { .i = IO_IRQ_MASK(KN02BA_IO_INR_RTC) }, + { .i = IO_IRQ_NR(KN02BA_IO_INR_RTC) } }, + { { .i = IO_IRQ_DMA }, + { .p = asic_dma_int } }, + { { .i = IO_IRQ_MASK(KN02BA_IO_INR_SCC0) }, + { .i = IO_IRQ_NR(KN02BA_IO_INR_SCC0) } }, + { { .i = IO_IRQ_MASK(KN02BA_IO_INR_SCC1) }, + { .i = IO_IRQ_NR(KN02BA_IO_INR_SCC1) } }, + { { .i = IO_IRQ_MASK(KN02BA_IO_INR_ASC) }, + { .i = IO_IRQ_NR(KN02BA_IO_INR_ASC) } }, + { { .i = IO_IRQ_MASK(KN02BA_IO_INR_LANCE) }, + { .i = IO_IRQ_NR(KN02BA_IO_INR_LANCE) } }, + { { .i = IO_IRQ_ALL }, + { .p = asic_all_int } }, +}; + +static void __init dec_init_kn02ba(void) +{ + /* IRQ routing. */ + memcpy(&dec_interrupt, &kn02ba_interrupt, + sizeof(kn02ba_interrupt)); + + /* CPU IRQ priorities. */ + memcpy(&cpu_mask_nr_tbl, &kn02ba_cpu_mask_nr_tbl, + sizeof(kn02ba_cpu_mask_nr_tbl)); + + /* I/O ASIC IRQ priorities. */ + memcpy(&asic_mask_nr_tbl, &kn02ba_asic_mask_nr_tbl, + sizeof(kn02ba_asic_mask_nr_tbl)); + + mips_cpu_irq_init(); + init_ioasic_irqs(IO_IRQ_BASE); + +} /* dec_init_kn02ba */ + + +/* + * Machine-specific initialisation for KN02-CA, aka DS5000/xx, + * (xx = 20, 25, 33), aka MAXine. Also applies to KN04(-CA), aka + * DS5000/50, aka 4MAXine. + */ +static int kn02ca_interrupt[DEC_NR_INTS] __initdata = { + [DEC_IRQ_CASCADE] = DEC_CPU_IRQ_NR(KN02CA_CPU_INR_CASCADE), + [DEC_IRQ_AB_RECV] = IO_IRQ_NR(KN02CA_IO_INR_AB_RECV), + [DEC_IRQ_AB_XMIT] = IO_IRQ_NR(KN02CA_IO_INR_AB_XMIT), + [DEC_IRQ_DZ11] = -1, + [DEC_IRQ_ASC] = IO_IRQ_NR(KN02CA_IO_INR_ASC), + [DEC_IRQ_FLOPPY] = IO_IRQ_NR(KN02CA_IO_INR_FLOPPY), + [DEC_IRQ_FPU] = DEC_CPU_IRQ_NR(DEC_CPU_INR_FPU), + [DEC_IRQ_HALT] = DEC_CPU_IRQ_NR(KN02CA_CPU_INR_HALT), + [DEC_IRQ_ISDN] = IO_IRQ_NR(KN02CA_IO_INR_ISDN), + [DEC_IRQ_LANCE] = IO_IRQ_NR(KN02CA_IO_INR_LANCE), + [DEC_IRQ_BUS] = DEC_CPU_IRQ_NR(KN02CA_CPU_INR_BUS), + [DEC_IRQ_PSU] = -1, + [DEC_IRQ_RTC] = DEC_CPU_IRQ_NR(KN02CA_CPU_INR_RTC), + [DEC_IRQ_SCC0] = IO_IRQ_NR(KN02CA_IO_INR_SCC0), + [DEC_IRQ_SCC1] = -1, + [DEC_IRQ_SII] = -1, + [DEC_IRQ_TC0] = IO_IRQ_NR(KN02CA_IO_INR_TC0), + [DEC_IRQ_TC1] = IO_IRQ_NR(KN02CA_IO_INR_TC1), + [DEC_IRQ_TC2] = -1, + [DEC_IRQ_TIMER] = DEC_CPU_IRQ_NR(KN02CA_CPU_INR_TIMER), + [DEC_IRQ_VIDEO] = IO_IRQ_NR(KN02CA_IO_INR_VIDEO), + [DEC_IRQ_ASC_MERR] = IO_IRQ_NR(IO_INR_ASC_MERR), + [DEC_IRQ_ASC_ERR] = IO_IRQ_NR(IO_INR_ASC_ERR), + [DEC_IRQ_ASC_DMA] = IO_IRQ_NR(IO_INR_ASC_DMA), + [DEC_IRQ_FLOPPY_ERR] = IO_IRQ_NR(IO_INR_FLOPPY_ERR), + [DEC_IRQ_ISDN_ERR] = IO_IRQ_NR(IO_INR_ISDN_ERR), + [DEC_IRQ_ISDN_RXDMA] = IO_IRQ_NR(IO_INR_ISDN_RXDMA), + [DEC_IRQ_ISDN_TXDMA] = IO_IRQ_NR(IO_INR_ISDN_TXDMA), + [DEC_IRQ_LANCE_MERR] = IO_IRQ_NR(IO_INR_LANCE_MERR), + [DEC_IRQ_SCC0A_RXERR] = IO_IRQ_NR(IO_INR_SCC0A_RXERR), + [DEC_IRQ_SCC0A_RXDMA] = IO_IRQ_NR(IO_INR_SCC0A_RXDMA), + [DEC_IRQ_SCC0A_TXERR] = IO_IRQ_NR(IO_INR_SCC0A_TXERR), + [DEC_IRQ_SCC0A_TXDMA] = IO_IRQ_NR(IO_INR_SCC0A_TXDMA), + [DEC_IRQ_AB_RXERR] = IO_IRQ_NR(IO_INR_AB_RXERR), + [DEC_IRQ_AB_RXDMA] = IO_IRQ_NR(IO_INR_AB_RXDMA), + [DEC_IRQ_AB_TXERR] = IO_IRQ_NR(IO_INR_AB_TXERR), + [DEC_IRQ_AB_TXDMA] = IO_IRQ_NR(IO_INR_AB_TXDMA), + [DEC_IRQ_SCC1A_RXERR] = -1, + [DEC_IRQ_SCC1A_RXDMA] = -1, + [DEC_IRQ_SCC1A_TXERR] = -1, + [DEC_IRQ_SCC1A_TXDMA] = -1, +}; + +static int_ptr kn02ca_cpu_mask_nr_tbl[][2] __initdata = { + { { .i = DEC_CPU_IRQ_MASK(KN02CA_CPU_INR_BUS) }, + { .i = DEC_CPU_IRQ_NR(KN02CA_CPU_INR_BUS) } }, + { { .i = DEC_CPU_IRQ_MASK(KN02CA_CPU_INR_RTC) }, + { .i = DEC_CPU_IRQ_NR(KN02CA_CPU_INR_RTC) } }, + { { .i = DEC_CPU_IRQ_MASK(KN02CA_CPU_INR_CASCADE) }, + { .p = kn02xa_io_int } }, + { { .i = DEC_CPU_IRQ_ALL }, + { .p = cpu_all_int } }, +}; + +static int_ptr kn02ca_asic_mask_nr_tbl[][2] __initdata = { + { { .i = IO_IRQ_DMA }, + { .p = asic_dma_int } }, + { { .i = IO_IRQ_MASK(KN02CA_IO_INR_SCC0) }, + { .i = IO_IRQ_NR(KN02CA_IO_INR_SCC0) } }, + { { .i = IO_IRQ_MASK(KN02CA_IO_INR_ASC) }, + { .i = IO_IRQ_NR(KN02CA_IO_INR_ASC) } }, + { { .i = IO_IRQ_MASK(KN02CA_IO_INR_LANCE) }, + { .i = IO_IRQ_NR(KN02CA_IO_INR_LANCE) } }, + { { .i = IO_IRQ_MASK(KN02CA_IO_INR_TC1) }, + { .i = IO_IRQ_NR(KN02CA_IO_INR_TC1) } }, + { { .i = IO_IRQ_MASK(KN02CA_IO_INR_TC0) }, + { .i = IO_IRQ_NR(KN02CA_IO_INR_TC0) } }, + { { .i = IO_IRQ_ALL }, + { .p = asic_all_int } }, +}; + +static void __init dec_init_kn02ca(void) +{ + /* IRQ routing. */ + memcpy(&dec_interrupt, &kn02ca_interrupt, + sizeof(kn02ca_interrupt)); + + /* CPU IRQ priorities. */ + memcpy(&cpu_mask_nr_tbl, &kn02ca_cpu_mask_nr_tbl, + sizeof(kn02ca_cpu_mask_nr_tbl)); + + /* I/O ASIC IRQ priorities. */ + memcpy(&asic_mask_nr_tbl, &kn02ca_asic_mask_nr_tbl, + sizeof(kn02ca_asic_mask_nr_tbl)); + + mips_cpu_irq_init(); + init_ioasic_irqs(IO_IRQ_BASE); + +} /* dec_init_kn02ca */ + + +/* + * Machine-specific initialisation for KN03, aka DS5000/240, + * aka 3max+ and DS5900, aka BIGmax. Also applies to KN05, aka + * DS5000/260, aka 4max+ and DS5900/260. + */ +static int kn03_interrupt[DEC_NR_INTS] __initdata = { + [DEC_IRQ_CASCADE] = DEC_CPU_IRQ_NR(KN03_CPU_INR_CASCADE), + [DEC_IRQ_AB_RECV] = -1, + [DEC_IRQ_AB_XMIT] = -1, + [DEC_IRQ_DZ11] = -1, + [DEC_IRQ_ASC] = IO_IRQ_NR(KN03_IO_INR_ASC), + [DEC_IRQ_FLOPPY] = -1, + [DEC_IRQ_FPU] = DEC_CPU_IRQ_NR(DEC_CPU_INR_FPU), + [DEC_IRQ_HALT] = DEC_CPU_IRQ_NR(KN03_CPU_INR_HALT), + [DEC_IRQ_ISDN] = -1, + [DEC_IRQ_LANCE] = IO_IRQ_NR(KN03_IO_INR_LANCE), + [DEC_IRQ_BUS] = DEC_CPU_IRQ_NR(KN03_CPU_INR_BUS), + [DEC_IRQ_PSU] = IO_IRQ_NR(KN03_IO_INR_PSU), + [DEC_IRQ_RTC] = DEC_CPU_IRQ_NR(KN03_CPU_INR_RTC), + [DEC_IRQ_SCC0] = IO_IRQ_NR(KN03_IO_INR_SCC0), + [DEC_IRQ_SCC1] = IO_IRQ_NR(KN03_IO_INR_SCC1), + [DEC_IRQ_SII] = -1, + [DEC_IRQ_TC0] = IO_IRQ_NR(KN03_IO_INR_TC0), + [DEC_IRQ_TC1] = IO_IRQ_NR(KN03_IO_INR_TC1), + [DEC_IRQ_TC2] = IO_IRQ_NR(KN03_IO_INR_TC2), + [DEC_IRQ_TIMER] = -1, + [DEC_IRQ_VIDEO] = -1, + [DEC_IRQ_ASC_MERR] = IO_IRQ_NR(IO_INR_ASC_MERR), + [DEC_IRQ_ASC_ERR] = IO_IRQ_NR(IO_INR_ASC_ERR), + [DEC_IRQ_ASC_DMA] = IO_IRQ_NR(IO_INR_ASC_DMA), + [DEC_IRQ_FLOPPY_ERR] = -1, + [DEC_IRQ_ISDN_ERR] = -1, + [DEC_IRQ_ISDN_RXDMA] = -1, + [DEC_IRQ_ISDN_TXDMA] = -1, + [DEC_IRQ_LANCE_MERR] = IO_IRQ_NR(IO_INR_LANCE_MERR), + [DEC_IRQ_SCC0A_RXERR] = IO_IRQ_NR(IO_INR_SCC0A_RXERR), + [DEC_IRQ_SCC0A_RXDMA] = IO_IRQ_NR(IO_INR_SCC0A_RXDMA), + [DEC_IRQ_SCC0A_TXERR] = IO_IRQ_NR(IO_INR_SCC0A_TXERR), + [DEC_IRQ_SCC0A_TXDMA] = IO_IRQ_NR(IO_INR_SCC0A_TXDMA), + [DEC_IRQ_AB_RXERR] = -1, + [DEC_IRQ_AB_RXDMA] = -1, + [DEC_IRQ_AB_TXERR] = -1, + [DEC_IRQ_AB_TXDMA] = -1, + [DEC_IRQ_SCC1A_RXERR] = IO_IRQ_NR(IO_INR_SCC1A_RXERR), + [DEC_IRQ_SCC1A_RXDMA] = IO_IRQ_NR(IO_INR_SCC1A_RXDMA), + [DEC_IRQ_SCC1A_TXERR] = IO_IRQ_NR(IO_INR_SCC1A_TXERR), + [DEC_IRQ_SCC1A_TXDMA] = IO_IRQ_NR(IO_INR_SCC1A_TXDMA), +}; + +static int_ptr kn03_cpu_mask_nr_tbl[][2] __initdata = { + { { .i = DEC_CPU_IRQ_MASK(KN03_CPU_INR_BUS) }, + { .i = DEC_CPU_IRQ_NR(KN03_CPU_INR_BUS) } }, + { { .i = DEC_CPU_IRQ_MASK(KN03_CPU_INR_RTC) }, + { .i = DEC_CPU_IRQ_NR(KN03_CPU_INR_RTC) } }, + { { .i = DEC_CPU_IRQ_MASK(KN03_CPU_INR_CASCADE) }, + { .p = kn03_io_int } }, + { { .i = DEC_CPU_IRQ_ALL }, + { .p = cpu_all_int } }, +}; + +static int_ptr kn03_asic_mask_nr_tbl[][2] __initdata = { + { { .i = IO_IRQ_DMA }, + { .p = asic_dma_int } }, + { { .i = IO_IRQ_MASK(KN03_IO_INR_SCC0) }, + { .i = IO_IRQ_NR(KN03_IO_INR_SCC0) } }, + { { .i = IO_IRQ_MASK(KN03_IO_INR_SCC1) }, + { .i = IO_IRQ_NR(KN03_IO_INR_SCC1) } }, + { { .i = IO_IRQ_MASK(KN03_IO_INR_ASC) }, + { .i = IO_IRQ_NR(KN03_IO_INR_ASC) } }, + { { .i = IO_IRQ_MASK(KN03_IO_INR_LANCE) }, + { .i = IO_IRQ_NR(KN03_IO_INR_LANCE) } }, + { { .i = IO_IRQ_MASK(KN03_IO_INR_TC2) }, + { .i = IO_IRQ_NR(KN03_IO_INR_TC2) } }, + { { .i = IO_IRQ_MASK(KN03_IO_INR_TC1) }, + { .i = IO_IRQ_NR(KN03_IO_INR_TC1) } }, + { { .i = IO_IRQ_MASK(KN03_IO_INR_TC0) }, + { .i = IO_IRQ_NR(KN03_IO_INR_TC0) } }, + { { .i = IO_IRQ_ALL }, + { .p = asic_all_int } }, +}; + +static void __init dec_init_kn03(void) +{ + /* IRQ routing. */ + memcpy(&dec_interrupt, &kn03_interrupt, + sizeof(kn03_interrupt)); + + /* CPU IRQ priorities. */ + memcpy(&cpu_mask_nr_tbl, &kn03_cpu_mask_nr_tbl, + sizeof(kn03_cpu_mask_nr_tbl)); + + /* I/O ASIC IRQ priorities. */ + memcpy(&asic_mask_nr_tbl, &kn03_asic_mask_nr_tbl, + sizeof(kn03_asic_mask_nr_tbl)); + + mips_cpu_irq_init(); + init_ioasic_irqs(IO_IRQ_BASE); + +} /* dec_init_kn03 */ + + +void __init arch_init_irq(void) +{ + switch (mips_machtype) { + case MACH_DS23100: /* DS2100/DS3100 Pmin/Pmax */ + dec_init_kn01(); + break; + case MACH_DS5100: /* DS5100 MIPSmate */ + dec_init_kn230(); + break; + case MACH_DS5000_200: /* DS5000/200 3max */ + dec_init_kn02(); + break; + case MACH_DS5000_1XX: /* DS5000/1xx 3min */ + dec_init_kn02ba(); + break; + case MACH_DS5000_2X0: /* DS5000/240 3max+ */ + case MACH_DS5900: /* DS5900 bigmax */ + dec_init_kn03(); + break; + case MACH_DS5000_XX: /* Personal DS5000/xx */ + dec_init_kn02ca(); + break; + case MACH_DS5800: /* DS5800 Isis */ + panic("Don't know how to set this up!"); + break; + case MACH_DS5400: /* DS5400 MIPSfair */ + panic("Don't know how to set this up!"); + break; + case MACH_DS5500: /* DS5500 MIPSfair-2 */ + panic("Don't know how to set this up!"); + break; + } + + /* Free the FPU interrupt if the exception is present. */ + if (!cpu_has_nofpuex) { + cpu_fpu_mask = 0; + dec_interrupt[DEC_IRQ_FPU] = -1; + } + /* Free the halt interrupt unused on R4k systems. */ + if (current_cpu_type() == CPU_R4000SC || + current_cpu_type() == CPU_R4400SC) + dec_interrupt[DEC_IRQ_HALT] = -1; + + /* Register board interrupts: FPU and cascade. */ + if (IS_ENABLED(CONFIG_MIPS_FP_SUPPORT) && + dec_interrupt[DEC_IRQ_FPU] >= 0 && cpu_has_fpu) { + struct irq_desc *desc_fpu; + int irq_fpu; + + irq_fpu = dec_interrupt[DEC_IRQ_FPU]; + if (request_irq(irq_fpu, no_action, IRQF_NO_THREAD, "fpu", + NULL)) + pr_err("Failed to register fpu interrupt\n"); + desc_fpu = irq_to_desc(irq_fpu); + fpu_kstat_irq = this_cpu_ptr(desc_fpu->kstat_irqs); + } + if (dec_interrupt[DEC_IRQ_CASCADE] >= 0) { + if (request_irq(dec_interrupt[DEC_IRQ_CASCADE], no_action, + IRQF_NO_THREAD, "cascade", NULL)) + pr_err("Failed to register cascade interrupt\n"); + } + /* Register the bus error interrupt. */ + if (dec_interrupt[DEC_IRQ_BUS] >= 0 && busirq_handler) { + if (request_irq(dec_interrupt[DEC_IRQ_BUS], busirq_handler, + busirq_flags, "bus error", busirq_handler)) + pr_err("Failed to register bus error interrupt\n"); + } + /* Register the HALT interrupt. */ + if (dec_interrupt[DEC_IRQ_HALT] >= 0) { + if (request_irq(dec_interrupt[DEC_IRQ_HALT], dec_intr_halt, + IRQF_NO_THREAD, "halt", NULL)) + pr_err("Failed to register halt interrupt\n"); + } +} + +asmlinkage unsigned int dec_irq_dispatch(unsigned int irq) +{ + do_IRQ(irq); + return 0; +} diff --git a/arch/mips/dec/tc.c b/arch/mips/dec/tc.c new file mode 100644 index 000000000..dba583976 --- /dev/null +++ b/arch/mips/dec/tc.c @@ -0,0 +1,95 @@ +/* + * TURBOchannel architecture calls. + * + * Copyright (c) Harald Koerfgen, 1998 + * Copyright (c) 2001, 2003, 2005, 2006 Maciej W. Rozycki + * Copyright (c) 2005 James Simmons + * + * This file is subject to the terms and conditions of the GNU + * General Public License. See the file "COPYING" in the main + * directory of this archive for more details. + */ +#include <linux/compiler.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/string.h> +#include <linux/tc.h> +#include <linux/types.h> + +#include <asm/addrspace.h> +#include <asm/bootinfo.h> +#include <asm/paccess.h> + +#include <asm/dec/interrupts.h> +#include <asm/dec/prom.h> +#include <asm/dec/system.h> + +/* + * Protected read byte from TURBOchannel slot space. + */ +int tc_preadb(u8 *valp, void __iomem *addr) +{ + return get_dbe(*valp, (u8 *)addr); +} + +/* + * Get TURBOchannel bus information as specified by the spec, plus + * the slot space base address and the number of slots. + */ +int __init tc_bus_get_info(struct tc_bus *tbus) +{ + if (!dec_tc_bus) + return -ENXIO; + + memcpy(&tbus->info, rex_gettcinfo(), sizeof(tbus->info)); + tbus->slot_base = CPHYSADDR((long)rex_slot_address(0)); + + switch (mips_machtype) { + case MACH_DS5000_200: + tbus->num_tcslots = 7; + break; + case MACH_DS5000_2X0: + case MACH_DS5900: + tbus->ext_slot_base = 0x20000000; + tbus->ext_slot_size = 0x20000000; + fallthrough; + case MACH_DS5000_1XX: + tbus->num_tcslots = 3; + break; + case MACH_DS5000_XX: + tbus->num_tcslots = 2; + default: + break; + } + return 0; +} + +/* + * Get the IRQ for the specified slot. + */ +void __init tc_device_get_irq(struct tc_dev *tdev) +{ + switch (tdev->slot) { + case 0: + tdev->interrupt = dec_interrupt[DEC_IRQ_TC0]; + break; + case 1: + tdev->interrupt = dec_interrupt[DEC_IRQ_TC1]; + break; + case 2: + tdev->interrupt = dec_interrupt[DEC_IRQ_TC2]; + break; + /* + * Yuck! DS5000/200 onboard devices + */ + case 5: + tdev->interrupt = dec_interrupt[DEC_IRQ_TC5]; + break; + case 6: + tdev->interrupt = dec_interrupt[DEC_IRQ_TC6]; + break; + default: + tdev->interrupt = -1; + break; + } +} diff --git a/arch/mips/dec/time.c b/arch/mips/dec/time.c new file mode 100644 index 000000000..c38686f89 --- /dev/null +++ b/arch/mips/dec/time.c @@ -0,0 +1,172 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 1991, 1992, 1995 Linus Torvalds + * Copyright (C) 2000, 2003 Maciej W. Rozycki + * + * This file contains the time handling details for PC-style clocks as + * found in some MIPS systems. + * + */ +#include <linux/bcd.h> +#include <linux/init.h> +#include <linux/mc146818rtc.h> +#include <linux/param.h> + +#include <asm/cpu-features.h> +#include <asm/ds1287.h> +#include <asm/time.h> +#include <asm/dec/interrupts.h> +#include <asm/dec/ioasic.h> +#include <asm/dec/machtype.h> + +void read_persistent_clock64(struct timespec64 *ts) +{ + unsigned int year, mon, day, hour, min, sec, real_year; + unsigned long flags; + + spin_lock_irqsave(&rtc_lock, flags); + + do { + sec = CMOS_READ(RTC_SECONDS); + min = CMOS_READ(RTC_MINUTES); + hour = CMOS_READ(RTC_HOURS); + day = CMOS_READ(RTC_DAY_OF_MONTH); + mon = CMOS_READ(RTC_MONTH); + year = CMOS_READ(RTC_YEAR); + /* + * The PROM will reset the year to either '72 or '73. + * Therefore we store the real year separately, in one + * of unused BBU RAM locations. + */ + real_year = CMOS_READ(RTC_DEC_YEAR); + } while (sec != CMOS_READ(RTC_SECONDS)); + + spin_unlock_irqrestore(&rtc_lock, flags); + + if (!(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { + sec = bcd2bin(sec); + min = bcd2bin(min); + hour = bcd2bin(hour); + day = bcd2bin(day); + mon = bcd2bin(mon); + year = bcd2bin(year); + } + + year += real_year - 72 + 2000; + + ts->tv_sec = mktime64(year, mon, day, hour, min, sec); + ts->tv_nsec = 0; +} + +/* + * In order to set the CMOS clock precisely, update_persistent_clock64 has to + * be called 500 ms after the second nowtime has started, because when + * nowtime is written into the registers of the CMOS clock, it will + * jump to the next second precisely 500 ms later. Check the Dallas + * DS1287 data sheet for details. + */ +int update_persistent_clock64(struct timespec64 now) +{ + time64_t nowtime = now.tv_sec; + int retval = 0; + int real_seconds, real_minutes, cmos_minutes; + unsigned char save_control, save_freq_select; + + /* irq are locally disabled here */ + spin_lock(&rtc_lock); + /* tell the clock it's being set */ + save_control = CMOS_READ(RTC_CONTROL); + CMOS_WRITE((save_control | RTC_SET), RTC_CONTROL); + + /* stop and reset prescaler */ + save_freq_select = CMOS_READ(RTC_FREQ_SELECT); + CMOS_WRITE((save_freq_select | RTC_DIV_RESET2), RTC_FREQ_SELECT); + + cmos_minutes = CMOS_READ(RTC_MINUTES); + if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) + cmos_minutes = bcd2bin(cmos_minutes); + + /* + * since we're only adjusting minutes and seconds, + * don't interfere with hour overflow. This avoids + * messing with unknown time zones but requires your + * RTC not to be off by more than 15 minutes + */ + real_minutes = div_s64_rem(nowtime, 60, &real_seconds); + if (((abs(real_minutes - cmos_minutes) + 15) / 30) & 1) + real_minutes += 30; /* correct for half hour time zone */ + real_minutes %= 60; + + if (abs(real_minutes - cmos_minutes) < 30) { + if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { + real_seconds = bin2bcd(real_seconds); + real_minutes = bin2bcd(real_minutes); + } + CMOS_WRITE(real_seconds, RTC_SECONDS); + CMOS_WRITE(real_minutes, RTC_MINUTES); + } else { + printk_once(KERN_NOTICE + "set_rtc_mmss: can't update from %d to %d\n", + cmos_minutes, real_minutes); + retval = -1; + } + + /* The following flags have to be released exactly in this order, + * otherwise the DS1287 will not reset the oscillator and will not + * update precisely 500 ms later. You won't find this mentioned + * in the Dallas Semiconductor data sheets, but who believes data + * sheets anyway ... -- Markus Kuhn + */ + CMOS_WRITE(save_control, RTC_CONTROL); + CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT); + spin_unlock(&rtc_lock); + + return retval; +} + +void __init plat_time_init(void) +{ + int ioasic_clock = 0; + u32 start, end; + int i = HZ / 8; + + /* Set up the rate of periodic DS1287 interrupts. */ + ds1287_set_base_clock(HZ); + + /* On some I/O ASIC systems we have the I/O ASIC's counter. */ + if (IOASIC) + ioasic_clock = dec_ioasic_clocksource_init() == 0; + if (cpu_has_counter) { + ds1287_timer_state(); + while (!ds1287_timer_state()) + ; + + start = read_c0_count(); + + while (i--) + while (!ds1287_timer_state()) + ; + + end = read_c0_count(); + + mips_hpt_frequency = (end - start) * 8; + printk(KERN_INFO "MIPS counter frequency %dHz\n", + mips_hpt_frequency); + + /* + * All R4k DECstations suffer from the CP0 Count erratum, + * so we can't use the timer as a clock source, and a clock + * event both at a time. An accurate wall clock is more + * important than a high-precision interval timer so only + * use the timer as a clock source, and not a clock event + * if there's no I/O ASIC counter available to serve as a + * clock source. + */ + if (!ioasic_clock) { + init_r4k_clocksource(); + mips_hpt_frequency = 0; + } + } + + ds1287_clockevent_init(dec_interrupt[DEC_IRQ_RTC]); +} diff --git a/arch/mips/dec/wbflush.c b/arch/mips/dec/wbflush.c new file mode 100644 index 000000000..dad64d178 --- /dev/null +++ b/arch/mips/dec/wbflush.c @@ -0,0 +1,92 @@ +/* + * Setup the right wbflush routine for the different DECstations. + * + * Created with information from: + * DECstation 3100 Desktop Workstation Functional Specification + * DECstation 5000/200 KN02 System Module Functional Specification + * mipsel-linux-objdump --disassemble vmunix | grep "wbflush" :-) + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 1998 Harald Koerfgen + * Copyright (C) 2002 Maciej W. Rozycki + */ + +#include <linux/export.h> +#include <linux/init.h> + +#include <asm/bootinfo.h> +#include <asm/wbflush.h> +#include <asm/barrier.h> + +static void wbflush_kn01(void); +static void wbflush_kn210(void); +static void wbflush_mips(void); + +void (*__wbflush) (void); + +void __init wbflush_setup(void) +{ + switch (mips_machtype) { + case MACH_DS23100: + case MACH_DS5000_200: /* DS5000 3max */ + __wbflush = wbflush_kn01; + break; + case MACH_DS5100: /* DS5100 MIPSMATE */ + __wbflush = wbflush_kn210; + break; + case MACH_DS5000_1XX: /* DS5000/100 3min */ + case MACH_DS5000_XX: /* Personal DS5000/2x */ + case MACH_DS5000_2X0: /* DS5000/240 3max+ */ + case MACH_DS5900: /* DS5900 bigmax */ + default: + __wbflush = wbflush_mips; + break; + } +} + +/* + * For the DS3100 and DS5000/200 the R2020/R3220 writeback buffer functions + * as part of Coprocessor 0. + */ +static void wbflush_kn01(void) +{ + asm(".set\tpush\n\t" + ".set\tnoreorder\n\t" + "1:\tbc0f\t1b\n\t" + "nop\n\t" + ".set\tpop"); +} + +/* + * For the DS5100 the writeback buffer seems to be a part of Coprocessor 3. + * But CP3 has to enabled first. + */ +static void wbflush_kn210(void) +{ + asm(".set\tpush\n\t" + ".set\tnoreorder\n\t" + "mfc0\t$2,$12\n\t" + "lui\t$3,0x8000\n\t" + "or\t$3,$2,$3\n\t" + "mtc0\t$3,$12\n\t" + "nop\n" + "1:\tbc3f\t1b\n\t" + "nop\n\t" + "mtc0\t$2,$12\n\t" + "nop\n\t" + ".set\tpop" + : : : "$2", "$3"); +} + +/* + * I/O ASIC systems use a standard writeback buffer that gets flushed + * upon an uncached read. + */ +static void wbflush_mips(void) +{ + __fast_iob(); +} +EXPORT_SYMBOL(__wbflush); |