Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/shaggy...
[linux-2.6] / arch / v850 / kernel / rte_cb_multi.c
1 /*
2  * include/asm-v850/rte_multi.c -- Support for Multi debugger monitor ROM
3  *      on Midas lab RTE-CB series of evaluation boards
4  *
5  *  Copyright (C) 2001,02,03  NEC Electronics Corporation
6  *  Copyright (C) 2001,02,03  Miles Bader <miles@gnu.org>
7  *
8  * This file is subject to the terms and conditions of the GNU General
9  * Public License.  See the file COPYING in the main directory of this
10  * archive for more details.
11  *
12  * Written by Miles Bader <miles@gnu.org>
13  */
14
15 #include <linux/init.h>
16
17 #include <asm/machdep.h>
18
19 #define IRQ_ADDR(irq) (0x80 + (irq) * 0x10)
20
21 /* A table of which interrupt vectors to install, since blindly
22    installing all of them makes the debugger stop working.  This is a
23    list of offsets in the interrupt vector area; each entry means to
24    copy that particular 16-byte vector.  An entry less than zero ends
25    the table.  */
26 static long multi_intv_install_table[] = {
27         /* Trap vectors */
28         0x40, 0x50,             
29
30 #ifdef CONFIG_RTE_CB_MULTI_DBTRAP
31         /* Illegal insn / dbtrap.  These are used by multi, so only handle
32            them if configured to do so.  */
33         0x60,
34 #endif
35
36         /* GINT1 - GINT3 (note, not GINT0!) */
37         IRQ_ADDR (IRQ_GINT(1)),
38         IRQ_ADDR (IRQ_GINT(2)),
39         IRQ_ADDR (IRQ_GINT(3)),
40
41         /* Timer D interrupts (up to 4 timers) */
42         IRQ_ADDR (IRQ_INTCMD(0)),
43 #if IRQ_INTCMD_NUM > 1
44         IRQ_ADDR (IRQ_INTCMD(1)),
45 #if IRQ_INTCMD_NUM > 2
46         IRQ_ADDR (IRQ_INTCMD(2)),
47 #if IRQ_INTCMD_NUM > 3
48         IRQ_ADDR (IRQ_INTCMD(3)),
49 #endif
50 #endif
51 #endif
52         
53         /* UART interrupts (up to 3 channels) */
54         IRQ_ADDR (IRQ_INTSER (0)), /* err */
55         IRQ_ADDR (IRQ_INTSR  (0)), /* rx */
56         IRQ_ADDR (IRQ_INTST  (0)), /* tx */
57 #if IRQ_INTSR_NUM > 1
58         IRQ_ADDR (IRQ_INTSER (1)), /* err */
59         IRQ_ADDR (IRQ_INTSR  (1)), /* rx */
60         IRQ_ADDR (IRQ_INTST  (1)), /* tx */
61 #if IRQ_INTSR_NUM > 2
62         IRQ_ADDR (IRQ_INTSER (2)), /* err */
63         IRQ_ADDR (IRQ_INTSR  (2)), /* rx */
64         IRQ_ADDR (IRQ_INTST  (2)), /* tx */
65 #endif
66 #endif
67
68         -1
69 };
70
71 /* Early initialization for kernel using Multi debugger ROM monitor.  */
72 void __init multi_init (void)
73 {
74         /* We're using the Multi debugger monitor, so we have to install
75            the interrupt vectors.  The monitor doesn't allow them to be
76            initially downloaded into their final destination because
77            it's in the monitor's scratch-RAM area.  Unfortunately, Multi
78            also doesn't deal correctly with ELF sections where the LMA
79            and VMA differ -- it just ignores the LMA -- so we can't use
80            that feature to work around the problem.  What we do instead
81            is just put the interrupt vectors into a normal section, and
82            do the necessary copying and relocation here.  Since the
83            interrupt vector basically only contains `jr' instructions
84            and no-ops, it's not that hard.  */
85         extern unsigned long _intv_load_start, _intv_start;
86         register unsigned long *src = &_intv_load_start;
87         register unsigned long *dst = (unsigned long *)INTV_BASE;
88         register unsigned long jr_fixup = (char *)&_intv_start - (char *)dst;
89         register long *ii;
90
91         /* Copy interrupt vectors as instructed by multi_intv_install_table. */
92         for (ii = multi_intv_install_table; *ii >= 0; ii++) {
93                 /* Copy 16-byte interrupt vector at offset *ii.  */
94                 int boffs;
95                 for (boffs = 0; boffs < 0x10; boffs += sizeof *src) {
96                         /* Copy a single word, fixing up the jump offs
97                            if it's a `jr' instruction.  */
98                         int woffs = (*ii + boffs) / sizeof *src;
99                         unsigned long word = src[woffs];
100
101                         if ((word & 0xFC0) == 0x780) {
102                                 /* A `jr' insn, fix up its offset (and yes, the
103                                    weird half-word swapping is intentional). */
104                                 unsigned short hi = word & 0xFFFF;
105                                 unsigned short lo = word >> 16;
106                                 unsigned long udisp22
107                                         = lo + ((hi & 0x3F) << 16);
108                                 long disp22 = (long)(udisp22 << 10) >> 10;
109
110                                 disp22 += jr_fixup;
111
112                                 hi = ((disp22 >> 16) & 0x3F) | 0x780;
113                                 lo = disp22 & 0xFFFF;
114
115                                 word = hi + (lo << 16);
116                         }
117
118                         dst[woffs] = word;
119                 }
120         }
121 }