summaryrefslogtreecommitdiffstats
path: root/arch/mips/dec
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1999-01-17 03:49:37 +0000
committerRalf Baechle <ralf@linux-mips.org>1999-01-17 03:49:37 +0000
commit317b660279d583d4db3fc112e3ef2e56b20bec87 (patch)
treeedd0330546242932075dae028c437a613e2ad6b8 /arch/mips/dec
parent78c388aed2b7184182c08428db1de6c872d815f5 (diff)
First round of commits for support for DECstations and Bagets.
Diffstat (limited to 'arch/mips/dec')
-rw-r--r--arch/mips/dec/Makefile30
-rw-r--r--arch/mips/dec/boot/Makefile26
-rw-r--r--arch/mips/dec/boot/decstation.c91
-rw-r--r--arch/mips/dec/boot/ld.ecoff43
-rw-r--r--arch/mips/dec/int-handler.S362
-rw-r--r--arch/mips/dec/irq.c270
-rw-r--r--arch/mips/dec/prom/Makefile29
-rw-r--r--arch/mips/dec/prom/cmdline.c47
-rw-r--r--arch/mips/dec/prom/dectypes.h14
-rw-r--r--arch/mips/dec/prom/identify.c99
-rw-r--r--arch/mips/dec/prom/init.c93
-rw-r--r--arch/mips/dec/prom/locore.S30
-rw-r--r--arch/mips/dec/prom/memory.c95
-rw-r--r--arch/mips/dec/prom/prom.h51
-rw-r--r--arch/mips/dec/promcon.c71
-rw-r--r--arch/mips/dec/reset.c22
-rw-r--r--arch/mips/dec/rtc-dec.c36
-rw-r--r--arch/mips/dec/serial.c97
-rw-r--r--arch/mips/dec/setup.c484
-rw-r--r--arch/mips/dec/time.c439
-rw-r--r--arch/mips/dec/wbflush.c111
21 files changed, 2540 insertions, 0 deletions
diff --git a/arch/mips/dec/Makefile b/arch/mips/dec/Makefile
new file mode 100644
index 000000000..0d23b8fbd
--- /dev/null
+++ b/arch/mips/dec/Makefile
@@ -0,0 +1,30 @@
+#
+# Makefile for the DECstation family specific parts of the kernel
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+
+.S.s:
+ $(CPP) $(CFLAGS) $< -o $*.s
+.S.o:
+ $(CC) $(CFLAGS) -c $< -o $*.o
+
+all: dec.o
+O_TARGET := dec.o
+O_OBJS := int-handler.o setup.o irq.o time.o reset.o rtc-dec.o wbflush.o
+
+ifdef CONFIG_PROM_CONSOLE
+O_OBJS += promcon.o
+endif
+
+ifdef CONFIG_SERIAL
+O_OBJS += serial.o
+endif
+
+int-handler.o: int-handler.S
+
+clean:
+
+include $(TOPDIR)/Rules.make
diff --git a/arch/mips/dec/boot/Makefile b/arch/mips/dec/boot/Makefile
new file mode 100644
index 000000000..1257b560c
--- /dev/null
+++ b/arch/mips/dec/boot/Makefile
@@ -0,0 +1,26 @@
+#
+# Makefile for the DECstation family specific parts of the kernel
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+
+.S.s:
+ $(CPP) $(CFLAGS) $< -o $*.s
+.S.o:
+ $(CC) $(CFLAGS) -c $< -o $*.o
+
+netboot: all
+ mipsel-linux-ld -N -G 0 -T ld.ecoff ../../boot/zImage \
+ dec_boot.o ramdisk.img -o nbImage
+
+all: dec_boot.o
+
+O_TARGET := dec_boot.o
+O_OBJS := decstation.o
+
+clean:
+ rm -f nbImage
+
+include $(TOPDIR)/Rules.make
diff --git a/arch/mips/dec/boot/decstation.c b/arch/mips/dec/boot/decstation.c
new file mode 100644
index 000000000..b8f8c2e05
--- /dev/null
+++ b/arch/mips/dec/boot/decstation.c
@@ -0,0 +1,91 @@
+/*
+ * arch/mips/dec/decstation.c
+ */
+#include <linux/config.h>
+
+#define RELOC
+#define INITRD
+#define DEBUG_BOOT
+
+/*
+ * Magic number indicating REX PROM available on DECSTATION.
+ */
+#define REX_PROM_MAGIC 0x30464354
+
+#define REX_PROM_CLEARCACHE 0x7c/4
+#define REX_PROM_PRINTF 0x30/4
+
+#define VEC_RESET 0xBFC00000 /* Prom base address */
+#define PMAX_PROM_ENTRY(x) (VEC_RESET+((x)*8)) /* Prom jump table */
+#define PMAX_PROM_PRINTF PMAX_PROM_ENTRY(17)
+
+#define PARAM (k_start + 0x2000)
+
+#define LOADER_TYPE (*(unsigned char *) (PARAM+0x210))
+#define INITRD_START (*(unsigned long *) (PARAM+0x218))
+#define INITRD_SIZE (*(unsigned long *) (PARAM+0x21c))
+
+extern int _ftext, _end; /* begin and end of kernel image */
+extern void *__rd_start, *__rd_end; /* begin and end of ramdisk image */
+extern void kernel_entry(int, char **, unsigned long, int *);
+
+void * memcpy(void * dest, const void *src, unsigned int count)
+{
+ unsigned long *tmp = (unsigned long *) dest, *s = (unsigned long *) src;
+
+ count >>= 2;
+ while (count--)
+ *tmp++ = *s++;
+
+ return dest;
+}
+
+void dec_entry(int argc, char **argv,
+ unsigned long magic, int *prom_vec)
+{
+ void (*rex_clear_cache)(void);
+ int (*prom_printf)(char *, ...);
+ unsigned long k_start, len;
+
+ /*
+ * The DS5100 leaves cpu with BEV enabled, clear it.
+ */
+ asm( "lui\t$8,0x3000\n\t"
+ "mtc0\t$8,$12\n\t"
+ ".section\t.sdata\n\t"
+ ".section\t.sbss\n\t"
+ ".section\t.text"
+ : : : "$8");
+
+#ifdef DEBUG_BOOT
+ if (magic == REX_PROM_MAGIC) {
+ prom_printf = (int (*)(char *, ...)) *(prom_vec + REX_PROM_PRINTF);
+ } else {
+ prom_printf = (int (*)(char *, ...)) PMAX_PROM_PRINTF;
+ }
+ prom_printf("Launching kernel...\n");
+#endif
+
+ k_start = (unsigned long) (&kernel_entry) & 0xffff0000;
+
+#ifdef RELOC
+ /*
+ * Now copy kernel image to it's destination.
+ */
+ len = ((unsigned long) (&_end) - k_start);
+ memcpy((void *)k_start, &_ftext, len);
+#endif
+
+ if (magic == REX_PROM_MAGIC) {
+ rex_clear_cache = (void (*)(void)) * (prom_vec + REX_PROM_CLEARCACHE);
+ rex_clear_cache();
+ }
+
+#ifdef CONFIG_BLK_DEV_INITRD
+ LOADER_TYPE = 1;
+ INITRD_START = (long)&__rd_start;
+ INITRD_SIZE = (long)&__rd_end - (long)&__rd_start;
+#endif
+
+ kernel_entry(argc, argv, magic, prom_vec);
+}
diff --git a/arch/mips/dec/boot/ld.ecoff b/arch/mips/dec/boot/ld.ecoff
new file mode 100644
index 000000000..8298ffae8
--- /dev/null
+++ b/arch/mips/dec/boot/ld.ecoff
@@ -0,0 +1,43 @@
+OUTPUT_FORMAT("ecoff-littlemips")
+OUTPUT_ARCH(mips)
+ENTRY(dec_entry)
+SECTIONS
+{
+ . = 0x80200000;
+
+ .text :
+ {
+ _ftext = .;
+ *(.text)
+ *(.fixup)
+ }
+ .rdata :
+ {
+ *(.rodata .rdata)
+ }
+ .data :
+ {
+ . = ALIGN(0x1000);
+ ramdisk.img (.data)
+ *(.data)
+ }
+ .sdata :
+ {
+ *(.sdata)
+ }
+ _gp = .;
+ .sbss :
+ {
+ *(.sbss)
+ *(.scommon)
+ }
+ .bss :
+ {
+ *(.dynbss)
+ *(.bss)
+ *(COMMON)
+ }
+ /DISCARD/ : {
+ *(.reginfo .mdebug .note)
+ }
+}
diff --git a/arch/mips/dec/int-handler.S b/arch/mips/dec/int-handler.S
new file mode 100644
index 000000000..db63068ca
--- /dev/null
+++ b/arch/mips/dec/int-handler.S
@@ -0,0 +1,362 @@
+/*
+ * arch/mips/dec/int-handler.S
+ *
+ * Copyright (C) 1995, 1996, 1997 Paul M. Antoine and Harald Koerfgen
+ *
+ * Written by Ralf Baechle and Andreas Busse, modified for DECStation
+ * support by Paul Antoine and Harald Koerfgen.
+ *
+ * completly rewritten:
+ * Copyright (C) 1998 Harald Koerfgen
+ *
+ */
+#include <asm/asm.h>
+#include <asm/regdef.h>
+#include <asm/mipsregs.h>
+#include <asm/stackframe.h>
+#include <asm/addrspace.h>
+
+#include <asm/dec/interrupts.h>
+
+
+ .text
+ .set noreorder
+/*
+ * decstation_handle_int: Interrupt handler for DECStations
+ *
+ * FIXME: Detection of spurious interrupts not yet implemented!
+ *
+ * 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
+ * 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
+ *
+ * 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
+ *
+ * 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
+ *
+ * We handle the IRQ according to _our_ priority.
+ * Priority is:
+ *
+ * Highest ---- RTC
+ * SCSI (if separate from TC)
+ * Ethernet (if separate from TC)
+ * Serial (if separate from TC)
+ * TurboChannel (if there is one!)
+ * Memory Controller (execept kmin)
+ * Lowest ---- Halt (if there is one!)
+ *
+ * then we just return, if multiple IRQs are pending then we will just take
+ * another exception, big deal.
+ *
+ */
+ .align 5
+ NESTED(decstation_handle_int, PT_SIZE, ra)
+ .set noat
+ SAVE_ALL
+ CLI # TEST: interrupts should be off
+ .set at
+ .set noreorder
+
+ /*
+ * Get pending Interrupts
+ */
+ mfc0 t0,CP0_CAUSE # get pending interrupts
+ mfc0 t2,CP0_STATUS
+ la t1,cpu_mask_tbl
+ and t0,t2 # isolate allowed ones
+
+ /* insert detection of spurious interrupts here */
+
+ /*
+ * Find irq with highest priority
+ */
+1: lw t2,(t1)
+ move t3,t0
+ and t3,t2
+ beq t3,zero,1b
+ addu t1,PTRSIZE # delay slot
+
+ /*
+ * Do the low-level stuff
+ */
+ lw a0,%lo(cpu_irq_nr-cpu_mask_tbl-PTRSIZE)(t1)
+ lw t0,%lo(cpu_ivec_tbl-cpu_mask_tbl-PTRSIZE)(t1)
+ bgez a0, handle_it # irq_nr >= 0?
+ # irq_nr < 0: a0 contains an address
+ nop
+ jr t0
+ nop # delay slot
+
+/*
+ * Handle "IRQ Controller" Interrupts
+ * Masked Interrupts are still visible and have to be masked "by hand".
+ * %hi(KN02_CSR_ADDR) does not work so all addresses are hardcoded :-(.
+ */
+ EXPORT(kn02_io_int)
+kn02_io_int: lui t0,0xbff0 # get interrupt status and mask
+ lw t0,(t0)
+ la t1,asic_mask_tbl
+ move t3,t0
+ sll t3,16 # shift interrupt status
+ b find_int
+ and t0,t3 # mask out allowed ones
+
+ EXPORT(kn03_io_int)
+kn03_io_int: lui t2,0xbf84 # upper part of IOASIC Address
+ lw t0,0x0110(t2) # get status: IOASIC isr
+ lw t3,0x0120(t2) # get mask: IOASIC isrm
+ la t1,asic_mask_tbl
+ b find_int
+ and t0,t3 # mask out allowed ones
+
+ EXPORT(kn02ba_io_int)
+kn02ba_io_int: lui t2,0xbc04
+ lw t0,0x0110(t2) # IOASIC isr, works for maxine also
+ lw t3,0x0120(t2) # IOASIC isrm
+ la t1,asic_mask_tbl
+ and t0,t3
+
+ /*
+ * Find irq with highest priority
+ */
+find_int: lw t2,(t1)
+ move t3,t0
+ and t3,t2
+ beq zero,t3,find_int
+ addu t1,PTRSIZE # delay slot
+
+ /*
+ * Do the low-level stuff
+ */
+ lw a0,%lo(asic_irq_nr-asic_mask_tbl-PTRSIZE)(t1)
+ nop
+
+handle_it: jal do_IRQ
+ move a1,sp
+ j ret_from_irq
+ nop
+
+ END(decstation_handle_int)
+/*
+ * Interrupt routines common to all DECStations first.
+ */
+ EXPORT(dec_intr_fpu)
+dec_intr_fpu: PANIC("Unimplemented FPU interrupt handler")
+
+/*
+ * Halt interrupt
+ */
+ EXPORT(intr_halt)
+intr_halt: la k0,0xbc000000
+ jr k0
+ nop
+
+/*
+ * Generic unimplemented interrupt routines - ivec_tbl is initialised to
+ * point all interrupts here. The table is then filled in by machine-specific
+ * initialisation in dec_setup().
+ */
+ EXPORT(dec_intr_unimplemented)
+dec_intr_unimplemented:
+ mfc0 a1,CP0_CAUSE # cheats way of printing an arg!
+ nop # to be sure...
+ PANIC("Unimplemented cpu interrupt! CP0_CAUSE: 0x%x");
+
+ EXPORT(asic_intr_unimplemented)
+asic_intr_unimplemented:
+ move a1,t0 # cheats way of printing an arg!
+ PANIC("Unimplemented asic interrupt! ASIC ISR: 0x%x");
+
+/*
+ * FIXME: This interrupt vector table is experimental. It is initialised with
+ * *_intr_unimplemented and filled in with the addresses of
+ * machine-specific interrupt routines in dec_setup() Paul 10/5/97.
+ *
+ * The mask_tbls contain the interrupt masks which are used. It is
+ * initialised with all possible interrupt status bits set, so that
+ * unused Interrupts are catched. Harald
+ */
+ .data
+ EXPORT(cpu_mask_tbl)
+cpu_mask_tbl:
+ .word 0x00000000
+ .word 0x00000000
+ .word 0x00000000
+ .word 0x00000000
+ .word 0x00000000
+ .word 0x00000000
+ .word 0x00000000 # these two are unlikely
+ .word 0x00000000 # to be used
+ .word 0x0000ff00 # End of list
+
+ EXPORT(cpu_irq_nr)
+cpu_irq_nr:
+ .word 0x00000000
+ .word 0x00000000
+ .word 0x00000000
+ .word 0x00000000
+ .word 0x00000000
+ .word 0x00000000
+ .word 0x00000000 # these two are unlikely
+ .word 0x00000000 # to be used
+ .word 0x00ffffff # End of list
+
+ EXPORT(cpu_ivec_tbl)
+cpu_ivec_tbl:
+ PTR dec_intr_unimplemented
+ PTR dec_intr_unimplemented
+ PTR dec_intr_unimplemented
+ PTR dec_intr_unimplemented
+ PTR dec_intr_unimplemented
+ PTR dec_intr_unimplemented
+ PTR dec_intr_unimplemented # these two are unlikely
+ PTR dec_intr_unimplemented # to be used
+ PTR dec_intr_unimplemented # EOL
+
+ EXPORT(asic_mask_tbl)
+asic_mask_tbl:
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ .word 0xffffffff # EOL
+
+ EXPORT(asic_irq_nr)
+asic_irq_nr:
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ .word 0xffffffff # EOL
+
+
diff --git a/arch/mips/dec/irq.c b/arch/mips/dec/irq.c
new file mode 100644
index 000000000..1090470a0
--- /dev/null
+++ b/arch/mips/dec/irq.c
@@ -0,0 +1,270 @@
+/*
+ * Code to handle DECstation IRQs plus some generic interrupt stuff.
+ *
+ * Copyright (C) 1992 Linus Torvalds
+ * Copyright (C) 1994, 1995, 1996, 1997 Ralf Baechle
+ *
+ * $Id: irq.c,v 1.1.2.1 1998/03/18 20:51:19 harald Exp $
+ */
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/kernel_stat.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/timex.h>
+#include <linux/malloc.h>
+#include <linux/random.h>
+
+#include <asm/bitops.h>
+#include <asm/bootinfo.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/mipsregs.h>
+#include <asm/system.h>
+#include <asm/wbflush.h>
+
+#include <asm/dec/interrupts.h>
+
+extern volatile unsigned int *isr; /* address of the interrupt status register */
+extern volatile unsigned int *imr; /* address of the interrupt mask register */
+extern decint_t dec_interrupt[NR_INTS];
+
+unsigned int local_bh_count[NR_CPUS];
+unsigned int local_irq_count[NR_CPUS];
+unsigned long spurious_count = 0;
+
+static inline void mask_irq(unsigned int irq_nr)
+{
+ unsigned int dummy;
+
+ if (dec_interrupt[irq_nr].iemask) { /* This is an ASIC interrupt */
+ *imr &= ~dec_interrupt[irq_nr].iemask;
+ dummy = *imr;
+ dummy = *imr;
+ } else /* This is a cpu interrupt */
+ set_cp0_status(ST0_IM, read_32bit_cp0_register(CP0_STATUS) & ~dec_interrupt[irq_nr].cpu_mask);
+}
+
+static inline void unmask_irq(unsigned int irq_nr)
+{
+ unsigned int dummy;
+
+ if (dec_interrupt[irq_nr].iemask) { /* This is an ASIC interrupt */
+ *imr |= dec_interrupt[irq_nr].iemask;
+ dummy = *imr;
+ dummy = *imr;
+ }
+ set_cp0_status(ST0_IM, read_32bit_cp0_register(CP0_STATUS) | dec_interrupt[irq_nr].cpu_mask);
+}
+
+void disable_irq(unsigned int irq_nr)
+{
+ unsigned long flags;
+
+ save_and_cli(flags);
+ mask_irq(irq_nr);
+ restore_flags(flags);
+}
+
+void enable_irq(unsigned int irq_nr)
+{
+ unsigned long flags;
+
+ save_and_cli(flags);
+ unmask_irq(irq_nr);
+ restore_flags(flags);
+}
+
+/*
+ * Pointers to the low-level handlers: first the general ones, then the
+ * fast ones, then the bad ones.
+ */
+extern void interrupt(void);
+
+static struct irqaction *irq_action[32] =
+{
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+int get_irq_list(char *buf)
+{
+ int i, len = 0;
+ struct irqaction *action;
+
+ for (i = 0; i < 32; i++) {
+ action = irq_action[i];
+ if (!action)
+ continue;
+ len += sprintf(buf + len, "%2d: %8d %c %s",
+ i, kstat.irqs[0][i],
+ (action->flags & SA_INTERRUPT) ? '+' : ' ',
+ action->name);
+ for (action = action->next; action; action = action->next) {
+ len += sprintf(buf + len, ",%s %s",
+ (action->flags & SA_INTERRUPT) ? " +" : "",
+ action->name);
+ }
+ len += sprintf(buf + len, "\n");
+ }
+ return len;
+}
+
+atomic_t __mips_bh_counter;
+
+/*
+ * do_IRQ handles IRQ's that have been installed without the
+ * SA_INTERRUPT flag: it uses the full signal-handling return
+ * and runs with other interrupts enabled. All relatively slow
+ * IRQ's should use this format: notably the keyboard/timer
+ * routines.
+ */
+asmlinkage void do_IRQ(int irq, struct pt_regs *regs)
+{
+ struct irqaction *action;
+ int do_random, cpu;
+
+ cpu = smp_processor_id();
+ hardirq_enter(cpu);
+ kstat.irqs[cpu][irq]++;
+
+ mask_irq(irq);
+ action = *(irq + irq_action);
+ if (action) {
+ if (!(action->flags & SA_INTERRUPT))
+ __sti();
+ action = *(irq + irq_action);
+ do_random = 0;
+ do {
+ do_random |= action->flags;
+ action->handler(irq, action->dev_id, regs);
+ action = action->next;
+ } while (action);
+ if (do_random & SA_SAMPLE_RANDOM)
+ add_interrupt_randomness(irq);
+ unmask_irq(irq);
+ __cli();
+ }
+ hardirq_exit(cpu);
+
+ /* unmasking and bottom half handling is done magically for us. */
+}
+
+/*
+ * Idea is to put all interrupts
+ * in a single table and differenciate them just by number.
+ */
+int setup_dec_irq(int irq, struct irqaction *new)
+{
+ int shared = 0;
+ struct irqaction *old, **p;
+ unsigned long flags;
+
+ p = irq_action + irq;
+ if ((old = *p) != NULL) {
+ /* Can't share interrupts unless both agree to */
+ if (!(old->flags & new->flags & SA_SHIRQ))
+ return -EBUSY;
+
+ /* Can't share interrupts unless both are same type */
+ if ((old->flags ^ new->flags) & SA_INTERRUPT)
+ return -EBUSY;
+
+ /* add new interrupt at end of irq queue */
+ do {
+ p = &old->next;
+ old = *p;
+ } while (old);
+ shared = 1;
+ }
+ if (new->flags & SA_SAMPLE_RANDOM)
+ rand_initialize_irq(irq);
+
+ save_and_cli(flags);
+ *p = new;
+
+ if (!shared) {
+ unmask_irq(irq);
+ }
+ restore_flags(flags);
+ return 0;
+}
+
+int request_irq(unsigned int irq,
+ void (*handler) (int, void *, struct pt_regs *),
+ unsigned long irqflags,
+ const char *devname,
+ void *dev_id)
+{
+ int retval;
+ struct irqaction *action;
+
+ if (irq >= 32)
+ return -EINVAL;
+ if (!handler)
+ return -EINVAL;
+
+ action = (struct irqaction *) kmalloc(sizeof(struct irqaction), GFP_KERNEL);
+ if (!action)
+ return -ENOMEM;
+
+ action->handler = handler;
+ action->flags = irqflags;
+ action->mask = 0;
+ action->name = devname;
+ action->next = NULL;
+ action->dev_id = dev_id;
+
+ retval = setup_dec_irq(irq, action);
+
+ if (retval)
+ kfree(action);
+ return retval;
+}
+
+void free_irq(unsigned int irq, void *dev_id)
+{
+ struct irqaction *action, **p;
+ unsigned long flags;
+
+ if (irq > 39) {
+ printk("Trying to free IRQ%d\n", irq);
+ return;
+ }
+ for (p = irq + irq_action; (action = *p) != NULL; p = &action->next) {
+ if (action->dev_id != dev_id)
+ continue;
+
+ /* Found it - now free it */
+ save_and_cli(flags);
+ *p = action->next;
+ if (!irq[irq_action])
+ mask_irq(irq);
+ restore_flags(flags);
+ kfree(action);
+ return;
+ }
+ printk("Trying to free free IRQ%d\n", irq);
+}
+
+unsigned long probe_irq_on(void)
+{
+ /* TODO */
+ return 0;
+}
+
+int probe_irq_off(unsigned long irqs)
+{
+ /* TODO */
+ return 0;
+}
+
+__initfunc(void init_IRQ(void))
+{
+ irq_setup();
+}
diff --git a/arch/mips/dec/prom/Makefile b/arch/mips/dec/prom/Makefile
new file mode 100644
index 000000000..33f906ece
--- /dev/null
+++ b/arch/mips/dec/prom/Makefile
@@ -0,0 +1,29 @@
+# $Id: $
+# Makefile for the DECstation prom monitor library routines
+# under Linux.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile...
+
+.S.s:
+ $(CPP) $(CFLAGS) $< -o $*.s
+.S.o:
+ $(CC) $(CFLAGS) -c $< -o $*.o
+
+OBJS = init.o memory.o cmdline.o identify.o locore.o
+
+all: rexlib.a
+
+rexlib.a: $(OBJS)
+ $(AR) rcs rexlib.a $(OBJS)
+ sync
+
+locore.o: locore.S
+
+dep:
+ $(CPP) -M *.c > .depend
+
+include $(TOPDIR)/Rules.make
diff --git a/arch/mips/dec/prom/cmdline.c b/arch/mips/dec/prom/cmdline.c
new file mode 100644
index 000000000..31d6ec318
--- /dev/null
+++ b/arch/mips/dec/prom/cmdline.c
@@ -0,0 +1,47 @@
+/*
+ * cmdline.c: read the command line passed to us by the PROM.
+ *
+ * Copyright (C) 1998 Harald Koerfgen
+ *
+ * $Id: $
+ */
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/config.h>
+#include <linux/string.h>
+
+#include <asm/bootinfo.h>
+
+#include "prom.h"
+
+#undef PROM_DEBUG
+
+#ifdef PROM_DEBUG
+extern int (*prom_printf)(char *, ...);
+#endif
+
+char arcs_cmdline[CL_SIZE];
+
+__initfunc(void prom_init_cmdline(int argc, char **argv, unsigned long magic))
+{
+ int start_arg, i;
+
+ /*
+ * collect args and prepare cmd_line
+ */
+ if (magic != REX_PROM_MAGIC)
+ start_arg = 1;
+ else
+ start_arg = 2;
+ for (i = start_arg; i < argc; i++) {
+ strcat(arcs_cmdline, argv[i]);
+ if (i < (argc - 1))
+ strcat(arcs_cmdline, " ");
+ }
+
+#ifdef PROM_DEBUG
+ prom_printf("arcs_cmdline: %s\n", &(arcs_cmdline[0]));
+#endif
+
+}
+
diff --git a/arch/mips/dec/prom/dectypes.h b/arch/mips/dec/prom/dectypes.h
new file mode 100644
index 000000000..707b6f1f5
--- /dev/null
+++ b/arch/mips/dec/prom/dectypes.h
@@ -0,0 +1,14 @@
+#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..65d7b5abf
--- /dev/null
+++ b/arch/mips/dec/prom/identify.c
@@ -0,0 +1,99 @@
+/*
+ * identify.c: machine identification code.
+ *
+ * Copyright (C) 1998 Harald Koerfgen and Paul M. Antoine
+ *
+ * $Id: $
+ */
+#include <linux/init.h>
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+
+#include <asm/bootinfo.h>
+
+#include "dectypes.h"
+#include "prom.h"
+
+extern char *(*prom_getenv)(char *);
+extern int (*prom_printf)(char *, ...);
+extern int (*rex_getsysid)(void);
+
+extern unsigned long mips_machgroup;
+extern unsigned long mips_machtype;
+
+__initfunc(void prom_identify_arch (unsigned int magic))
+{
+ unsigned char dec_cpunum, dec_firmrev, dec_etc;
+ int dec_systype;
+ unsigned long dec_sysid;
+
+ if (magic != REX_PROM_MAGIC) {
+ dec_sysid = simple_strtoul(prom_getenv("systype"), (char **)0, 0);
+ } else {
+ dec_sysid = rex_getsysid();
+ if (dec_sysid == 0) {
+ prom_printf("Zero sysid returned from PROMs! Assuming 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;
+
+ /* We're obviously one of the DEC machines */
+ mips_machgroup = MACH_GROUP_DEC;
+
+ /*
+ * FIXME: This may not be an exhaustive list of DECStations/Servers!
+ * Put all model-specific initialisation calls here.
+ */
+ prom_printf("This DECstation is a ");
+
+ switch (dec_systype) {
+ case DS2100_3100:
+ prom_printf("DS2100/3100\n");
+ mips_machtype = MACH_DS23100;
+ break;
+ case DS5100: /* DS5100 MIPSMATE */
+ prom_printf("DS5100\n");
+ mips_machtype = MACH_DS5100;
+ break;
+ case DS5000_200: /* DS5000 3max */
+ prom_printf("DS5000/200\n");
+ mips_machtype = MACH_DS5000_200;
+ break;
+ case DS5000_1XX: /* DS5000/100 3min */
+ prom_printf("DS5000/1xx\n");
+ mips_machtype = MACH_DS5000_1XX;
+ break;
+ case DS5000_2X0: /* DS5000/240 3max+ */
+ prom_printf("DS5000/2x0\n");
+ mips_machtype = MACH_DS5000_2X0;
+ break;
+ case DS5000_XX: /* Personal DS5000/2x */
+ prom_printf("Personal DS5000/xx\n");
+ mips_machtype = MACH_DS5000_XX;
+ break;
+ case DS5800: /* DS5800 Isis */
+ prom_printf("DS5800\n");
+ mips_machtype = MACH_DS5800;
+ break;
+ case DS5400: /* DS5400 MIPSfair */
+ prom_printf("DS5400\n");
+ mips_machtype = MACH_DS5400;
+ break;
+ case DS5500: /* DS5500 MIPSfair-2 */
+ prom_printf("DS5500\n");
+ mips_machtype = MACH_DS5500;
+ break;
+ default:
+ prom_printf("unknown, id is %x", dec_systype);
+ mips_machtype = MACH_DSUNKNOWN;
+ break;
+ }
+}
+
+
diff --git a/arch/mips/dec/prom/init.c b/arch/mips/dec/prom/init.c
new file mode 100644
index 000000000..717dda622
--- /dev/null
+++ b/arch/mips/dec/prom/init.c
@@ -0,0 +1,93 @@
+/*
+ * init.c: PROM library initialisation code.
+ *
+ * Copyright (C) 1998 Harald Koerfgen
+ *
+ * $Id: $
+ */
+#include <linux/init.h>
+#include <linux/config.h>
+#include "prom.h"
+
+/*
+ * PROM Interface (whichprom.c)
+ */
+typedef struct {
+ int pagesize;
+ unsigned char bitmap[0];
+} memmap;
+
+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);
+
+extern void prom_meminit(unsigned int);
+extern void prom_identify_arch(unsigned int);
+extern void prom_init_cmdline(int, char **, unsigned long);
+
+/*
+ * Detect which PROM's the DECSTATION has, and set the callback vectors
+ * appropriately.
+ */
+__initfunc(void which_prom(unsigned long magic, int *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 (magic == REX_PROM_MAGIC)
+ {
+ /*
+ * Set up prom abstraction structure with REX entry points.
+ */
+ rex_bootinit = (int (*)(void)) *(prom_vec + REX_PROM_BOOTINIT);
+ rex_bootread = (int (*)(void)) *(prom_vec + REX_PROM_BOOTREAD);
+ rex_getbitmap = (int (*)(memmap *)) *(prom_vec + REX_PROM_GETBITMAP);
+ prom_getchar = (int (*)(void)) *(prom_vec + REX_PROM_GETCHAR);
+ prom_getenv = (char *(*)(char *)) *(prom_vec + REX_PROM_GETENV);
+ rex_getsysid = (int (*)(void)) *(prom_vec + REX_PROM_GETSYSID);
+ rex_gettcinfo = (void *(*)(void)) *(prom_vec + REX_PROM_GETTCINFO);
+ prom_printf = (int (*)(char *, ...)) *(prom_vec + REX_PROM_PRINTF);
+ rex_slot_address = (unsigned long *(*)(int)) *(prom_vec + REX_PROM_SLOTADDR);
+ rex_clear_cache = (void (*)(void)) * (prom_vec + REX_PROM_CLEARCACHE);
+ }
+ else
+ {
+ /*
+ * Set up prom abstraction structure with non-REX entry points.
+ */
+ prom_getchar = (int (*)(void)) PMAX_PROM_GETCHAR;
+ prom_getenv = (char *(*)(char *)) PMAX_PROM_GETENV;
+ prom_printf = (int (*)(char *, ...)) PMAX_PROM_PRINTF;
+ pmax_open = (int (*)(char *, int)) PMAX_PROM_OPEN;
+ pmax_lseek = (int (*)(int, long, int)) PMAX_PROM_LSEEK;
+ pmax_read = (int (*)(int, void *, int)) PMAX_PROM_READ;
+ pmax_close = (int (*)(int)) PMAX_PROM_CLOSE;
+ }
+}
+
+__initfunc(int prom_init(int argc, char **argv,
+ unsigned long magic, int *prom_vec))
+{
+ /* Determine which PROM's we have (and therefore which machine we're on!) */
+ which_prom(magic, prom_vec);
+
+ prom_meminit(magic);
+ prom_identify_arch(magic);
+ prom_init_cmdline(argc, argv, magic);
+
+ return 0;
+}
diff --git a/arch/mips/dec/prom/locore.S b/arch/mips/dec/prom/locore.S
new file mode 100644
index 000000000..2255fd3e1
--- /dev/null
+++ b/arch/mips/dec/prom/locore.S
@@ -0,0 +1,30 @@
+/*
+ * 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..5fa828384
--- /dev/null
+++ b/arch/mips/dec/prom/memory.c
@@ -0,0 +1,95 @@
+/*
+ * memory.c: memory initialisation code.
+ *
+ * Copyright (C) 1998 Harald Koerfgen, Frieder Streffer and Paul M. Antoine
+ *
+ * $Id: $
+ */
+#include <asm/addrspace.h>
+#include <linux/init.h>
+#include <linux/config.h>
+#include <linux/string.h>
+#include "prom.h"
+
+typedef struct {
+ int pagesize;
+ unsigned char bitmap[0];
+} memmap;
+
+extern int (*rex_getbitmap)(memmap *);
+
+#undef PROM_DEBUG
+
+#ifdef PROM_DEBUG
+extern int (*prom_printf)(char *, ...);
+#endif
+
+extern unsigned long mips_memory_upper;
+
+volatile unsigned long mem_err = 0; /* So we know an error occured */
+
+/*
+ * 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
+
+__initfunc(unsigned long pmax_get_memory_size(void))
+{
+ volatile unsigned char *memory_page, dummy;
+ char old_handler[0x80];
+ extern char genexcept_early;
+
+ /* Install exception handler */
+ memcpy(&old_handler, (void *)(KSEG0 + 0x80), 0x80);
+ memcpy((void *)(KSEG0 + 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 *) KSEG1 + CHUNK_SIZE;
+ (mem_err== 0) && (memory_page < ((unsigned char *) KSEG1+0x1E000000));
+ memory_page += CHUNK_SIZE) {
+ dummy = *memory_page;
+ }
+ memcpy((void *)(KSEG0 + 0x80), &old_handler, 0x80);
+ return (unsigned long)memory_page - KSEG1 - CHUNK_SIZE;
+}
+
+/*
+ * Use the REX prom calls to get hold of the memory bitmap, and thence
+ * determine memory size.
+ */
+__initfunc(unsigned long rex_get_memory_size(void))
+{
+ int i, bitmap_size;
+ unsigned long mem_size = 0;
+ memmap *bm;
+
+ /* some free 64k */
+ bm = (memmap *) 0x80028000;
+
+ 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);
+ }
+ return (mem_size);
+}
+
+__initfunc(void prom_meminit(unsigned int magic))
+{
+ if (magic != REX_PROM_MAGIC)
+ mips_memory_upper = KSEG0 + pmax_get_memory_size();
+ else
+ mips_memory_upper = KSEG0 + rex_get_memory_size();
+
+#ifdef PROM_DEBUG
+ prom_printf("mips_memory_upper: 0x%08x\n", mips_memory_upper);
+#endif
+}
diff --git a/arch/mips/dec/prom/prom.h b/arch/mips/dec/prom/prom.h
new file mode 100644
index 000000000..c63372545
--- /dev/null
+++ b/arch/mips/dec/prom/prom.h
@@ -0,0 +1,51 @@
+/*
+ * Miscellaneous definitions used to call the routines contained in the boot
+ * PROMs on various models of DECSTATION's.
+ * the rights to redistribute these changes.
+ */
+
+#ifndef __ASM_DEC_PROM_H
+#define __ASM_DEC_PROM_H
+
+/*
+ * PMAX/3MAX PROM entry points for DS2100/3100's and DS5000/2xx's. Many of
+ * these will work for MIPSen as well!
+ */
+#define VEC_RESET 0xBFC00000 /* Prom base address */
+#define PMAX_PROM_ENTRY(x) (VEC_RESET+((x)*8)) /* Prom jump table */
+
+#define PMAX_PROM_HALT PMAX_PROM_ENTRY(2) /* valid on MIPSen */
+#define PMAX_PROM_AUTOBOOT PMAX_PROM_ENTRY(5) /* valid on MIPSen */
+#define PMAX_PROM_OPEN PMAX_PROM_ENTRY(6)
+#define PMAX_PROM_READ PMAX_PROM_ENTRY(7)
+#define PMAX_PROM_CLOSE PMAX_PROM_ENTRY(10)
+#define PMAX_PROM_LSEEK PMAX_PROM_ENTRY(11)
+#define PMAX_PROM_GETCHAR PMAX_PROM_ENTRY(12)
+#define PMAX_PROM_PUTCHAR PMAX_PROM_ENTRY(13) /* 12 on MIPSen */
+#define PMAX_PROM_GETS PMAX_PROM_ENTRY(15)
+#define PMAX_PROM_PRINTF PMAX_PROM_ENTRY(17)
+#define PMAX_PROM_GETENV PMAX_PROM_ENTRY(33) /* valid on MIPSen */
+
+/*
+ * Magic number indicating REX PROM available on DECSTATION. Found in
+ * register a2 on transfer of control to program from PROM.
+ */
+#define REX_PROM_MAGIC 0x30464354
+
+/*
+ * 3MIN/MAXINE PROM entry points for DS5000/1xx's, DS5000/xx's, and
+ * DS5000/2x0.
+ */
+#define REX_PROM_GETBITMAP 0x84/4 /* get mem bitmap */
+#define REX_PROM_GETCHAR 0x24/4 /* getch() */
+#define REX_PROM_GETENV 0x64/4 /* get env. variable */
+#define REX_PROM_GETSYSID 0x80/4 /* get system id */
+#define REX_PROM_GETTCINFO 0xa4/4
+#define REX_PROM_PRINTF 0x30/4 /* printf() */
+#define REX_PROM_SLOTADDR 0x6c/4 /* slotaddr */
+#define REX_PROM_BOOTINIT 0x54/4 /* open() */
+#define REX_PROM_BOOTREAD 0x58/4 /* read() */
+#define REX_PROM_CLEARCACHE 0x7c/4
+
+#endif /* __ASM_DEC_PROM_H */
+
diff --git a/arch/mips/dec/promcon.c b/arch/mips/dec/promcon.c
new file mode 100644
index 000000000..8399e5db1
--- /dev/null
+++ b/arch/mips/dec/promcon.c
@@ -0,0 +1,71 @@
+/*
+ * Wrap-around code for a console using the
+ * DECstation PROM io-routines.
+ *
+ * Copyright (c) 1998 Harald Koerfgen
+ */
+
+#include <linux/tty.h>
+#include <linux/major.h>
+#include <linux/ptrace.h>
+#include <linux/init.h>
+#include <linux/console.h>
+#include <linux/fs.h>
+
+extern int (*prom_getchar) (void);
+extern int (*prom_printf) (char *,...);
+
+static void prom_console_write(struct console *co, const char *s,
+ unsigned count)
+{
+ unsigned i;
+
+ /*
+ * Now, do each character
+ */
+ for (i = 0; i < count; i++) {
+ if (*s == 10)
+ prom_printf("%c", 13);
+ prom_printf("%c", *s++);
+ }
+}
+
+static int prom_console_wait_key(struct console *co)
+{
+ return prom_getchar();
+}
+
+__initfunc(static int prom_console_setup(struct console *co, char *options))
+{
+ return 0;
+}
+
+static kdev_t prom_console_device(struct console *c)
+{
+ return MKDEV(TTY_MAJOR, 64 + c->index);
+}
+
+static struct console sercons =
+{
+ "ttyS",
+ prom_console_write,
+ NULL,
+ prom_console_device,
+ prom_console_wait_key,
+ NULL,
+ prom_console_setup,
+ CON_PRINTBUFFER,
+ -1,
+ 0,
+ NULL
+};
+
+/*
+ * Register console.
+ */
+
+__initfunc(long prom_console_init(long kmem_start, long kmem_end))
+{
+ register_console(&sercons);
+ return kmem_start;
+}
diff --git a/arch/mips/dec/reset.c b/arch/mips/dec/reset.c
new file mode 100644
index 000000000..644935747
--- /dev/null
+++ b/arch/mips/dec/reset.c
@@ -0,0 +1,22 @@
+
+
+/*
+ * linux/arch/mips/dec/reset.c
+ *
+ * Reset a DECstation machine.
+ *
+ * $Id: reset.c,v 1.3 1998/03/04 08:29:10 ralf Exp $
+ */
+
+void dec_machine_restart(char *command)
+{
+}
+
+void dec_machine_halt(void)
+{
+}
+
+void dec_machine_power_off(void)
+{
+ /* DECstations don't have a software power switch */
+}
diff --git a/arch/mips/dec/rtc-dec.c b/arch/mips/dec/rtc-dec.c
new file mode 100644
index 000000000..f54bc89e2
--- /dev/null
+++ b/arch/mips/dec/rtc-dec.c
@@ -0,0 +1,36 @@
+
+/* $Id: rtc-jazz.c,v 1.2 1998/06/25 20:19:14 ralf Exp $
+
+ * 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.
+ *
+ * RTC routines for DECstation style attached Dallas chip.
+ *
+ * Copyright (C) 1998 by Ralf Baechle, Harald Koerfgen
+ */
+#include <linux/mc146818rtc.h>
+
+extern char *dec_rtc_base;
+
+static unsigned char dec_rtc_read_data(unsigned long addr)
+{
+ return (dec_rtc_base[addr * 4]);
+}
+
+static void dec_rtc_write_data(unsigned char data, unsigned long addr)
+{
+ dec_rtc_base[addr * 4] = data;
+}
+
+static int dec_rtc_bcd_mode(void)
+{
+ return 0;
+}
+
+struct rtc_ops dec_rtc_ops =
+{
+ &dec_rtc_read_data,
+ &dec_rtc_write_data,
+ &dec_rtc_bcd_mode
+};
diff --git a/arch/mips/dec/serial.c b/arch/mips/dec/serial.c
new file mode 100644
index 000000000..fcf324199
--- /dev/null
+++ b/arch/mips/dec/serial.c
@@ -0,0 +1,97 @@
+/*
+ * sercons.c
+ * choose the right serial device at boot time
+ *
+ * triemer 6-SEP-1998
+ * sercons.c is designed to allow the three different kinds
+ * of serial devices under the decstation world to co-exist
+ * in the same kernel. The idea here is to abstract
+ * the pieces of the drivers that are common to this file
+ * so that they do not clash at compile time and runtime.
+ *
+ * HK 16-SEP-1998 v0.002
+ * removed the PROM console as this is not a real serial
+ * device. Added support for PROM console in drivers/char/tty_io.c
+ * instead. Although it may work to enable more than one
+ * console device I strongly recommend to use only one.
+ */
+
+#include <asm/init.h>
+#include <asm/dec/machtype.h>
+
+#ifdef CONFIG_ZS
+extern int zs_init(void);
+#endif
+
+#ifdef CONFIG_DZ
+extern int dz_init(void);
+#endif
+
+#ifdef CONFIG_SERIAL_CONSOLE
+
+#ifdef CONFIG_ZS
+extern long zs_serial_console_init(long, long);
+#endif
+
+#ifdef CONFIG_DZ
+extern long dz_serial_console_init(long, long);
+#endif
+
+#endif
+
+/* rs_init - starts up the serial interface -
+ handle normal case of starting up the serial interface */
+
+#ifdef CONFIG_SERIAL
+
+__initfunc(int rs_init(void))
+{
+
+#if defined(CONFIG_ZS) && defined(CONFIG_DZ)
+ if (IOASIC)
+ return zs_init();
+ else
+ return dz_init();
+#else
+
+#ifdef CONFIG_ZS
+ return zs_init();
+#endif
+
+#ifdef CONFIG_DZ
+ return dz_init();
+#endif
+
+#endif
+}
+
+#endif
+
+#ifdef CONFIG_SERIAL_CONSOLE
+
+/* serial_console_init handles the special case of starting
+ * up the console on the serial port
+ */
+__initfunc(long serial_console_init(long kmem_start, long kmem_end))
+{
+#if defined(CONFIG_ZS) && defined(CONFIG_DZ)
+ if (IOASIC)
+ kmem_start = zs_serial_console_init(kmem_start, kmem_end);
+ else
+ kmem_start = dz_serial_console_init(kmem_start, kmem_end);
+#else
+
+#ifdef CONFIG_ZS
+ kmem_start = zs_serial_console_init(kmem_start, kmem_end);
+#endif
+
+#ifdef CONFIG_DZ
+ kmem_start = dz_serial_console_init(kmem_start, kmem_end);
+#endif
+
+#endif
+
+ return kmem_start;
+}
+
+#endif
diff --git a/arch/mips/dec/setup.c b/arch/mips/dec/setup.c
new file mode 100644
index 000000000..9999de881
--- /dev/null
+++ b/arch/mips/dec/setup.c
@@ -0,0 +1,484 @@
+/*
+ * Setup the interrupt stuff.
+ *
+ * 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
+ */
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/mc146818rtc.h>
+#include <linux/param.h>
+#include <asm/mipsregs.h>
+#include <asm/bootinfo.h>
+#include <asm/init.h>
+#include <asm/irq.h>
+#include <asm/reboot.h>
+
+#include <asm/dec/interrupts.h>
+#include <asm/dec/kn01.h>
+#include <asm/dec/kn02.h>
+#include <asm/dec/kn02xa.h>
+#include <asm/dec/kn03.h>
+#include <asm/dec/ioasic_ints.h>
+
+extern asmlinkage void decstation_handle_int(void);
+
+void dec_init_kn01(void);
+void dec_init_kn230(void);
+void dec_init_kn02(void);
+void dec_init_kn02ba(void);
+void dec_init_kn02ca(void);
+void dec_init_kn03(void);
+
+char *dec_rtc_base = (char *) KN01_RTC_BASE; /* Assume DS2100/3100 initially */
+
+decint_t dec_interrupt[NR_INTS];
+
+/*
+ * Information regarding the IRQ Controller
+ *
+ * isr and imr are also hardcoded for different machines in int_handler.S
+ */
+
+volatile unsigned int *isr = 0L; /* address of the interrupt status register */
+volatile unsigned int *imr = 0L; /* address of the interrupt mask register */
+
+extern void dec_machine_restart(char *command);
+extern void dec_machine_halt(void);
+extern void dec_machine_power_off(void);
+
+extern void wbflush_setup(void);
+
+extern struct rtc_ops dec_rtc_ops;
+
+extern void intr_halt(void);
+
+extern int setup_dec_irq(int, struct irqaction *);
+
+void (*board_time_init) (struct irqaction * irq);
+
+__initfunc(static void dec_irq_setup(void))
+{
+ switch (mips_machtype) {
+ case MACH_DS23100:
+ dec_init_kn01();
+ break;
+ case MACH_DS5100: /* DS5100 MIPSMATE */
+ dec_init_kn230();
+ break;
+ case MACH_DS5000_200: /* DS5000 3max */
+ dec_init_kn02();
+ break;
+ case MACH_DS5000_1XX: /* DS5000/100 3min */
+ dec_init_kn02ba();
+ break;
+ case MACH_DS5000_2X0: /* DS5000/240 3max+ */
+ dec_init_kn03();
+ break;
+ case MACH_DS5000_XX: /* Personal DS5000/2x */
+ 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;
+ }
+ set_except_vector(0, decstation_handle_int);
+}
+
+/*
+ * enable the periodic interrupts
+ */
+__initfunc(static void dec_time_init(struct irqaction *irq))
+{
+ /*
+ * Here we go, enable periodic rtc interrupts.
+ * Frequency is 128 Hz.
+ */
+ CMOS_WRITE(RTC_REF_CLCK_32KHZ | 0x9, RTC_REG_A);
+ CMOS_WRITE(CMOS_READ(RTC_REG_B) | RTC_PIE, RTC_REG_B);
+ setup_dec_irq(CLOCK, irq);
+}
+
+__initfunc(void decstation_setup(void))
+{
+ irq_setup = dec_irq_setup;
+ board_time_init = dec_time_init;
+
+ wbflush_setup();
+
+ _machine_restart = dec_machine_restart;
+ _machine_halt = dec_machine_halt;
+ _machine_power_off = dec_machine_power_off;
+
+ rtc_ops = &dec_rtc_ops;
+}
+
+/*
+ * Machine-specific initialisation for kn01, aka Pmax, aka DS2100, DS3100,
+ * and possibly also the DS5100.
+ */
+__initfunc(void dec_init_kn01(void))
+{
+ /*
+ * Setup some memory addresses.
+ */
+ dec_rtc_base = (char *) KN01_RTC_BASE;
+
+ /*
+ * Setup interrupt structure
+ */
+ dec_interrupt[CLOCK].cpu_mask = IE_IRQ3;
+ dec_interrupt[CLOCK].iemask = 0;
+ cpu_mask_tbl[0] = IE_IRQ3;
+ cpu_irq_nr[0] = CLOCK;
+
+ dec_interrupt[SCSI_INT].cpu_mask = IE_IRQ0;
+ dec_interrupt[SCSI_INT].iemask = 0;
+ cpu_mask_tbl[1] = IE_IRQ0;
+ cpu_irq_nr[1] = SCSI_INT;
+
+ dec_interrupt[ETHER].cpu_mask = IE_IRQ1;
+ dec_interrupt[ETHER].iemask = 0;
+ cpu_mask_tbl[2] = IE_IRQ1;
+ cpu_irq_nr[2] = ETHER;
+
+ dec_interrupt[SERIAL].cpu_mask = IE_IRQ2;
+ dec_interrupt[SERIAL].iemask = 0;
+ cpu_mask_tbl[3] = IE_IRQ2;
+ cpu_irq_nr[3] = SERIAL;
+
+ dec_interrupt[MEMORY].cpu_mask = IE_IRQ4;
+ dec_interrupt[MEMORY].iemask = 0;
+ cpu_mask_tbl[4] = IE_IRQ4;
+ cpu_irq_nr[4] = MEMORY;
+
+ dec_interrupt[FPU].cpu_mask = IE_IRQ5;
+ dec_interrupt[FPU].iemask = 0;
+ cpu_mask_tbl[5] = IE_IRQ5;
+ cpu_irq_nr[5] = FPU;
+} /* dec_init_kn01 */
+
+/*
+ * Machine-specific initialisation for kn230, aka MIPSmate, aka DS5100
+ *
+ * There are a lot of experiments to do, this is definitely incomplete.
+ */
+__initfunc(void dec_init_kn230(void))
+{
+ /*
+ * Setup some memory addresses.
+ */
+ dec_rtc_base = (char *) KN01_RTC_BASE;
+
+ /*
+ * Setup interrupt structure
+ */
+ dec_interrupt[CLOCK].cpu_mask = IE_IRQ2;
+ dec_interrupt[CLOCK].iemask = 0;
+ cpu_mask_tbl[0] = IE_IRQ2;
+ cpu_irq_nr[0] = CLOCK;
+
+ dec_interrupt[FPU].cpu_mask = IE_IRQ5;
+ dec_interrupt[FPU].iemask = 0;
+ cpu_mask_tbl[5] = IE_IRQ5;
+ cpu_irq_nr[5] = FPU;
+} /* dec_init_kn230 */
+
+/*
+ * Machine-specific initialisation for kn02, aka 3max, aka DS5000/2xx.
+ */
+__initfunc(void dec_init_kn02(void))
+{
+ /*
+ * Setup some memory addresses. FIXME: probably incomplete!
+ */
+ dec_rtc_base = (char *) KN02_RTC_BASE;
+ isr = (volatile unsigned int *) KN02_CSR_ADDR;
+ imr = (volatile unsigned int *) KN02_CSR_ADDR;
+
+ /*
+ * Setup IOASIC interrupt
+ */
+ cpu_ivec_tbl[1] = kn02_io_int;
+ cpu_mask_tbl[1] = IE_IRQ0;
+ cpu_irq_nr[1] = -1;
+ *imr = *imr & 0xff00ff00;
+
+ /*
+ * Setup interrupt structure
+ */
+ dec_interrupt[CLOCK].cpu_mask = IE_IRQ1;
+ dec_interrupt[CLOCK].iemask = 0;
+ cpu_mask_tbl[0] = IE_IRQ1;
+ cpu_irq_nr[0] = CLOCK;
+
+ dec_interrupt[SCSI_INT].cpu_mask = IE_IRQ0;
+ dec_interrupt[SCSI_INT].iemask = KN02_SLOT5;
+ asic_mask_tbl[0] = KN02_SLOT5;
+ asic_irq_nr[0] = SCSI_INT;
+
+ dec_interrupt[ETHER].cpu_mask = IE_IRQ0;
+ dec_interrupt[ETHER].iemask = KN02_SLOT6;
+ asic_mask_tbl[1] = KN02_SLOT6;
+ asic_irq_nr[1] = ETHER;
+
+ dec_interrupt[SERIAL].cpu_mask = IE_IRQ0;
+ dec_interrupt[SERIAL].iemask = KN02_SLOT7;
+ asic_mask_tbl[2] = KN02_SLOT7;
+ asic_irq_nr[2] = SERIAL;
+
+ dec_interrupt[TC0].cpu_mask = IE_IRQ0;
+ dec_interrupt[TC0].iemask = KN02_SLOT0;
+ asic_mask_tbl[3] = KN02_SLOT0;
+ asic_irq_nr[3] = TC0;
+
+ dec_interrupt[TC1].cpu_mask = IE_IRQ0;
+ dec_interrupt[TC1].iemask = KN02_SLOT1;
+ asic_mask_tbl[4] = KN02_SLOT1;
+ asic_irq_nr[4] = TC1;
+
+ dec_interrupt[TC2].cpu_mask = IE_IRQ0;
+ dec_interrupt[TC2].iemask = KN02_SLOT2;
+ asic_mask_tbl[5] = KN02_SLOT2;
+ asic_irq_nr[5] = TC2;
+
+ dec_interrupt[MEMORY].cpu_mask = IE_IRQ3;
+ dec_interrupt[MEMORY].iemask = 0;
+ cpu_mask_tbl[1] = IE_IRQ3;
+ cpu_irq_nr[1] = MEMORY;
+
+ dec_interrupt[FPU].cpu_mask = IE_IRQ5;
+ dec_interrupt[FPU].iemask = 0;
+ cpu_mask_tbl[2] = IE_IRQ5;
+ cpu_irq_nr[2] = FPU;
+
+} /* dec_init_kn02 */
+
+/*
+ * Machine-specific initialisation for kn02ba, aka 3min, aka DS5000/1xx.
+ */
+__initfunc(void dec_init_kn02ba(void))
+{
+ /*
+ * Setup some memory addresses.
+ */
+ dec_rtc_base = (char *) KN02XA_RTC_BASE;
+ isr = (volatile unsigned int *) KN02XA_SIR_ADDR;
+ imr = (volatile unsigned int *) KN02XA_SIRM_ADDR;
+
+ /*
+ * Setup IOASIC interrupt
+ */
+ cpu_mask_tbl[0] = IE_IRQ3;
+ cpu_irq_nr[0] = -1;
+ cpu_ivec_tbl[0] = kn02ba_io_int;
+ *imr = 0;
+
+ /*
+ * Setup interrupt structure
+ */
+ dec_interrupt[CLOCK].cpu_mask = IE_IRQ3;
+ dec_interrupt[CLOCK].iemask = KMIN_CLOCK;
+ asic_mask_tbl[0] = KMIN_CLOCK;
+ asic_irq_nr[0] = CLOCK;
+
+ dec_interrupt[SCSI_INT].cpu_mask = IE_IRQ3;
+ dec_interrupt[SCSI_INT].iemask = KMIN_SCSI_INTS;
+ asic_mask_tbl[1] = KMIN_SCSI_INTS;
+ asic_irq_nr[1] = SCSI_INT;
+
+ dec_interrupt[ETHER].cpu_mask = IE_IRQ3;
+ dec_interrupt[ETHER].iemask = LANCE_INTS;
+ asic_mask_tbl[2] = LANCE_INTS;
+ asic_irq_nr[2] = ETHER;
+
+ dec_interrupt[SERIAL].cpu_mask = IE_IRQ3;
+ dec_interrupt[SERIAL].iemask = SERIAL_INTS;
+ asic_mask_tbl[3] = SERIAL_INTS;
+ asic_irq_nr[3] = SERIAL;
+
+ dec_interrupt[MEMORY].cpu_mask = IE_IRQ3;
+ dec_interrupt[MEMORY].iemask = KMIN_TIMEOUT;
+ asic_mask_tbl[4] = KMIN_TIMEOUT;
+ asic_irq_nr[4] = MEMORY;
+
+ dec_interrupt[TC0].cpu_mask = IE_IRQ0;
+ dec_interrupt[TC0].iemask = 0;
+ cpu_mask_tbl[1] = IE_IRQ0;
+ cpu_irq_nr[1] = TC0;
+
+ dec_interrupt[TC1].cpu_mask = IE_IRQ1;
+ dec_interrupt[TC1].iemask = 0;
+ cpu_mask_tbl[2] = IE_IRQ1;
+ cpu_irq_nr[2] = TC1;
+
+ dec_interrupt[TC2].cpu_mask = IE_IRQ2;
+ dec_interrupt[TC2].iemask = 0;
+ cpu_mask_tbl[3] = IE_IRQ2;
+ cpu_irq_nr[3] = TC2;
+
+ dec_interrupt[HALT].cpu_mask = IE_IRQ4;
+ dec_interrupt[HALT].iemask = 0;
+ cpu_mask_tbl[4] = IE_IRQ4;
+ cpu_irq_nr[4] = HALT;
+
+ dec_interrupt[FPU].cpu_mask = IE_IRQ5;
+ dec_interrupt[FPU].iemask = 0;
+ cpu_mask_tbl[5] = IE_IRQ5;
+ cpu_irq_nr[5] = FPU;
+
+} /* dec_init_kn02ba */
+
+/*
+ * Machine-specific initialisation for kn02ca, aka maxine, aka DS5000/2x.
+ */
+__initfunc(void dec_init_kn02ca(void))
+{
+ /*
+ * Setup some memory addresses. FIXME: probably incomplete!
+ */
+ dec_rtc_base = (char *) KN02XA_RTC_BASE;
+ isr = (volatile unsigned int *) KN02XA_SIR_ADDR;
+ imr = (volatile unsigned int *) KN02XA_SIRM_ADDR;
+
+ /*
+ * Setup IOASIC interrupt
+ */
+ cpu_ivec_tbl[1] = kn02ba_io_int;
+ cpu_irq_nr[1] = -1;
+ cpu_mask_tbl[1] = IE_IRQ3;
+ *imr = 0;
+
+ /*
+ * Setup interrupt structure
+ */
+ dec_interrupt[CLOCK].cpu_mask = IE_IRQ1;
+ dec_interrupt[CLOCK].iemask = 0;
+ cpu_mask_tbl[0] = IE_IRQ1;
+ cpu_irq_nr[0] = CLOCK;
+
+ dec_interrupt[SCSI_INT].cpu_mask = IE_IRQ3;
+ dec_interrupt[SCSI_INT].iemask = SCSI_INTS;
+ asic_mask_tbl[0] = SCSI_INTS;
+ asic_irq_nr[0] = SCSI_INT;
+
+ dec_interrupt[ETHER].cpu_mask = IE_IRQ3;
+ dec_interrupt[ETHER].iemask = LANCE_INTS;
+ asic_mask_tbl[1] = LANCE_INTS;
+ asic_irq_nr[1] = ETHER;
+
+ dec_interrupt[SERIAL].cpu_mask = IE_IRQ3;
+ dec_interrupt[SERIAL].iemask = XINE_SERIAL_INTS;
+ asic_mask_tbl[2] = XINE_SERIAL_INTS;
+ asic_irq_nr[2] = SERIAL;
+
+ dec_interrupt[TC0].cpu_mask = IE_IRQ3;
+ dec_interrupt[TC0].iemask = MAXINE_TC0;
+ asic_mask_tbl[3] = MAXINE_TC0;
+ asic_irq_nr[3] = TC0;
+
+ dec_interrupt[TC1].cpu_mask = IE_IRQ3;
+ dec_interrupt[TC1].iemask = MAXINE_TC1;
+ asic_mask_tbl[4] = MAXINE_TC1;
+ asic_irq_nr[4] = TC1;
+
+ dec_interrupt[MEMORY].cpu_mask = IE_IRQ2;
+ dec_interrupt[MEMORY].iemask = 0;
+ cpu_mask_tbl[2] = IE_IRQ2;
+ cpu_irq_nr[2] = MEMORY;
+
+ dec_interrupt[HALT].cpu_mask = IE_IRQ4;
+ dec_interrupt[HALT].iemask = 0;
+ cpu_mask_tbl[3] = IE_IRQ4;
+ cpu_irq_nr[3] = HALT;
+
+ dec_interrupt[FPU].cpu_mask = IE_IRQ5;
+ dec_interrupt[FPU].iemask = 0;
+ cpu_mask_tbl[4] = IE_IRQ5;
+ cpu_irq_nr[4] = FPU;
+
+} /* dec_init_kn02ca */
+
+/*
+ * Machine-specific initialisation for kn03, aka 3max+, aka DS5000/240.
+ */
+__initfunc(void dec_init_kn03(void))
+{
+ /*
+ * Setup some memory addresses. FIXME: probably incomplete!
+ */
+ dec_rtc_base = (char *) KN03_RTC_BASE;
+ isr = (volatile unsigned int *) KN03_SIR_ADDR;
+ imr = (volatile unsigned int *) KN03_SIRM_ADDR;
+
+ /*
+ * Setup IOASIC interrupt
+ */
+ cpu_ivec_tbl[1] = kn03_io_int;
+ cpu_mask_tbl[1] = IE_IRQ0;
+ cpu_irq_nr[1] = -1;
+ *imr = 0;
+
+ /*
+ * Setup interrupt structure
+ */
+ dec_interrupt[CLOCK].cpu_mask = IE_IRQ1;
+ dec_interrupt[CLOCK].iemask = 0;
+ cpu_mask_tbl[0] = IE_IRQ1;
+ cpu_irq_nr[0] = CLOCK;
+
+ dec_interrupt[SCSI_INT].cpu_mask = IE_IRQ0;
+ dec_interrupt[SCSI_INT].iemask = SCSI_INTS;
+ asic_mask_tbl[0] = SCSI_INTS;
+ asic_irq_nr[0] = SCSI_INT;
+
+ dec_interrupt[ETHER].cpu_mask = IE_IRQ0;
+ dec_interrupt[ETHER].iemask = LANCE_INTS;
+ asic_mask_tbl[1] = LANCE_INTS;
+ asic_irq_nr[1] = ETHER;
+
+ dec_interrupt[SERIAL].cpu_mask = IE_IRQ0;
+ dec_interrupt[SERIAL].iemask = SERIAL_INTS;
+ asic_mask_tbl[2] = SERIAL_INTS;
+ asic_irq_nr[2] = SERIAL;
+
+ dec_interrupt[TC0].cpu_mask = IE_IRQ0;
+ dec_interrupt[TC0].iemask = KN03_TC0;
+ asic_mask_tbl[3] = KN03_TC0;
+ asic_irq_nr[3] = TC0;
+
+ dec_interrupt[TC1].cpu_mask = IE_IRQ0;
+ dec_interrupt[TC1].iemask = KN03_TC1;
+ asic_mask_tbl[4] = KN03_TC1;
+ asic_irq_nr[4] = TC1;
+
+ dec_interrupt[TC2].cpu_mask = IE_IRQ0;
+ dec_interrupt[TC2].iemask = KN03_TC2;
+ asic_mask_tbl[5] = KN03_TC2;
+ asic_irq_nr[5] = TC2;
+
+ dec_interrupt[MEMORY].cpu_mask = IE_IRQ3;
+ dec_interrupt[MEMORY].iemask = 0;
+ cpu_mask_tbl[2] = IE_IRQ3;
+ cpu_irq_nr[2] = MEMORY;
+
+ dec_interrupt[HALT].cpu_mask = IE_IRQ4;
+ dec_interrupt[HALT].iemask = 0;
+ cpu_mask_tbl[3] = IE_IRQ4;
+ cpu_irq_nr[3] = HALT;
+
+ dec_interrupt[FPU].cpu_mask = IE_IRQ5;
+ dec_interrupt[FPU].iemask = 0;
+ cpu_mask_tbl[4] = IE_IRQ5;
+ cpu_irq_nr[4] = FPU;
+
+} /* dec_init_kn03 */
diff --git a/arch/mips/dec/time.c b/arch/mips/dec/time.c
new file mode 100644
index 000000000..649df1b9c
--- /dev/null
+++ b/arch/mips/dec/time.c
@@ -0,0 +1,439 @@
+
+/*
+ * linux/arch/mips/kernel/time.c
+ *
+ * Copyright (C) 1991, 1992, 1995 Linus Torvalds
+ *
+ * This file contains the time handling details for PC-style clocks as
+ * found in some MIPS systems.
+ *
+ */
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/param.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+
+#include <asm/bootinfo.h>
+#include <asm/mipsregs.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#include <linux/mc146818rtc.h>
+#include <linux/timex.h>
+
+extern volatile unsigned long lost_ticks;
+
+/*
+ * Change this if you have some constant time drift
+ */
+/* This is the value for the PC-style PICs. */
+/* #define USECS_PER_JIFFY (1000020/HZ) */
+
+/* This is for machines which generate the exact clock. */
+#define USECS_PER_JIFFY (1000000/HZ)
+
+/* Cycle counter value at the previous timer interrupt.. */
+
+static unsigned int timerhi = 0, timerlo = 0;
+
+/*
+ * On MIPS only R4000 and better have a cycle counter.
+ *
+ * FIXME: Does playing with the RP bit in c0_status interfere with this code?
+ */
+static unsigned long do_fast_gettimeoffset(void)
+{
+ u32 count;
+ unsigned long res, tmp;
+
+ /* Last jiffy when do_fast_gettimeoffset() was called. */
+ static unsigned long last_jiffies = 0;
+ unsigned long quotient;
+
+ /*
+ * Cached "1/(clocks per usec)*2^32" value.
+ * It has to be recalculated once each jiffy.
+ */
+ static unsigned long cached_quotient = 0;
+
+ tmp = jiffies;
+
+ quotient = cached_quotient;
+
+ if (last_jiffies != tmp) {
+ last_jiffies = tmp;
+ __asm__(".set\tnoreorder\n\t"
+ ".set\tnoat\n\t"
+ ".set\tmips3\n\t"
+ "lwu\t%0,%2\n\t"
+ "dsll32\t$1,%1,0\n\t"
+ "or\t$1,$1,%0\n\t"
+ "ddivu\t$0,$1,%3\n\t"
+ "mflo\t$1\n\t"
+ "dsll32\t%0,%4,0\n\t"
+ "nop\n\t"
+ "ddivu\t$0,%0,$1\n\t"
+ "mflo\t%0\n\t"
+ ".set\tmips0\n\t"
+ ".set\tat\n\t"
+ ".set\treorder"
+ : "=&r"(quotient)
+ : "r"(timerhi),
+ "m"(timerlo),
+ "r"(tmp),
+ "r"(USECS_PER_JIFFY)
+ : "$1");
+ cached_quotient = quotient;
+ }
+ /* Get last timer tick in absolute kernel time */
+ count = read_32bit_cp0_register(CP0_COUNT);
+
+ /* .. relative to previous jiffy (32 bits is enough) */
+ count -= timerlo;
+//printk("count: %08lx, %08lx:%08lx\n", count, timerhi, timerlo);
+
+ __asm__("multu\t%1,%2\n\t"
+ "mfhi\t%0"
+ : "=r"(res)
+ : "r"(count),
+ "r"(quotient));
+
+ /*
+ * Due to possible jiffies inconsistencies, we need to check
+ * the result so that we'll get a timer that is monotonic.
+ */
+ if (res >= USECS_PER_JIFFY)
+ res = USECS_PER_JIFFY - 1;
+
+ return res;
+}
+
+/* This function must be called with interrupts disabled
+ * It was inspired by Steve McCanne's microtime-i386 for BSD. -- jrs
+ *
+ * However, the pc-audio speaker driver changes the divisor so that
+ * it gets interrupted rather more often - it loads 64 into the
+ * counter rather than 11932! This has an adverse impact on
+ * do_gettimeoffset() -- it stops working! What is also not
+ * good is that the interval that our timer function gets called
+ * is no longer 10.0002 ms, but 9.9767 ms. To get around this
+ * would require using a different timing source. Maybe someone
+ * could use the RTC - I know that this can interrupt at frequencies
+ * ranging from 8192Hz to 2Hz. If I had the energy, I'd somehow fix
+ * it so that at startup, the timer code in sched.c would select
+ * using either the RTC or the 8253 timer. The decision would be
+ * based on whether there was any other device around that needed
+ * to trample on the 8253. I'd set up the RTC to interrupt at 1024 Hz,
+ * and then do some jiggery to have a version of do_timer that
+ * advanced the clock by 1/1024 s. Every time that reached over 1/100
+ * of a second, then do all the old code. If the time was kept correct
+ * then do_gettimeoffset could just return 0 - there is no low order
+ * divider that can be accessed.
+ *
+ * Ideally, you would be able to use the RTC for the speaker driver,
+ * but it appears that the speaker driver really needs interrupt more
+ * often than every 120 us or so.
+ *
+ * Anyway, this needs more thought.... pjsg (1993-08-28)
+ *
+ * If you are really that interested, you should be reading
+ * comp.protocols.time.ntp!
+ */
+
+#define TICK_SIZE tick
+
+static unsigned long do_slow_gettimeoffset(void)
+{
+ /*
+ * This is a kludge until I find a way for the
+ * DECstations without bus cycle counter. HK
+ */
+ return 0;
+}
+
+static unsigned long (*do_gettimeoffset) (void) = do_slow_gettimeoffset;
+
+/*
+ * This version of gettimeofday has near microsecond resolution.
+ */
+void do_gettimeofday(struct timeval *tv)
+{
+ unsigned long flags;
+
+ save_and_cli(flags);
+ *tv = xtime;
+ tv->tv_usec += do_gettimeoffset();
+
+ /*
+ * xtime is atomically updated in timer_bh. lost_ticks is
+ * nonzero if the timer bottom half hasnt executed yet.
+ */
+ if (lost_ticks)
+ tv->tv_usec += USECS_PER_JIFFY;
+
+ restore_flags(flags);
+
+ if (tv->tv_usec >= 1000000) {
+ tv->tv_usec -= 1000000;
+ tv->tv_sec++;
+ }
+}
+
+void do_settimeofday(struct timeval *tv)
+{
+ cli();
+ /* This is revolting. We need to set the xtime.tv_usec
+ * correctly. However, the value in this location is
+ * is value at the last tick.
+ * Discover what correction gettimeofday
+ * would have done, and then undo it!
+ */
+ tv->tv_usec -= do_gettimeoffset();
+
+ if (tv->tv_usec < 0) {
+ tv->tv_usec += 1000000;
+ tv->tv_sec--;
+ }
+ xtime = *tv;
+ time_state = TIME_BAD;
+ time_maxerror = MAXPHASE;
+ time_esterror = MAXPHASE;
+ sti();
+}
+
+/*
+ * In order to set the CMOS clock precisely, set_rtc_mmss 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 Motorola
+ * MC146818A or Dallas DS12887 data sheet for details.
+ */
+static int set_rtc_mmss(unsigned long nowtime)
+{
+ int retval = 0;
+ int real_seconds, real_minutes, cmos_minutes;
+ unsigned char save_control, save_freq_select;
+
+ save_control = CMOS_READ(RTC_CONTROL); /* tell the clock it's being set */
+ CMOS_WRITE((save_control | RTC_SET), RTC_CONTROL);
+
+ save_freq_select = CMOS_READ(RTC_FREQ_SELECT); /* stop and reset prescaler */
+ 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)
+ BCD_TO_BIN(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_seconds = nowtime % 60;
+ real_minutes = nowtime / 60;
+ 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) {
+ BIN_TO_BCD(real_seconds);
+ BIN_TO_BCD(real_minutes);
+ }
+ CMOS_WRITE(real_seconds, RTC_SECONDS);
+ CMOS_WRITE(real_minutes, RTC_MINUTES);
+ } else
+ retval = -1;
+
+ /* The following flags have to be released exactly in this order,
+ * otherwise the DS12887 (popular MC146818A clone with integrated
+ * battery and quartz) 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);
+
+ return retval;
+}
+
+/* last time the cmos clock got updated */
+static long last_rtc_update = 0;
+
+/*
+ * timer_interrupt() needs to keep up the real-time clock,
+ * as well as call the "do_timer()" routine every clocktick
+ */
+static void inline
+ timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ volatile unsigned char dummy;
+
+ dummy = CMOS_READ(RTC_REG_C); /* ACK RTC Interrupt */
+ do_timer(regs);
+
+ /*
+ * If we have an externally synchronized Linux clock, then update
+ * CMOS clock accordingly every ~11 minutes. Set_rtc_mmss() has to be
+ * called as close as possible to 500 ms before the new second starts.
+ */
+ if (time_state != TIME_BAD && xtime.tv_sec > last_rtc_update + 660 &&
+ xtime.tv_usec > 500000 - (tick >> 1) &&
+ xtime.tv_usec < 500000 + (tick >> 1))
+ if (set_rtc_mmss(xtime.tv_sec) == 0)
+ last_rtc_update = xtime.tv_sec;
+ else
+ last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */
+}
+
+static void r4k_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ unsigned int count;
+
+ /*
+ * The cycle counter is only 32 bit which is good for about
+ * a minute at current count rates of upto 150MHz or so.
+ */
+ count = read_32bit_cp0_register(CP0_COUNT);
+ timerhi += (count < timerlo); /* Wrap around */
+ timerlo = count;
+
+ timer_interrupt(irq, dev_id, regs);
+}
+
+/* Converts Gregorian date to seconds since 1970-01-01 00:00:00.
+ * Assumes input in normal date format, i.e. 1980-12-31 23:59:59
+ * => year=1980, mon=12, day=31, hour=23, min=59, sec=59.
+ *
+ * [For the Julian calendar (which was used in Russia before 1917,
+ * Britain & colonies before 1752, anywhere else before 1582,
+ * and is still in use by some communities) leave out the
+ * -year/100+year/400 terms, and add 10.]
+ *
+ * This algorithm was first published by Gauss (I think).
+ *
+ * WARNING: this function will overflow on 2106-02-07 06:28:16 on
+ * machines were long is 32-bit! (However, as time_t is signed, we
+ * will already get problems at other places on 2038-01-19 03:14:08)
+ */
+static inline unsigned long mktime(unsigned int year, unsigned int mon,
+ unsigned int day, unsigned int hour,
+ unsigned int min, unsigned int sec)
+{
+ if (0 >= (int) (mon -= 2)) { /* 1..12 -> 11,12,1..10 */
+ mon += 12; /* Puts Feb last since it has leap day */
+ year -= 1;
+ }
+ return (((
+ (unsigned long) (year / 4 - year / 100 + year / 400 + 367 * mon / 12 + day) +
+ year * 365 - 719499
+ ) * 24 + hour /* now have hours */
+ ) * 60 + min /* now have minutes */
+ ) * 60 + sec; /* finally seconds */
+}
+
+char cyclecounter_available;
+
+static inline void init_cycle_counter(void)
+{
+ switch (mips_cputype) {
+ case CPU_UNKNOWN:
+ case CPU_R2000:
+ case CPU_R3000:
+ case CPU_R3000A:
+ case CPU_R3041:
+ case CPU_R3051:
+ case CPU_R3052:
+ case CPU_R3081:
+ case CPU_R3081E:
+ case CPU_R6000:
+ case CPU_R6000A:
+ case CPU_R8000: /* Not shure about that one, play safe */
+ cyclecounter_available = 0;
+ break;
+ case CPU_R4000PC:
+ case CPU_R4000SC:
+ case CPU_R4000MC:
+ case CPU_R4200:
+ case CPU_R4400PC:
+ case CPU_R4400SC:
+ case CPU_R4400MC:
+ case CPU_R4600:
+ case CPU_R10000:
+ case CPU_R4300:
+ case CPU_R4650:
+ case CPU_R4700:
+ case CPU_R5000:
+ case CPU_R5000A:
+ case CPU_R4640:
+ case CPU_NEVADA:
+ cyclecounter_available = 1;
+ break;
+ }
+}
+
+struct irqaction irq0 =
+{timer_interrupt, SA_INTERRUPT, 0,
+ "timer", NULL, NULL};
+
+
+void (*board_time_init) (struct irqaction * irq);
+
+__initfunc(void time_init(void))
+{
+ unsigned int year, mon, day, hour, min, sec;
+ int i;
+
+ /* The Linux interpretation of the CMOS clock register contents:
+ * When the Update-In-Progress (UIP) flag goes from 1 to 0, the
+ * RTC registers show the second which has precisely just started.
+ * Let's hope other operating systems interpret the RTC the same way.
+ */
+ /* read RTC exactly on falling edge of update flag */
+ for (i = 0; i < 1000000; i++) /* may take up to 1 second... */
+ if (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP)
+ break;
+ for (i = 0; i < 1000000; i++) /* must try at least 2.228 ms */
+ if (!(CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP))
+ break;
+ do { /* Isn't this overkill ? UIP above should guarantee consistency */
+ 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);
+ } while (sec != CMOS_READ(RTC_SECONDS));
+ if (!(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY) || RTC_ALWAYS_BCD) {
+ BCD_TO_BIN(sec);
+ BCD_TO_BIN(min);
+ BCD_TO_BIN(hour);
+ BCD_TO_BIN(day);
+ BCD_TO_BIN(mon);
+ BCD_TO_BIN(year);
+ }
+ /*
+ * The DECstation RTC is used as a TOY (Time Of Year).
+ * The PROM will reset the year to either '70, '71 or '72.
+ * This hack will only work until Dec 31 2001.
+ */
+ year += 1925;
+
+ xtime.tv_sec = mktime(year, mon, day, hour, min, sec);
+ xtime.tv_usec = 0;
+
+ init_cycle_counter();
+
+ if (cyclecounter_available) {
+ write_32bit_cp0_register(CP0_COUNT, 0);
+ do_gettimeoffset = do_fast_gettimeoffset;
+ irq0.handler = r4k_timer_interrupt;
+ }
+ board_time_init(&irq0);
+}
diff --git a/arch/mips/dec/wbflush.c b/arch/mips/dec/wbflush.c
new file mode 100644
index 000000000..3c7338c92
--- /dev/null
+++ b/arch/mips/dec/wbflush.c
@@ -0,0 +1,111 @@
+/*
+ * 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
+ */
+
+#include <asm/bootinfo.h>
+#include <asm/init.h>
+
+static void wbflush_kn01(void);
+static void wbflush_kn210(void);
+static void wbflush_kn02ba(void);
+static void wbflush_kn03(void);
+
+void (*__wbflush) (void);
+
+__initfunc(void wbflush_setup(void))
+{
+ switch (mips_machtype) {
+ case MACH_DS23100:
+ __wbflush = wbflush_kn01;
+ break;
+ case MACH_DS5100: /* DS5100 MIPSMATE */
+ __wbflush = wbflush_kn210;
+ break;
+ case MACH_DS5000_200: /* DS5000 3max */
+ __wbflush = wbflush_kn01;
+ break;
+ case MACH_DS5000_1XX: /* DS5000/100 3min */
+ __wbflush = wbflush_kn02ba;
+ break;
+ case MACH_DS5000_2X0: /* DS5000/240 3max+ */
+ __wbflush = wbflush_kn03;
+ break;
+ case MACH_DS5000_XX: /* Personal DS5000/2x */
+ __wbflush = wbflush_kn02ba;
+ break;
+ }
+}
+
+/*
+ * For the DS3100 and DS5000/200 the 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");
+}
+
+/*
+ * Looks like some magic with the System Interrupt Mask Register
+ * in the famous IOASIC for kmins and maxines.
+ */
+static void wbflush_kn02ba(void)
+{
+ asm(".set\tpush\n\t"
+ ".set\tnoreorder\n\t"
+ "lui\t$2,0xbc04\n\t"
+ "lw\t$3,0x120($2)\n\t"
+ "lw\t$3,0x120($2)\n\t"
+ ".set\tpop"
+ : : :"$2", "$3");
+}
+
+/*
+ * The DS500/2x0 doesnt need to write back the WB.
+ */
+static void wbflush_kn03(void)
+{
+ asm(".set\tpush\n\t"
+ ".set\tnoreorder\n\t"
+ "lui\t$2,0xbf84\n\t"
+ "lw\t$3,0x120($2)\n\t"
+ "lw\t$3,0x120($2)\n\t"
+ ".set\tpop"
+ : : :"$2", "$3");
+}