aboutsummaryrefslogtreecommitdiffstats
path: root/arch/v850/kernel/rte_cb_multi.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/v850/kernel/rte_cb_multi.c')
-rw-r--r--arch/v850/kernel/rte_cb_multi.c121
1 files changed, 121 insertions, 0 deletions
diff --git a/arch/v850/kernel/rte_cb_multi.c b/arch/v850/kernel/rte_cb_multi.c
new file mode 100644
index 000000000000..963d55ab34cc
--- /dev/null
+++ b/arch/v850/kernel/rte_cb_multi.c
@@ -0,0 +1,121 @@
+/*
+ * include/asm-v850/rte_multi.c -- Support for Multi debugger monitor ROM
+ * on Midas lab RTE-CB series of evaluation boards
+ *
+ * Copyright (C) 2001,02,03 NEC Electronics Corporation
+ * Copyright (C) 2001,02,03 Miles Bader <miles@gnu.org>
+ *
+ * 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.
+ *
+ * Written by Miles Bader <miles@gnu.org>
+ */
+
+#include <linux/init.h>
+
+#include <asm/machdep.h>
+
+#define IRQ_ADDR(irq) (0x80 + (irq) * 0x10)
+
+/* A table of which interrupt vectors to install, since blindly
+ installing all of them makes the debugger stop working. This is a
+ list of offsets in the interrupt vector area; each entry means to
+ copy that particular 16-byte vector. An entry less than zero ends
+ the table. */
+static long multi_intv_install_table[] = {
+ /* Trap vectors */
+ 0x40, 0x50,
+
+#ifdef CONFIG_RTE_CB_MULTI_DBTRAP
+ /* Illegal insn / dbtrap. These are used by multi, so only handle
+ them if configured to do so. */
+ 0x60,
+#endif
+
+ /* GINT1 - GINT3 (note, not GINT0!) */
+ IRQ_ADDR (IRQ_GINT(1)),
+ IRQ_ADDR (IRQ_GINT(2)),
+ IRQ_ADDR (IRQ_GINT(3)),
+
+ /* Timer D interrupts (up to 4 timers) */
+ IRQ_ADDR (IRQ_INTCMD(0)),
+#if IRQ_INTCMD_NUM > 1
+ IRQ_ADDR (IRQ_INTCMD(1)),
+#if IRQ_INTCMD_NUM > 2
+ IRQ_ADDR (IRQ_INTCMD(2)),
+#if IRQ_INTCMD_NUM > 3
+ IRQ_ADDR (IRQ_INTCMD(3)),
+#endif
+#endif
+#endif
+
+ /* UART interrupts (up to 3 channels) */
+ IRQ_ADDR (IRQ_INTSER (0)), /* err */
+ IRQ_ADDR (IRQ_INTSR (0)), /* rx */
+ IRQ_ADDR (IRQ_INTST (0)), /* tx */
+#if IRQ_INTSR_NUM > 1
+ IRQ_ADDR (IRQ_INTSER (1)), /* err */
+ IRQ_ADDR (IRQ_INTSR (1)), /* rx */
+ IRQ_ADDR (IRQ_INTST (1)), /* tx */
+#if IRQ_INTSR_NUM > 2
+ IRQ_ADDR (IRQ_INTSER (2)), /* err */
+ IRQ_ADDR (IRQ_INTSR (2)), /* rx */
+ IRQ_ADDR (IRQ_INTST (2)), /* tx */
+#endif
+#endif
+
+ -1
+};
+
+/* Early initialization for kernel using Multi debugger ROM monitor. */
+void __init multi_init (void)
+{
+ /* We're using the Multi debugger monitor, so we have to install
+ the interrupt vectors. The monitor doesn't allow them to be
+ initially downloaded into their final destination because
+ it's in the monitor's scratch-RAM area. Unfortunately, Multi
+ also doesn't deal correctly with ELF sections where the LMA
+ and VMA differ -- it just ignores the LMA -- so we can't use
+ that feature to work around the problem. What we do instead
+ is just put the interrupt vectors into a normal section, and
+ do the necessary copying and relocation here. Since the
+ interrupt vector basically only contains `jr' instructions
+ and no-ops, it's not that hard. */
+ extern unsigned long _intv_load_start, _intv_start;
+ register unsigned long *src = &_intv_load_start;
+ register unsigned long *dst = (unsigned long *)INTV_BASE;
+ register unsigned long jr_fixup = (char *)&_intv_start - (char *)dst;
+ register long *ii;
+
+ /* Copy interrupt vectors as instructed by multi_intv_install_table. */
+ for (ii = multi_intv_install_table; *ii >= 0; ii++) {
+ /* Copy 16-byte interrupt vector at offset *ii. */
+ int boffs;
+ for (boffs = 0; boffs < 0x10; boffs += sizeof *src) {
+ /* Copy a single word, fixing up the jump offs
+ if it's a `jr' instruction. */
+ int woffs = (*ii + boffs) / sizeof *src;
+ unsigned long word = src[woffs];
+
+ if ((word & 0xFC0) == 0x780) {
+ /* A `jr' insn, fix up its offset (and yes, the
+ weird half-word swapping is intentional). */
+ unsigned short hi = word & 0xFFFF;
+ unsigned short lo = word >> 16;
+ unsigned long udisp22
+ = lo + ((hi & 0x3F) << 16);
+ long disp22 = (long)(udisp22 << 10) >> 10;
+
+ disp22 += jr_fixup;
+
+ hi = ((disp22 >> 16) & 0x3F) | 0x780;
+ lo = disp22 & 0xFFFF;
+
+ word = hi + (lo << 16);
+ }
+
+ dst[woffs] = word;
+ }
+ }
+}