summaryrefslogtreecommitdiffstats
path: root/arch/v850/kernel
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /arch/v850/kernel
downloadop-kernel-dev-1da177e4c3f41524e886b7f1b8a0c1fc7321cac2.zip
op-kernel-dev-1da177e4c3f41524e886b7f1b8a0c1fc7321cac2.tar.gz
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
Diffstat (limited to 'arch/v850/kernel')
-rw-r--r--arch/v850/kernel/Makefile40
-rw-r--r--arch/v850/kernel/anna-rom.ld16
-rw-r--r--arch/v850/kernel/anna.c208
-rw-r--r--arch/v850/kernel/anna.ld20
-rw-r--r--arch/v850/kernel/as85ep1-rom.ld21
-rw-r--r--arch/v850/kernel/as85ep1.c240
-rw-r--r--arch/v850/kernel/as85ep1.ld49
-rw-r--r--arch/v850/kernel/asm-consts.c61
-rw-r--r--arch/v850/kernel/bug.c142
-rw-r--r--arch/v850/kernel/entry.S1121
-rw-r--r--arch/v850/kernel/fpga85e2c.c171
-rw-r--r--arch/v850/kernel/fpga85e2c.ld62
-rw-r--r--arch/v850/kernel/gbus_int.c271
-rw-r--r--arch/v850/kernel/head.S128
-rw-r--r--arch/v850/kernel/highres_timer.c132
-rw-r--r--arch/v850/kernel/init_task.c49
-rw-r--r--arch/v850/kernel/intv.S87
-rw-r--r--arch/v850/kernel/irq.c744
-rw-r--r--arch/v850/kernel/ma.c70
-rw-r--r--arch/v850/kernel/mach.c17
-rw-r--r--arch/v850/kernel/mach.h56
-rw-r--r--arch/v850/kernel/me2.c74
-rw-r--r--arch/v850/kernel/memcons.c135
-rw-r--r--arch/v850/kernel/module.c237
-rw-r--r--arch/v850/kernel/process.c236
-rw-r--r--arch/v850/kernel/procfs.c67
-rw-r--r--arch/v850/kernel/ptrace.c282
-rw-r--r--arch/v850/kernel/rte_cb.c200
-rw-r--r--arch/v850/kernel/rte_cb_leds.c138
-rw-r--r--arch/v850/kernel/rte_cb_multi.c121
-rw-r--r--arch/v850/kernel/rte_ma1_cb-rom.ld14
-rw-r--r--arch/v850/kernel/rte_ma1_cb.c106
-rw-r--r--arch/v850/kernel/rte_ma1_cb.ld57
-rw-r--r--arch/v850/kernel/rte_mb_a_pci.c796
-rw-r--r--arch/v850/kernel/rte_me2_cb.c300
-rw-r--r--arch/v850/kernel/rte_me2_cb.ld30
-rw-r--r--arch/v850/kernel/rte_nb85e_cb-multi.ld57
-rw-r--r--arch/v850/kernel/rte_nb85e_cb.c82
-rw-r--r--arch/v850/kernel/rte_nb85e_cb.ld22
-rw-r--r--arch/v850/kernel/semaphore.c166
-rw-r--r--arch/v850/kernel/setup.c286
-rw-r--r--arch/v850/kernel/signal.c525
-rw-r--r--arch/v850/kernel/sim.c179
-rw-r--r--arch/v850/kernel/sim.ld13
-rw-r--r--arch/v850/kernel/sim85e2.c201
-rw-r--r--arch/v850/kernel/sim85e2.ld36
-rw-r--r--arch/v850/kernel/simcons.c166
-rw-r--r--arch/v850/kernel/syscalls.c197
-rw-r--r--arch/v850/kernel/teg.c63
-rw-r--r--arch/v850/kernel/time.c198
-rw-r--r--arch/v850/kernel/v850_ksyms.c78
-rw-r--r--arch/v850/kernel/v850e2_cache.c127
-rw-r--r--arch/v850/kernel/v850e_cache.c174
-rw-r--r--arch/v850/kernel/v850e_intc.c104
-rw-r--r--arch/v850/kernel/v850e_timer_d.c54
-rw-r--r--arch/v850/kernel/v850e_utils.c62
-rw-r--r--arch/v850/kernel/vmlinux.lds.S285
57 files changed, 9573 insertions, 0 deletions
diff --git a/arch/v850/kernel/Makefile b/arch/v850/kernel/Makefile
new file mode 100644
index 0000000..3930482
--- /dev/null
+++ b/arch/v850/kernel/Makefile
@@ -0,0 +1,40 @@
+#
+# arch/v850/kernel/Makefile
+#
+# 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.
+#
+
+extra-y := head.o init_task.o vmlinux.lds
+
+obj-y += intv.o entry.o process.o syscalls.o time.o semaphore.o setup.o \
+ signal.o irq.o mach.o ptrace.o bug.o
+obj-$(CONFIG_MODULES) += module.o v850_ksyms.o
+# chip-specific code
+obj-$(CONFIG_V850E_MA1) += ma.o
+obj-$(CONFIG_V850E_ME2) += me2.o
+obj-$(CONFIG_V850E_TEG) += teg.o
+obj-$(CONFIG_V850E_AS85EP1) += as85ep1.o
+obj-$(CONFIG_V850E2_ANNA) += anna.o
+# platform-specific code
+obj-$(CONFIG_V850E_SIM) += sim.o simcons.o
+obj-$(CONFIG_V850E2_SIM85E2) += sim85e2.o memcons.o
+obj-$(CONFIG_V850E2_FPGA85E2C) += fpga85e2c.o memcons.o
+obj-$(CONFIG_RTE_CB) += rte_cb.o rte_cb_leds.o
+obj-$(CONFIG_RTE_CB_MA1) += rte_ma1_cb.o
+obj-$(CONFIG_RTE_CB_ME2) += rte_me2_cb.o
+obj-$(CONFIG_RTE_CB_NB85E) += rte_nb85e_cb.o
+obj-$(CONFIG_RTE_CB_MULTI) += rte_cb_multi.o
+obj-$(CONFIG_RTE_MB_A_PCI) += rte_mb_a_pci.o
+obj-$(CONFIG_RTE_GBUS_INT) += gbus_int.o
+# feature-specific code
+obj-$(CONFIG_V850E_INTC) += v850e_intc.o
+obj-$(CONFIG_V850E_TIMER_D) += v850e_timer_d.o v850e_utils.o
+obj-$(CONFIG_V850E_CACHE) += v850e_cache.o
+obj-$(CONFIG_V850E2_CACHE) += v850e2_cache.o
+obj-$(CONFIG_V850E_HIGHRES_TIMER) += highres_timer.o
+obj-$(CONFIG_PROC_FS) += procfs.o
diff --git a/arch/v850/kernel/anna-rom.ld b/arch/v850/kernel/anna-rom.ld
new file mode 100644
index 0000000..7c54e7e
--- /dev/null
+++ b/arch/v850/kernel/anna-rom.ld
@@ -0,0 +1,16 @@
+/* Linker script for the Midas labs Anna V850E2 evaluation board
+ (CONFIG_V850E2_ANNA), with kernel in ROM (CONFIG_ROM_KERNEL). */
+
+MEMORY {
+ /* 8MB of flash ROM. */
+ ROM : ORIGIN = 0, LENGTH = 0x00800000
+
+ /* 1MB of static RAM. This memory is mirrored 64 times. */
+ SRAM : ORIGIN = SRAM_ADDR, LENGTH = SRAM_SIZE
+ /* 64MB of DRAM. */
+ SDRAM : ORIGIN = SDRAM_ADDR, LENGTH = SDRAM_SIZE
+}
+
+SECTIONS {
+ ROMK_SECTIONS(ROM, SRAM)
+}
diff --git a/arch/v850/kernel/anna.c b/arch/v850/kernel/anna.c
new file mode 100644
index 0000000..6aaeab5
--- /dev/null
+++ b/arch/v850/kernel/anna.c
@@ -0,0 +1,208 @@
+/*
+ * arch/v850/kernel/anna.c -- Anna V850E2 evaluation chip/board
+ *
+ * Copyright (C) 2002,03 NEC Electronics Corporation
+ * Copyright (C) 2002,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/config.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/bootmem.h>
+#include <linux/major.h>
+#include <linux/irq.h>
+
+#include <asm/machdep.h>
+#include <asm/atomic.h>
+#include <asm/page.h>
+#include <asm/v850e_timer_d.h>
+#include <asm/v850e_uart.h>
+
+#include "mach.h"
+
+
+/* SRAM and SDRAM are vaguely contiguous (with a big hole in between; see
+ mach_reserve_bootmem for details); use both as one big area. */
+#define RAM_START SRAM_ADDR
+#define RAM_END (SDRAM_ADDR + SDRAM_SIZE)
+
+/* The bits of this port are connected to an 8-LED bar-graph. */
+#define LEDS_PORT 0
+
+
+static void anna_led_tick (void);
+
+
+void __init mach_early_init (void)
+{
+ ANNA_ILBEN = 0;
+
+ V850E2_CSC(0) = 0x402F;
+ V850E2_CSC(1) = 0x4000;
+ V850E2_BPC = 0;
+ V850E2_BSC = 0xAAAA;
+ V850E2_BEC = 0;
+
+#if 0
+ V850E2_BHC = 0xFFFF; /* icache all memory, dcache all */
+#else
+ V850E2_BHC = 0; /* cache no memory */
+#endif
+ V850E2_BCT(0) = 0xB088;
+ V850E2_BCT(1) = 0x0008;
+ V850E2_DWC(0) = 0x0027;
+ V850E2_DWC(1) = 0;
+ V850E2_BCC = 0x0006;
+ V850E2_ASC = 0;
+ V850E2_LBS = 0x0089;
+ V850E2_SCR(3) = 0x21A9;
+ V850E2_RFS(3) = 0x8121;
+
+ v850e_intc_disable_irqs ();
+}
+
+void __init mach_setup (char **cmdline)
+{
+ ANNA_PORT_PM (LEDS_PORT) = 0; /* Make all LED pins output pins. */
+ mach_tick = anna_led_tick;
+}
+
+void __init mach_get_physical_ram (unsigned long *ram_start,
+ unsigned long *ram_len)
+{
+ *ram_start = RAM_START;
+ *ram_len = RAM_END - RAM_START;
+}
+
+void __init mach_reserve_bootmem ()
+{
+ /* The space between SRAM and SDRAM is filled with duplicate
+ images of SRAM. Prevent the kernel from using them. */
+ reserve_bootmem (SRAM_ADDR + SRAM_SIZE,
+ SDRAM_ADDR - (SRAM_ADDR + SRAM_SIZE));
+}
+
+void mach_gettimeofday (struct timespec *tv)
+{
+ tv->tv_sec = 0;
+ tv->tv_nsec = 0;
+}
+
+void __init mach_sched_init (struct irqaction *timer_action)
+{
+ /* Start hardware timer. */
+ v850e_timer_d_configure (0, HZ);
+ /* Install timer interrupt handler. */
+ setup_irq (IRQ_INTCMD(0), timer_action);
+}
+
+static struct v850e_intc_irq_init irq_inits[] = {
+ { "IRQ", 0, NUM_MACH_IRQS, 1, 7 },
+ { "PIN", IRQ_INTP(0), IRQ_INTP_NUM, 1, 4 },
+ { "CCC", IRQ_INTCCC(0), IRQ_INTCCC_NUM, 1, 5 },
+ { "CMD", IRQ_INTCMD(0), IRQ_INTCMD_NUM, 1, 5 },
+ { "DMA", IRQ_INTDMA(0), IRQ_INTDMA_NUM, 1, 2 },
+ { "DMXER", IRQ_INTDMXER,1, 1, 2 },
+ { "SRE", IRQ_INTSRE(0), IRQ_INTSRE_NUM, 3, 3 },
+ { "SR", IRQ_INTSR(0), IRQ_INTSR_NUM, 3, 4 },
+ { "ST", IRQ_INTST(0), IRQ_INTST_NUM, 3, 5 },
+ { 0 }
+};
+#define NUM_IRQ_INITS ((sizeof irq_inits / sizeof irq_inits[0]) - 1)
+
+static struct hw_interrupt_type hw_itypes[NUM_IRQ_INITS];
+
+void __init mach_init_irqs (void)
+{
+ v850e_intc_init_irq_types (irq_inits, hw_itypes);
+}
+
+void machine_restart (char *__unused)
+{
+#ifdef CONFIG_RESET_GUARD
+ disable_reset_guard ();
+#endif
+ asm ("jmp r0"); /* Jump to the reset vector. */
+}
+
+EXPORT_SYMBOL(machine_restart);
+
+void machine_halt (void)
+{
+#ifdef CONFIG_RESET_GUARD
+ disable_reset_guard ();
+#endif
+ local_irq_disable (); /* Ignore all interrupts. */
+ ANNA_PORT_IO(LEDS_PORT) = 0xAA; /* Note that we halted. */
+ for (;;)
+ asm ("halt; nop; nop; nop; nop; nop");
+}
+
+EXPORT_SYMBOL(machine_halt);
+
+void machine_power_off (void)
+{
+ machine_halt ();
+}
+
+EXPORT_SYMBOL(machine_power_off);
+
+/* Called before configuring an on-chip UART. */
+void anna_uart_pre_configure (unsigned chan, unsigned cflags, unsigned baud)
+{
+ /* The Anna connects some general-purpose I/O pins on the CPU to
+ the RTS/CTS lines of UART 1's serial connection. I/O pins P07
+ and P37 are RTS and CTS respectively. */
+ if (chan == 1) {
+ ANNA_PORT_PM(0) &= ~0x80; /* P07 in output mode */
+ ANNA_PORT_PM(3) |= 0x80; /* P37 in input mode */
+ }
+}
+
+/* Minimum and maximum bounds for the moving upper LED boundary in the
+ clock tick display. We can't use the last bit because it's used for
+ UART0's CTS output. */
+#define MIN_MAX_POS 0
+#define MAX_MAX_POS 6
+
+/* There are MAX_MAX_POS^2 - MIN_MAX_POS^2 cycles in the animation, so if
+ we pick 6 and 0 as above, we get 49 cycles, which is when divided into
+ the standard 100 value for HZ, gives us an almost 1s total time. */
+#define TICKS_PER_FRAME \
+ (HZ / (MAX_MAX_POS * MAX_MAX_POS - MIN_MAX_POS * MIN_MAX_POS))
+
+static void anna_led_tick ()
+{
+ static unsigned counter = 0;
+
+ if (++counter == TICKS_PER_FRAME) {
+ static int pos = 0, max_pos = MAX_MAX_POS, dir = 1;
+
+ if (dir > 0 && pos == max_pos) {
+ dir = -1;
+ if (max_pos == MIN_MAX_POS)
+ max_pos = MAX_MAX_POS;
+ else
+ max_pos--;
+ } else {
+ if (dir < 0 && pos == 0)
+ dir = 1;
+
+ if (pos + dir <= max_pos) {
+ /* Each bit of port 0 has a LED. */
+ clear_bit (pos, &ANNA_PORT_IO(LEDS_PORT));
+ pos += dir;
+ set_bit (pos, &ANNA_PORT_IO(LEDS_PORT));
+ }
+ }
+
+ counter = 0;
+ }
+}
diff --git a/arch/v850/kernel/anna.ld b/arch/v850/kernel/anna.ld
new file mode 100644
index 0000000..df7f80f
--- /dev/null
+++ b/arch/v850/kernel/anna.ld
@@ -0,0 +1,20 @@
+/* Linker script for the Midas labs Anna V850E2 evaluation board
+ (CONFIG_V850E2_ANNA). */
+
+MEMORY {
+ /* 256KB of internal memory (followed by one mirror). */
+ iMEM0 : ORIGIN = 0, LENGTH = 0x00040000
+ /* 256KB of internal memory (followed by one mirror). */
+ iMEM1 : ORIGIN = 0x00040000, LENGTH = 0x00040000
+
+ /* 1MB of static RAM. This memory is mirrored 64 times. */
+ SRAM : ORIGIN = SRAM_ADDR, LENGTH = SRAM_SIZE
+ /* 64MB of DRAM. */
+ SDRAM : ORIGIN = SDRAM_ADDR, LENGTH = SDRAM_SIZE
+}
+
+SECTIONS {
+ .intv : { INTV_CONTENTS } > iMEM0
+ .sram : { RAMK_KRAM_CONTENTS } > SRAM
+ .root : { ROOT_FS_CONTENTS } > SDRAM
+}
diff --git a/arch/v850/kernel/as85ep1-rom.ld b/arch/v850/kernel/as85ep1-rom.ld
new file mode 100644
index 0000000..fe2a9a3
--- /dev/null
+++ b/arch/v850/kernel/as85ep1-rom.ld
@@ -0,0 +1,21 @@
+/* Linker script for the NEC AS85EP1 V850E evaluation board
+ (CONFIG_V850E_AS85EP1), with kernel in ROM (CONFIG_ROM_KERNEL). */
+
+MEMORY {
+ /* 4MB of flash ROM. */
+ ROM : ORIGIN = 0, LENGTH = 0x00400000
+
+ /* 1MB of static RAM. */
+ SRAM : ORIGIN = SRAM_ADDR, LENGTH = SRAM_SIZE
+
+ /* About 58MB of DRAM. This can actually be at one of two
+ positions, determined by jumper JP3; we have to use the first
+ position because the second is partially out of processor
+ instruction addressing range (though in the second position
+ there's actually 64MB available). */
+ SDRAM : ORIGIN = SDRAM_ADDR, LENGTH = SDRAM_SIZE
+}
+
+SECTIONS {
+ ROMK_SECTIONS(ROM, SRAM)
+}
diff --git a/arch/v850/kernel/as85ep1.c b/arch/v850/kernel/as85ep1.c
new file mode 100644
index 0000000..4059b1d
--- /dev/null
+++ b/arch/v850/kernel/as85ep1.c
@@ -0,0 +1,240 @@
+/*
+ * arch/v850/kernel/as85ep1.c -- AS85EP1 V850E evaluation chip/board
+ *
+ * Copyright (C) 2002,03 NEC Electronics Corporation
+ * Copyright (C) 2002,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/config.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/bootmem.h>
+#include <linux/major.h>
+#include <linux/irq.h>
+
+#include <asm/machdep.h>
+#include <asm/atomic.h>
+#include <asm/page.h>
+#include <asm/v850e_timer_d.h>
+#include <asm/v850e_uart.h>
+
+#include "mach.h"
+
+
+/* SRAM and SDRAM are vaguely contiguous (with a big hole in between; see
+ mach_reserve_bootmem for details); use both as one big area. */
+#define RAM_START SRAM_ADDR
+#define RAM_END (SDRAM_ADDR + SDRAM_SIZE)
+
+/* The bits of this port are connected to an 8-LED bar-graph. */
+#define LEDS_PORT 4
+
+
+static void as85ep1_led_tick (void);
+
+extern char _intv_copy_src_start, _intv_copy_src_end;
+extern char _intv_copy_dst_start;
+
+
+void __init mach_early_init (void)
+{
+#ifndef CONFIG_ROM_KERNEL
+ const u32 *src;
+ register u32 *dst asm ("ep");
+#endif
+
+ AS85EP1_CSC(0) = 0x0403;
+ AS85EP1_BCT(0) = 0xB8B8;
+ AS85EP1_DWC(0) = 0x0104;
+ AS85EP1_BCC = 0x0012;
+ AS85EP1_ASC = 0;
+ AS85EP1_LBS = 0x00A9;
+
+ AS85EP1_PORT_PMC(6) = 0xFF; /* valid A0,A1,A20-A25 */
+ AS85EP1_PORT_PMC(7) = 0x0E; /* valid CS1-CS3 */
+ AS85EP1_PORT_PMC(9) = 0xFF; /* valid D16-D23 */
+ AS85EP1_PORT_PMC(10) = 0xFF; /* valid D24-D31 */
+
+ AS85EP1_RFS(1) = 0x800c;
+ AS85EP1_RFS(3) = 0x800c;
+ AS85EP1_SCR(1) = 0x20A9;
+ AS85EP1_SCR(3) = 0x20A9;
+
+#ifndef CONFIG_ROM_KERNEL
+ /* The early chip we have is buggy, and writing the interrupt
+ vectors into low RAM may screw up, so for non-ROM kernels, we
+ only rely on the reset vector being downloaded, and copy the
+ rest of the interrupt vectors into place here. The specific bug
+ is that writing address N, where (N & 0x10) == 0x10, will _also_
+ write to address (N - 0x10). We avoid this (effectively) by
+ writing in 16-byte chunks backwards from the end. */
+
+ AS85EP1_IRAMM = 0x3; /* "write-mode" for the internal instruction memory */
+
+ src = (u32 *)(((u32)&_intv_copy_src_end - 1) & ~0xF);
+ dst = (u32 *)&_intv_copy_dst_start
+ + (src - (u32 *)&_intv_copy_src_start);
+ do {
+ u32 t0 = src[0], t1 = src[1], t2 = src[2], t3 = src[3];
+ dst[0] = t0; dst[1] = t1; dst[2] = t2; dst[3] = t3;
+ dst -= 4;
+ src -= 4;
+ } while (src > (u32 *)&_intv_copy_src_start);
+
+ AS85EP1_IRAMM = 0x0; /* "read-mode" for the internal instruction memory */
+#endif /* !CONFIG_ROM_KERNEL */
+
+ v850e_intc_disable_irqs ();
+}
+
+void __init mach_setup (char **cmdline)
+{
+ AS85EP1_PORT_PMC (LEDS_PORT) = 0; /* Make the LEDs port an I/O port. */
+ AS85EP1_PORT_PM (LEDS_PORT) = 0; /* Make all the bits output pins. */
+ mach_tick = as85ep1_led_tick;
+}
+
+void __init mach_get_physical_ram (unsigned long *ram_start,
+ unsigned long *ram_len)
+{
+ *ram_start = RAM_START;
+ *ram_len = RAM_END - RAM_START;
+}
+
+/* Convenience macros. */
+#define SRAM_END (SRAM_ADDR + SRAM_SIZE)
+#define SDRAM_END (SDRAM_ADDR + SDRAM_SIZE)
+
+void __init mach_reserve_bootmem ()
+{
+ if (SDRAM_ADDR < RAM_END && SDRAM_ADDR > RAM_START)
+ /* We can't use the space between SRAM and SDRAM, so
+ prevent the kernel from trying. */
+ reserve_bootmem (SRAM_END, SDRAM_ADDR - SRAM_END);
+}
+
+void mach_gettimeofday (struct timespec *tv)
+{
+ tv->tv_sec = 0;
+ tv->tv_nsec = 0;
+}
+
+void __init mach_sched_init (struct irqaction *timer_action)
+{
+ /* Start hardware timer. */
+ v850e_timer_d_configure (0, HZ);
+ /* Install timer interrupt handler. */
+ setup_irq (IRQ_INTCMD(0), timer_action);
+}
+
+static struct v850e_intc_irq_init irq_inits[] = {
+ { "IRQ", 0, NUM_MACH_IRQS, 1, 7 },
+ { "CCC", IRQ_INTCCC(0), IRQ_INTCCC_NUM, 1, 5 },
+ { "CMD", IRQ_INTCMD(0), IRQ_INTCMD_NUM, 1, 5 },
+ { "SRE", IRQ_INTSRE(0), IRQ_INTSRE_NUM, 3, 3 },
+ { "SR", IRQ_INTSR(0), IRQ_INTSR_NUM, 3, 4 },
+ { "ST", IRQ_INTST(0), IRQ_INTST_NUM, 3, 5 },
+ { 0 }
+};
+#define NUM_IRQ_INITS ((sizeof irq_inits / sizeof irq_inits[0]) - 1)
+
+static struct hw_interrupt_type hw_itypes[NUM_IRQ_INITS];
+
+void __init mach_init_irqs (void)
+{
+ v850e_intc_init_irq_types (irq_inits, hw_itypes);
+}
+
+void machine_restart (char *__unused)
+{
+#ifdef CONFIG_RESET_GUARD
+ disable_reset_guard ();
+#endif
+ asm ("jmp r0"); /* Jump to the reset vector. */
+}
+
+EXPORT_SYMBOL(machine_restart);
+
+void machine_halt (void)
+{
+#ifdef CONFIG_RESET_GUARD
+ disable_reset_guard ();
+#endif
+ local_irq_disable (); /* Ignore all interrupts. */
+ AS85EP1_PORT_IO (LEDS_PORT) = 0xAA; /* Note that we halted. */
+ for (;;)
+ asm ("halt; nop; nop; nop; nop; nop");
+}
+
+EXPORT_SYMBOL(machine_halt);
+
+void machine_power_off (void)
+{
+ machine_halt ();
+}
+
+EXPORT_SYMBOL(machine_power_off);
+
+/* Called before configuring an on-chip UART. */
+void as85ep1_uart_pre_configure (unsigned chan, unsigned cflags, unsigned baud)
+{
+ /* Make the shared uart/port pins be uart pins. */
+ AS85EP1_PORT_PMC(3) |= (0x5 << chan);
+
+ /* The AS85EP1 connects some general-purpose I/O pins on the CPU to
+ the RTS/CTS lines of UART 1's serial connection. I/O pins P53
+ and P54 are RTS and CTS respectively. */
+ if (chan == 1) {
+ /* Put P53 & P54 in I/O port mode. */
+ AS85EP1_PORT_PMC(5) &= ~0x18;
+ /* Make P53 an output, and P54 an input. */
+ AS85EP1_PORT_PM(5) |= 0x10;
+ }
+}
+
+/* Minimum and maximum bounds for the moving upper LED boundary in the
+ clock tick display. */
+#define MIN_MAX_POS 0
+#define MAX_MAX_POS 7
+
+/* There are MAX_MAX_POS^2 - MIN_MAX_POS^2 cycles in the animation, so if
+ we pick 6 and 0 as above, we get 49 cycles, which is when divided into
+ the standard 100 value for HZ, gives us an almost 1s total time. */
+#define TICKS_PER_FRAME \
+ (HZ / (MAX_MAX_POS * MAX_MAX_POS - MIN_MAX_POS * MIN_MAX_POS))
+
+static void as85ep1_led_tick ()
+{
+ static unsigned counter = 0;
+
+ if (++counter == TICKS_PER_FRAME) {
+ static int pos = 0, max_pos = MAX_MAX_POS, dir = 1;
+
+ if (dir > 0 && pos == max_pos) {
+ dir = -1;
+ if (max_pos == MIN_MAX_POS)
+ max_pos = MAX_MAX_POS;
+ else
+ max_pos--;
+ } else {
+ if (dir < 0 && pos == 0)
+ dir = 1;
+
+ if (pos + dir <= max_pos) {
+ /* Each bit of port 0 has a LED. */
+ set_bit (pos, &AS85EP1_PORT_IO(LEDS_PORT));
+ pos += dir;
+ clear_bit (pos, &AS85EP1_PORT_IO(LEDS_PORT));
+ }
+ }
+
+ counter = 0;
+ }
+}
diff --git a/arch/v850/kernel/as85ep1.ld b/arch/v850/kernel/as85ep1.ld
new file mode 100644
index 0000000..ef2c439
--- /dev/null
+++ b/arch/v850/kernel/as85ep1.ld
@@ -0,0 +1,49 @@
+/* Linker script for the NEC AS85EP1 V850E evaluation board
+ (CONFIG_V850E_AS85EP1). */
+
+MEMORY {
+ /* 1MB of internal instruction memory. */
+ iMEM0 : ORIGIN = 0, LENGTH = 0x00100000
+
+ /* 1MB of static RAM. */
+ SRAM : ORIGIN = SRAM_ADDR, LENGTH = SRAM_SIZE
+
+ /* About 58MB of DRAM. This can actually be at one of two
+ positions, determined by jump JP3; we have to use the first
+ position because the second is partially out of processor
+ instruction addressing range (though in the second position
+ there's actually 64MB available). */
+ SDRAM : ORIGIN = SDRAM_ADDR, LENGTH = SDRAM_SIZE
+}
+
+SECTIONS {
+ .resetv : {
+ __intv_start = . ;
+ *(.intv.reset) /* Reset vector */
+ } > iMEM0
+
+ .sram : {
+ RAMK_KRAM_CONTENTS
+
+ /* We stick most of the interrupt vectors here; they'll be
+ copied into the proper location by the early init code (we
+ can't put them directly in the right place because of
+ hardware bugs). The vectors shouldn't need to be
+ relocated, so we don't have to use `> ... AT> ...' to
+ split the load/vm addresses (and we can't because of
+ problems with the loader). */
+ . = ALIGN (0x10) ;
+ __intv_copy_src_start = . ;
+ *(.intv.common) /* Vectors common to all v850e proc. */
+ *(.intv.mach) /* Machine-specific int. vectors. */
+ . = ALIGN (0x10) ;
+ __intv_copy_src_end = . ;
+ } > SRAM
+
+ /* Where we end up putting the vectors. */
+ __intv_copy_dst_start = 0x10 ;
+ __intv_copy_dst_end = __intv_copy_dst_start + (__intv_copy_src_end - __intv_copy_src_start) ;
+ __intv_end = __intv_copy_dst_end ;
+
+ .root : { ROOT_FS_CONTENTS } > SDRAM
+}
diff --git a/arch/v850/kernel/asm-consts.c b/arch/v850/kernel/asm-consts.c
new file mode 100644
index 0000000..24f2913
--- /dev/null
+++ b/arch/v850/kernel/asm-consts.c
@@ -0,0 +1,61 @@
+/*
+ * This program is used to generate definitions needed by
+ * assembly language modules.
+ *
+ * We use the technique used in the OSF Mach kernel code:
+ * generate asm statements containing #defines,
+ * compile this file to assembler, and then extract the
+ * #defines from the assembly-language output.
+ */
+
+#include <linux/stddef.h>
+#include <linux/sched.h>
+#include <linux/kernel_stat.h>
+#include <linux/ptrace.h>
+#include <linux/hardirq.h>
+#include <asm/irq.h>
+#include <asm/errno.h>
+
+#define DEFINE(sym, val) \
+ asm volatile("\n->" #sym " %0 " #val : : "i" (val))
+
+#define BLANK() asm volatile("\n->" : : )
+
+int main (void)
+{
+ /* offsets into the task struct */
+ DEFINE (TASK_STATE, offsetof (struct task_struct, state));
+ DEFINE (TASK_FLAGS, offsetof (struct task_struct, flags));
+ DEFINE (TASK_PTRACE, offsetof (struct task_struct, ptrace));
+ DEFINE (TASK_BLOCKED, offsetof (struct task_struct, blocked));
+ DEFINE (TASK_THREAD, offsetof (struct task_struct, thread));
+ DEFINE (TASK_THREAD_INFO, offsetof (struct task_struct, thread_info));
+ DEFINE (TASK_MM, offsetof (struct task_struct, mm));
+ DEFINE (TASK_ACTIVE_MM, offsetof (struct task_struct, active_mm));
+ DEFINE (TASK_PID, offsetof (struct task_struct, pid));
+
+ /* offsets into the kernel_stat struct */
+ DEFINE (STAT_IRQ, offsetof (struct kernel_stat, irqs));
+
+
+ /* signal defines */
+ DEFINE (SIGSEGV, SIGSEGV);
+ DEFINE (SEGV_MAPERR, SEGV_MAPERR);
+ DEFINE (SIGTRAP, SIGTRAP);
+ DEFINE (SIGCHLD, SIGCHLD);
+ DEFINE (SIGILL, SIGILL);
+ DEFINE (TRAP_TRACE, TRAP_TRACE);
+
+ /* ptrace flag bits */
+ DEFINE (PT_PTRACED, PT_PTRACED);
+ DEFINE (PT_DTRACE, PT_DTRACE);
+
+ /* error values */
+ DEFINE (ENOSYS, ENOSYS);
+
+ /* clone flag bits */
+ DEFINE (CLONE_VFORK, CLONE_VFORK);
+ DEFINE (CLONE_VM, CLONE_VM);
+
+ return 0;
+}
diff --git a/arch/v850/kernel/bug.c b/arch/v850/kernel/bug.c
new file mode 100644
index 0000000..c78cf750
--- /dev/null
+++ b/arch/v850/kernel/bug.c
@@ -0,0 +1,142 @@
+/*
+ * arch/v850/kernel/bug.c -- Bug reporting functions
+ *
+ * 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/kernel.h>
+#include <linux/reboot.h>
+#include <linux/sched.h>
+#include <linux/module.h>
+
+#include <asm/errno.h>
+#include <asm/ptrace.h>
+#include <asm/processor.h>
+#include <asm/current.h>
+
+/* We should use __builtin_return_address, but it doesn't work in gcc-2.90
+ (which is currently our standard compiler on the v850). */
+#define ret_addr() ({ register u32 lp asm ("lp"); lp; })
+#define stack_addr() ({ register u32 sp asm ("sp"); sp; })
+
+void __bug ()
+{
+ printk (KERN_CRIT "kernel BUG at PC 0x%x (SP ~0x%x)!\n",
+ ret_addr() - 4, /* - 4 for `jarl' */
+ stack_addr());
+ machine_halt ();
+}
+
+int bad_trap (int trap_num, struct pt_regs *regs)
+{
+ printk (KERN_CRIT
+ "unimplemented trap %d called at 0x%08lx, pid %d!\n",
+ trap_num, regs->pc, current->pid);
+ return -ENOSYS;
+}
+
+#ifdef CONFIG_RESET_GUARD
+void unexpected_reset (unsigned long ret_addr, unsigned long kmode,
+ struct task_struct *task, unsigned long sp)
+{
+ printk (KERN_CRIT
+ "unexpected reset in %s mode, pid %d"
+ " (ret_addr = 0x%lx, sp = 0x%lx)\n",
+ kmode ? "kernel" : "user",
+ task ? task->pid : -1,
+ ret_addr, sp);
+
+ machine_halt ();
+}
+#endif /* CONFIG_RESET_GUARD */
+
+
+
+struct spec_reg_name {
+ const char *name;
+ int gpr;
+};
+
+struct spec_reg_name spec_reg_names[] = {
+ { "sp", GPR_SP },
+ { "gp", GPR_GP },
+ { "tp", GPR_TP },
+ { "ep", GPR_EP },
+ { "lp", GPR_LP },
+ { 0, 0 }
+};
+
+void show_regs (struct pt_regs *regs)
+{
+ int gpr_base, gpr_offs;
+
+ printk (" pc 0x%08lx psw 0x%08lx kernel_mode %d\n",
+ regs->pc, regs->psw, regs->kernel_mode);
+ printk (" ctpc 0x%08lx ctpsw 0x%08lx ctbp 0x%08lx\n",
+ regs->ctpc, regs->ctpsw, regs->ctbp);
+
+ for (gpr_base = 0; gpr_base < NUM_GPRS; gpr_base += 4) {
+ for (gpr_offs = 0; gpr_offs < 4; gpr_offs++) {
+ int gpr = gpr_base + gpr_offs;
+ long val = regs->gpr[gpr];
+ struct spec_reg_name *srn;
+
+ for (srn = spec_reg_names; srn->name; srn++)
+ if (srn->gpr == gpr)
+ break;
+
+ if (srn->name)
+ printk ("%7s 0x%08lx", srn->name, val);
+ else
+ printk (" r%02d 0x%08lx", gpr, val);
+ }
+
+ printk ("\n");
+ }
+}
+
+/*
+ * TASK is a pointer to the task whose backtrace we want to see (or NULL
+ * for current task), SP is the stack pointer of the first frame that
+ * should be shown in the back trace (or NULL if the entire call-chain of
+ * the task should be shown).
+ */
+void show_stack (struct task_struct *task, unsigned long *sp)
+{
+ unsigned long addr, end;
+
+ if (sp)
+ addr = (unsigned long)sp;
+ else if (task)
+ addr = task_sp (task);
+ else
+ addr = stack_addr ();
+
+ addr = addr & ~3;
+ end = (addr + THREAD_SIZE - 1) & THREAD_MASK;
+
+ while (addr < end) {
+ printk ("%8lX: ", addr);
+ while (addr < end) {
+ printk (" %8lX", *(unsigned long *)addr);
+ addr += sizeof (unsigned long);
+ if (! (addr & 0xF))
+ break;
+ }
+ printk ("\n");
+ }
+}
+
+void dump_stack ()
+{
+ show_stack (0, 0);
+}
+
+EXPORT_SYMBOL(dump_stack);
diff --git a/arch/v850/kernel/entry.S b/arch/v850/kernel/entry.S
new file mode 100644
index 0000000..895e27b
--- /dev/null
+++ b/arch/v850/kernel/entry.S
@@ -0,0 +1,1121 @@
+/*
+ * arch/v850/kernel/entry.S -- Low-level system-call handling, trap handlers,
+ * and context-switching
+ *
+ * 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/sys.h>
+
+#include <asm/entry.h>
+#include <asm/current.h>
+#include <asm/thread_info.h>
+#include <asm/clinkage.h>
+#include <asm/processor.h>
+#include <asm/irq.h>
+#include <asm/errno.h>
+
+#include <asm/asm-consts.h>
+
+
+/* Make a slightly more convenient alias for C_SYMBOL_NAME. */
+#define CSYM C_SYMBOL_NAME
+
+
+/* The offset of the struct pt_regs in a state-save-frame on the stack. */
+#define PTO STATE_SAVE_PT_OFFSET
+
+
+/* Save argument registers to the state-save-frame pointed to by EP. */
+#define SAVE_ARG_REGS \
+ sst.w r6, PTO+PT_GPR(6)[ep]; \
+ sst.w r7, PTO+PT_GPR(7)[ep]; \
+ sst.w r8, PTO+PT_GPR(8)[ep]; \
+ sst.w r9, PTO+PT_GPR(9)[ep]
+/* Restore argument registers from the state-save-frame pointed to by EP. */
+#define RESTORE_ARG_REGS \
+ sld.w PTO+PT_GPR(6)[ep], r6; \
+ sld.w PTO+PT_GPR(7)[ep], r7; \
+ sld.w PTO+PT_GPR(8)[ep], r8; \
+ sld.w PTO+PT_GPR(9)[ep], r9
+
+/* Save value return registers to the state-save-frame pointed to by EP. */
+#define SAVE_RVAL_REGS \
+ sst.w r10, PTO+PT_GPR(10)[ep]; \
+ sst.w r11, PTO+PT_GPR(11)[ep]
+/* Restore value return registers from the state-save-frame pointed to by EP. */
+#define RESTORE_RVAL_REGS \
+ sld.w PTO+PT_GPR(10)[ep], r10; \
+ sld.w PTO+PT_GPR(11)[ep], r11
+
+
+#define SAVE_CALL_CLOBBERED_REGS_BEFORE_ARGS \
+ sst.w r1, PTO+PT_GPR(1)[ep]; \
+ sst.w r5, PTO+PT_GPR(5)[ep]
+#define SAVE_CALL_CLOBBERED_REGS_AFTER_RVAL \
+ sst.w r12, PTO+PT_GPR(12)[ep]; \
+ sst.w r13, PTO+PT_GPR(13)[ep]; \
+ sst.w r14, PTO+PT_GPR(14)[ep]; \
+ sst.w r15, PTO+PT_GPR(15)[ep]; \
+ sst.w r16, PTO+PT_GPR(16)[ep]; \
+ sst.w r17, PTO+PT_GPR(17)[ep]; \
+ sst.w r18, PTO+PT_GPR(18)[ep]; \
+ sst.w r19, PTO+PT_GPR(19)[ep]
+#define RESTORE_CALL_CLOBBERED_REGS_BEFORE_ARGS \
+ sld.w PTO+PT_GPR(1)[ep], r1; \
+ sld.w PTO+PT_GPR(5)[ep], r5
+#define RESTORE_CALL_CLOBBERED_REGS_AFTER_RVAL \
+ sld.w PTO+PT_GPR(12)[ep], r12; \
+ sld.w PTO+PT_GPR(13)[ep], r13; \
+ sld.w PTO+PT_GPR(14)[ep], r14; \
+ sld.w PTO+PT_GPR(15)[ep], r15; \
+ sld.w PTO+PT_GPR(16)[ep], r16; \
+ sld.w PTO+PT_GPR(17)[ep], r17; \
+ sld.w PTO+PT_GPR(18)[ep], r18; \
+ sld.w PTO+PT_GPR(19)[ep], r19
+
+/* Save `call clobbered' registers to the state-save-frame pointed to by EP. */
+#define SAVE_CALL_CLOBBERED_REGS \
+ SAVE_CALL_CLOBBERED_REGS_BEFORE_ARGS; \
+ SAVE_ARG_REGS; \
+ SAVE_RVAL_REGS; \
+ SAVE_CALL_CLOBBERED_REGS_AFTER_RVAL
+/* Restore `call clobbered' registers from the state-save-frame pointed to
+ by EP. */
+#define RESTORE_CALL_CLOBBERED_REGS \
+ RESTORE_CALL_CLOBBERED_REGS_BEFORE_ARGS; \
+ RESTORE_ARG_REGS; \
+ RESTORE_RVAL_REGS; \
+ RESTORE_CALL_CLOBBERED_REGS_AFTER_RVAL
+
+/* Save `call clobbered' registers except for the return-value registers
+ to the state-save-frame pointed to by EP. */
+#define SAVE_CALL_CLOBBERED_REGS_NO_RVAL \
+ SAVE_CALL_CLOBBERED_REGS_BEFORE_ARGS; \
+ SAVE_ARG_REGS; \
+ SAVE_CALL_CLOBBERED_REGS_AFTER_RVAL
+/* Restore `call clobbered' registers except for the return-value registers
+ from the state-save-frame pointed to by EP. */
+#define RESTORE_CALL_CLOBBERED_REGS_NO_RVAL \
+ RESTORE_CALL_CLOBBERED_REGS_BEFORE_ARGS; \
+ RESTORE_ARG_REGS; \
+ RESTORE_CALL_CLOBBERED_REGS_AFTER_RVAL
+
+/* Save `call saved' registers to the state-save-frame pointed to by EP. */
+#define SAVE_CALL_SAVED_REGS \
+ sst.w r2, PTO+PT_GPR(2)[ep]; \
+ sst.w r20, PTO+PT_GPR(20)[ep]; \
+ sst.w r21, PTO+PT_GPR(21)[ep]; \
+ sst.w r22, PTO+PT_GPR(22)[ep]; \
+ sst.w r23, PTO+PT_GPR(23)[ep]; \
+ sst.w r24, PTO+PT_GPR(24)[ep]; \
+ sst.w r25, PTO+PT_GPR(25)[ep]; \
+ sst.w r26, PTO+PT_GPR(26)[ep]; \
+ sst.w r27, PTO+PT_GPR(27)[ep]; \
+ sst.w r28, PTO+PT_GPR(28)[ep]; \
+ sst.w r29, PTO+PT_GPR(29)[ep]
+/* Restore `call saved' registers from the state-save-frame pointed to by EP. */
+#define RESTORE_CALL_SAVED_REGS \
+ sld.w PTO+PT_GPR(2)[ep], r2; \
+ sld.w PTO+PT_GPR(20)[ep], r20; \
+ sld.w PTO+PT_GPR(21)[ep], r21; \
+ sld.w PTO+PT_GPR(22)[ep], r22; \
+ sld.w PTO+PT_GPR(23)[ep], r23; \
+ sld.w PTO+PT_GPR(24)[ep], r24; \
+ sld.w PTO+PT_GPR(25)[ep], r25; \
+ sld.w PTO+PT_GPR(26)[ep], r26; \
+ sld.w PTO+PT_GPR(27)[ep], r27; \
+ sld.w PTO+PT_GPR(28)[ep], r28; \
+ sld.w PTO+PT_GPR(29)[ep], r29
+
+
+/* Save the PC stored in the special register SAVEREG to the state-save-frame
+ pointed to by EP. r19 is clobbered. */
+#define SAVE_PC(savereg) \
+ stsr SR_ ## savereg, r19; \
+ sst.w r19, PTO+PT_PC[ep]
+/* Restore the PC from the state-save-frame pointed to by EP, to the special
+ register SAVEREG. LP is clobbered (it is used as a scratch register
+ because the POP_STATE macro restores it, and this macro is usually used
+ inside POP_STATE). */
+#define RESTORE_PC(savereg) \
+ sld.w PTO+PT_PC[ep], lp; \
+ ldsr lp, SR_ ## savereg
+/* Save the PSW register stored in the special register SAVREG to the
+ state-save-frame pointed to by EP. r19 is clobbered. */
+#define SAVE_PSW(savereg) \
+ stsr SR_ ## savereg, r19; \
+ sst.w r19, PTO+PT_PSW[ep]
+/* Restore the PSW register from the state-save-frame pointed to by EP, to
+ the special register SAVEREG. LP is clobbered (it is used as a scratch
+ register because the POP_STATE macro restores it, and this macro is
+ usually used inside POP_STATE). */
+#define RESTORE_PSW(savereg) \
+ sld.w PTO+PT_PSW[ep], lp; \
+ ldsr lp, SR_ ## savereg
+
+/* Save CTPC/CTPSW/CTBP registers to the state-save-frame pointed to by REG.
+ r19 is clobbered. */
+#define SAVE_CT_REGS \
+ stsr SR_CTPC, r19; \
+ sst.w r19, PTO+PT_CTPC[ep]; \
+ stsr SR_CTPSW, r19; \
+ sst.w r19, PTO+PT_CTPSW[ep]; \
+ stsr SR_CTBP, r19; \
+ sst.w r19, PTO+PT_CTBP[ep]
+/* Restore CTPC/CTPSW/CTBP registers from the state-save-frame pointed to by EP.
+ LP is clobbered (it is used as a scratch register because the POP_STATE
+ macro restores it, and this macro is usually used inside POP_STATE). */
+#define RESTORE_CT_REGS \
+ sld.w PTO+PT_CTPC[ep], lp; \
+ ldsr lp, SR_CTPC; \
+ sld.w PTO+PT_CTPSW[ep], lp; \
+ ldsr lp, SR_CTPSW; \
+ sld.w PTO+PT_CTBP[ep], lp; \
+ ldsr lp, SR_CTBP
+
+
+/* Push register state, except for the stack pointer, on the stack in the
+ form of a state-save-frame (plus some extra padding), in preparation for
+ a system call. This macro makes sure that the EP, GP, and LP
+ registers are saved, and TYPE identifies the set of extra registers to
+ be saved as well. Also copies (the new value of) SP to EP. */
+#define PUSH_STATE(type) \
+ addi -STATE_SAVE_SIZE, sp, sp; /* Make room on the stack. */ \
+ st.w ep, PTO+PT_GPR(GPR_EP)[sp]; \
+ mov sp, ep; \
+ sst.w gp, PTO+PT_GPR(GPR_GP)[ep]; \
+ sst.w lp, PTO+PT_GPR(GPR_LP)[ep]; \
+ type ## _STATE_SAVER
+/* Pop a register state pushed by PUSH_STATE, except for the stack pointer,
+ from the the stack. */
+#define POP_STATE(type) \
+ mov sp, ep; \
+ type ## _STATE_RESTORER; \
+ sld.w PTO+PT_GPR(GPR_GP)[ep], gp; \
+ sld.w PTO+PT_GPR(GPR_LP)[ep], lp; \
+ sld.w PTO+PT_GPR(GPR_EP)[ep], ep; \
+ addi STATE_SAVE_SIZE, sp, sp /* Clean up our stack space. */
+
+
+/* Switch to the kernel stack if necessary, and push register state on the
+ stack in the form of a state-save-frame. Also load the current task
+ pointer if switching from user mode. The stack-pointer (r3) should have
+ already been saved to the memory location SP_SAVE_LOC (the reason for
+ this is that the interrupt vectors may be beyond a 22-bit signed offset
+ jump from the actual interrupt handler, and this allows them to save the
+ stack-pointer and use that register to do an indirect jump). This macro
+ makes sure that `special' registers, system registers, and the stack
+ pointer are saved; TYPE identifies the set of extra registers to be
+ saved as well. SYSCALL_NUM is the register in which the system-call
+ number this state is for is stored (r0 if this isn't a system call).
+ Interrupts should already be disabled when calling this. */
+#define SAVE_STATE(type, syscall_num, sp_save_loc) \
+ tst1 0, KM; /* See if already in kernel mode. */ \
+ bz 1f; \
+ ld.w sp_save_loc, sp; /* ... yes, use saved SP. */ \
+ br 2f; \
+1: ld.w KSP, sp; /* ... no, switch to kernel stack. */ \
+2: PUSH_STATE(type); \
+ ld.b KM, r19; /* Remember old kernel-mode. */ \
+ sst.w r19, PTO+PT_KERNEL_MODE[ep]; \
+ ld.w sp_save_loc, r19; /* Remember old SP. */ \
+ sst.w r19, PTO+PT_GPR(GPR_SP)[ep]; \
+ mov 1, r19; /* Now definitely in kernel-mode. */ \
+ st.b r19, KM; \
+ GET_CURRENT_TASK(CURRENT_TASK); /* Fetch the current task pointer. */ \
+ /* Save away the syscall number. */ \
+ sst.w syscall_num, PTO+PT_CUR_SYSCALL[ep]
+
+
+/* Save register state not normally saved by PUSH_STATE for TYPE, to the
+ state-save-frame on the stack; also copies SP to EP. r19 may be trashed. */
+#define SAVE_EXTRA_STATE(type) \
+ mov sp, ep; \
+ type ## _EXTRA_STATE_SAVER
+/* Restore register state not normally restored by POP_STATE for TYPE,
+ from the state-save-frame on the stack; also copies SP to EP.
+ r19 may be trashed. */
+#define RESTORE_EXTRA_STATE(type) \
+ mov sp, ep; \
+ type ## _EXTRA_STATE_RESTORER
+
+/* Save any call-clobbered registers not normally saved by PUSH_STATE for
+ TYPE, to the state-save-frame on the stack.
+ EP may be trashed, but is not guaranteed to contain a copy of SP
+ (unlike after most SAVE_... macros). r19 may be trashed. */
+#define SAVE_EXTRA_STATE_FOR_SCHEDULE(type) \
+ type ## _SCHEDULE_EXTRA_STATE_SAVER
+/* Restore any call-clobbered registers not normally restored by
+ POP_STATE for TYPE, to the state-save-frame on the stack.
+ EP may be trashed, but is not guaranteed to contain a copy of SP
+ (unlike after most RESTORE_... macros). r19 may be trashed. */
+#define RESTORE_EXTRA_STATE_FOR_SCHEDULE(type) \
+ type ## _SCHEDULE_EXTRA_STATE_RESTORER
+
+
+/* These are extra_state_saver/restorer values for a user trap. Note
+ that we save the argument registers so that restarted syscalls will
+ function properly (otherwise it wouldn't be necessary), and we must
+ _not_ restore the return-value registers (so that traps can return a
+ value!), but call-clobbered registers are not saved at all, as the
+ caller of the syscall function should have saved them. */
+
+#define TRAP_RET reti
+/* Traps don't save call-clobbered registers (but do still save arg regs).
+ We preserve PSw to keep long-term state, namely interrupt status (for traps
+ from kernel-mode), and the single-step flag (for user traps). */
+#define TRAP_STATE_SAVER \
+ SAVE_ARG_REGS; \
+ SAVE_PC(EIPC); \
+ SAVE_PSW(EIPSW)
+/* When traps return, they just leave call-clobbered registers (except for arg
+ regs) with whatever value they have from the kernel. Traps don't preserve
+ the PSW, but we zero EIPSW to ensure it doesn't contain anything dangerous
+ (in particular, the single-step flag). */
+#define TRAP_STATE_RESTORER \
+ RESTORE_ARG_REGS; \
+ RESTORE_PC(EIPC); \
+ RESTORE_PSW(EIPSW)
+/* Save registers not normally saved by traps. We need to save r12, even
+ though it's nominally call-clobbered, because it's used when restarting
+ a system call (the signal-handling path uses SAVE_EXTRA_STATE, and
+ expects r12 to be restored when the trap returns). */
+#define TRAP_EXTRA_STATE_SAVER \
+ SAVE_RVAL_REGS; \
+ sst.w r12, PTO+PT_GPR(12)[ep]; \
+ SAVE_CALL_SAVED_REGS; \
+ SAVE_CT_REGS
+#define TRAP_EXTRA_STATE_RESTORER \
+ RESTORE_RVAL_REGS; \
+ sld.w PTO+PT_GPR(12)[ep], r12; \
+ RESTORE_CALL_SAVED_REGS; \
+ RESTORE_CT_REGS
+/* Save registers prior to calling scheduler (just before trap returns).
+ We have to save the return-value registers to preserve the trap's return
+ value. Note that ..._SCHEDULE_EXTRA_STATE_SAVER, unlike most ..._SAVER
+ macros, is required to setup EP itself if EP is needed (this is because
+ in many cases, the macro is empty). */
+#define TRAP_SCHEDULE_EXTRA_STATE_SAVER \
+ mov sp, ep; \
+ SAVE_RVAL_REGS
+/* Note that ..._SCHEDULE_EXTRA_STATE_RESTORER, unlike most ..._RESTORER
+ macros, is required to setup EP itself if EP is needed (this is because
+ in many cases, the macro is empty). */
+#define TRAP_SCHEDULE_EXTRA_STATE_RESTORER \
+ mov sp, ep; \
+ RESTORE_RVAL_REGS
+
+/* Register saving/restoring for maskable interrupts. */
+#define IRQ_RET reti
+#define IRQ_STATE_SAVER \
+ SAVE_CALL_CLOBBERED_REGS; \
+ SAVE_PC(EIPC); \
+ SAVE_PSW(EIPSW)
+#define IRQ_STATE_RESTORER \
+ RESTORE_CALL_CLOBBERED_REGS; \
+ RESTORE_PC(EIPC); \
+ RESTORE_PSW(EIPSW)
+#define IRQ_EXTRA_STATE_SAVER \
+ SAVE_CALL_SAVED_REGS; \
+ SAVE_CT_REGS
+#define IRQ_EXTRA_STATE_RESTORER \
+ RESTORE_CALL_SAVED_REGS; \
+ RESTORE_CT_REGS
+#define IRQ_SCHEDULE_EXTRA_STATE_SAVER /* nothing */
+#define IRQ_SCHEDULE_EXTRA_STATE_RESTORER /* nothing */
+
+/* Register saving/restoring for non-maskable interrupts. */
+#define NMI_RET reti
+#define NMI_STATE_SAVER \
+ SAVE_CALL_CLOBBERED_REGS; \
+ SAVE_PC(FEPC); \
+ SAVE_PSW(FEPSW);
+#define NMI_STATE_RESTORER \
+ RESTORE_CALL_CLOBBERED_REGS; \
+ RESTORE_PC(FEPC); \
+ RESTORE_PSW(FEPSW);
+#define NMI_EXTRA_STATE_SAVER \
+ SAVE_CALL_SAVED_REGS; \
+ SAVE_CT_REGS
+#define NMI_EXTRA_STATE_RESTORER \
+ RESTORE_CALL_SAVED_REGS; \
+ RESTORE_CT_REGS
+#define NMI_SCHEDULE_EXTRA_STATE_SAVER /* nothing */
+#define NMI_SCHEDULE_EXTRA_STATE_RESTORER /* nothing */
+
+/* Register saving/restoring for debug traps. */
+#define DBTRAP_RET .long 0x014607E0 /* `dbret', but gas doesn't support it. */
+#define DBTRAP_STATE_SAVER \
+ SAVE_CALL_CLOBBERED_REGS; \
+ SAVE_PC(DBPC); \
+ SAVE_PSW(DBPSW)
+#define DBTRAP_STATE_RESTORER \
+ RESTORE_CALL_CLOBBERED_REGS; \
+ RESTORE_PC(DBPC); \
+ RESTORE_PSW(DBPSW)
+#define DBTRAP_EXTRA_STATE_SAVER \
+ SAVE_CALL_SAVED_REGS; \
+ SAVE_CT_REGS
+#define DBTRAP_EXTRA_STATE_RESTORER \
+ RESTORE_CALL_SAVED_REGS; \
+ RESTORE_CT_REGS
+#define DBTRAP_SCHEDULE_EXTRA_STATE_SAVER /* nothing */
+#define DBTRAP_SCHEDULE_EXTRA_STATE_RESTORER /* nothing */
+
+/* Register saving/restoring for a context switch. We don't need to save
+ too many registers, because context-switching looks like a function call
+ (via the function `switch_thread'), so callers will save any
+ call-clobbered registers themselves. We do need to save the CT regs, as
+ they're normally not saved during kernel entry (the kernel doesn't use
+ them). We save PSW so that interrupt-status state will correctly follow
+ each thread (mostly NMI vs. normal-IRQ/trap), though for the most part
+ it doesn't matter since threads are always in almost exactly the same
+ processor state during a context switch. The stack pointer and return
+ value are handled by switch_thread itself. */
+#define SWITCH_STATE_SAVER \
+ SAVE_CALL_SAVED_REGS; \
+ SAVE_PSW(PSW); \
+ SAVE_CT_REGS
+#define SWITCH_STATE_RESTORER \
+ RESTORE_CALL_SAVED_REGS; \
+ RESTORE_PSW(PSW); \
+ RESTORE_CT_REGS
+
+
+/* Restore register state from the state-save-frame on the stack, switch back
+ to the user stack if necessary, and return from the trap/interrupt.
+ EXTRA_STATE_RESTORER is a sequence of assembly language statements to
+ restore anything not restored by this macro. Only registers not saved by
+ the C compiler are restored (that is, R3(sp), R4(gp), R31(lp), and
+ anything restored by EXTRA_STATE_RESTORER). */
+#define RETURN(type) \
+ ld.b PTO+PT_KERNEL_MODE[sp], r19; \
+ di; /* Disable interrupts */ \
+ cmp r19, r0; /* See if returning to kernel mode, */\
+ bne 2f; /* ... if so, skip resched &c. */ \
+ \
+ /* We're returning to user mode, so check for various conditions that \
+ trigger rescheduling. */ \
+ GET_CURRENT_THREAD(r18); \
+ ld.w TI_FLAGS[r18], r19; \
+ andi _TIF_NEED_RESCHED, r19, r0; \
+ bnz 3f; /* Call the scheduler. */ \
+5: andi _TIF_SIGPENDING, r19, r18; \
+ ld.w TASK_PTRACE[CURRENT_TASK], r19; /* ptrace flags */ \
+ or r18, r19; /* see if either is non-zero */ \
+ bnz 4f; /* if so, handle them */ \
+ \
+/* Return to user state. */ \
+1: st.b r0, KM; /* Now officially in user state. */ \
+ \
+/* Final return. The stack-pointer fiddling is not needed when returning \
+ to kernel-mode, but they don't hurt, and this way we can share the \
+ (sometimes rather lengthy) POP_STATE macro. */ \
+2: POP_STATE(type); \
+ st.w sp, KSP; /* Save the kernel stack pointer. */ \
+ ld.w PT_GPR(GPR_SP)-PT_SIZE[sp], sp; /* Restore stack pointer. */ \
+ type ## _RET; /* Return from the trap/interrupt. */ \
+ \
+/* Call the scheduler before returning from a syscall/trap. */ \
+3: SAVE_EXTRA_STATE_FOR_SCHEDULE(type); /* Prepare to call scheduler. */ \
+ jarl call_scheduler, lp; /* Call scheduler */ \
+ di; /* The scheduler enables interrupts */\
+ RESTORE_EXTRA_STATE_FOR_SCHEDULE(type); \
+ GET_CURRENT_THREAD(r18); \
+ ld.w TI_FLAGS[r18], r19; \
+ br 5b; /* Continue with return path. */ \
+ \
+/* Handle a signal or ptraced process return. \
+ r18 should be non-zero if there are pending signals. */ \
+4: /* Not all registers are saved by the normal trap/interrupt entry \
+ points (for instance, call-saved registers (because the normal \
+ C-compiler calling sequence in the kernel makes sure they're \
+ preserved), and call-clobbered registers in the case of \
+ traps), but signal handlers may want to examine or change the \
+ complete register state. Here we save anything not saved by \
+ the normal entry sequence, so that it may be safely restored \
+ (in a possibly modified form) after do_signal returns. */ \
+ SAVE_EXTRA_STATE(type); /* Save state not saved by entry. */ \
+ jarl handle_signal_or_ptrace_return, lp; \
+ RESTORE_EXTRA_STATE(type); /* Restore extra regs. */ \
+ br 1b
+
+
+/* Jump to the appropriate function for the system call number in r12
+ (r12 is not preserved), or return an error if r12 is not valid. The
+ LP register should point to the location where the called function
+ should return. [note that MAKE_SYS_CALL uses label 1] */
+#define MAKE_SYS_CALL \
+ /* Figure out which function to use for this system call. */ \
+ shl 2, r12; \
+ /* See if the system call number is valid. */ \
+ addi lo(CSYM(sys_call_table) - sys_call_table_end), r12, r0; \
+ bnh 1f; \
+ mov hilo(CSYM(sys_call_table)), r19; \
+ add r19, r12; \
+ ld.w 0[r12], r12; \
+ /* Make the system call. */ \
+ jmp [r12]; \
+ /* The syscall number is invalid, return an error. */ \
+1: addi -ENOSYS, r0, r10; \
+ jmp [lp]
+
+
+ .text
+
+/*
+ * User trap.
+ *
+ * Trap 0 system calls are also handled here.
+ *
+ * The stack-pointer (r3) should have already been saved to the memory
+ * location ENTRY_SP (the reason for this is that the interrupt vectors may be
+ * beyond a 22-bit signed offset jump from the actual interrupt handler, and
+ * this allows them to save the stack-pointer and use that register to do an
+ * indirect jump).
+ *
+ * Syscall protocol:
+ * Syscall number in r12, args in r6-r9
+ * Return value in r10
+ */
+G_ENTRY(trap):
+ SAVE_STATE (TRAP, r12, ENTRY_SP) // Save registers.
+ stsr SR_ECR, r19 // Find out which trap it was.
+ ei // Enable interrupts.
+ mov hilo(ret_from_trap), lp // where the trap should return
+
+ // The following two shifts (1) clear out extraneous NMI data in the
+ // upper 16-bits, (2) convert the 0x40 - 0x5f range of trap ECR
+ // numbers into the (0-31) << 2 range we want, (3) set the flags.
+ shl 27, r19 // chop off all high bits
+ shr 25, r19 // scale back down and then << 2
+ bnz 2f // See if not trap 0.
+
+ // Trap 0 is a `short' system call, skip general trap table.
+ MAKE_SYS_CALL // Jump to the syscall function.
+
+2: // For other traps, use a table lookup.
+ mov hilo(CSYM(trap_table)), r18
+ add r19, r18
+ ld.w 0[r18], r18
+ jmp [r18] // Jump to the trap handler.
+END(trap)
+
+/* This is just like ret_from_trap, but first restores extra registers
+ saved by some wrappers. */
+L_ENTRY(restore_extra_regs_and_ret_from_trap):
+ RESTORE_EXTRA_STATE(TRAP)
+ // fall through
+END(restore_extra_regs_and_ret_from_trap)
+
+/* Entry point used to return from a syscall/trap. */
+L_ENTRY(ret_from_trap):
+ RETURN(TRAP)
+END(ret_from_trap)
+
+
+/* This the initial entry point for a new child thread, with an appropriate
+ stack in place that makes it look the the child is in the middle of an
+ syscall. This function is actually `returned to' from switch_thread
+ (copy_thread makes ret_from_fork the return address in each new thread's
+ saved context). */
+C_ENTRY(ret_from_fork):
+ mov r10, r6 // switch_thread returns the prev task.
+ jarl CSYM(schedule_tail), lp // ...which is schedule_tail's arg
+ mov r0, r10 // Child's fork call should return 0.
+ br ret_from_trap // Do normal trap return.
+C_END(ret_from_fork)
+
+
+/*
+ * Trap 1: `long' system calls
+ * `Long' syscall protocol:
+ * Syscall number in r12, args in r6-r9, r13-r14
+ * Return value in r10
+ */
+L_ENTRY(syscall_long):
+ // Push extra arguments on the stack. Note that by default, the trap
+ // handler reserves enough stack space for 6 arguments, so we don't
+ // have to make any additional room.
+ st.w r13, 16[sp] // arg 5
+ st.w r14, 20[sp] // arg 6
+
+ // Make sure r13 and r14 are preserved, in case we have to restart a
+ // system call because of a signal (ep has already been set by caller).
+ st.w r13, PTO+PT_GPR(13)[sp]
+ st.w r14, PTO+PT_GPR(13)[sp]
+ mov hilo(ret_from_long_syscall), lp
+
+ MAKE_SYS_CALL // Jump to the syscall function.
+END(syscall_long)
+
+/* Entry point used to return from a long syscall. Only needed to restore
+ r13/r14 if the general trap mechanism doesnt' do so. */
+L_ENTRY(ret_from_long_syscall):
+ ld.w PTO+PT_GPR(13)[sp], r13 // Restore the extra registers
+ ld.w PTO+PT_GPR(13)[sp], r14
+ br ret_from_trap // The rest is the same as other traps
+END(ret_from_long_syscall)
+
+
+/* These syscalls need access to the struct pt_regs on the stack, so we
+ implement them in assembly (they're basically all wrappers anyway). */
+
+L_ENTRY(sys_fork_wrapper):
+#ifdef CONFIG_MMU
+ addi SIGCHLD, r0, r6 // Arg 0: flags
+ ld.w PTO+PT_GPR(GPR_SP)[sp], r7 // Arg 1: child SP (use parent's)
+ movea PTO, sp, r8 // Arg 2: parent context
+ mov r0, r9 // Arg 3/4/5: 0
+ st.w r0, 16[sp]
+ st.w r0, 20[sp]
+ mov hilo(CSYM(do_fork)), r18 // Where the real work gets done
+ br save_extra_state_tramp // Save state and go there
+#else
+ // fork almost works, enough to trick you into looking elsewhere :-(
+ addi -EINVAL, r0, r10
+ jmp [lp]
+#endif
+END(sys_fork_wrapper)
+
+L_ENTRY(sys_vfork_wrapper):
+ addi CLONE_VFORK | CLONE_VM | SIGCHLD, r0, r6 // Arg 0: flags
+ ld.w PTO+PT_GPR(GPR_SP)[sp], r7 // Arg 1: child SP (use parent's)
+ movea PTO, sp, r8 // Arg 2: parent context
+ mov r0, r9 // Arg 3/4/5: 0
+ st.w r0, 16[sp]
+ st.w r0, 20[sp]
+ mov hilo(CSYM(do_fork)), r18 // Where the real work gets done
+ br save_extra_state_tramp // Save state and go there
+END(sys_vfork_wrapper)
+
+L_ENTRY(sys_clone_wrapper):
+ ld.w PTO+PT_GPR(GPR_SP)[sp], r19// parent's stack pointer
+ cmp r7, r0 // See if child SP arg (arg 1) is 0.
+ cmov z, r19, r7, r7 // ... and use the parent's if so.
+ movea PTO, sp, r8 // Arg 2: parent context
+ mov r0, r9 // Arg 3/4/5: 0
+ st.w r0, 16[sp]
+ st.w r0, 20[sp]
+ mov hilo(CSYM(do_fork)), r18 // Where the real work gets done
+ br save_extra_state_tramp // Save state and go there
+END(sys_clone_wrapper)
+
+
+L_ENTRY(sys_execve_wrapper):
+ movea PTO, sp, r9 // add user context as 4th arg
+ jr CSYM(sys_execve) // Do real work (tail-call).
+END(sys_execve_wrapper)
+
+
+L_ENTRY(sys_sigsuspend_wrapper):
+ movea PTO, sp, r7 // add user context as 2nd arg
+ mov hilo(CSYM(sys_sigsuspend)), r18 // syscall function
+ jarl save_extra_state_tramp, lp // Save state and do it
+ br restore_extra_regs_and_ret_from_trap
+END(sys_sigsuspend_wrapper)
+L_ENTRY(sys_rt_sigsuspend_wrapper):
+ movea PTO, sp, r8 // add user context as 3rd arg
+ mov hilo(CSYM(sys_rt_sigsuspend)), r18 // syscall function
+ jarl save_extra_state_tramp, lp // Save state and do it
+ br restore_extra_regs_and_ret_from_trap
+END(sys_rt_sigsuspend_wrapper)
+
+L_ENTRY(sys_sigreturn_wrapper):
+ movea PTO, sp, r6 // add user context as 1st arg
+ mov hilo(CSYM(sys_sigreturn)), r18 // syscall function
+ jarl save_extra_state_tramp, lp // Save state and do it
+ br restore_extra_regs_and_ret_from_trap
+END(sys_sigreturn_wrapper)
+L_ENTRY(sys_rt_sigreturn_wrapper):
+ movea PTO, sp, r6 // add user context as 1st arg
+ mov hilo(CSYM(sys_rt_sigreturn)), r18// syscall function
+ jarl save_extra_state_tramp, lp // Save state and do it
+ br restore_extra_regs_and_ret_from_trap
+END(sys_rt_sigreturn_wrapper)
+
+
+/* Save any state not saved by SAVE_STATE(TRAP), and jump to r18.
+ It's main purpose is to share the rather lengthy code sequence that
+ SAVE_STATE expands into among the above wrapper functions. */
+L_ENTRY(save_extra_state_tramp):
+ SAVE_EXTRA_STATE(TRAP) // Save state not saved by entry.
+ jmp [r18] // Do the work the caller wants
+END(save_extra_state_tramp)
+
+
+/*
+ * Hardware maskable interrupts.
+ *
+ * The stack-pointer (r3) should have already been saved to the memory
+ * location ENTRY_SP (the reason for this is that the interrupt vectors may be
+ * beyond a 22-bit signed offset jump from the actual interrupt handler, and
+ * this allows them to save the stack-pointer and use that register to do an
+ * indirect jump).
+ */
+G_ENTRY(irq):
+ SAVE_STATE (IRQ, r0, ENTRY_SP) // Save registers.
+
+ stsr SR_ECR, r6 // Find out which interrupt it was.
+ movea PTO, sp, r7 // User regs are arg2
+
+ // All v850 implementations I know about encode their interrupts as
+ // multiples of 0x10, starting at 0x80 (after NMIs and software
+ // interrupts). Convert this number into a simple IRQ index for the
+ // rest of the kernel. We also clear the upper 16 bits, which hold
+ // NMI info, and don't appear to be cleared when a NMI returns.
+ shl 16, r6 // clear upper 16 bits
+ shr 20, r6 // shift back, and remove lower nibble
+ add -8, r6 // remove bias for irqs
+
+ // Call the high-level interrupt handling code.
+ jarl CSYM(handle_irq), lp
+
+ RETURN(IRQ)
+END(irq)
+
+
+/*
+ * Debug trap / illegal-instruction exception
+ *
+ * The stack-pointer (r3) should have already been saved to the memory
+ * location ENTRY_SP (the reason for this is that the interrupt vectors may be
+ * beyond a 22-bit signed offset jump from the actual interrupt handler, and
+ * this allows them to save the stack-pointer and use that register to do an
+ * indirect jump).
+ */
+G_ENTRY(dbtrap):
+ SAVE_STATE (DBTRAP, r0, ENTRY_SP)// Save registers.
+
+ /* First see if we came from kernel mode; if so, the dbtrap
+ instruction has a special meaning, to set the DIR (`debug
+ information register') register. This is because the DIR register
+ can _only_ be manipulated/read while in `debug mode,' and debug
+ mode is only active while we're inside the dbtrap handler. The
+ exact functionality is: { DIR = (DIR | r6) & ~r7; return DIR; }. */
+ ld.b PTO+PT_KERNEL_MODE[sp], r19
+ cmp r19, r0
+ bz 1f
+
+ stsr SR_DIR, r10
+ or r6, r10
+ not r7, r7
+ and r7, r10
+ ldsr r10, SR_DIR
+ stsr SR_DIR, r10 // Confirm the value we set
+ st.w r10, PTO+PT_GPR(10)[sp] // return it
+ br 3f
+
+1: ei // Enable interrupts.
+
+ /* The default signal type we raise. */
+ mov SIGTRAP, r6
+
+ /* See if it's a single-step trap. */
+ stsr SR_DBPSW, r19
+ andi 0x0800, r19, r19
+ bnz 2f
+
+ /* Look to see if the preceding instruction was is a dbtrap or not,
+ to decide which signal we should use. */
+ stsr SR_DBPC, r19 // PC following trapping insn
+ ld.hu -2[r19], r19
+ ori 0xf840, r0, r20 // DBTRAP insn
+ cmp r19, r20 // Was this trap caused by DBTRAP?
+ cmov ne, SIGILL, r6, r6 // Choose signal appropriately
+
+ /* Raise the desired signal. */
+2: mov CURRENT_TASK, r7 // Arg 1: task
+ jarl CSYM(send_sig), lp // tail call
+
+3: RETURN(DBTRAP)
+END(dbtrap)
+
+
+/*
+ * Hardware non-maskable interrupts.
+ *
+ * The stack-pointer (r3) should have already been saved to the memory
+ * location ENTRY_SP (the reason for this is that the interrupt vectors may be
+ * beyond a 22-bit signed offset jump from the actual interrupt handler, and
+ * this allows them to save the stack-pointer and use that register to do an
+ * indirect jump).
+ */
+G_ENTRY(nmi):
+ SAVE_STATE (NMI, r0, NMI_ENTRY_SP); /* Save registers. */
+
+ stsr SR_ECR, r6; /* Find out which nmi it was. */
+ shr 20, r6; /* Extract NMI code in bits 20-24. */
+ movea PTO, sp, r7; /* User regs are arg2. */
+
+ /* Non-maskable interrupts always lie right after maskable interrupts.
+ Call the generic IRQ handler, with two arguments, the IRQ number,
+ and a pointer to the user registers, to handle the specifics.
+ (we subtract one because the first NMI has code 1). */
+ addi FIRST_NMI - 1, r6, r6
+ jarl CSYM(handle_irq), lp
+
+ RETURN(NMI)
+END(nmi)
+
+
+/*
+ * Trap with no handler
+ */
+L_ENTRY(bad_trap_wrapper):
+ mov r19, r6 // Arg 0: trap number
+ movea PTO, sp, r7 // Arg 1: user regs
+ jr CSYM(bad_trap) // tail call handler
+END(bad_trap_wrapper)
+
+
+/*
+ * Invoke the scheduler, called from the trap/irq kernel exit path.
+ *
+ * This basically just calls `schedule', but also arranges for extra
+ * registers to be saved for ptrace'd processes, so ptrace can modify them.
+ */
+L_ENTRY(call_scheduler):
+ ld.w TASK_PTRACE[CURRENT_TASK], r19 // See if task is ptrace'd
+ cmp r19, r0
+ bnz 1f // ... yes, do special stuff
+ jr CSYM(schedule) // ... no, just tail-call scheduler
+
+ // Save extra regs for ptrace'd task. We want to save anything
+ // that would otherwise only be `implicitly' saved by the normal
+ // compiler calling-convention.
+1: mov sp, ep // Setup EP for SAVE_CALL_SAVED_REGS
+ SAVE_CALL_SAVED_REGS // Save call-saved registers to stack
+ mov lp, r20 // Save LP in a callee-saved register
+
+ jarl CSYM(schedule), lp // Call scheduler
+
+ mov r20, lp
+ mov sp, ep // We can't rely on EP after return
+ RESTORE_CALL_SAVED_REGS // Restore (possibly modified) regs
+ jmp [lp] // Return to the return path
+END(call_scheduler)
+
+
+/*
+ * This is an out-of-line handler for two special cases during the kernel
+ * trap/irq exit sequence:
+ *
+ * (1) If r18 is non-zero then a signal needs to be handled, which is
+ * done, and then the caller returned to.
+ *
+ * (2) If r18 is non-zero then we're returning to a ptraced process, which
+ * has several special cases -- single-stepping and trap tracing, both
+ * of which require using the `dbret' instruction to exit the kernel
+ * instead of the normal `reti' (this is because the CPU not correctly
+ * single-step after a reti). In this case, of course, this handler
+ * never returns to the caller.
+ *
+ * In either case, all registers should have been saved to the current
+ * state-save-frame on the stack, except for callee-saved registers.
+ *
+ * [These two different cases are combined merely to avoid bloating the
+ * macro-inlined code, not because they really make much sense together!]
+ */
+L_ENTRY(handle_signal_or_ptrace_return):
+ cmp r18, r0 // See if handling a signal
+ bz 1f // ... nope, go do ptrace return
+
+ // Handle a signal
+ mov lp, r20 // Save link-pointer
+ mov r10, r21 // Save return-values (for trap)
+ mov r11, r22
+
+ movea PTO, sp, r6 // Arg 1: struct pt_regs *regs
+ mov r0, r7 // Arg 2: sigset_t *oldset
+ jarl CSYM(do_signal), lp // Handle the signal
+ di // sig handling enables interrupts
+
+ mov r20, lp // Restore link-pointer
+ mov r21, r10 // Restore return-values (for trap)
+ mov r22, r11
+ ld.w TASK_PTRACE[CURRENT_TASK], r19 // check ptrace flags too
+ cmp r19, r0
+ bnz 1f // ... some set, so look more
+2: jmp [lp] // ... none set, so return normally
+
+ // ptrace return
+1: ld.w PTO+PT_PSW[sp], r19 // Look at user-processes's flags
+ andi 0x0800, r19, r19 // See if single-step flag is set
+ bz 2b // ... nope, return normally
+
+ // Return as if from a dbtrap insn
+ st.b r0, KM // Now officially in user state.
+ POP_STATE(DBTRAP) // Restore regs
+ st.w sp, KSP // Save the kernel stack pointer.
+ ld.w PT_GPR(GPR_SP)-PT_SIZE[sp], sp // Restore user stack pointer.
+ DBTRAP_RET // Return from the trap/interrupt.
+END(handle_signal_or_ptrace_return)
+
+
+/*
+ * This is where we switch between two threads. The arguments are:
+ * r6 -- pointer to the struct thread for the `current' process
+ * r7 -- pointer to the struct thread for the `new' process.
+ * when this function returns, it will return to the new thread.
+ */
+C_ENTRY(switch_thread):
+ // Return the previous task (r10 is not clobbered by restore below)
+ mov CURRENT_TASK, r10
+ // First, push the current processor state on the stack
+ PUSH_STATE(SWITCH)
+ // Now save the location of the kernel stack pointer for this thread;
+ // since we've pushed all other state on the stack, this is enough to
+ // restore it all later.
+ st.w sp, THREAD_KSP[r6]
+ // Now restore the stack pointer from the new process
+ ld.w THREAD_KSP[r7], sp
+ // ... and restore all state from that
+ POP_STATE(SWITCH)
+ // Update the current task pointer
+ GET_CURRENT_TASK(CURRENT_TASK)
+ // Now return into the new thread
+ jmp [lp]
+C_END(switch_thread)
+
+
+ .data
+
+ .align 4
+C_DATA(trap_table):
+ .long bad_trap_wrapper // trap 0, doesn't use trap table.
+ .long syscall_long // trap 1, `long' syscall.
+ .long bad_trap_wrapper
+ .long bad_trap_wrapper
+ .long bad_trap_wrapper
+ .long bad_trap_wrapper
+ .long bad_trap_wrapper
+ .long bad_trap_wrapper
+ .long bad_trap_wrapper
+ .long bad_trap_wrapper
+ .long bad_trap_wrapper
+ .long bad_trap_wrapper
+ .long bad_trap_wrapper
+ .long bad_trap_wrapper
+ .long bad_trap_wrapper
+ .long bad_trap_wrapper
+C_END(trap_table)
+
+
+ .section .rodata
+
+ .align 4
+C_DATA(sys_call_table):
+ .long CSYM(sys_restart_syscall) // 0
+ .long CSYM(sys_exit)
+ .long sys_fork_wrapper
+ .long CSYM(sys_read)
+ .long CSYM(sys_write)
+ .long CSYM(sys_open) // 5
+ .long CSYM(sys_close)
+ .long CSYM(sys_waitpid)
+ .long CSYM(sys_creat)
+ .long CSYM(sys_link)
+ .long CSYM(sys_unlink) // 10
+ .long sys_execve_wrapper
+ .long CSYM(sys_chdir)
+ .long CSYM(sys_time)
+ .long CSYM(sys_mknod)
+ .long CSYM(sys_chmod) // 15
+ .long CSYM(sys_chown)
+ .long CSYM(sys_ni_syscall) // was: break
+ .long CSYM(sys_ni_syscall) // was: oldstat (aka stat)
+ .long CSYM(sys_lseek)
+ .long CSYM(sys_getpid) // 20
+ .long CSYM(sys_mount)
+ .long CSYM(sys_oldumount)
+ .long CSYM(sys_setuid)
+ .long CSYM(sys_getuid)
+ .long CSYM(sys_stime) // 25
+ .long CSYM(sys_ptrace)
+ .long CSYM(sys_alarm)
+ .long CSYM(sys_ni_syscall) // was: oldfstat (aka fstat)
+ .long CSYM(sys_pause)
+ .long CSYM(sys_utime) // 30
+ .long CSYM(sys_ni_syscall) // was: stty
+ .long CSYM(sys_ni_syscall) // was: gtty
+ .long CSYM(sys_access)
+ .long CSYM(sys_nice)
+ .long CSYM(sys_ni_syscall) // 35, was: ftime
+ .long CSYM(sys_sync)
+ .long CSYM(sys_kill)
+ .long CSYM(sys_rename)
+ .long CSYM(sys_mkdir)
+ .long CSYM(sys_rmdir) // 40
+ .long CSYM(sys_dup)
+ .long CSYM(sys_pipe)
+ .long CSYM(sys_times)
+ .long CSYM(sys_ni_syscall) // was: prof
+ .long CSYM(sys_brk) // 45
+ .long CSYM(sys_setgid)
+ .long CSYM(sys_getgid)
+ .long CSYM(sys_signal)
+ .long CSYM(sys_geteuid)
+ .long CSYM(sys_getegid) // 50
+ .long CSYM(sys_acct)
+ .long CSYM(sys_umount) // recycled never used phys()
+ .long CSYM(sys_ni_syscall) // was: lock
+ .long CSYM(sys_ioctl)
+ .long CSYM(sys_fcntl) // 55
+ .long CSYM(sys_ni_syscall) // was: mpx
+ .long CSYM(sys_setpgid)
+ .long CSYM(sys_ni_syscall) // was: ulimit
+ .long CSYM(sys_ni_syscall)
+ .long CSYM(sys_umask) // 60
+ .long CSYM(sys_chroot)
+ .long CSYM(sys_ustat)
+ .long CSYM(sys_dup2)
+ .long CSYM(sys_getppid)
+ .long CSYM(sys_getpgrp) // 65
+ .long CSYM(sys_setsid)
+ .long CSYM(sys_sigaction)
+ .long CSYM(sys_sgetmask)
+ .long CSYM(sys_ssetmask)
+ .long CSYM(sys_setreuid) // 70
+ .long CSYM(sys_setregid)
+ .long sys_sigsuspend_wrapper
+ .long CSYM(sys_sigpending)
+ .long CSYM(sys_sethostname)
+ .long CSYM(sys_setrlimit) // 75
+ .long CSYM(sys_getrlimit)
+ .long CSYM(sys_getrusage)
+ .long CSYM(sys_gettimeofday)
+ .long CSYM(sys_settimeofday)
+ .long CSYM(sys_getgroups) // 80
+ .long CSYM(sys_setgroups)
+ .long CSYM(sys_select)
+ .long CSYM(sys_symlink)
+ .long CSYM(sys_ni_syscall) // was: oldlstat (aka lstat)
+ .long CSYM(sys_readlink) // 85
+ .long CSYM(sys_uselib)
+ .long CSYM(sys_swapon)
+ .long CSYM(sys_reboot)
+ .long CSYM(old_readdir)
+ .long CSYM(sys_mmap) // 90
+ .long CSYM(sys_munmap)
+ .long CSYM(sys_truncate)
+ .long CSYM(sys_ftruncate)
+ .long CSYM(sys_fchmod)
+ .long CSYM(sys_fchown) // 95
+ .long CSYM(sys_getpriority)
+ .long CSYM(sys_setpriority)
+ .long CSYM(sys_ni_syscall) // was: profil
+ .long CSYM(sys_statfs)
+ .long CSYM(sys_fstatfs) // 100
+ .long CSYM(sys_ni_syscall) // i386: ioperm
+ .long CSYM(sys_socketcall)
+ .long CSYM(sys_syslog)
+ .long CSYM(sys_setitimer)
+ .long CSYM(sys_getitimer) // 105
+ .long CSYM(sys_newstat)
+ .long CSYM(sys_newlstat)
+ .long CSYM(sys_newfstat)
+ .long CSYM(sys_ni_syscall) // was: olduname (aka uname)
+ .long CSYM(sys_ni_syscall) // 110, i386: iopl
+ .long CSYM(sys_vhangup)
+ .long CSYM(sys_ni_syscall) // was: idle
+ .long CSYM(sys_ni_syscall) // i386: vm86old
+ .long CSYM(sys_wait4)
+ .long CSYM(sys_swapoff) // 115
+ .long CSYM(sys_sysinfo)
+ .long CSYM(sys_ipc)
+ .long CSYM(sys_fsync)
+ .long sys_sigreturn_wrapper
+ .long sys_clone_wrapper // 120
+ .long CSYM(sys_setdomainname)
+ .long CSYM(sys_newuname)
+ .long CSYM(sys_ni_syscall) // i386: modify_ldt, m68k: cacheflush
+ .long CSYM(sys_adjtimex)
+ .long CSYM(sys_ni_syscall) // 125 - sys_mprotect
+ .long CSYM(sys_sigprocmask)
+ .long CSYM(sys_ni_syscall) // sys_create_module
+ .long CSYM(sys_init_module)
+ .long CSYM(sys_delete_module)
+ .long CSYM(sys_ni_syscall) // 130 - sys_get_kernel_syms
+ .long CSYM(sys_quotactl)
+ .long CSYM(sys_getpgid)
+ .long CSYM(sys_fchdir)
+ .long CSYM(sys_bdflush)
+ .long CSYM(sys_sysfs) // 135
+ .long CSYM(sys_personality)
+ .long CSYM(sys_ni_syscall) // for afs_syscall
+ .long CSYM(sys_setfsuid)
+ .long CSYM(sys_setfsgid)
+ .long CSYM(sys_llseek) // 140
+ .long CSYM(sys_getdents)
+ .long CSYM(sys_select) // for backward compat; remove someday
+ .long CSYM(sys_flock)
+ .long CSYM(sys_ni_syscall) // sys_msync
+ .long CSYM(sys_readv) // 145
+ .long CSYM(sys_writev)
+ .long CSYM(sys_getsid)
+ .long CSYM(sys_fdatasync)
+ .long CSYM(sys_sysctl)
+ .long CSYM(sys_ni_syscall) // 150 - sys_mlock
+ .long CSYM(sys_ni_syscall) // sys_munlock
+ .long CSYM(sys_ni_syscall) // sys_mlockall
+ .long CSYM(sys_ni_syscall) // sys_munlockall
+ .long CSYM(sys_sched_setparam)
+ .long CSYM(sys_sched_getparam) // 155
+ .long CSYM(sys_sched_setscheduler)
+ .long CSYM(sys_sched_getscheduler)
+ .long CSYM(sys_sched_yield)
+ .long CSYM(sys_sched_get_priority_max)
+ .long CSYM(sys_sched_get_priority_min) // 160
+ .long CSYM(sys_sched_rr_get_interval)
+ .long CSYM(sys_nanosleep)
+ .long CSYM(sys_ni_syscall) // sys_mremap
+ .long CSYM(sys_setresuid)
+ .long CSYM(sys_getresuid) // 165
+ .long CSYM(sys_ni_syscall) // for vm86
+ .long CSYM(sys_ni_syscall) // sys_query_module
+ .long CSYM(sys_poll)
+ .long CSYM(sys_nfsservctl)
+ .long CSYM(sys_setresgid) // 170
+ .long CSYM(sys_getresgid)
+ .long CSYM(sys_prctl)
+ .long sys_rt_sigreturn_wrapper
+ .long CSYM(sys_rt_sigaction)
+ .long CSYM(sys_rt_sigprocmask) // 175
+ .long CSYM(sys_rt_sigpending)
+ .long CSYM(sys_rt_sigtimedwait)
+ .long CSYM(sys_rt_sigqueueinfo)
+ .long sys_rt_sigsuspend_wrapper
+ .long CSYM(sys_pread64) // 180
+ .long CSYM(sys_pwrite64)
+ .long CSYM(sys_lchown)
+ .long CSYM(sys_getcwd)
+ .long CSYM(sys_capget)
+ .long CSYM(sys_capset) // 185
+ .long CSYM(sys_sigaltstack)
+ .long CSYM(sys_sendfile)
+ .long CSYM(sys_ni_syscall) // streams1
+ .long CSYM(sys_ni_syscall) // streams2
+ .long sys_vfork_wrapper // 190
+ .long CSYM(sys_ni_syscall)
+ .long CSYM(sys_mmap2)
+ .long CSYM(sys_truncate64)
+ .long CSYM(sys_ftruncate64)
+ .long CSYM(sys_stat64) // 195
+ .long CSYM(sys_lstat64)
+ .long CSYM(sys_fstat64)
+ .long CSYM(sys_fcntl64)
+ .long CSYM(sys_getdents64)
+ .long CSYM(sys_pivot_root) // 200
+ .long CSYM(sys_gettid)
+ .long CSYM(sys_tkill)
+sys_call_table_end:
+C_END(sys_call_table)
diff --git a/arch/v850/kernel/fpga85e2c.c b/arch/v850/kernel/fpga85e2c.c
new file mode 100644
index 0000000..4bac514
--- /dev/null
+++ b/arch/v850/kernel/fpga85e2c.c
@@ -0,0 +1,171 @@
+/*
+ * arch/v850/kernel/fpga85e2c.h -- Machine-dependent defs for
+ * FPGA implementation of V850E2/NA85E2C
+ *
+ * Copyright (C) 2002,03 NEC Electronics Corporation
+ * Copyright (C) 2002,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/config.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/mm.h>
+#include <linux/swap.h>
+#include <linux/bootmem.h>
+#include <linux/irq.h>
+#include <linux/bitops.h>
+
+#include <asm/atomic.h>
+#include <asm/page.h>
+#include <asm/machdep.h>
+
+#include "mach.h"
+
+extern void memcons_setup (void);
+
+
+#define REG_DUMP_ADDR 0x220000
+
+
+extern struct irqaction reg_snap_action; /* fwd decl */
+
+
+void __init mach_early_init (void)
+{
+ int i;
+ const u32 *src;
+ register u32 *dst asm ("ep");
+ extern u32 _intv_end, _intv_load_start;
+
+ /* Set bus sizes: CS0 32-bit, CS1 16-bit, CS7 8-bit,
+ everything else 32-bit. */
+ V850E2_BSC = 0x2AA6;
+ for (i = 2; i <= 6; i++)
+ CSDEV(i) = 0; /* 32 bit */
+
+ /* Ensure that the simulator halts on a panic, instead of going
+ into an infinite loop inside the panic function. */
+ panic_timeout = -1;
+
+ /* Move the interrupt vectors into their real location. Note that
+ any relocations there are relative to the real location, so we
+ don't have to fix anything up. We use a loop instead of calling
+ memcpy to keep this a leaf function (to avoid a function
+ prologue being generated). */
+ dst = 0x10; /* &_intv_start + 0x10. */
+ src = &_intv_load_start;
+ do {
+ u32 t0 = src[0], t1 = src[1], t2 = src[2], t3 = src[3];
+ u32 t4 = src[4], t5 = src[5], t6 = src[6], t7 = src[7];
+ dst[0] = t0; dst[1] = t1; dst[2] = t2; dst[3] = t3;
+ dst[4] = t4; dst[5] = t5; dst[6] = t6; dst[7] = t7;
+ dst += 8;
+ src += 8;
+ } while (dst < &_intv_end);
+}
+
+void __init mach_setup (char **cmdline)
+{
+ memcons_setup ();
+
+ /* Setup up NMI0 to copy the registers to a known memory location.
+ The FGPA board has a button that produces NMI0 when pressed, so
+ this allows us to push the button, and then look at memory to see
+ what's in the registers (there's no other way to easily do so).
+ We have to use `setup_irq' instead of `request_irq' because it's
+ still too early to do memory allocation. */
+ setup_irq (IRQ_NMI (0), &reg_snap_action);
+}
+
+void mach_get_physical_ram (unsigned long *ram_start, unsigned long *ram_len)
+{
+ *ram_start = ERAM_ADDR;
+ *ram_len = ERAM_SIZE;
+}
+
+void __init mach_sched_init (struct irqaction *timer_action)
+{
+ /* Setup up the timer interrupt. The FPGA peripheral control
+ registers _only_ work with single-bit writes (set1/clr1)! */
+ __clear_bit (RPU_GTMC_CE_BIT, &RPU_GTMC);
+ __clear_bit (RPU_GTMC_CLK_BIT, &RPU_GTMC);
+ __set_bit (RPU_GTMC_CE_BIT, &RPU_GTMC);
+
+ /* We use the first RPU interrupt, which occurs every 8.192ms. */
+ setup_irq (IRQ_RPU (0), timer_action);
+}
+
+
+void mach_gettimeofday (struct timespec *tv)
+{
+ tv->tv_sec = 0;
+ tv->tv_nsec = 0;
+}
+
+void machine_halt (void) __attribute__ ((noreturn));
+void machine_halt (void)
+{
+ for (;;) {
+ DWC(0) = 0x7777;
+ DWC(1) = 0x7777;
+ ASC = 0xffff;
+ FLGREG(0) = 1; /* Halt immediately. */
+ asm ("di; halt; nop; nop; nop; nop; nop");
+ }
+}
+
+EXPORT_SYMBOL(machine_halt);
+
+void machine_restart (char *__unused)
+{
+ machine_halt ();
+}
+
+EXPORT_SYMBOL(machine_restart);
+
+void machine_power_off (void)
+{
+ machine_halt ();
+}
+
+EXPORT_SYMBOL(machine_power_off);
+
+
+/* Interrupts */
+
+struct v850e_intc_irq_init irq_inits[] = {
+ { "IRQ", 0, NUM_MACH_IRQS, 1, 7 },
+ { "RPU", IRQ_RPU(0), IRQ_RPU_NUM, 1, 6 },
+ { 0 }
+};
+#define NUM_IRQ_INITS ((sizeof irq_inits / sizeof irq_inits[0]) - 1)
+
+struct hw_interrupt_type hw_itypes[NUM_IRQ_INITS];
+
+/* Initialize interrupts. */
+void __init mach_init_irqs (void)
+{
+ v850e_intc_init_irq_types (irq_inits, hw_itypes);
+}
+
+
+/* An interrupt handler that copies the registers to a known memory location,
+ for debugging purposes. */
+
+static void make_reg_snap (int irq, void *dummy, struct pt_regs *regs)
+{
+ (*(unsigned *)REG_DUMP_ADDR)++;
+ (*(struct pt_regs *)(REG_DUMP_ADDR + sizeof (unsigned))) = *regs;
+}
+
+static int reg_snap_dev_id;
+static struct irqaction reg_snap_action = {
+ make_reg_snap, 0, CPU_MASK_NONE, "reg_snap", &reg_snap_dev_id, 0
+};
diff --git a/arch/v850/kernel/fpga85e2c.ld b/arch/v850/kernel/fpga85e2c.ld
new file mode 100644
index 0000000..b5d4578
--- /dev/null
+++ b/arch/v850/kernel/fpga85e2c.ld
@@ -0,0 +1,62 @@
+/* Linker script for the FPGA implementation of the V850E2 NA85E2C cpu core
+ (CONFIG_V850E2_FPGA85E2C). */
+
+MEMORY {
+ /* Reset vector. */
+ RESET : ORIGIN = 0, LENGTH = 0x10
+ /* Interrupt vectors. */
+ INTV : ORIGIN = 0x10, LENGTH = 0x470
+ /* The `window' in RAM were we're allowed to load stuff. */
+ RAM_LOW : ORIGIN = 0x480, LENGTH = 0x0005FB80
+ /* Some more ram above the window were we can put bss &c. */
+ RAM_HIGH : ORIGIN = 0x00060000, LENGTH = 0x000A0000
+ /* This is the area visible from the outside world (we can use
+ this only for uninitialized data). */
+ VISIBLE : ORIGIN = 0x00200000, LENGTH = 0x00060000
+}
+
+SECTIONS {
+ .reset : {
+ __kram_start = . ;
+ __intv_start = . ;
+ *(.intv.reset) /* Reset vector */
+ } > RESET
+
+ .ram_low : {
+ __r0_ram = . ; /* Must be near address 0. */
+ . = . + 32 ;
+
+ TEXT_CONTENTS
+ DATA_CONTENTS
+ ROOT_FS_CONTENTS
+ RAMK_INIT_CONTENTS_NO_END
+ INITRAMFS_CONTENTS
+ } > RAM_LOW
+
+ /* Where the interrupt vectors are initially loaded. */
+ __intv_load_start = . ;
+
+ .intv : {
+ *(.intv.common) /* Vectors common to all v850e proc. */
+ *(.intv.mach) /* Machine-specific int. vectors. */
+ __intv_end = . ;
+ } > INTV AT> RAM_LOW
+
+ .ram_high : {
+ /* This is here so that when we free init memory the
+ load-time copy of the interrupt vectors and any empty
+ space at the end of the `RAM_LOW' area is freed too. */
+ . = ALIGN (4096);
+ __init_end = . ;
+
+ BSS_CONTENTS
+ __kram_end = . ;
+ BOOTMAP_CONTENTS
+ } > RAM_HIGH
+
+ .visible : {
+ _memcons_output = . ;
+ . = . + 0x8000 ;
+ _memcons_output_end = . ;
+ } > VISIBLE
+}
diff --git a/arch/v850/kernel/gbus_int.c b/arch/v850/kernel/gbus_int.c
new file mode 100644
index 0000000..92918b8
--- /dev/null
+++ b/arch/v850/kernel/gbus_int.c
@@ -0,0 +1,271 @@
+/*
+ * arch/v850/kernel/gbus_int.c -- Midas labs GBUS interrupt support
+ *
+ * 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/types.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/signal.h>
+
+#include <asm/machdep.h>
+
+
+/* The number of shared GINT interrupts. */
+#define NUM_GINTS 4
+
+/* For each GINT interrupt, how many GBUS interrupts are using it. */
+static unsigned gint_num_active_irqs[NUM_GINTS] = { 0 };
+
+/* A table of GINTn interrupts we actually use.
+ Note that we don't use GINT0 because all the boards we support treat it
+ specially. */
+struct used_gint {
+ unsigned gint;
+ unsigned priority;
+} used_gint[] = {
+ { 1, GBUS_INT_PRIORITY_HIGH },
+ { 3, GBUS_INT_PRIORITY_LOW }
+};
+#define NUM_USED_GINTS (sizeof used_gint / sizeof used_gint[0])
+
+/* A table of which GINT is used by each GBUS interrupts (they are
+ assigned based on priority). */
+static unsigned char gbus_int_gint[IRQ_GBUS_INT_NUM];
+
+
+/* Interrupt enabling/disabling. */
+
+/* Enable interrupt handling for interrupt IRQ. */
+void gbus_int_enable_irq (unsigned irq)
+{
+ unsigned gint = gbus_int_gint[irq - GBUS_INT_BASE_IRQ];
+ GBUS_INT_ENABLE (GBUS_INT_IRQ_WORD(irq), gint)
+ |= GBUS_INT_IRQ_MASK (irq);
+}
+
+/* Disable interrupt handling for interrupt IRQ. Note that any
+ interrupts received while disabled will be delivered once the
+ interrupt is enabled again, unless they are explicitly cleared using
+ `gbus_int_clear_pending_irq'. */
+void gbus_int_disable_irq (unsigned irq)
+{
+ unsigned gint = gbus_int_gint[irq - GBUS_INT_BASE_IRQ];
+ GBUS_INT_ENABLE (GBUS_INT_IRQ_WORD(irq), gint)
+ &= ~GBUS_INT_IRQ_MASK (irq);
+}
+
+/* Return true if interrupt handling for interrupt IRQ is enabled. */
+int gbus_int_irq_enabled (unsigned irq)
+{
+ unsigned gint = gbus_int_gint[irq - GBUS_INT_BASE_IRQ];
+ return (GBUS_INT_ENABLE (GBUS_INT_IRQ_WORD(irq), gint)
+ & GBUS_INT_IRQ_MASK(irq));
+}
+
+/* Disable all GBUS irqs. */
+void gbus_int_disable_irqs ()
+{
+ unsigned w, n;
+ for (w = 0; w < GBUS_INT_NUM_WORDS; w++)
+ for (n = 0; n < IRQ_GINT_NUM; n++)
+ GBUS_INT_ENABLE (w, n) = 0;
+}
+
+/* Clear any pending interrupts for IRQ. */
+void gbus_int_clear_pending_irq (unsigned irq)
+{
+ GBUS_INT_CLEAR (GBUS_INT_IRQ_WORD(irq)) = GBUS_INT_IRQ_MASK (irq);
+}
+
+/* Return true if interrupt IRQ is pending (but disabled). */
+int gbus_int_irq_pending (unsigned irq)
+{
+ return (GBUS_INT_STATUS (GBUS_INT_IRQ_WORD(irq))
+ & GBUS_INT_IRQ_MASK(irq));
+}
+
+
+/* Delegating interrupts. */
+
+/* Handle a shared GINT interrupt by passing to the appropriate GBUS
+ interrupt handler. */
+static irqreturn_t gbus_int_handle_irq (int irq, void *dev_id,
+ struct pt_regs *regs)
+{
+ unsigned w;
+ irqreturn_t rval = IRQ_NONE;
+ unsigned gint = irq - IRQ_GINT (0);
+
+ for (w = 0; w < GBUS_INT_NUM_WORDS; w++) {
+ unsigned status = GBUS_INT_STATUS (w);
+ unsigned enable = GBUS_INT_ENABLE (w, gint);
+
+ /* Only pay attention to enabled interrupts. */
+ status &= enable;
+ if (status) {
+ irq = IRQ_GBUS_INT (w * GBUS_INT_BITS_PER_WORD);
+ do {
+ /* There's an active interrupt in word
+ W, find out which one, and call its
+ handler. */
+
+ while (! (status & 0x1)) {
+ irq++;
+ status >>= 1;
+ }
+ status &= ~0x1;
+
+ /* Recursively call handle_irq to handle it. */
+ handle_irq (irq, regs);
+ rval = IRQ_HANDLED;
+ } while (status);
+ }
+ }
+
+ /* Toggle the `all enable' bit back and forth, which should cause
+ another edge transition if there are any other interrupts
+ still pending, and so result in another CPU interrupt. */
+ GBUS_INT_ENABLE (0, gint) &= ~0x1;
+ GBUS_INT_ENABLE (0, gint) |= 0x1;
+
+ return rval;
+}
+
+
+/* Initialize GBUS interrupt sources. */
+
+static void irq_nop (unsigned irq) { }
+
+static unsigned gbus_int_startup_irq (unsigned irq)
+{
+ unsigned gint = gbus_int_gint[irq - GBUS_INT_BASE_IRQ];
+
+ if (gint_num_active_irqs[gint] == 0) {
+ /* First enable the CPU interrupt. */
+ int rval =
+ request_irq (IRQ_GINT(gint), gbus_int_handle_irq,
+ SA_INTERRUPT,
+ "gbus_int_handler",
+ &gint_num_active_irqs[gint]);
+ if (rval != 0)
+ return rval;
+ }
+
+ gint_num_active_irqs[gint]++;
+
+ gbus_int_clear_pending_irq (irq);
+ gbus_int_enable_irq (irq);
+
+ return 0;
+}
+
+static void gbus_int_shutdown_irq (unsigned irq)
+{
+ unsigned gint = gbus_int_gint[irq - GBUS_INT_BASE_IRQ];
+
+ gbus_int_disable_irq (irq);
+
+ if (--gint_num_active_irqs[gint] == 0)
+ /* Disable the CPU interrupt. */
+ free_irq (IRQ_GINT(gint), &gint_num_active_irqs[gint]);
+}
+
+/* Initialize HW_IRQ_TYPES for INTC-controlled irqs described in array
+ INITS (which is terminated by an entry with the name field == 0). */
+void __init gbus_int_init_irq_types (struct gbus_int_irq_init *inits,
+ struct hw_interrupt_type *hw_irq_types)
+{
+ struct gbus_int_irq_init *init;
+ for (init = inits; init->name; init++) {
+ unsigned i;
+ struct hw_interrupt_type *hwit = hw_irq_types++;
+
+ hwit->typename = init->name;
+
+ hwit->startup = gbus_int_startup_irq;
+ hwit->shutdown = gbus_int_shutdown_irq;
+ hwit->enable = gbus_int_enable_irq;
+ hwit->disable = gbus_int_disable_irq;
+ hwit->ack = irq_nop;
+ hwit->end = irq_nop;
+
+ /* Initialize kernel IRQ infrastructure for this interrupt. */
+ init_irq_handlers(init->base, init->num, init->interval, hwit);
+
+ /* Set the interrupt priorities. */
+ for (i = 0; i < init->num; i++) {
+ unsigned j;
+ for (j = 0; j < NUM_USED_GINTS; j++)
+ if (used_gint[j].priority > init->priority)
+ break;
+ /* Wherever we stopped looking is one past the
+ GINT we want. */
+ gbus_int_gint[init->base + i * init->interval
+ - GBUS_INT_BASE_IRQ]
+ = used_gint[j > 0 ? j - 1 : 0].gint;
+ }
+ }
+}
+
+
+/* Initialize IRQS. */
+
+/* Chip interrupts (GINTn) shared among GBUS interrupts. */
+static struct hw_interrupt_type gint_hw_itypes[NUM_USED_GINTS];
+
+
+/* GBUS interrupts themselves. */
+
+struct gbus_int_irq_init gbus_irq_inits[] __initdata = {
+ /* First set defaults. */
+ { "GBUS_INT", IRQ_GBUS_INT(0), IRQ_GBUS_INT_NUM, 1, 6},
+ { 0 }
+};
+#define NUM_GBUS_IRQ_INITS \
+ ((sizeof gbus_irq_inits / sizeof gbus_irq_inits[0]) - 1)
+
+static struct hw_interrupt_type gbus_hw_itypes[NUM_GBUS_IRQ_INITS];
+
+
+/* Initialize GBUS interrupts. */
+void __init gbus_int_init_irqs (void)
+{
+ unsigned i;
+
+ /* First initialize the shared gint interrupts. */
+ for (i = 0; i < NUM_USED_GINTS; i++) {
+ unsigned gint = used_gint[i].gint;
+ struct v850e_intc_irq_init gint_irq_init[2];
+
+ /* We initialize one GINT interrupt at a time. */
+ gint_irq_init[0].name = "GINT";
+ gint_irq_init[0].base = IRQ_GINT (gint);
+ gint_irq_init[0].num = 1;
+ gint_irq_init[0].interval = 1;
+ gint_irq_init[0].priority = used_gint[i].priority;
+
+ gint_irq_init[1].name = 0; /* Terminate the vector. */
+
+ v850e_intc_init_irq_types (gint_irq_init, gint_hw_itypes);
+ }
+
+ /* Then the GBUS interrupts. */
+ gbus_int_disable_irqs ();
+ gbus_int_init_irq_types (gbus_irq_inits, gbus_hw_itypes);
+ /* Turn on the `all enable' bits, which are ANDed with
+ individual interrupt enable bits; we only want to bother with
+ the latter. They are the first bit in the first word of each
+ interrupt-enable area. */
+ for (i = 0; i < NUM_USED_GINTS; i++)
+ GBUS_INT_ENABLE (0, used_gint[i].gint) = 0x1;
+}
diff --git a/arch/v850/kernel/head.S b/arch/v850/kernel/head.S
new file mode 100644
index 0000000..c490b93
--- /dev/null
+++ b/arch/v850/kernel/head.S
@@ -0,0 +1,128 @@
+/*
+ * arch/v850/kernel/head.S -- Lowest-level startup code
+ *
+ * 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 <asm/clinkage.h>
+#include <asm/current.h>
+#include <asm/entry.h>
+#include <asm/thread_info.h>
+#include <asm/irq.h>
+
+
+/* Make a slightly more convenient alias for C_SYMBOL_NAME. */
+#define CSYM C_SYMBOL_NAME
+
+
+ .text
+
+ // Define `mach_early_init' as a weak symbol
+ .global CSYM(mach_early_init)
+ .weak CSYM(mach_early_init)
+
+C_ENTRY(start):
+ // Make sure interrupts are turned off, just in case
+ di
+
+#ifdef CONFIG_RESET_GUARD
+ // See if we got here via an unexpected reset
+ ld.w RESET_GUARD, r19 // Check current value of reset guard
+ mov RESET_GUARD_ACTIVE, r20
+ cmp r19, r20
+ bne 1f // Guard was not active
+
+ // If we get here, the reset guard was active. Load up some
+ // interesting values as arguments, and jump to the handler.
+ st.w r0, RESET_GUARD // Allow further resets to succeed
+ mov lp, r6 // Arg 0: return address
+ ld.b KM, r7 // Arg 1: kernel mode
+ mov sp, r9 // Arg 3: stack pointer
+ ld.w KSP, r19 // maybe switch to kernel stack
+ cmp r7, r0 // see if already in kernel mode
+ cmov z, r19, sp, sp // and switch to kernel stack if not
+ GET_CURRENT_TASK(r8) // Arg 2: task pointer
+ jr CSYM(unexpected_reset)
+
+1: st.w r20, RESET_GUARD // Turn on reset guard
+#endif /* CONFIG_RESET_GUARD */
+
+ // Setup a temporary stack for doing pre-initialization function calls.
+ //
+ // We can't use the initial kernel stack, because (1) it may be
+ // located in memory we're not allowed to touch, and (2) since
+ // it's in the data segment, calling memcpy to initialize that
+ // area from ROM will overwrite memcpy's return address.
+ mov hilo(CSYM(_init_stack_end) - 4), sp
+
+ // See if there's a platform-specific early-initialization routine
+ // defined; it's a weak symbol, so it will have an address of zero if
+ // there's not.
+ mov hilo(CSYM(mach_early_init)), r6
+ cmp r6, r0
+ bz 3f
+
+ // There is one, so call it. If this function is written in C, it
+ // should be very careful -- the stack pointer is valid, but very
+ // little else is (e.g., bss is not zeroed yet, and initialized data
+ // hasn't been).
+ jarl 2f, lp // first figure out return address
+2: add 3f - ., lp
+ jmp [r6] // do call
+3:
+
+#ifdef CONFIG_ROM_KERNEL
+ // Copy the data area from ROM to RAM
+ mov hilo(CSYM(_rom_copy_dst_start)), r6
+ mov hilo(CSYM(_rom_copy_src_start)), r7
+ mov hilo(CSYM(_rom_copy_dst_end)), r8
+ sub r6, r8
+ jarl CSYM(memcpy), lp
+#endif
+
+ // Load the initial thread's stack, and current task pointer (in r16)
+ mov hilo(CSYM(init_thread_union)), r19
+ movea THREAD_SIZE, r19, sp
+ ld.w TI_TASK[r19], CURRENT_TASK
+
+#ifdef CONFIG_TIME_BOOTUP
+ /* This stuff must come after mach_early_init, because interrupts may
+ not work until after its been called. */
+ jarl CSYM(highres_timer_reset), lp
+ jarl CSYM(highres_timer_start), lp
+#endif
+
+ // Kernel stack pointer save location
+ st.w sp, KSP
+
+ // Assert that we're in `kernel mode'
+ mov 1, r19
+ st.w r19, KM
+
+#ifdef CONFIG_ZERO_BSS
+ // Zero bss area, since we can't rely upon any loader to do so
+ mov hilo(CSYM(_sbss)), r6
+ mov r0, r7
+ mov hilo(CSYM(_ebss)), r8
+ sub r6, r8
+ jarl CSYM(memset), lp
+#endif
+
+ // What happens if the main kernel function returns (it shouldn't)
+ mov hilo(CSYM(machine_halt)), lp
+
+ // Start the linux kernel. We use an indirect jump to get extra
+ // range, because on some platforms this initial startup code
+ // (and the associated platform-specific code in mach_early_init)
+ // are located far away from the main kernel, e.g. so that they
+ // can initialize RAM first and copy the kernel or something.
+ mov hilo(CSYM(start_kernel)), r12
+ jmp [r12]
+C_END(start)
diff --git a/arch/v850/kernel/highres_timer.c b/arch/v850/kernel/highres_timer.c
new file mode 100644
index 0000000..b16ad1e
--- /dev/null
+++ b/arch/v850/kernel/highres_timer.c
@@ -0,0 +1,132 @@
+/*
+ * arch/v850/kernel/highres_timer.c -- High resolution timing routines
+ *
+ * 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 <asm/system.h>
+#include <asm/v850e_timer_d.h>
+#include <asm/highres_timer.h>
+
+#define HIGHRES_TIMER_USEC_SHIFT 12
+
+/* Pre-calculated constant used for converting ticks to real time
+ units. We initialize it to prevent it being put into BSS. */
+static u32 highres_timer_usec_prescale = 1;
+
+void highres_timer_slow_tick_irq (void) __attribute__ ((noreturn));
+void highres_timer_slow_tick_irq (void)
+{
+ /* This is an interrupt handler, so it must be very careful to
+ not to trash any registers. At this point, the stack-pointer
+ (r3) has been saved in the chip ram location ENTRY_SP by the
+ interrupt vector, so we can use it as a scratch register; we
+ must also restore it before returning. */
+ asm ("ld.w %0[r0], sp;"
+ "add 1, sp;"
+ "st.w sp, %0[r0];"
+ "ld.w %1[r0], sp;" /* restore pre-irq stack-pointer */
+ "reti"
+ ::
+ "i" (HIGHRES_TIMER_SLOW_TICKS_ADDR),
+ "i" (ENTRY_SP_ADDR)
+ : "memory");
+}
+
+void highres_timer_reset (void)
+{
+ V850E_TIMER_D_TMD (HIGHRES_TIMER_TIMER_D_UNIT) = 0;
+ HIGHRES_TIMER_SLOW_TICKS = 0;
+}
+
+void highres_timer_start (void)
+{
+ u32 fast_tick_rate;
+
+ /* Start hardware timer. */
+ v850e_timer_d_configure (HIGHRES_TIMER_TIMER_D_UNIT,
+ HIGHRES_TIMER_SLOW_TICK_RATE);
+
+ fast_tick_rate =
+ (V850E_TIMER_D_BASE_FREQ
+ >> V850E_TIMER_D_DIVLOG2 (HIGHRES_TIMER_TIMER_D_UNIT));
+
+ /* The obvious way of calculating microseconds from fast ticks
+ is to do:
+
+ usec = fast_ticks * 10^6 / fast_tick_rate
+
+ However, divisions are much slower than multiplications, and
+ the above calculation can overflow, so we do this instead:
+
+ usec = fast_ticks * (10^6 * 2^12 / fast_tick_rate) / 2^12
+
+ since we can pre-calculate (10^6 * (2^12 / fast_tick_rate))
+ and use a shift for dividing by 2^12, this avoids division,
+ and is almost as accurate (it differs by about 2 microseconds
+ at the extreme value of the fast-tick counter's ranger). */
+ highres_timer_usec_prescale = ((1000000 << HIGHRES_TIMER_USEC_SHIFT)
+ / fast_tick_rate);
+
+ /* Enable the interrupt (which is hardwired to this use), and
+ give it the highest priority. */
+ V850E_INTC_IC (IRQ_INTCMD (HIGHRES_TIMER_TIMER_D_UNIT)) = 0;
+}
+
+void highres_timer_stop (void)
+{
+ /* Stop the timer. */
+ V850E_TIMER_D_TMCD (HIGHRES_TIMER_TIMER_D_UNIT) =
+ V850E_TIMER_D_TMCD_CAE;
+ /* Disable its interrupt, just in case. */
+ v850e_intc_disable_irq (IRQ_INTCMD (HIGHRES_TIMER_TIMER_D_UNIT));
+}
+
+inline void highres_timer_read_ticks (u32 *slow_ticks, u32 *fast_ticks)
+{
+ int flags;
+ u32 fast_ticks_1, fast_ticks_2, _slow_ticks;
+
+ local_irq_save (flags);
+ fast_ticks_1 = V850E_TIMER_D_TMD (HIGHRES_TIMER_TIMER_D_UNIT);
+ _slow_ticks = HIGHRES_TIMER_SLOW_TICKS;
+ fast_ticks_2 = V850E_TIMER_D_TMD (HIGHRES_TIMER_TIMER_D_UNIT);
+ local_irq_restore (flags);
+
+ if (fast_ticks_2 < fast_ticks_1)
+ _slow_ticks++;
+
+ *slow_ticks = _slow_ticks;
+ *fast_ticks = fast_ticks_2;
+}
+
+inline void highres_timer_ticks_to_timeval (u32 slow_ticks, u32 fast_ticks,
+ struct timeval *tv)
+{
+ unsigned long sec, sec_rem, usec;
+
+ usec = ((fast_ticks * highres_timer_usec_prescale)
+ >> HIGHRES_TIMER_USEC_SHIFT);
+
+ sec = slow_ticks / HIGHRES_TIMER_SLOW_TICK_RATE;
+ sec_rem = slow_ticks % HIGHRES_TIMER_SLOW_TICK_RATE;
+
+ usec += sec_rem * (1000000 / HIGHRES_TIMER_SLOW_TICK_RATE);
+
+ tv->tv_sec = sec;
+ tv->tv_usec = usec;
+}
+
+void highres_timer_read (struct timeval *tv)
+{
+ u32 fast_ticks, slow_ticks;
+ highres_timer_read_ticks (&slow_ticks, &fast_ticks);
+ highres_timer_ticks_to_timeval (slow_ticks, fast_ticks, tv);
+}
diff --git a/arch/v850/kernel/init_task.c b/arch/v850/kernel/init_task.c
new file mode 100644
index 0000000..ed2f93c
--- /dev/null
+++ b/arch/v850/kernel/init_task.c
@@ -0,0 +1,49 @@
+/*
+ * arch/v850/kernel/init_task.c -- Initial task/thread structures
+ *
+ * Copyright (C) 2002,03 NEC Electronics Corporation
+ * Copyright (C) 2002,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.
+ */
+
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/init_task.h>
+#include <linux/fs.h>
+#include <linux/mqueue.h>
+
+#include <asm/uaccess.h>
+#include <asm/pgtable.h>
+
+static struct fs_struct init_fs = INIT_FS;
+static struct files_struct init_files = INIT_FILES;
+static struct signal_struct init_signals = INIT_SIGNALS (init_signals);
+static struct sighand_struct init_sighand = INIT_SIGHAND(init_sighand);
+struct mm_struct init_mm = INIT_MM (init_mm);
+
+EXPORT_SYMBOL(init_mm);
+
+/*
+ * Initial task structure.
+ *
+ * All other task structs will be allocated on slabs in fork.c
+ */
+struct task_struct init_task = INIT_TASK (init_task);
+
+EXPORT_SYMBOL(init_task);
+
+/*
+ * Initial thread structure.
+ *
+ * We need to make sure that this is 8192-byte aligned due to the
+ * way process stacks are handled. This is done by having a special
+ * "init_task" linker map entry.
+ */
+union thread_union init_thread_union
+ __attribute__((__section__(".data.init_task"))) =
+ { INIT_THREAD_INFO(init_task) };
diff --git a/arch/v850/kernel/intv.S b/arch/v850/kernel/intv.S
new file mode 100644
index 0000000..671e4c6
--- /dev/null
+++ b/arch/v850/kernel/intv.S
@@ -0,0 +1,87 @@
+/*
+ * arch/v850/kernel/intv.S -- Interrupt vectors
+ *
+ * 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 <asm/clinkage.h>
+#include <asm/irq.h>
+#include <asm/machdep.h>
+#include <asm/entry.h>
+
+#ifdef CONFIG_V850E_HIGHRES_TIMER
+#include <asm/highres_timer.h>
+#endif
+
+/* Jump to an interrupt/trap handler. These handlers (defined in entry.S)
+ expect the stack-pointer to be saved in ENTRY_SP, so we use sp to do an
+ indirect jump (which avoids problems when the handler is more than a signed
+ 22-bit offset away). */
+#define JUMP_TO_HANDLER(name, sp_save_loc) \
+ st.w sp, sp_save_loc; \
+ mov hilo(name), sp; \
+ jmp [sp]
+
+
+ /* Reset vector. */
+ .section .intv.reset, "ax"
+ .org 0x0
+ mov hilo(C_SYMBOL_NAME(start)), r1;
+ jmp [r1]
+
+
+ /* Generic interrupt vectors. */
+ .section .intv.common, "ax"
+ .balign 0x10
+ JUMP_TO_HANDLER (nmi, NMI_ENTRY_SP) // 0x10 - NMI0
+ .balign 0x10
+ JUMP_TO_HANDLER (nmi, NMI_ENTRY_SP) // 0x20 - NMI1
+ .balign 0x10
+ JUMP_TO_HANDLER (nmi, NMI_ENTRY_SP) // 0x30 - NMI2
+
+ .balign 0x10
+ JUMP_TO_HANDLER (trap, ENTRY_SP) // 0x40 - TRAP0n
+ .balign 0x10
+ JUMP_TO_HANDLER (trap, ENTRY_SP) // 0x50 - TRAP1n
+
+ .balign 0x10
+ JUMP_TO_HANDLER (dbtrap, ENTRY_SP) // 0x60 - Illegal op / DBTRAP insn
+
+
+ /* Hardware interrupt vectors. */
+ .section .intv.mach, "ax"
+ .org 0x0
+
+#if defined (CONFIG_V850E_HIGHRES_TIMER) && defined (IRQ_INTCMD)
+
+ /* Interrupts before the highres timer interrupt. */
+ .rept IRQ_INTCMD (HIGHRES_TIMER_TIMER_D_UNIT)
+ .balign 0x10
+ JUMP_TO_HANDLER (irq, ENTRY_SP)
+ .endr
+
+ /* The highres timer interrupt. */
+ .balign 0x10
+ JUMP_TO_HANDLER (C_SYMBOL_NAME (highres_timer_slow_tick_irq), ENTRY_SP)
+
+ /* Interrupts after the highres timer interrupt. */
+ .rept NUM_CPU_IRQS - IRQ_INTCMD (HIGHRES_TIMER_TIMER_D_UNIT) - 1
+ .balign 0x10
+ JUMP_TO_HANDLER (irq, ENTRY_SP)
+ .endr
+
+#else /* No highres timer */
+
+ .rept NUM_CPU_IRQS
+ .balign 0x10
+ JUMP_TO_HANDLER (irq, ENTRY_SP)
+ .endr
+
+#endif /* Highres timer */
diff --git a/arch/v850/kernel/irq.c b/arch/v850/kernel/irq.c
new file mode 100644
index 0000000..336cbf2
--- /dev/null
+++ b/arch/v850/kernel/irq.c
@@ -0,0 +1,744 @@
+/*
+ * arch/v850/kernel/irq.c -- High-level interrupt handling
+ *
+ * Copyright (C) 2001,02,03,04 NEC Electronics Corporation
+ * Copyright (C) 2001,02,03,04 Miles Bader <miles@gnu.org>
+ * Copyright (C) 1994-2000 Ralf Baechle
+ * Copyright (C) 1992 Linus Torvalds
+ *
+ * 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.
+ *
+ * This file was was derived from the mips version, arch/mips/kernel/irq.c
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/irq.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel_stat.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/random.h>
+#include <linux/seq_file.h>
+
+#include <asm/system.h>
+
+/*
+ * Controller mappings for all interrupt sources:
+ */
+irq_desc_t irq_desc[NR_IRQS] __cacheline_aligned = {
+ [0 ... NR_IRQS-1] = {
+ .handler = &no_irq_type,
+ .lock = SPIN_LOCK_UNLOCKED
+ }
+};
+
+/*
+ * Special irq handlers.
+ */
+
+irqreturn_t no_action(int cpl, void *dev_id, struct pt_regs *regs)
+{
+ return IRQ_NONE;
+}
+
+/*
+ * Generic no controller code
+ */
+
+static void enable_none(unsigned int irq) { }
+static unsigned int startup_none(unsigned int irq) { return 0; }
+static void disable_none(unsigned int irq) { }
+static void ack_none(unsigned int irq)
+{
+ /*
+ * 'what should we do if we get a hw irq event on an illegal vector'.
+ * each architecture has to answer this themselves, it doesn't deserve
+ * a generic callback i think.
+ */
+ printk("received IRQ %d with unknown interrupt type\n", irq);
+}
+
+/* startup is the same as "enable", shutdown is same as "disable" */
+#define shutdown_none disable_none
+#define end_none enable_none
+
+struct hw_interrupt_type no_irq_type = {
+ "none",
+ startup_none,
+ shutdown_none,
+ enable_none,
+ disable_none,
+ ack_none,
+ end_none
+};
+
+volatile unsigned long irq_err_count, spurious_count;
+
+/*
+ * Generic, controller-independent functions:
+ */
+
+int show_interrupts(struct seq_file *p, void *v)
+{
+ int i = *(loff_t *) v;
+ struct irqaction * action;
+ unsigned long flags;
+
+ if (i == 0) {
+ seq_puts(p, " ");
+ for (i=0; i < 1 /*smp_num_cpus*/; i++)
+ seq_printf(p, "CPU%d ", i);
+ seq_putc(p, '\n');
+ }
+
+ if (i < NR_IRQS) {
+ int j, count, num;
+ const char *type_name = irq_desc[i].handler->typename;
+ spin_lock_irqsave(&irq_desc[j].lock, flags);
+ action = irq_desc[i].action;
+ if (!action)
+ goto skip;
+
+ count = 0;
+ num = -1;
+ for (j = 0; j < NR_IRQS; j++)
+ if (irq_desc[j].handler->typename == type_name) {
+ if (i == j)
+ num = count;
+ count++;
+ }
+
+ seq_printf(p, "%3d: ",i);
+ seq_printf(p, "%10u ", kstat_irqs(i));
+ if (count > 1) {
+ int prec = (num >= 100 ? 3 : num >= 10 ? 2 : 1);
+ seq_printf(p, " %*s%d", 14 - prec, type_name, num);
+ } else
+ seq_printf(p, " %14s", type_name);
+
+ seq_printf(p, " %s", action->name);
+ for (action=action->next; action; action = action->next)
+ seq_printf(p, ", %s", action->name);
+ seq_putc(p, '\n');
+skip:
+ spin_unlock_irqrestore(&irq_desc[j].lock, flags);
+ } else if (i == NR_IRQS)
+ seq_printf(p, "ERR: %10lu\n", irq_err_count);
+ return 0;
+}
+
+/*
+ * This should really return information about whether
+ * we should do bottom half handling etc. Right now we
+ * end up _always_ checking the bottom half, which is a
+ * waste of time and is not what some drivers would
+ * prefer.
+ */
+int handle_IRQ_event(unsigned int irq, struct pt_regs * regs, struct irqaction * action)
+{
+ int status = 1; /* Force the "do bottom halves" bit */
+ int ret;
+
+ if (!(action->flags & SA_INTERRUPT))
+ local_irq_enable();
+
+ do {
+ ret = action->handler(irq, action->dev_id, regs);
+ if (ret == IRQ_HANDLED)
+ status |= action->flags;
+ action = action->next;
+ } while (action);
+ if (status & SA_SAMPLE_RANDOM)
+ add_interrupt_randomness(irq);
+ local_irq_disable();
+
+ return status;
+}
+
+/*
+ * Generic enable/disable code: this just calls
+ * down into the PIC-specific version for the actual
+ * hardware disable after having gotten the irq
+ * controller lock.
+ */
+
+/**
+ * disable_irq_nosync - disable an irq without waiting
+ * @irq: Interrupt to disable
+ *
+ * Disable the selected interrupt line. Disables of an interrupt
+ * stack. Unlike disable_irq(), this function does not ensure existing
+ * instances of the IRQ handler have completed before returning.
+ *
+ * This function may be called from IRQ context.
+ */
+
+void inline disable_irq_nosync(unsigned int irq)
+{
+ irq_desc_t *desc = irq_desc + irq;
+ unsigned long flags;
+
+ spin_lock_irqsave(&desc->lock, flags);
+ if (!desc->depth++) {
+ desc->status |= IRQ_DISABLED;
+ desc->handler->disable(irq);
+ }
+ spin_unlock_irqrestore(&desc->lock, flags);
+}
+
+/**
+ * disable_irq - disable an irq and wait for completion
+ * @irq: Interrupt to disable
+ *
+ * Disable the selected interrupt line. Disables of an interrupt
+ * stack. That is for two disables you need two enables. This
+ * function waits for any pending IRQ handlers for this interrupt
+ * to complete before returning. If you use this function while
+ * holding a resource the IRQ handler may need you will deadlock.
+ *
+ * This function may be called - with care - from IRQ context.
+ */
+
+void disable_irq(unsigned int irq)
+{
+ disable_irq_nosync(irq);
+ synchronize_irq(irq);
+}
+
+/**
+ * enable_irq - enable interrupt handling on an irq
+ * @irq: Interrupt to enable
+ *
+ * Re-enables the processing of interrupts on this IRQ line
+ * providing no disable_irq calls are now in effect.
+ *
+ * This function may be called from IRQ context.
+ */
+
+void enable_irq(unsigned int irq)
+{
+ irq_desc_t *desc = irq_desc + irq;
+ unsigned long flags;
+
+ spin_lock_irqsave(&desc->lock, flags);
+ switch (desc->depth) {
+ case 1: {
+ unsigned int status = desc->status & ~IRQ_DISABLED;
+ desc->status = status;
+ if ((status & (IRQ_PENDING | IRQ_REPLAY)) == IRQ_PENDING) {
+ desc->status = status | IRQ_REPLAY;
+ hw_resend_irq(desc->handler,irq);
+ }
+ desc->handler->enable(irq);
+ /* fall-through */
+ }
+ default:
+ desc->depth--;
+ break;
+ case 0:
+ printk("enable_irq(%u) unbalanced from %p\n", irq,
+ __builtin_return_address(0));
+ }
+ spin_unlock_irqrestore(&desc->lock, flags);
+}
+
+/* Handle interrupt IRQ. REGS are the registers at the time of ther
+ interrupt. */
+unsigned int handle_irq (int irq, struct pt_regs *regs)
+{
+ /*
+ * We ack quickly, we don't want the irq controller
+ * thinking we're snobs just because some other CPU has
+ * disabled global interrupts (we have already done the
+ * INT_ACK cycles, it's too late to try to pretend to the
+ * controller that we aren't taking the interrupt).
+ *
+ * 0 return value means that this irq is already being
+ * handled by some other CPU. (or is disabled)
+ */
+ int cpu = smp_processor_id();
+ irq_desc_t *desc = irq_desc + irq;
+ struct irqaction * action;
+ unsigned int status;
+
+ irq_enter();
+ kstat_cpu(cpu).irqs[irq]++;
+ spin_lock(&desc->lock);
+ desc->handler->ack(irq);
+ /*
+ REPLAY is when Linux resends an IRQ that was dropped earlier
+ WAITING is used by probe to mark irqs that are being tested
+ */
+ status = desc->status & ~(IRQ_REPLAY | IRQ_WAITING);
+ status |= IRQ_PENDING; /* we _want_ to handle it */
+
+ /*
+ * If the IRQ is disabled for whatever reason, we cannot
+ * use the action we have.
+ */
+ action = NULL;
+ if (likely(!(status & (IRQ_DISABLED | IRQ_INPROGRESS)))) {
+ action = desc->action;
+ status &= ~IRQ_PENDING; /* we commit to handling */
+ status |= IRQ_INPROGRESS; /* we are handling it */
+ }
+ desc->status = status;
+
+ /*
+ * If there is no IRQ handler or it was disabled, exit early.
+ Since we set PENDING, if another processor is handling
+ a different instance of this same irq, the other processor
+ will take care of it.
+ */
+ if (unlikely(!action))
+ goto out;
+
+ /*
+ * Edge triggered interrupts need to remember
+ * pending events.
+ * This applies to any hw interrupts that allow a second
+ * instance of the same irq to arrive while we are in handle_irq
+ * or in the handler. But the code here only handles the _second_
+ * instance of the irq, not the third or fourth. So it is mostly
+ * useful for irq hardware that does not mask cleanly in an
+ * SMP environment.
+ */
+ for (;;) {
+ spin_unlock(&desc->lock);
+ handle_IRQ_event(irq, regs, action);
+ spin_lock(&desc->lock);
+
+ if (likely(!(desc->status & IRQ_PENDING)))
+ break;
+ desc->status &= ~IRQ_PENDING;
+ }
+ desc->status &= ~IRQ_INPROGRESS;
+
+out:
+ /*
+ * The ->end() handler has to deal with interrupts which got
+ * disabled while the handler was running.
+ */
+ desc->handler->end(irq);
+ spin_unlock(&desc->lock);
+
+ irq_exit();
+
+ return 1;
+}
+
+/**
+ * request_irq - allocate an interrupt line
+ * @irq: Interrupt line to allocate
+ * @handler: Function to be called when the IRQ occurs
+ * @irqflags: Interrupt type flags
+ * @devname: An ascii name for the claiming device
+ * @dev_id: A cookie passed back to the handler function
+ *
+ * This call allocates interrupt resources and enables the
+ * interrupt line and IRQ handling. From the point this
+ * call is made your handler function may be invoked. Since
+ * your handler function must clear any interrupt the board
+ * raises, you must take care both to initialise your hardware
+ * and to set up the interrupt handler in the right order.
+ *
+ * Dev_id must be globally unique. Normally the address of the
+ * device data structure is used as the cookie. Since the handler
+ * receives this value it makes sense to use it.
+ *
+ * If your interrupt is shared you must pass a non NULL dev_id
+ * as this is required when freeing the interrupt.
+ *
+ * Flags:
+ *
+ * SA_SHIRQ Interrupt is shared
+ *
+ * SA_INTERRUPT Disable local interrupts while processing
+ *
+ * SA_SAMPLE_RANDOM The interrupt can be used for entropy
+ *
+ */
+
+int request_irq(unsigned int irq,
+ irqreturn_t (*handler)(int, void *, struct pt_regs *),
+ unsigned long irqflags,
+ const char * devname,
+ void *dev_id)
+{
+ int retval;
+ struct irqaction * action;
+
+#if 1
+ /*
+ * Sanity-check: shared interrupts should REALLY pass in
+ * a real dev-ID, otherwise we'll have trouble later trying
+ * to figure out which interrupt is which (messes up the
+ * interrupt freeing logic etc).
+ */
+ if (irqflags & SA_SHIRQ) {
+ if (!dev_id)
+ printk("Bad boy: %s (at 0x%x) called us without a dev_id!\n", devname, (&irq)[-1]);
+ }
+#endif
+
+ if (irq >= NR_IRQS)
+ 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;
+ cpus_clear(action->mask);
+ action->name = devname;
+ action->next = NULL;
+ action->dev_id = dev_id;
+
+ retval = setup_irq(irq, action);
+ if (retval)
+ kfree(action);
+ return retval;
+}
+
+EXPORT_SYMBOL(request_irq);
+
+/**
+ * free_irq - free an interrupt
+ * @irq: Interrupt line to free
+ * @dev_id: Device identity to free
+ *
+ * Remove an interrupt handler. The handler is removed and if the
+ * interrupt line is no longer in use by any driver it is disabled.
+ * On a shared IRQ the caller must ensure the interrupt is disabled
+ * on the card it drives before calling this function. The function
+ * does not return until any executing interrupts for this IRQ
+ * have completed.
+ *
+ * This function may be called from interrupt context.
+ *
+ * Bugs: Attempting to free an irq in a handler for the same irq hangs
+ * the machine.
+ */
+
+void free_irq(unsigned int irq, void *dev_id)
+{
+ irq_desc_t *desc;
+ struct irqaction **p;
+ unsigned long flags;
+
+ if (irq >= NR_IRQS)
+ return;
+
+ desc = irq_desc + irq;
+ spin_lock_irqsave(&desc->lock,flags);
+ p = &desc->action;
+ for (;;) {
+ struct irqaction * action = *p;
+ if (action) {
+ struct irqaction **pp = p;
+ p = &action->next;
+ if (action->dev_id != dev_id)
+ continue;
+
+ /* Found it - now remove it from the list of entries */
+ *pp = action->next;
+ if (!desc->action) {
+ desc->status |= IRQ_DISABLED;
+ desc->handler->shutdown(irq);
+ }
+ spin_unlock_irqrestore(&desc->lock,flags);
+
+ synchronize_irq(irq);
+ kfree(action);
+ return;
+ }
+ printk("Trying to free free IRQ%d\n",irq);
+ spin_unlock_irqrestore(&desc->lock,flags);
+ return;
+ }
+}
+
+EXPORT_SYMBOL(free_irq);
+
+/*
+ * IRQ autodetection code..
+ *
+ * This depends on the fact that any interrupt that
+ * comes in on to an unassigned handler will get stuck
+ * with "IRQ_WAITING" cleared and the interrupt
+ * disabled.
+ */
+
+static DECLARE_MUTEX(probe_sem);
+
+/**
+ * probe_irq_on - begin an interrupt autodetect
+ *
+ * Commence probing for an interrupt. The interrupts are scanned
+ * and a mask of potential interrupt lines is returned.
+ *
+ */
+
+unsigned long probe_irq_on(void)
+{
+ unsigned int i;
+ irq_desc_t *desc;
+ unsigned long val;
+ unsigned long delay;
+
+ down(&probe_sem);
+ /*
+ * something may have generated an irq long ago and we want to
+ * flush such a longstanding irq before considering it as spurious.
+ */
+ for (i = NR_IRQS-1; i > 0; i--) {
+ desc = irq_desc + i;
+
+ spin_lock_irq(&desc->lock);
+ if (!irq_desc[i].action)
+ irq_desc[i].handler->startup(i);
+ spin_unlock_irq(&desc->lock);
+ }
+
+ /* Wait for longstanding interrupts to trigger. */
+ for (delay = jiffies + HZ/50; time_after(delay, jiffies); )
+ /* about 20ms delay */ barrier();
+
+ /*
+ * enable any unassigned irqs
+ * (we must startup again here because if a longstanding irq
+ * happened in the previous stage, it may have masked itself)
+ */
+ for (i = NR_IRQS-1; i > 0; i--) {
+ desc = irq_desc + i;
+
+ spin_lock_irq(&desc->lock);
+ if (!desc->action) {
+ desc->status |= IRQ_AUTODETECT | IRQ_WAITING;
+ if (desc->handler->startup(i))
+ desc->status |= IRQ_PENDING;
+ }
+ spin_unlock_irq(&desc->lock);
+ }
+
+ /*
+ * Wait for spurious interrupts to trigger
+ */
+ for (delay = jiffies + HZ/10; time_after(delay, jiffies); )
+ /* about 100ms delay */ barrier();
+
+ /*
+ * Now filter out any obviously spurious interrupts
+ */
+ val = 0;
+ for (i = 0; i < NR_IRQS; i++) {
+ irq_desc_t *desc = irq_desc + i;
+ unsigned int status;
+
+ spin_lock_irq(&desc->lock);
+ status = desc->status;
+
+ if (status & IRQ_AUTODETECT) {
+ /* It triggered already - consider it spurious. */
+ if (!(status & IRQ_WAITING)) {
+ desc->status = status & ~IRQ_AUTODETECT;
+ desc->handler->shutdown(i);
+ } else
+ if (i < 32)
+ val |= 1 << i;
+ }
+ spin_unlock_irq(&desc->lock);
+ }
+
+ return val;
+}
+
+EXPORT_SYMBOL(probe_irq_on);
+
+/*
+ * Return a mask of triggered interrupts (this
+ * can handle only legacy ISA interrupts).
+ */
+
+/**
+ * probe_irq_mask - scan a bitmap of interrupt lines
+ * @val: mask of interrupts to consider
+ *
+ * Scan the ISA bus interrupt lines and return a bitmap of
+ * active interrupts. The interrupt probe logic state is then
+ * returned to its previous value.
+ *
+ * Note: we need to scan all the irq's even though we will
+ * only return ISA irq numbers - just so that we reset them
+ * all to a known state.
+ */
+unsigned int probe_irq_mask(unsigned long val)
+{
+ int i;
+ unsigned int mask;
+
+ mask = 0;
+ for (i = 0; i < NR_IRQS; i++) {
+ irq_desc_t *desc = irq_desc + i;
+ unsigned int status;
+
+ spin_lock_irq(&desc->lock);
+ status = desc->status;
+
+ if (status & IRQ_AUTODETECT) {
+ if (i < 16 && !(status & IRQ_WAITING))
+ mask |= 1 << i;
+
+ desc->status = status & ~IRQ_AUTODETECT;
+ desc->handler->shutdown(i);
+ }
+ spin_unlock_irq(&desc->lock);
+ }
+ up(&probe_sem);
+
+ return mask & val;
+}
+
+/*
+ * Return the one interrupt that triggered (this can
+ * handle any interrupt source).
+ */
+
+/**
+ * probe_irq_off - end an interrupt autodetect
+ * @val: mask of potential interrupts (unused)
+ *
+ * Scans the unused interrupt lines and returns the line which
+ * appears to have triggered the interrupt. If no interrupt was
+ * found then zero is returned. If more than one interrupt is
+ * found then minus the first candidate is returned to indicate
+ * their is doubt.
+ *
+ * The interrupt probe logic state is returned to its previous
+ * value.
+ *
+ * BUGS: When used in a module (which arguably shouldnt happen)
+ * nothing prevents two IRQ probe callers from overlapping. The
+ * results of this are non-optimal.
+ */
+
+int probe_irq_off(unsigned long val)
+{
+ int i, irq_found, nr_irqs;
+
+ nr_irqs = 0;
+ irq_found = 0;
+ for (i = 0; i < NR_IRQS; i++) {
+ irq_desc_t *desc = irq_desc + i;
+ unsigned int status;
+
+ spin_lock_irq(&desc->lock);
+ status = desc->status;
+
+ if (status & IRQ_AUTODETECT) {
+ if (!(status & IRQ_WAITING)) {
+ if (!nr_irqs)
+ irq_found = i;
+ nr_irqs++;
+ }
+ desc->status = status & ~IRQ_AUTODETECT;
+ desc->handler->shutdown(i);
+ }
+ spin_unlock_irq(&desc->lock);
+ }
+ up(&probe_sem);
+
+ if (nr_irqs > 1)
+ irq_found = -irq_found;
+ return irq_found;
+}
+
+EXPORT_SYMBOL(probe_irq_off);
+
+/* this was setup_x86_irq but it seems pretty generic */
+int setup_irq(unsigned int irq, struct irqaction * new)
+{
+ int shared = 0;
+ unsigned long flags;
+ struct irqaction *old, **p;
+ irq_desc_t *desc = irq_desc + irq;
+
+ /*
+ * Some drivers like serial.c use request_irq() heavily,
+ * so we have to be careful not to interfere with a
+ * running system.
+ */
+ if (new->flags & SA_SAMPLE_RANDOM) {
+ /*
+ * This function might sleep, we want to call it first,
+ * outside of the atomic block.
+ * Yes, this might clear the entropy pool if the wrong
+ * driver is attempted to be loaded, without actually
+ * installing a new handler, but is this really a problem,
+ * only the sysadmin is able to do this.
+ */
+ rand_initialize_irq(irq);
+ }
+
+ /*
+ * The following block of code has to be executed atomically
+ */
+ spin_lock_irqsave(&desc->lock,flags);
+ p = &desc->action;
+ if ((old = *p) != NULL) {
+ /* Can't share interrupts unless both agree to */
+ if (!(old->flags & new->flags & SA_SHIRQ)) {
+ spin_unlock_irqrestore(&desc->lock,flags);
+ return -EBUSY;
+ }
+
+ /* add new interrupt at end of irq queue */
+ do {
+ p = &old->next;
+ old = *p;
+ } while (old);
+ shared = 1;
+ }
+
+ *p = new;
+
+ if (!shared) {
+ desc->depth = 0;
+ desc->status &= ~(IRQ_DISABLED | IRQ_AUTODETECT | IRQ_WAITING | IRQ_INPROGRESS);
+ desc->handler->startup(irq);
+ }
+ spin_unlock_irqrestore(&desc->lock,flags);
+
+ /* register_irq_proc(irq); */
+ return 0;
+}
+
+/* Initialize irq handling for IRQs.
+ BASE_IRQ, BASE_IRQ+INTERVAL, ..., BASE_IRQ+NUM*INTERVAL
+ to IRQ_TYPE. An IRQ_TYPE of 0 means to use a generic interrupt type. */
+void __init
+init_irq_handlers (int base_irq, int num, int interval,
+ struct hw_interrupt_type *irq_type)
+{
+ while (num-- > 0) {
+ irq_desc[base_irq].status = IRQ_DISABLED;
+ irq_desc[base_irq].action = NULL;
+ irq_desc[base_irq].depth = 1;
+ irq_desc[base_irq].handler = irq_type;
+ base_irq += interval;
+ }
+}
+
+#if defined(CONFIG_PROC_FS) && defined(CONFIG_SYSCTL)
+void init_irq_proc(void)
+{
+}
+#endif /* CONFIG_PROC_FS && CONFIG_SYSCTL */
diff --git a/arch/v850/kernel/ma.c b/arch/v850/kernel/ma.c
new file mode 100644
index 0000000..b3dfbc5
--- /dev/null
+++ b/arch/v850/kernel/ma.c
@@ -0,0 +1,70 @@
+/*
+ * arch/v850/kernel/ma.c -- V850E/MA series of cpu chips
+ *
+ * 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/config.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/mm.h>
+#include <linux/swap.h>
+#include <linux/bootmem.h>
+#include <linux/irq.h>
+
+#include <asm/atomic.h>
+#include <asm/page.h>
+#include <asm/machdep.h>
+#include <asm/v850e_timer_d.h>
+
+#include "mach.h"
+
+void __init mach_sched_init (struct irqaction *timer_action)
+{
+ /* Start hardware timer. */
+ v850e_timer_d_configure (0, HZ);
+ /* Install timer interrupt handler. */
+ setup_irq (IRQ_INTCMD(0), timer_action);
+}
+
+static struct v850e_intc_irq_init irq_inits[] = {
+ { "IRQ", 0, NUM_MACH_IRQS, 1, 7 },
+ { "CMD", IRQ_INTCMD(0), IRQ_INTCMD_NUM, 1, 5 },
+ { "DMA", IRQ_INTDMA(0), IRQ_INTDMA_NUM, 1, 2 },
+ { "CSI", IRQ_INTCSI(0), IRQ_INTCSI_NUM, 4, 4 },
+ { "SER", IRQ_INTSER(0), IRQ_INTSER_NUM, 4, 3 },
+ { "SR", IRQ_INTSR(0), IRQ_INTSR_NUM, 4, 4 },
+ { "ST", IRQ_INTST(0), IRQ_INTST_NUM, 4, 5 },
+ { 0 }
+};
+#define NUM_IRQ_INITS ((sizeof irq_inits / sizeof irq_inits[0]) - 1)
+
+static struct hw_interrupt_type hw_itypes[NUM_IRQ_INITS];
+
+/* Initialize MA chip interrupts. */
+void __init ma_init_irqs (void)
+{
+ v850e_intc_init_irq_types (irq_inits, hw_itypes);
+}
+
+/* Called before configuring an on-chip UART. */
+void ma_uart_pre_configure (unsigned chan, unsigned cflags, unsigned baud)
+{
+ /* We only know about the first two UART channels (though
+ specific chips may have more). */
+ if (chan < 2) {
+ unsigned bits = 0x3 << (chan * 3);
+ /* Specify that the relevant pins on the chip should do
+ serial I/O, not direct I/O. */
+ MA_PORT4_PMC |= bits;
+ /* Specify that we're using the UART, not the CSI device. */
+ MA_PORT4_PFC |= bits;
+ }
+}
diff --git a/arch/v850/kernel/mach.c b/arch/v850/kernel/mach.c
new file mode 100644
index 0000000..b9db278
--- /dev/null
+++ b/arch/v850/kernel/mach.c
@@ -0,0 +1,17 @@
+/*
+ * arch/v850/kernel/mach.c -- Defaults for some things defined by "mach.h"
+ *
+ * Copyright (C) 2001 NEC Corporation
+ * Copyright (C) 2001 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 "mach.h"
+
+/* Called with each timer tick, if non-zero. */
+void (*mach_tick)(void) = 0;
diff --git a/arch/v850/kernel/mach.h b/arch/v850/kernel/mach.h
new file mode 100644
index 0000000..9e0e481
--- /dev/null
+++ b/arch/v850/kernel/mach.h
@@ -0,0 +1,56 @@
+/*
+ * arch/v850/kernel/mach.h -- Machine-dependent functions used by v850 port
+ *
+ * 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>
+ */
+
+#ifndef __V850_MACH_H__
+#define __V850_MACH_H__
+
+#include <linux/kernel.h>
+#include <linux/time.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/fs.h>
+#include <linux/seq_file.h>
+
+#include <asm/ptrace.h>
+#include <asm/entry.h>
+#include <asm/clinkage.h>
+
+void mach_setup (char **cmdline);
+void mach_gettimeofday (struct timespec *tv);
+void mach_sched_init (struct irqaction *timer_action);
+void mach_get_physical_ram (unsigned long *ram_start, unsigned long *ram_len);
+void mach_init_irqs (void);
+
+/* If defined, is called very early in the kernel initialization. The
+ stack pointer is valid, but very little has been initialized (e.g.,
+ bss is not zeroed yet) when this is called, so care must taken. */
+void mach_early_init (void);
+
+/* If defined, called after the bootmem allocator has been initialized,
+ to allow the platform-dependent code to reserve any areas of RAM that
+ the kernel shouldn't touch. */
+void mach_reserve_bootmem (void) __attribute__ ((__weak__));
+
+/* Called with each timer tick, if non-zero. */
+extern void (*mach_tick) (void);
+
+/* The following establishes aliases for various mach_ functions to the
+ name by which the rest of the kernel calls them. These statements
+ should only have an effect in the file that defines the actual functions. */
+#define MACH_ALIAS(to, from) \
+ asm (".global " macrology_stringify (C_SYMBOL_NAME (to)) ";" \
+ macrology_stringify (C_SYMBOL_NAME (to)) \
+ " = " macrology_stringify (C_SYMBOL_NAME (from)))
+/* e.g.: MACH_ALIAS (kernel_name, arch_spec_name); */
+
+#endif /* __V850_MACH_H__ */
diff --git a/arch/v850/kernel/me2.c b/arch/v850/kernel/me2.c
new file mode 100644
index 0000000..6527c21
--- /dev/null
+++ b/arch/v850/kernel/me2.c
@@ -0,0 +1,74 @@
+/*
+ * arch/v850/kernel/me2.c -- V850E/ME2 chip-specific support
+ *
+ * Copyright (C) 2003 NEC Corporation
+ * Copyright (C) 2003 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/config.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/mm.h>
+#include <linux/swap.h>
+#include <linux/bootmem.h>
+#include <linux/irq.h>
+
+#include <asm/atomic.h>
+#include <asm/page.h>
+#include <asm/machdep.h>
+#include <asm/v850e_timer_d.h>
+
+#include "mach.h"
+
+void __init mach_sched_init (struct irqaction *timer_action)
+{
+ /* Start hardware timer. */
+ v850e_timer_d_configure (0, HZ);
+ /* Install timer interrupt handler. */
+ setup_irq (IRQ_INTCMD(0), timer_action);
+}
+
+static struct v850e_intc_irq_init irq_inits[] = {
+ { "IRQ", 0, NUM_CPU_IRQS, 1, 7 },
+ { "INTP", IRQ_INTP(0), IRQ_INTP_NUM, 1, 5 },
+ { "CMD", IRQ_INTCMD(0), IRQ_INTCMD_NUM, 1, 3 },
+ { "UBTIRE", IRQ_INTUBTIRE(0), IRQ_INTUBTIRE_NUM, 5, 4 },
+ { "UBTIR", IRQ_INTUBTIR(0), IRQ_INTUBTIR_NUM, 5, 4 },
+ { "UBTIT", IRQ_INTUBTIT(0), IRQ_INTUBTIT_NUM, 5, 4 },
+ { "UBTIF", IRQ_INTUBTIF(0), IRQ_INTUBTIF_NUM, 5, 4 },
+ { "UBTITO", IRQ_INTUBTITO(0), IRQ_INTUBTITO_NUM, 5, 4 },
+ { 0 }
+};
+#define NUM_IRQ_INITS ((sizeof irq_inits / sizeof irq_inits[0]) - 1)
+
+static struct hw_interrupt_type hw_itypes[NUM_IRQ_INITS];
+
+/* Initialize V850E/ME2 chip interrupts. */
+void __init me2_init_irqs (void)
+{
+ v850e_intc_init_irq_types (irq_inits, hw_itypes);
+}
+
+/* Called before configuring an on-chip UART. */
+void me2_uart_pre_configure (unsigned chan, unsigned cflags, unsigned baud)
+{
+ if (chan == 0) {
+ /* Specify that the relevent pins on the chip should do
+ serial I/O, not direct I/O. */
+ ME2_PORT1_PMC |= 0xC;
+ /* Specify that we're using the UART, not the CSI device. */
+ ME2_PORT1_PFC |= 0xC;
+ } else if (chan == 1) {
+ /* Specify that the relevent pins on the chip should do
+ serial I/O, not direct I/O. */
+ ME2_PORT2_PMC |= 0x6;
+ /* Specify that we're using the UART, not the CSI device. */
+ ME2_PORT2_PFC |= 0x6;
+ }
+}
diff --git a/arch/v850/kernel/memcons.c b/arch/v850/kernel/memcons.c
new file mode 100644
index 0000000..491614c
--- /dev/null
+++ b/arch/v850/kernel/memcons.c
@@ -0,0 +1,135 @@
+/*
+ * arch/v850/kernel/memcons.c -- Console I/O to a memory buffer
+ *
+ * Copyright (C) 2001,02 NEC Corporation
+ * Copyright (C) 2001,02 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/kernel.h>
+#include <linux/console.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/init.h>
+
+/* If this device is enabled, the linker map should define start and
+ end points for its buffer. */
+extern char memcons_output[], memcons_output_end;
+
+/* Current offset into the buffer. */
+static unsigned long memcons_offs = 0;
+
+/* Spinlock protecting memcons_offs. */
+static DEFINE_SPINLOCK(memcons_lock);
+
+
+static size_t write (const char *buf, size_t len)
+{
+ int flags;
+ char *point;
+
+ spin_lock_irqsave (memcons_lock, flags);
+
+ point = memcons_output + memcons_offs;
+ if (point + len >= &memcons_output_end) {
+ len = &memcons_output_end - point;
+ memcons_offs = 0;
+ } else
+ memcons_offs += len;
+
+ spin_unlock_irqrestore (memcons_lock, flags);
+
+ memcpy (point, buf, len);
+
+ return len;
+}
+
+
+/* Low-level console. */
+
+static void memcons_write (struct console *co, const char *buf, unsigned len)
+{
+ while (len > 0)
+ len -= write (buf, len);
+}
+
+static struct tty_driver *tty_driver;
+
+static struct tty_driver *memcons_device (struct console *co, int *index)
+{
+ *index = co->index;
+ return tty_driver;
+}
+
+static struct console memcons =
+{
+ .name = "memcons",
+ .write = memcons_write,
+ .device = memcons_device,
+ .flags = CON_PRINTBUFFER,
+ .index = -1,
+};
+
+void memcons_setup (void)
+{
+ register_console (&memcons);
+ printk (KERN_INFO "Console: static memory buffer (memcons)\n");
+}
+
+/* Higher level TTY interface. */
+
+int memcons_tty_open (struct tty_struct *tty, struct file *filp)
+{
+ return 0;
+}
+
+int memcons_tty_write (struct tty_struct *tty, const unsigned char *buf, int len)
+{
+ return write (buf, len);
+}
+
+int memcons_tty_write_room (struct tty_struct *tty)
+{
+ return &memcons_output_end - (memcons_output + memcons_offs);
+}
+
+int memcons_tty_chars_in_buffer (struct tty_struct *tty)
+{
+ /* We have no buffer. */
+ return 0;
+}
+
+static struct tty_operations ops = {
+ .open = memcons_tty_open,
+ .write = memcons_tty_write,
+ .write_room = memcons_tty_write_room,
+ .chars_in_buffer = memcons_tty_chars_in_buffer,
+};
+
+int __init memcons_tty_init (void)
+{
+ int err;
+ struct tty_driver *driver = alloc_tty_driver(1);
+ if (!driver)
+ return -ENOMEM;
+
+ driver->name = "memcons";
+ driver->major = TTY_MAJOR;
+ driver->minor_start = 64;
+ driver->type = TTY_DRIVER_TYPE_SYSCONS;
+ driver->init_termios = tty_std_termios;
+ tty_set_operations(driver, &ops);
+ err = tty_register_driver(driver);
+ if (err) {
+ put_tty_driver(driver);
+ return err;
+ }
+ tty_driver = driver;
+ return 0;
+}
+__initcall (memcons_tty_init);
diff --git a/arch/v850/kernel/module.c b/arch/v850/kernel/module.c
new file mode 100644
index 0000000..64aeb3e
--- /dev/null
+++ b/arch/v850/kernel/module.c
@@ -0,0 +1,237 @@
+/*
+ * arch/v850/kernel/module.c -- Architecture-specific module functions
+ *
+ * Copyright (C) 2002,03 NEC Electronics Corporation
+ * Copyright (C) 2002,03 Miles Bader <miles@gnu.org>
+ * Copyright (C) 2001,03 Rusty Russell
+ *
+ * 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>
+ *
+ * Derived in part from arch/ppc/kernel/module.c
+ */
+
+#include <linux/kernel.h>
+#include <linux/vmalloc.h>
+#include <linux/moduleloader.h>
+#include <linux/elf.h>
+
+#if 0
+#define DEBUGP printk
+#else
+#define DEBUGP(fmt , ...)
+#endif
+
+void *module_alloc (unsigned long size)
+{
+ return size == 0 ? 0 : vmalloc (size);
+}
+
+void module_free (struct module *mod, void *module_region)
+{
+ vfree (module_region);
+ /* FIXME: If module_region == mod->init_region, trim exception
+ table entries. */
+}
+
+int module_finalize (const Elf_Ehdr *hdr, const Elf_Shdr *sechdrs,
+ struct module *mod)
+{
+ return 0;
+}
+
+/* Count how many different relocations (different symbol, different
+ addend) */
+static unsigned int count_relocs(const Elf32_Rela *rela, unsigned int num)
+{
+ unsigned int i, j, ret = 0;
+
+ /* Sure, this is order(n^2), but it's usually short, and not
+ time critical */
+ for (i = 0; i < num; i++) {
+ for (j = 0; j < i; j++) {
+ /* If this addend appeared before, it's
+ already been counted */
+ if (ELF32_R_SYM(rela[i].r_info)
+ == ELF32_R_SYM(rela[j].r_info)
+ && rela[i].r_addend == rela[j].r_addend)
+ break;
+ }
+ if (j == i) ret++;
+ }
+ return ret;
+}
+
+/* Get the potential trampolines size required of the init and
+ non-init sections */
+static unsigned long get_plt_size(const Elf32_Ehdr *hdr,
+ const Elf32_Shdr *sechdrs,
+ const char *secstrings,
+ int is_init)
+{
+ unsigned long ret = 0;
+ unsigned i;
+
+ /* Everything marked ALLOC (this includes the exported
+ symbols) */
+ for (i = 1; i < hdr->e_shnum; i++) {
+ /* If it's called *.init*, and we're not init, we're
+ not interested */
+ if ((strstr(secstrings + sechdrs[i].sh_name, ".init") != 0)
+ != is_init)
+ continue;
+
+ if (sechdrs[i].sh_type == SHT_RELA) {
+ DEBUGP("Found relocations in section %u\n", i);
+ DEBUGP("Ptr: %p. Number: %u\n",
+ (void *)hdr + sechdrs[i].sh_offset,
+ sechdrs[i].sh_size / sizeof(Elf32_Rela));
+ ret += count_relocs((void *)hdr
+ + sechdrs[i].sh_offset,
+ sechdrs[i].sh_size
+ / sizeof(Elf32_Rela))
+ * sizeof(struct v850_plt_entry);
+ }
+ }
+
+ return ret;
+}
+
+int module_frob_arch_sections(Elf32_Ehdr *hdr,
+ Elf32_Shdr *sechdrs,
+ char *secstrings,
+ struct module *me)
+{
+ unsigned int i;
+
+ /* Find .plt and .pltinit sections */
+ for (i = 0; i < hdr->e_shnum; i++) {
+ if (strcmp(secstrings + sechdrs[i].sh_name, ".init.plt") == 0)
+ me->arch.init_plt_section = i;
+ else if (strcmp(secstrings + sechdrs[i].sh_name, ".plt") == 0)
+ me->arch.core_plt_section = i;
+ }
+ if (!me->arch.core_plt_section || !me->arch.init_plt_section) {
+ printk("Module doesn't contain .plt or .plt.init sections.\n");
+ return -ENOEXEC;
+ }
+
+ /* Override their sizes */
+ sechdrs[me->arch.core_plt_section].sh_size
+ = get_plt_size(hdr, sechdrs, secstrings, 0);
+ sechdrs[me->arch.init_plt_section].sh_size
+ = get_plt_size(hdr, sechdrs, secstrings, 1);
+ return 0;
+}
+
+int apply_relocate (Elf32_Shdr *sechdrs, const char *strtab,
+ unsigned int symindex, unsigned int relsec,
+ struct module *mod)
+{
+ printk ("Barf\n");
+ return -ENOEXEC;
+}
+
+/* Set up a trampoline in the PLT to bounce us to the distant function */
+static uint32_t do_plt_call (void *location, Elf32_Addr val,
+ Elf32_Shdr *sechdrs, struct module *mod)
+{
+ struct v850_plt_entry *entry;
+ /* Instructions used to do the indirect jump. */
+ uint32_t tramp[2];
+
+ /* We have to trash a register, so we assume that any control
+ transfer more than 21-bits away must be a function call
+ (so we can use a call-clobbered register). */
+ tramp[0] = 0x0621 + ((val & 0xffff) << 16); /* mov sym, r1 ... */
+ tramp[1] = ((val >> 16) & 0xffff) + 0x610000; /* ...; jmp r1 */
+
+ /* Init, or core PLT? */
+ if (location >= mod->module_core
+ && location < mod->module_core + mod->core_size)
+ entry = (void *)sechdrs[mod->arch.core_plt_section].sh_addr;
+ else
+ entry = (void *)sechdrs[mod->arch.init_plt_section].sh_addr;
+
+ /* Find this entry, or if that fails, the next avail. entry */
+ while (entry->tramp[0])
+ if (entry->tramp[0] == tramp[0] && entry->tramp[1] == tramp[1])
+ return (uint32_t)entry;
+ else
+ entry++;
+
+ entry->tramp[0] = tramp[0];
+ entry->tramp[1] = tramp[1];
+
+ return (uint32_t)entry;
+}
+
+int apply_relocate_add (Elf32_Shdr *sechdrs, const char *strtab,
+ unsigned int symindex, unsigned int relsec,
+ struct module *mod)
+{
+ unsigned int i;
+ Elf32_Rela *rela = (void *)sechdrs[relsec].sh_addr;
+
+ DEBUGP ("Applying relocate section %u to %u\n", relsec,
+ sechdrs[relsec].sh_info);
+
+ for (i = 0; i < sechdrs[relsec].sh_size / sizeof (*rela); i++) {
+ /* This is where to make the change */
+ uint32_t *loc
+ = ((void *)sechdrs[sechdrs[relsec].sh_info].sh_addr
+ + rela[i].r_offset);
+ /* This is the symbol it is referring to. Note that all
+ undefined symbols have been resolved. */
+ Elf32_Sym *sym
+ = ((Elf32_Sym *)sechdrs[symindex].sh_addr
+ + ELF32_R_SYM (rela[i].r_info));
+ uint32_t val = sym->st_value + rela[i].r_addend;
+
+ switch (ELF32_R_TYPE (rela[i].r_info)) {
+ case R_V850_32:
+ /* We write two shorts instead of a long because even
+ 32-bit insns only need half-word alignment, but
+ 32-bit data writes need to be long-word aligned. */
+ val += ((uint16_t *)loc)[0];
+ val += ((uint16_t *)loc)[1] << 16;
+ ((uint16_t *)loc)[0] = val & 0xffff;
+ ((uint16_t *)loc)[1] = (val >> 16) & 0xffff;
+ break;
+
+ case R_V850_22_PCREL:
+ /* Maybe jump indirectly via a PLT table entry. */
+ if ((int32_t)(val - (uint32_t)loc) > 0x1fffff
+ || (int32_t)(val - (uint32_t)loc) < -0x200000)
+ val = do_plt_call (loc, val, sechdrs, mod);
+
+ val -= (uint32_t)loc;
+
+ /* We write two shorts instead of a long because
+ even 32-bit insns only need half-word alignment,
+ but 32-bit data writes need to be long-word
+ aligned. */
+ ((uint16_t *)loc)[0] =
+ (*(uint16_t *)loc & 0xffc0) /* opcode + reg */
+ | ((val >> 16) & 0xffc03f); /* offs high */
+ ((uint16_t *)loc)[1] =
+ (val & 0xffff); /* offs low */
+ break;
+
+ default:
+ printk (KERN_ERR "module %s: Unknown reloc: %u\n",
+ mod->name, ELF32_R_TYPE (rela[i].r_info));
+ return -ENOEXEC;
+ }
+ }
+
+ return 0;
+}
+
+void
+module_arch_cleanup(struct module *mod)
+{
+}
diff --git a/arch/v850/kernel/process.c b/arch/v850/kernel/process.c
new file mode 100644
index 0000000..9c708c3
--- /dev/null
+++ b/arch/v850/kernel/process.c
@@ -0,0 +1,236 @@
+/*
+ * arch/v850/kernel/process.c -- Arch-dependent process handling
+ *
+ * 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/config.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+#include <linux/stddef.h>
+#include <linux/unistd.h>
+#include <linux/ptrace.h>
+#include <linux/slab.h>
+#include <linux/user.h>
+#include <linux/a.out.h>
+#include <linux/reboot.h>
+
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <asm/pgtable.h>
+
+extern void ret_from_fork (void);
+
+
+/* The idle loop. */
+void default_idle (void)
+{
+ while (1) {
+ while (! need_resched ())
+ asm ("halt; nop; nop; nop; nop; nop" ::: "cc");
+ schedule ();
+ }
+}
+
+void (*idle)(void) = default_idle;
+
+/*
+ * The idle thread. There's no useful work to be
+ * done, so just try to conserve power and have a
+ * low exit latency (ie sit in a loop waiting for
+ * somebody to say that they'd like to reschedule)
+ */
+void cpu_idle (void)
+{
+ /* endless idle loop with no priority at all */
+ (*idle) ();
+}
+
+/*
+ * This is the mechanism for creating a new kernel thread.
+ *
+ * NOTE! Only a kernel-only process (ie the swapper or direct descendants who
+ * haven't done an "execve()") should use this: it will work within a system
+ * call from a "real" process, but the process memory space will not be free'd
+ * until both the parent and the child have exited.
+ */
+int kernel_thread (int (*fn)(void *), void *arg, unsigned long flags)
+{
+ register mm_segment_t fs = get_fs ();
+ register unsigned long syscall asm (SYSCALL_NUM);
+ register unsigned long arg0 asm (SYSCALL_ARG0);
+ register unsigned long ret asm (SYSCALL_RET);
+
+ set_fs (KERNEL_DS);
+
+ /* Clone this thread. Note that we don't pass the clone syscall's
+ second argument -- it's ignored for calls from kernel mode (the
+ child's SP is always set to the top of the kernel stack). */
+ arg0 = flags | CLONE_VM;
+ syscall = __NR_clone;
+ asm volatile ("trap " SYSCALL_SHORT_TRAP
+ : "=r" (ret), "=r" (syscall)
+ : "1" (syscall), "r" (arg0)
+ : SYSCALL_SHORT_CLOBBERS);
+
+ if (ret == 0) {
+ /* In child thread, call FN and exit. */
+ arg0 = (*fn) (arg);
+ syscall = __NR_exit;
+ asm volatile ("trap " SYSCALL_SHORT_TRAP
+ : "=r" (ret), "=r" (syscall)
+ : "1" (syscall), "r" (arg0)
+ : SYSCALL_SHORT_CLOBBERS);
+ }
+
+ /* In parent. */
+ set_fs (fs);
+
+ return ret;
+}
+
+void flush_thread (void)
+{
+ set_fs (USER_DS);
+}
+
+int copy_thread (int nr, unsigned long clone_flags,
+ unsigned long stack_start, unsigned long stack_size,
+ struct task_struct *p, struct pt_regs *regs)
+{
+ /* Start pushing stuff from the top of the child's kernel stack. */
+ unsigned long orig_ksp = (unsigned long)p->thread_info + THREAD_SIZE;
+ unsigned long ksp = orig_ksp;
+ /* We push two `state save' stack fames (see entry.S) on the new
+ kernel stack:
+ 1) The innermost one is what switch_thread would have
+ pushed, and is used when we context switch to the child
+ thread for the first time. It's set up to return to
+ ret_from_fork in entry.S.
+ 2) The outermost one (nearest the top) is what a syscall
+ trap would have pushed, and is set up to return to the
+ same location as the parent thread, but with a return
+ value of 0. */
+ struct pt_regs *child_switch_regs, *child_trap_regs;
+
+ /* Trap frame. */
+ ksp -= STATE_SAVE_SIZE;
+ child_trap_regs = (struct pt_regs *)(ksp + STATE_SAVE_PT_OFFSET);
+ /* Switch frame. */
+ ksp -= STATE_SAVE_SIZE;
+ child_switch_regs = (struct pt_regs *)(ksp + STATE_SAVE_PT_OFFSET);
+
+ /* First copy parent's register state to child. */
+ *child_switch_regs = *regs;
+ *child_trap_regs = *regs;
+
+ /* switch_thread returns to the restored value of the lp
+ register (r31), so we make that the place where we want to
+ jump when the child thread begins running. */
+ child_switch_regs->gpr[GPR_LP] = (v850_reg_t)ret_from_fork;
+
+ if (regs->kernel_mode)
+ /* Since we're returning to kernel-mode, make sure the child's
+ stored kernel stack pointer agrees with what the actual
+ stack pointer will be at that point (the trap return code
+ always restores the SP, even when returning to
+ kernel-mode). */
+ child_trap_regs->gpr[GPR_SP] = orig_ksp;
+ else
+ /* Set the child's user-mode stack-pointer (the name
+ `stack_start' is a misnomer, it's just the initial SP
+ value). */
+ child_trap_regs->gpr[GPR_SP] = stack_start;
+
+ /* Thread state for the child (everything else is on the stack). */
+ p->thread.ksp = ksp;
+
+ return 0;
+}
+
+/*
+ * fill in the user structure for a core dump..
+ */
+void dump_thread (struct pt_regs *regs, struct user *dump)
+{
+#if 0 /* Later. XXX */
+ dump->magic = CMAGIC;
+ dump->start_code = 0;
+ dump->start_stack = regs->gpr[GPR_SP];
+ dump->u_tsize = ((unsigned long) current->mm->end_code) >> PAGE_SHIFT;
+ dump->u_dsize = ((unsigned long) (current->mm->brk +
+ (PAGE_SIZE-1))) >> PAGE_SHIFT;
+ dump->u_dsize -= dump->u_tsize;
+ dump->u_ssize = 0;
+
+ if (dump->start_stack < TASK_SIZE)
+ dump->u_ssize = ((unsigned long) (TASK_SIZE - dump->start_stack)) >> PAGE_SHIFT;
+
+ dump->u_ar0 = (struct user_regs_struct *)((int)&dump->regs - (int)dump);
+ dump->regs = *regs;
+ dump->u_fpvalid = 0;
+#endif
+}
+
+/*
+ * sys_execve() executes a new program.
+ */
+int sys_execve (char *name, char **argv, char **envp, struct pt_regs *regs)
+{
+ char *filename = getname (name);
+ int error = PTR_ERR (filename);
+
+ if (! IS_ERR (filename)) {
+ error = do_execve (filename, argv, envp, regs);
+ putname (filename);
+ }
+
+ return error;
+}
+
+
+/*
+ * These bracket the sleeping functions..
+ */
+#define first_sched ((unsigned long)__sched_text_start)
+#define last_sched ((unsigned long)__sched_text_end)
+
+unsigned long get_wchan (struct task_struct *p)
+{
+#if 0 /* Barf. Figure out the stack-layout later. XXX */
+ unsigned long fp, pc;
+ int count = 0;
+
+ if (!p || p == current || p->state == TASK_RUNNING)
+ return 0;
+
+ pc = thread_saved_pc (p);
+
+ /* This quite disgusting function walks up the stack, following
+ saved return address, until it something that's out of bounds
+ (as defined by `first_sched' and `last_sched'). It then
+ returns the last PC that was in-bounds. */
+ do {
+ if (fp < stack_page + sizeof (struct task_struct) ||
+ fp >= 8184+stack_page)
+ return 0;
+ pc = ((unsigned long *)fp)[1];
+ if (pc < first_sched || pc >= last_sched)
+ return pc;
+ fp = *(unsigned long *) fp;
+ } while (count++ < 16);
+#endif
+
+ return 0;
+}
diff --git a/arch/v850/kernel/procfs.c b/arch/v850/kernel/procfs.c
new file mode 100644
index 0000000..e6f9d06
--- /dev/null
+++ b/arch/v850/kernel/procfs.c
@@ -0,0 +1,67 @@
+/*
+ * arch/v850/kernel/procfs.c -- Introspection functions for /proc filesystem
+ *
+ * Copyright (C) 2001,02 NEC Corporation
+ * Copyright (C) 2001,02 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 "mach.h"
+
+static int cpuinfo_print (struct seq_file *m, void *v)
+{
+ extern unsigned long loops_per_jiffy;
+
+ seq_printf (m, "CPU-Family: v850\nCPU-Arch: %s\n", CPU_ARCH);
+
+#ifdef CPU_MODEL_LONG
+ seq_printf (m, "CPU-Model: %s (%s)\n", CPU_MODEL, CPU_MODEL_LONG);
+#else
+ seq_printf (m, "CPU-Model: %s\n", CPU_MODEL);
+#endif
+
+#ifdef CPU_CLOCK_FREQ
+ seq_printf (m, "CPU-Clock: %ld (%ld MHz)\n",
+ (long)CPU_CLOCK_FREQ,
+ (long)CPU_CLOCK_FREQ / 1000000);
+#endif
+
+ seq_printf (m, "BogoMips: %lu.%02lu\n",
+ loops_per_jiffy/(500000/HZ),
+ (loops_per_jiffy/(5000/HZ)) % 100);
+
+#ifdef PLATFORM_LONG
+ seq_printf (m, "Platform: %s (%s)\n", PLATFORM, PLATFORM_LONG);
+#elif defined (PLATFORM)
+ seq_printf (m, "Platform: %s\n", PLATFORM);
+#endif
+
+ return 0;
+}
+
+static void *cpuinfo_start (struct seq_file *m, loff_t *pos)
+{
+ return *pos < NR_CPUS ? ((void *) 0x12345678) : NULL;
+}
+
+static void *cpuinfo_next (struct seq_file *m, void *v, loff_t *pos)
+{
+ ++*pos;
+ return cpuinfo_start (m, pos);
+}
+
+static void cpuinfo_stop (struct seq_file *m, void *v)
+{
+}
+
+struct seq_operations cpuinfo_op = {
+ .start = cpuinfo_start,
+ .next = cpuinfo_next,
+ .stop = cpuinfo_stop,
+ .show = cpuinfo_print
+};
diff --git a/arch/v850/kernel/ptrace.c b/arch/v850/kernel/ptrace.c
new file mode 100644
index 0000000..8fa7807
--- /dev/null
+++ b/arch/v850/kernel/ptrace.c
@@ -0,0 +1,282 @@
+/*
+ * arch/v850/kernel/ptrace.c -- `ptrace' system call
+ *
+ * Copyright (C) 2002,03,04 NEC Electronics Corporation
+ * Copyright (C) 2002,03,04 Miles Bader <miles@gnu.org>
+ *
+ * Derived from arch/mips/kernel/ptrace.c:
+ *
+ * Copyright (C) 1992 Ross Biro
+ * Copyright (C) Linus Torvalds
+ * Copyright (C) 1994, 95, 96, 97, 98, 2000 Ralf Baechle
+ * Copyright (C) 1996 David S. Miller
+ * Kevin D. Kissell, kevink@mips.com and Carsten Langgaard, carstenl@mips.com
+ * Copyright (C) 1999 MIPS Technologies, Inc.
+ *
+ * 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/kernel.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/smp_lock.h>
+#include <linux/ptrace.h>
+
+#include <asm/errno.h>
+#include <asm/ptrace.h>
+#include <asm/processor.h>
+#include <asm/uaccess.h>
+
+/* Returns the address where the register at REG_OFFS in P is stashed away. */
+static v850_reg_t *reg_save_addr (unsigned reg_offs, struct task_struct *t)
+{
+ struct pt_regs *regs;
+
+ /* Three basic cases:
+
+ (1) A register normally saved before calling the scheduler, is
+ available in the kernel entry pt_regs structure at the top
+ of the kernel stack. The kernel trap/irq exit path takes
+ care to save/restore almost all registers for ptrace'd
+ processes.
+
+ (2) A call-clobbered register, where the process P entered the
+ kernel via [syscall] trap, is not stored anywhere; that's
+ OK, because such registers are not expected to be preserved
+ when the trap returns anyway (so we don't actually bother to
+ test for this case).
+
+ (3) A few registers not used at all by the kernel, and so
+ normally never saved except by context-switches, are in the
+ context switch state. */
+
+ if (reg_offs == PT_CTPC || reg_offs == PT_CTPSW || reg_offs == PT_CTBP)
+ /* Register saved during context switch. */
+ regs = thread_saved_regs (t);
+ else
+ /* Register saved during kernel entry (or not available). */
+ regs = task_regs (t);
+
+ return (v850_reg_t *)((char *)regs + reg_offs);
+}
+
+/* Set the bits SET and clear the bits CLEAR in the v850e DIR
+ (`debug information register'). Returns the new value of DIR. */
+static inline v850_reg_t set_dir (v850_reg_t set, v850_reg_t clear)
+{
+ register v850_reg_t rval asm ("r10");
+ register v850_reg_t arg0 asm ("r6") = set;
+ register v850_reg_t arg1 asm ("r7") = clear;
+
+ /* The dbtrap handler has exactly this functionality when called
+ from kernel mode. 0xf840 is a `dbtrap' insn. */
+ asm (".short 0xf840" : "=r" (rval) : "r" (arg0), "r" (arg1));
+
+ return rval;
+}
+
+/* Makes sure hardware single-stepping is (globally) enabled.
+ Returns true if successful. */
+static inline int enable_single_stepping (void)
+{
+ static int enabled = 0; /* Remember whether we already did it. */
+ if (! enabled) {
+ /* Turn on the SE (`single-step enable') bit, 0x100, in the
+ DIR (`debug information register'). This may fail if a
+ processor doesn't support it or something. We also try
+ to clear bit 0x40 (`INI'), which is necessary to use the
+ debug stuff on the v850e2; on the v850e, clearing 0x40
+ shouldn't cause any problem. */
+ v850_reg_t dir = set_dir (0x100, 0x40);
+ /* Make sure it really got set. */
+ if (dir & 0x100)
+ enabled = 1;
+ }
+ return enabled;
+}
+
+/* Try to set CHILD's single-step flag to VAL. Returns true if successful. */
+static int set_single_step (struct task_struct *t, int val)
+{
+ v850_reg_t *psw_addr = reg_save_addr(PT_PSW, t);
+ if (val) {
+ /* Make sure single-stepping is enabled. */
+ if (! enable_single_stepping ())
+ return 0;
+ /* Set T's single-step flag. */
+ *psw_addr |= 0x800;
+ } else
+ *psw_addr &= ~0x800;
+ return 1;
+}
+
+int sys_ptrace(long request, long pid, long addr, long data)
+{
+ struct task_struct *child;
+ int rval;
+
+ lock_kernel();
+
+ if (request == PTRACE_TRACEME) {
+ /* are we already being traced? */
+ if (current->ptrace & PT_PTRACED) {
+ rval = -EPERM;
+ goto out;
+ }
+ /* set the ptrace bit in the process flags. */
+ current->ptrace |= PT_PTRACED;
+ rval = 0;
+ goto out;
+ }
+ rval = -ESRCH;
+ read_lock(&tasklist_lock);
+ child = find_task_by_pid(pid);
+ if (child)
+ get_task_struct(child);
+ read_unlock(&tasklist_lock);
+ if (!child)
+ goto out;
+
+ rval = -EPERM;
+ if (pid == 1) /* you may not mess with init */
+ goto out_tsk;
+
+ if (request == PTRACE_ATTACH) {
+ rval = ptrace_attach(child);
+ goto out_tsk;
+ }
+ rval = ptrace_check_attach(child, request == PTRACE_KILL);
+ if (rval < 0)
+ goto out_tsk;
+
+ switch (request) {
+ unsigned long val, copied;
+
+ case PTRACE_PEEKTEXT: /* read word at location addr. */
+ case PTRACE_PEEKDATA:
+ copied = access_process_vm(child, addr, &val, sizeof(val), 0);
+ rval = -EIO;
+ if (copied != sizeof(val))
+ break;
+ rval = put_user(val, (unsigned long *)data);
+ goto out;
+
+ case PTRACE_POKETEXT: /* write the word at location addr. */
+ case PTRACE_POKEDATA:
+ rval = 0;
+ if (access_process_vm(child, addr, &data, sizeof(data), 1)
+ == sizeof(data))
+ break;
+ rval = -EIO;
+ goto out;
+
+ /* Read/write the word at location ADDR in the registers. */
+ case PTRACE_PEEKUSR:
+ case PTRACE_POKEUSR:
+ rval = 0;
+ if (addr >= PT_SIZE && request == PTRACE_PEEKUSR) {
+ /* Special requests that don't actually correspond
+ to offsets in struct pt_regs. */
+ if (addr == PT_TEXT_ADDR)
+ val = child->mm->start_code;
+ else if (addr == PT_DATA_ADDR)
+ val = child->mm->start_data;
+ else if (addr == PT_TEXT_LEN)
+ val = child->mm->end_code
+ - child->mm->start_code;
+ else
+ rval = -EIO;
+ } else if (addr >= 0 && addr < PT_SIZE && (addr & 0x3) == 0) {
+ v850_reg_t *reg_addr = reg_save_addr(addr, child);
+ if (request == PTRACE_PEEKUSR)
+ val = *reg_addr;
+ else
+ *reg_addr = data;
+ } else
+ rval = -EIO;
+
+ if (rval == 0 && request == PTRACE_PEEKUSR)
+ rval = put_user (val, (unsigned long *)data);
+ goto out;
+
+ /* Continue and stop at next (return from) syscall */
+ case PTRACE_SYSCALL:
+ /* Restart after a signal. */
+ case PTRACE_CONT:
+ /* Execute a single instruction. */
+ case PTRACE_SINGLESTEP:
+ rval = -EIO;
+ if ((unsigned long) data > _NSIG)
+ break;
+
+ /* Turn CHILD's single-step flag on or off. */
+ if (! set_single_step (child, request == PTRACE_SINGLESTEP))
+ break;
+
+ if (request == PTRACE_SYSCALL)
+ set_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
+ else
+ clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
+
+ child->exit_code = data;
+ wake_up_process(child);
+ rval = 0;
+ break;
+
+ /*
+ * make the child exit. Best I can do is send it a sigkill.
+ * perhaps it should be put in the status that it wants to
+ * exit.
+ */
+ case PTRACE_KILL:
+ rval = 0;
+ if (child->exit_state == EXIT_ZOMBIE) /* already dead */
+ break;
+ child->exit_code = SIGKILL;
+ wake_up_process(child);
+ break;
+
+ case PTRACE_DETACH: /* detach a process that was attached. */
+ set_single_step (child, 0); /* Clear single-step flag */
+ rval = ptrace_detach(child, data);
+ break;
+
+ default:
+ rval = -EIO;
+ goto out;
+ }
+
+out_tsk:
+ put_task_struct(child);
+out:
+ unlock_kernel();
+ return rval;
+}
+
+asmlinkage void syscall_trace(void)
+{
+ if (!test_thread_flag(TIF_SYSCALL_TRACE))
+ return;
+ if (!(current->ptrace & PT_PTRACED))
+ return;
+ /* The 0x80 provides a way for the tracing parent to distinguish
+ between a syscall stop and SIGTRAP delivery */
+ ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD)
+ ? 0x80 : 0));
+ /*
+ * this isn't the same as continuing with a signal, but it will do
+ * for normal use. strace only continues with a signal if the
+ * stopping signal is not SIGTRAP. -brl
+ */
+ if (current->exit_code) {
+ send_sig(current->exit_code, current, 1);
+ current->exit_code = 0;
+ }
+}
+
+void ptrace_disable (struct task_struct *child)
+{
+ /* nothing to do */
+}
diff --git a/arch/v850/kernel/rte_cb.c b/arch/v850/kernel/rte_cb.c
new file mode 100644
index 0000000..7ba397f
--- /dev/null
+++ b/arch/v850/kernel/rte_cb.c
@@ -0,0 +1,200 @@
+/*
+ * include/asm-v850/rte_cb.c -- 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/config.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/fs.h>
+#include <linux/module.h>
+
+#include <asm/machdep.h>
+#include <asm/v850e_uart.h>
+
+#include "mach.h"
+
+static void led_tick (void);
+
+/* LED access routines. */
+extern unsigned read_leds (int pos, char *buf, int len);
+extern unsigned write_leds (int pos, const char *buf, int len);
+
+#ifdef CONFIG_RTE_CB_MULTI
+extern void multi_init (void);
+#endif
+
+
+void __init rte_cb_early_init (void)
+{
+ v850e_intc_disable_irqs ();
+
+#ifdef CONFIG_RTE_CB_MULTI
+ multi_init ();
+#endif
+}
+
+void __init mach_setup (char **cmdline)
+{
+#ifdef CONFIG_RTE_MB_A_PCI
+ /* Probe for Mother-A, and print a message if we find it. */
+ *(volatile unsigned long *)MB_A_SRAM_ADDR = 0xDEADBEEF;
+ if (*(volatile unsigned long *)MB_A_SRAM_ADDR == 0xDEADBEEF) {
+ *(volatile unsigned long *)MB_A_SRAM_ADDR = 0x12345678;
+ if (*(volatile unsigned long *)MB_A_SRAM_ADDR == 0x12345678)
+ printk (KERN_INFO
+ " NEC SolutionGear/Midas lab"
+ " RTE-MOTHER-A motherboard\n");
+ }
+#endif /* CONFIG_RTE_MB_A_PCI */
+
+ mach_tick = led_tick;
+}
+
+void machine_restart (char *__unused)
+{
+#ifdef CONFIG_RESET_GUARD
+ disable_reset_guard ();
+#endif
+ asm ("jmp r0"); /* Jump to the reset vector. */
+}
+
+EXPORT_SYMBOL(machine_restart);
+
+/* This says `HALt.' in LEDese. */
+static unsigned char halt_leds_msg[] = { 0x76, 0x77, 0x38, 0xF8 };
+
+void machine_halt (void)
+{
+#ifdef CONFIG_RESET_GUARD
+ disable_reset_guard ();
+#endif
+
+ /* Ignore all interrupts. */
+ local_irq_disable ();
+
+ /* Write a little message. */
+ write_leds (0, halt_leds_msg, sizeof halt_leds_msg);
+
+ /* Really halt. */
+ for (;;)
+ asm ("halt; nop; nop; nop; nop; nop");
+}
+
+EXPORT_SYMBOL(machine_halt);
+
+void machine_power_off (void)
+{
+ machine_halt ();
+}
+
+EXPORT_SYMBOL(machine_power_off);
+
+
+/* Animated LED display for timer tick. */
+
+#define TICK_UPD_FREQ 6
+static int tick_frames[][10] = {
+ { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, -1 },
+ { 0x63, 0x5c, -1 },
+ { 0x5c, 0x00, -1 },
+ { 0x63, 0x00, -1 },
+ { -1 }
+};
+
+static void led_tick ()
+{
+ static unsigned counter = 0;
+
+ if (++counter == (HZ / TICK_UPD_FREQ)) {
+ /* Which frame we're currently displaying for each digit. */
+ static unsigned frame_nums[LED_NUM_DIGITS] = { 0 };
+ /* Display image. */
+ static unsigned char image[LED_NUM_DIGITS] = { 0 };
+ unsigned char prev_image[LED_NUM_DIGITS];
+ int write_to_leds = 1; /* true if we should actually display */
+ int digit;
+
+ /* We check to see if the physical LEDs contains what we last
+ wrote to them; if not, we suppress display (this is so that
+ users can write to the LEDs, and not have their output
+ overwritten). As a special case, we start writing again if
+ all the LEDs are blank, or our display image is all zeros
+ (indicating that this is the initial update, when the actual
+ LEDs might contain random data). */
+ read_leds (0, prev_image, LED_NUM_DIGITS);
+ for (digit = 0; digit < LED_NUM_DIGITS; digit++)
+ if (image[digit] != prev_image[digit]
+ && image[digit] && prev_image[digit])
+ {
+ write_to_leds = 0;
+ break;
+ }
+
+ /* Update display image. */
+ for (digit = 0;
+ digit < LED_NUM_DIGITS && tick_frames[digit][0] >= 0;
+ digit++)
+ {
+ int frame = tick_frames[digit][frame_nums[digit]];
+ if (frame < 0) {
+ image[digit] = tick_frames[digit][0];
+ frame_nums[digit] = 1;
+ } else {
+ image[digit] = frame;
+ frame_nums[digit]++;
+ break;
+ }
+ }
+
+ if (write_to_leds)
+ /* Write the display image to the physical LEDs. */
+ write_leds (0, image, LED_NUM_DIGITS);
+
+ counter = 0;
+ }
+}
+
+
+/* Mother-A interrupts. */
+
+#ifdef CONFIG_RTE_GBUS_INT
+
+#define L GBUS_INT_PRIORITY_LOW
+#define M GBUS_INT_PRIORITY_MEDIUM
+#define H GBUS_INT_PRIORITY_HIGH
+
+static struct gbus_int_irq_init gbus_irq_inits[] = {
+#ifdef CONFIG_RTE_MB_A_PCI
+ { "MB_A_LAN", IRQ_MB_A_LAN, 1, 1, L },
+ { "MB_A_PCI1", IRQ_MB_A_PCI1(0), IRQ_MB_A_PCI1_NUM, 1, L },
+ { "MB_A_PCI2", IRQ_MB_A_PCI2(0), IRQ_MB_A_PCI2_NUM, 1, L },
+ { "MB_A_EXT", IRQ_MB_A_EXT(0), IRQ_MB_A_EXT_NUM, 1, L },
+ { "MB_A_USB_OC",IRQ_MB_A_USB_OC(0), IRQ_MB_A_USB_OC_NUM, 1, L },
+ { "MB_A_PCMCIA_OC",IRQ_MB_A_PCMCIA_OC, 1, 1, L },
+#endif
+ { 0 }
+};
+#define NUM_GBUS_IRQ_INITS \
+ ((sizeof gbus_irq_inits / sizeof gbus_irq_inits[0]) - 1)
+
+static struct hw_interrupt_type gbus_hw_itypes[NUM_GBUS_IRQ_INITS];
+
+#endif /* CONFIG_RTE_GBUS_INT */
+
+
+void __init rte_cb_init_irqs (void)
+{
+#ifdef CONFIG_RTE_GBUS_INT
+ gbus_int_init_irqs ();
+ gbus_int_init_irq_types (gbus_irq_inits, gbus_hw_itypes);
+#endif /* CONFIG_RTE_GBUS_INT */
+}
diff --git a/arch/v850/kernel/rte_cb_leds.c b/arch/v850/kernel/rte_cb_leds.c
new file mode 100644
index 0000000..b662ad8
--- /dev/null
+++ b/arch/v850/kernel/rte_cb_leds.c
@@ -0,0 +1,138 @@
+/*
+ * include/asm-v850/rte_cb_leds.c -- Midas lab RTE-CB board LED device support
+ *
+ * Copyright (C) 2002,03 NEC Electronics Corporation
+ * Copyright (C) 2002,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/config.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+
+#include <asm/uaccess.h>
+
+#define LEDS_MINOR 169 /* Minor device number, using misc major. */
+
+/* The actual LED hardware is write-only, so we hold the contents here too. */
+static unsigned char leds_image[LED_NUM_DIGITS] = { 0 };
+
+/* Spinlock protecting the above leds. */
+static DEFINE_SPINLOCK(leds_lock);
+
+/* Common body of LED read/write functions, checks POS and LEN for
+ correctness, declares a variable using IMG_DECL, initialized pointing at
+ the POS position in the LED image buffer, and and iterates COPY_EXPR
+ until BUF is equal to the last buffer position; finally, sets LEN to be
+ the amount actually copied. IMG should be a variable declaration
+ (without an initializer or a terminating semicolon); POS, BUF, and LEN
+ should all be simple variables. */
+#define DO_LED_COPY(img_decl, pos, buf, len, copy_expr) \
+do { \
+ if (pos > LED_NUM_DIGITS) \
+ len = 0; \
+ else { \
+ if (pos + len > LED_NUM_DIGITS) \
+ len = LED_NUM_DIGITS - pos; \
+ \
+ if (len > 0) { \
+ int _flags; \
+ const char *_end = buf + len; \
+ img_decl = &leds_image[pos]; \
+ \
+ spin_lock_irqsave (leds_lock, _flags); \
+ do \
+ (copy_expr); \
+ while (buf != _end); \
+ spin_unlock_irqrestore (leds_lock, _flags); \
+ } \
+ } \
+} while (0)
+
+/* Read LEN bytes from LEDs at position POS, into BUF.
+ Returns actual amount read. */
+unsigned read_leds (unsigned pos, char *buf, unsigned len)
+{
+ DO_LED_COPY (const char *img, pos, buf, len, *buf++ = *img++);
+ return len;
+}
+
+/* Write LEN bytes to LEDs at position POS, from BUF.
+ Returns actual amount written. */
+unsigned write_leds (unsigned pos, const char *buf, unsigned len)
+{
+ /* We write the actual LED values backwards, because
+ increasing memory addresses reflect LEDs right-to-left. */
+ volatile char *led = &LED (LED_NUM_DIGITS - pos - 1);
+ /* We invert the value written to the hardware, because 1 = off,
+ and 0 = on. */
+ DO_LED_COPY (char *img, pos, buf, len,
+ *led-- = 0xFF ^ (*img++ = *buf++));
+ return len;
+}
+
+
+/* Device functions. */
+
+static ssize_t leds_dev_read (struct file *file, char *buf, size_t len,
+ loff_t *pos)
+{
+ char temp_buf[LED_NUM_DIGITS];
+ len = read_leds (*pos, temp_buf, len);
+ if (copy_to_user (buf, temp_buf, len))
+ return -EFAULT;
+ *pos += len;
+ return len;
+}
+
+static ssize_t leds_dev_write (struct file *file, const char *buf, size_t len,
+ loff_t *pos)
+{
+ char temp_buf[LED_NUM_DIGITS];
+ if (copy_from_user (temp_buf, buf, min_t(size_t, len, LED_NUM_DIGITS)))
+ return -EFAULT;
+ len = write_leds (*pos, temp_buf, len);
+ *pos += len;
+ return len;
+}
+
+static loff_t leds_dev_lseek (struct file *file, loff_t offs, int whence)
+{
+ if (whence == 1)
+ offs += file->f_pos; /* relative */
+ else if (whence == 2)
+ offs += LED_NUM_DIGITS; /* end-relative */
+
+ if (offs < 0 || offs > LED_NUM_DIGITS)
+ return -EINVAL;
+
+ file->f_pos = offs;
+
+ return 0;
+}
+
+static struct file_operations leds_fops = {
+ .read = leds_dev_read,
+ .write = leds_dev_write,
+ .llseek = leds_dev_lseek
+};
+
+static struct miscdevice leds_miscdev = {
+ .name = "leds",
+ .minor = LEDS_MINOR,
+ .fops = &leds_fops
+};
+
+int __init leds_dev_init (void)
+{
+ return misc_register (&leds_miscdev);
+}
+
+__initcall (leds_dev_init);
diff --git a/arch/v850/kernel/rte_cb_multi.c b/arch/v850/kernel/rte_cb_multi.c
new file mode 100644
index 0000000..963d55a
--- /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;
+ }
+ }
+}
diff --git a/arch/v850/kernel/rte_ma1_cb-rom.ld b/arch/v850/kernel/rte_ma1_cb-rom.ld
new file mode 100644
index 0000000..87b618f
--- /dev/null
+++ b/arch/v850/kernel/rte_ma1_cb-rom.ld
@@ -0,0 +1,14 @@
+/* Linker script for the Midas labs RTE-V850E/MA1-CB evaluation board
+ (CONFIG_RTE_CB_MA1), with kernel in ROM. */
+
+MEMORY {
+ ROM : ORIGIN = 0x00000000, LENGTH = 0x00100000
+ /* 1MB of SRAM. This memory is mirrored 4 times. */
+ SRAM : ORIGIN = SRAM_ADDR, LENGTH = SRAM_SIZE
+ /* 32MB of SDRAM. */
+ SDRAM : ORIGIN = SDRAM_ADDR, LENGTH = SDRAM_SIZE
+}
+
+SECTIONS {
+ ROMK_SECTIONS(ROM, SRAM)
+}
diff --git a/arch/v850/kernel/rte_ma1_cb.c b/arch/v850/kernel/rte_ma1_cb.c
new file mode 100644
index 0000000..3873e27
--- /dev/null
+++ b/arch/v850/kernel/rte_ma1_cb.c
@@ -0,0 +1,106 @@
+/*
+ * arch/v850/kernel/rte_ma1_cb.c -- Midas labs RTE-V850E/MA1-CB board
+ *
+ * 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/config.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/bootmem.h>
+
+#include <asm/atomic.h>
+#include <asm/page.h>
+#include <asm/ma1.h>
+#include <asm/rte_ma1_cb.h>
+#include <asm/v850e_timer_c.h>
+
+#include "mach.h"
+
+
+/* SRAM and SDRAM are almost contiguous (with a small hole in between;
+ see mach_reserve_bootmem for details), so just use both as one big area. */
+#define RAM_START SRAM_ADDR
+#define RAM_END (SDRAM_ADDR + SDRAM_SIZE)
+
+
+void __init mach_early_init (void)
+{
+ rte_cb_early_init ();
+}
+
+void __init mach_get_physical_ram (unsigned long *ram_start,
+ unsigned long *ram_len)
+{
+ *ram_start = RAM_START;
+ *ram_len = RAM_END - RAM_START;
+}
+
+void __init mach_reserve_bootmem ()
+{
+#ifdef CONFIG_RTE_CB_MULTI
+ /* Prevent the kernel from touching the monitor's scratch RAM. */
+ reserve_bootmem (MON_SCRATCH_ADDR, MON_SCRATCH_SIZE);
+#endif
+
+ /* The space between SRAM and SDRAM is filled with duplicate
+ images of SRAM. Prevent the kernel from using them. */
+ reserve_bootmem (SRAM_ADDR + SRAM_SIZE,
+ SDRAM_ADDR - (SRAM_ADDR + SRAM_SIZE));
+}
+
+void mach_gettimeofday (struct timespec *tv)
+{
+ tv->tv_sec = 0;
+ tv->tv_nsec = 0;
+}
+
+/* Called before configuring an on-chip UART. */
+void rte_ma1_cb_uart_pre_configure (unsigned chan,
+ unsigned cflags, unsigned baud)
+{
+ /* The RTE-MA1-CB connects some general-purpose I/O pins on the
+ CPU to the RTS/CTS lines of UART 0's serial connection.
+ I/O pins P42 and P43 are RTS and CTS respectively. */
+ if (chan == 0) {
+ /* Put P42 & P43 in I/O port mode. */
+ MA_PORT4_PMC &= ~0xC;
+ /* Make P42 an output, and P43 an input. */
+ MA_PORT4_PM = (MA_PORT4_PM & ~0xC) | 0x8;
+ }
+
+ /* Do pre-configuration for the actual UART. */
+ ma_uart_pre_configure (chan, cflags, baud);
+}
+
+void __init mach_init_irqs (void)
+{
+ unsigned tc;
+
+ /* Initialize interrupts. */
+ ma_init_irqs ();
+ rte_cb_init_irqs ();
+
+ /* Use falling-edge-sensitivity for interrupts . */
+ V850E_TIMER_C_SESC (0) &= ~0xC;
+ V850E_TIMER_C_SESC (1) &= ~0xF;
+
+ /* INTP000-INTP011 are shared with `Timer C', so we have to set
+ up Timer C to pass them through as raw interrupts. */
+ for (tc = 0; tc < 2; tc++)
+ /* Turn on the timer. */
+ V850E_TIMER_C_TMCC0 (tc) |= V850E_TIMER_C_TMCC0_CAE;
+
+ /* Make sure the relevant port0/port1 pins are assigned
+ interrupt duty. We used INTP001-INTP011 (don't screw with
+ INTP000 because the monitor uses it). */
+ MA_PORT0_PMC |= 0x4; /* P02 (INTP001) in IRQ mode. */
+ MA_PORT1_PMC |= 0x6; /* P11 (INTP010) & P12 (INTP011) in IRQ mode.*/
+}
diff --git a/arch/v850/kernel/rte_ma1_cb.ld b/arch/v850/kernel/rte_ma1_cb.ld
new file mode 100644
index 0000000..c8e16d1
--- /dev/null
+++ b/arch/v850/kernel/rte_ma1_cb.ld
@@ -0,0 +1,57 @@
+/* Linker script for the Midas labs RTE-V850E/MA1-CB evaluation board
+ (CONFIG_RTE_CB_MA1), with kernel in SDRAM, under Multi debugger. */
+
+MEMORY {
+ /* 1MB of SRAM; we can't use the last 32KB, because it's used by
+ the monitor scratch-RAM. This memory is mirrored 4 times. */
+ SRAM : ORIGIN = SRAM_ADDR, LENGTH = (SRAM_SIZE - MON_SCRATCH_SIZE)
+ /* Monitor scratch RAM; only the interrupt vectors should go here. */
+ MRAM : ORIGIN = MON_SCRATCH_ADDR, LENGTH = MON_SCRATCH_SIZE
+ /* 32MB of SDRAM. */
+ SDRAM : ORIGIN = SDRAM_ADDR, LENGTH = SDRAM_SIZE
+}
+
+#ifdef CONFIG_RTE_CB_MA1_KSRAM
+# define KRAM SRAM
+#else
+# define KRAM SDRAM
+#endif
+
+SECTIONS {
+ /* We can't use RAMK_KRAM_CONTENTS because that puts the whole
+ kernel in a single ELF segment, and the Multi debugger (which
+ we use to load the kernel) appears to have bizarre problems
+ dealing with it. */
+
+ .text : {
+ __kram_start = . ;
+ TEXT_CONTENTS
+ } > KRAM
+
+ .data : {
+ DATA_CONTENTS
+ BSS_CONTENTS
+ RAMK_INIT_CONTENTS
+ __kram_end = . ;
+ BOOTMAP_CONTENTS
+
+ /* The address at which the interrupt vectors are initially
+ loaded by the loader. We can't load the interrupt vectors
+ directly into their target location, because the monitor
+ ROM for the GHS Multi debugger barfs if we try.
+ 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 have the
+ `mach_early_init' function for Midas boards do the
+ necessary copying and relocation at runtime (this section
+ basically only contains `jr' instructions, so it's not
+ that hard). */
+ . = ALIGN (0x10) ;
+ __intv_load_start = . ;
+ INTV_CONTENTS
+ } > KRAM
+
+ .root ALIGN (4096) : { ROOT_FS_CONTENTS } > SDRAM
+}
diff --git a/arch/v850/kernel/rte_mb_a_pci.c b/arch/v850/kernel/rte_mb_a_pci.c
new file mode 100644
index 0000000..074b50a
--- /dev/null
+++ b/arch/v850/kernel/rte_mb_a_pci.c
@@ -0,0 +1,796 @@
+/*
+ * arch/v850/kernel/mb_a_pci.c -- PCI support for Midas lab RTE-MOTHER-A board
+ *
+ * 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/config.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/pci.h>
+
+#include <asm/machdep.h>
+
+/* __nomods_init is like __devinit, but is a no-op when modules are enabled.
+ This is used by some routines that can be called either during boot
+ or by a module. */
+#ifdef CONFIG_MODULES
+#define __nomods_init /*nothing*/
+#else
+#define __nomods_init __devinit
+#endif
+
+/* PCI devices on the Mother-A board can only do DMA to/from the MB SRAM
+ (the RTE-V850E/MA1-CB cpu board doesn't support PCI access to
+ CPU-board memory), and since linux DMA buffers are allocated in
+ normal kernel memory, we basically have to copy DMA blocks around
+ (this is like a `bounce buffer'). When a DMA block is `mapped', we
+ allocate an identically sized block in MB SRAM, and if we're doing
+ output to the device, copy the CPU-memory block to the MB-SRAM block.
+ When an active block is `unmapped', we will copy the block back to
+ CPU memory if necessary, and then deallocate the MB SRAM block.
+ Ack. */
+
+/* Where the motherboard SRAM is in the PCI-bus address space (the
+ first 512K of it is also mapped at PCI address 0). */
+#define PCI_MB_SRAM_ADDR 0x800000
+
+/* Convert CPU-view MB SRAM address to/from PCI-view addresses of the
+ same memory. */
+#define MB_SRAM_TO_PCI(mb_sram_addr) \
+ ((dma_addr_t)mb_sram_addr - MB_A_SRAM_ADDR + PCI_MB_SRAM_ADDR)
+#define PCI_TO_MB_SRAM(pci_addr) \
+ (void *)(pci_addr - PCI_MB_SRAM_ADDR + MB_A_SRAM_ADDR)
+
+static void pcibios_assign_resources (void);
+
+struct mb_pci_dev_irq {
+ unsigned dev; /* PCI device number */
+ unsigned irq_base; /* First IRQ */
+ unsigned query_pin; /* True if we should read the device's
+ Interrupt Pin info, and allocate
+ interrupt IRQ_BASE + PIN. */
+};
+
+/* PCI interrupts are mapped statically to GBUS interrupts. */
+static struct mb_pci_dev_irq mb_pci_dev_irqs[] = {
+ /* Motherboard SB82558 ethernet controller */
+ { 10, IRQ_MB_A_LAN, 0 },
+ /* PCI slot 1 */
+ { 8, IRQ_MB_A_PCI1(0), 1 },
+ /* PCI slot 2 */
+ { 9, IRQ_MB_A_PCI2(0), 1 }
+};
+#define NUM_MB_PCI_DEV_IRQS \
+ (sizeof mb_pci_dev_irqs / sizeof mb_pci_dev_irqs[0])
+
+
+/* PCI configuration primitives. */
+
+#define CONFIG_DMCFGA(bus, devfn, offs) \
+ (0x80000000 \
+ | ((offs) & ~0x3) \
+ | ((devfn) << 8) \
+ | ((bus)->number << 16))
+
+static int
+mb_pci_read (struct pci_bus *bus, unsigned devfn, int offs, int size, u32 *rval)
+{
+ u32 addr;
+ int flags;
+
+ local_irq_save (flags);
+
+ MB_A_PCI_PCICR = 0x7;
+ MB_A_PCI_DMCFGA = CONFIG_DMCFGA (bus, devfn, offs);
+
+ addr = MB_A_PCI_IO_ADDR + (offs & 0x3);
+
+ switch (size) {
+ case 1: *rval = *(volatile u8 *)addr; break;
+ case 2: *rval = *(volatile u16 *)addr; break;
+ case 4: *rval = *(volatile u32 *)addr; break;
+ }
+
+ if (MB_A_PCI_PCISR & 0x2000) {
+ MB_A_PCI_PCISR = 0x2000;
+ *rval = ~0;
+ }
+
+ MB_A_PCI_DMCFGA = 0;
+
+ local_irq_restore (flags);
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static int
+mb_pci_write (struct pci_bus *bus, unsigned devfn, int offs, int size, u32 val)
+{
+ u32 addr;
+ int flags;
+
+ local_irq_save (flags);
+
+ MB_A_PCI_PCICR = 0x7;
+ MB_A_PCI_DMCFGA = CONFIG_DMCFGA (bus, devfn, offs);
+
+ addr = MB_A_PCI_IO_ADDR + (offs & 0x3);
+
+ switch (size) {
+ case 1: *(volatile u8 *)addr = val; break;
+ case 2: *(volatile u16 *)addr = val; break;
+ case 4: *(volatile u32 *)addr = val; break;
+ }
+
+ if (MB_A_PCI_PCISR & 0x2000)
+ MB_A_PCI_PCISR = 0x2000;
+
+ MB_A_PCI_DMCFGA = 0;
+
+ local_irq_restore (flags);
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static struct pci_ops mb_pci_config_ops = {
+ .read = mb_pci_read,
+ .write = mb_pci_write,
+};
+
+
+/* PCI Initialization. */
+
+static struct pci_bus *mb_pci_bus = 0;
+
+/* Do initial PCI setup. */
+static int __devinit pcibios_init (void)
+{
+ u32 id = MB_A_PCI_PCIHIDR;
+ u16 vendor = id & 0xFFFF;
+ u16 device = (id >> 16) & 0xFFFF;
+
+ if (vendor == PCI_VENDOR_ID_PLX && device == PCI_DEVICE_ID_PLX_9080) {
+ printk (KERN_INFO
+ "PCI: PLX Technology PCI9080 HOST/PCI bridge\n");
+
+ MB_A_PCI_PCICR = 0x147;
+
+ MB_A_PCI_PCIBAR0 = 0x007FFF00;
+ MB_A_PCI_PCIBAR1 = 0x0000FF00;
+ MB_A_PCI_PCIBAR2 = 0x00800000;
+
+ MB_A_PCI_PCILTR = 0x20;
+
+ MB_A_PCI_PCIPBAM |= 0x3;
+
+ MB_A_PCI_PCISR = ~0; /* Clear errors. */
+
+ /* Reprogram the motherboard's IO/config address space,
+ as we don't support the GCS7 address space that the
+ default uses. */
+
+ /* Significant address bits used for decoding PCI GCS5 space
+ accessess. */
+ MB_A_PCI_DMRR = ~(MB_A_PCI_MEM_SIZE - 1);
+
+ /* I don't understand this, but the SolutionGear example code
+ uses such an offset, and it doesn't work without it. XXX */
+#if GCS5_SIZE == 0x00800000
+#define GCS5_CFG_OFFS 0x00800000
+#else
+#define GCS5_CFG_OFFS 0
+#endif
+
+ /* Address bit values for matching. Note that we have to give
+ the address from the motherboard's point of view, which is
+ different than the CPU's. */
+ /* PCI memory space. */
+ MB_A_PCI_DMLBAM = GCS5_CFG_OFFS + 0x0;
+ /* PCI I/O space. */
+ MB_A_PCI_DMLBAI =
+ GCS5_CFG_OFFS + (MB_A_PCI_IO_ADDR - GCS5_ADDR);
+
+ mb_pci_bus = pci_scan_bus (0, &mb_pci_config_ops, 0);
+
+ pcibios_assign_resources ();
+ } else
+ printk (KERN_ERR "PCI: HOST/PCI bridge not found\n");
+
+ return 0;
+}
+
+subsys_initcall (pcibios_init);
+
+char __devinit *pcibios_setup (char *option)
+{
+ /* Don't handle any options. */
+ return option;
+}
+
+
+int __nomods_init pcibios_enable_device (struct pci_dev *dev, int mask)
+{
+ u16 cmd, old_cmd;
+ int idx;
+ struct resource *r;
+
+ pci_read_config_word(dev, PCI_COMMAND, &cmd);
+ old_cmd = cmd;
+ for (idx = 0; idx < 6; idx++) {
+ r = &dev->resource[idx];
+ if (!r->start && r->end) {
+ printk(KERN_ERR "PCI: Device %s not available because "
+ "of resource collisions\n", pci_name(dev));
+ return -EINVAL;
+ }
+ if (r->flags & IORESOURCE_IO)
+ cmd |= PCI_COMMAND_IO;
+ if (r->flags & IORESOURCE_MEM)
+ cmd |= PCI_COMMAND_MEMORY;
+ }
+ if (cmd != old_cmd) {
+ printk("PCI: Enabling device %s (%04x -> %04x)\n",
+ pci_name(dev), old_cmd, cmd);
+ pci_write_config_word(dev, PCI_COMMAND, cmd);
+ }
+ return 0;
+}
+
+
+/* Resource allocation. */
+static void __devinit pcibios_assign_resources (void)
+{
+ struct pci_dev *dev = NULL;
+ struct resource *r;
+
+ for_each_pci_dev(dev) {
+ unsigned di_num;
+ unsigned class = dev->class >> 8;
+
+ if (class && class != PCI_CLASS_BRIDGE_HOST) {
+ unsigned r_num;
+ for(r_num = 0; r_num < 6; r_num++) {
+ r = &dev->resource[r_num];
+ if (!r->start && r->end)
+ pci_assign_resource (dev, r_num);
+ }
+ }
+
+ /* Assign interrupts. */
+ for (di_num = 0; di_num < NUM_MB_PCI_DEV_IRQS; di_num++) {
+ struct mb_pci_dev_irq *di = &mb_pci_dev_irqs[di_num];
+
+ if (di->dev == PCI_SLOT (dev->devfn)) {
+ unsigned irq = di->irq_base;
+
+ if (di->query_pin) {
+ /* Find out which interrupt pin
+ this device uses (each PCI
+ slot has 4). */
+ u8 irq_pin;
+
+ pci_read_config_byte (dev,
+ PCI_INTERRUPT_PIN,
+ &irq_pin);
+
+ if (irq_pin == 0)
+ /* Doesn't use interrupts. */
+ continue;
+ else
+ irq += irq_pin - 1;
+ }
+
+ pcibios_update_irq (dev, irq);
+ }
+ }
+ }
+}
+
+void __devinit pcibios_update_irq (struct pci_dev *dev, int irq)
+{
+ dev->irq = irq;
+ pci_write_config_byte (dev, PCI_INTERRUPT_LINE, irq);
+}
+
+void __devinit
+pcibios_resource_to_bus(struct pci_dev *dev, struct pci_bus_region *region,
+ struct resource *res)
+{
+ unsigned long offset = 0;
+
+ if (res->flags & IORESOURCE_IO) {
+ offset = MB_A_PCI_IO_ADDR;
+ } else if (res->flags & IORESOURCE_MEM) {
+ offset = MB_A_PCI_MEM_ADDR;
+ }
+
+ region->start = res->start - offset;
+ region->end = res->end - offset;
+}
+
+
+/* Stubs for things we don't use. */
+
+/* Called after each bus is probed, but before its children are examined. */
+void pcibios_fixup_bus(struct pci_bus *b)
+{
+}
+
+void
+pcibios_align_resource (void *data, struct resource *res,
+ unsigned long size, unsigned long align)
+{
+}
+
+void pcibios_set_master (struct pci_dev *dev)
+{
+}
+
+
+/* Mother-A SRAM memory allocation. This is a simple first-fit allocator. */
+
+/* A memory free-list node. */
+struct mb_sram_free_area {
+ void *mem;
+ unsigned long size;
+ struct mb_sram_free_area *next;
+};
+
+/* The tail of the free-list, which starts out containing all the SRAM. */
+static struct mb_sram_free_area mb_sram_free_tail = {
+ (void *)MB_A_SRAM_ADDR, MB_A_SRAM_SIZE, 0
+};
+
+/* The free-list. */
+static struct mb_sram_free_area *mb_sram_free_areas = &mb_sram_free_tail;
+
+/* The free-list of free free-list nodes. (:-) */
+static struct mb_sram_free_area *mb_sram_free_free_areas = 0;
+
+/* Spinlock protecting the above globals. */
+static DEFINE_SPINLOCK(mb_sram_lock);
+
+/* Allocate a memory block at least SIZE bytes long in the Mother-A SRAM
+ space. */
+static void *alloc_mb_sram (size_t size)
+{
+ struct mb_sram_free_area *prev, *fa;
+ int flags;
+ void *mem = 0;
+
+ spin_lock_irqsave (mb_sram_lock, flags);
+
+ /* Look for a free area that can contain SIZE bytes. */
+ for (prev = 0, fa = mb_sram_free_areas; fa; prev = fa, fa = fa->next)
+ if (fa->size >= size) {
+ /* Found one! */
+ mem = fa->mem;
+
+ if (fa->size == size) {
+ /* In fact, it fits exactly, so remove
+ this node from the free-list. */
+ if (prev)
+ prev->next = fa->next;
+ else
+ mb_sram_free_areas = fa->next;
+ /* Put it on the free-list-entry-free-list. */
+ fa->next = mb_sram_free_free_areas;
+ mb_sram_free_free_areas = fa;
+ } else {
+ /* FA is bigger than SIZE, so just
+ reduce its size to account for this
+ allocation. */
+ fa->mem += size;
+ fa->size -= size;
+ }
+
+ break;
+ }
+
+ spin_unlock_irqrestore (mb_sram_lock, flags);
+
+ return mem;
+}
+
+/* Return the memory area MEM of size SIZE to the MB SRAM free pool. */
+static void free_mb_sram (void *mem, size_t size)
+{
+ struct mb_sram_free_area *prev, *fa, *new_fa;
+ int flags;
+ void *end = mem + size;
+
+ spin_lock_irqsave (mb_sram_lock, flags);
+
+ retry:
+ /* Find an adjacent free-list entry. */
+ for (prev = 0, fa = mb_sram_free_areas; fa; prev = fa, fa = fa->next)
+ if (fa->mem == end) {
+ /* FA is just after MEM, grow down to encompass it. */
+ fa->mem = mem;
+ fa->size += size;
+ goto done;
+ } else if (fa->mem + fa->size == mem) {
+ struct mb_sram_free_area *next_fa = fa->next;
+
+ /* FA is just before MEM, expand to encompass it. */
+ fa->size += size;
+
+ /* See if FA can now be merged with its successor. */
+ if (next_fa && fa->mem + fa->size == next_fa->mem) {
+ /* Yup; merge NEXT_FA's info into FA. */
+ fa->size += next_fa->size;
+ fa->next = next_fa->next;
+ /* Free NEXT_FA. */
+ next_fa->next = mb_sram_free_free_areas;
+ mb_sram_free_free_areas = next_fa;
+ }
+ goto done;
+ } else if (fa->mem > mem)
+ /* We've reached the right spot in the free-list
+ without finding an adjacent free-area, so add
+ a new free area to hold mem. */
+ break;
+
+ /* Make a new free-list entry. */
+
+ /* First, get a free-list entry. */
+ if (! mb_sram_free_free_areas) {
+ /* There are none, so make some. */
+ void *block;
+ size_t block_size = sizeof (struct mb_sram_free_area) * 8;
+
+ /* Don't hold the lock while calling kmalloc (I'm not
+ sure whether it would be a problem, since we use
+ GFP_ATOMIC, but it makes me nervous). */
+ spin_unlock_irqrestore (mb_sram_lock, flags);
+
+ block = kmalloc (block_size, GFP_ATOMIC);
+ if (! block)
+ panic ("free_mb_sram: can't allocate free-list entry");
+
+ /* Now get the lock back. */
+ spin_lock_irqsave (mb_sram_lock, flags);
+
+ /* Add the new free free-list entries. */
+ while (block_size > 0) {
+ struct mb_sram_free_area *nfa = block;
+ nfa->next = mb_sram_free_free_areas;
+ mb_sram_free_free_areas = nfa;
+ block += sizeof *nfa;
+ block_size -= sizeof *nfa;
+ }
+
+ /* Since we dropped the lock to call kmalloc, the
+ free-list could have changed, so retry from the
+ beginning. */
+ goto retry;
+ }
+
+ /* Remove NEW_FA from the free-list of free-list entries. */
+ new_fa = mb_sram_free_free_areas;
+ mb_sram_free_free_areas = new_fa->next;
+
+ /* NEW_FA initially holds only MEM. */
+ new_fa->mem = mem;
+ new_fa->size = size;
+
+ /* Insert NEW_FA in the free-list between PREV and FA. */
+ new_fa->next = fa;
+ if (prev)
+ prev->next = new_fa;
+ else
+ mb_sram_free_areas = new_fa;
+
+ done:
+ spin_unlock_irqrestore (mb_sram_lock, flags);
+}
+
+
+/* Maintainence of CPU -> Mother-A DMA mappings. */
+
+struct dma_mapping {
+ void *cpu_addr;
+ void *mb_sram_addr;
+ size_t size;
+ struct dma_mapping *next;
+};
+
+/* A list of mappings from CPU addresses to MB SRAM addresses for active
+ DMA blocks (that have been `granted' to the PCI device). */
+static struct dma_mapping *active_dma_mappings = 0;
+
+/* A list of free mapping objects. */
+static struct dma_mapping *free_dma_mappings = 0;
+
+/* Spinlock protecting the above globals. */
+static DEFINE_SPINLOCK(dma_mappings_lock);
+
+static struct dma_mapping *new_dma_mapping (size_t size)
+{
+ int flags;
+ struct dma_mapping *mapping;
+ void *mb_sram_block = alloc_mb_sram (size);
+
+ if (! mb_sram_block)
+ return 0;
+
+ spin_lock_irqsave (dma_mappings_lock, flags);
+
+ if (! free_dma_mappings) {
+ /* We're out of mapping structures, make more. */
+ void *mblock;
+ size_t mblock_size = sizeof (struct dma_mapping) * 8;
+
+ /* Don't hold the lock while calling kmalloc (I'm not
+ sure whether it would be a problem, since we use
+ GFP_ATOMIC, but it makes me nervous). */
+ spin_unlock_irqrestore (dma_mappings_lock, flags);
+
+ mblock = kmalloc (mblock_size, GFP_ATOMIC);
+ if (! mblock) {
+ free_mb_sram (mb_sram_block, size);
+ return 0;
+ }
+
+ /* Get the lock back. */
+ spin_lock_irqsave (dma_mappings_lock, flags);
+
+ /* Add the new mapping structures to the free-list. */
+ while (mblock_size > 0) {
+ struct dma_mapping *fm = mblock;
+ fm->next = free_dma_mappings;
+ free_dma_mappings = fm;
+ mblock += sizeof *fm;
+ mblock_size -= sizeof *fm;
+ }
+ }
+
+ /* Get a mapping struct from the freelist. */
+ mapping = free_dma_mappings;
+ free_dma_mappings = mapping->next;
+
+ /* Initialize the mapping. Other fields should be filled in by
+ caller. */
+ mapping->mb_sram_addr = mb_sram_block;
+ mapping->size = size;
+
+ /* Add it to the list of active mappings. */
+ mapping->next = active_dma_mappings;
+ active_dma_mappings = mapping;
+
+ spin_unlock_irqrestore (dma_mappings_lock, flags);
+
+ return mapping;
+}
+
+static struct dma_mapping *find_dma_mapping (void *mb_sram_addr)
+{
+ int flags;
+ struct dma_mapping *mapping;
+
+ spin_lock_irqsave (dma_mappings_lock, flags);
+
+ for (mapping = active_dma_mappings; mapping; mapping = mapping->next)
+ if (mapping->mb_sram_addr == mb_sram_addr) {
+ spin_unlock_irqrestore (dma_mappings_lock, flags);
+ return mapping;
+ }
+
+ panic ("find_dma_mapping: unmapped PCI DMA addr 0x%x",
+ MB_SRAM_TO_PCI (mb_sram_addr));
+}
+
+static struct dma_mapping *deactivate_dma_mapping (void *mb_sram_addr)
+{
+ int flags;
+ struct dma_mapping *mapping, *prev;
+
+ spin_lock_irqsave (dma_mappings_lock, flags);
+
+ for (prev = 0, mapping = active_dma_mappings;
+ mapping;
+ prev = mapping, mapping = mapping->next)
+ {
+ if (mapping->mb_sram_addr == mb_sram_addr) {
+ /* This is the MAPPING; deactivate it. */
+ if (prev)
+ prev->next = mapping->next;
+ else
+ active_dma_mappings = mapping->next;
+
+ spin_unlock_irqrestore (dma_mappings_lock, flags);
+
+ return mapping;
+ }
+ }
+
+ panic ("deactivate_dma_mapping: unmapped PCI DMA addr 0x%x",
+ MB_SRAM_TO_PCI (mb_sram_addr));
+}
+
+/* Return MAPPING to the freelist. */
+static inline void
+free_dma_mapping (struct dma_mapping *mapping)
+{
+ int flags;
+
+ free_mb_sram (mapping->mb_sram_addr, mapping->size);
+
+ spin_lock_irqsave (dma_mappings_lock, flags);
+
+ mapping->next = free_dma_mappings;
+ free_dma_mappings = mapping;
+
+ spin_unlock_irqrestore (dma_mappings_lock, flags);
+}
+
+
+/* Single PCI DMA mappings. */
+
+/* `Grant' to PDEV the memory block at CPU_ADDR, for doing DMA. The
+ 32-bit PCI bus mastering address to use is returned. the device owns
+ this memory until either pci_unmap_single or pci_dma_sync_single is
+ performed. */
+dma_addr_t
+pci_map_single (struct pci_dev *pdev, void *cpu_addr, size_t size, int dir)
+{
+ struct dma_mapping *mapping = new_dma_mapping (size);
+
+ if (! mapping)
+ return 0;
+
+ mapping->cpu_addr = cpu_addr;
+
+ if (dir == PCI_DMA_BIDIRECTIONAL || dir == PCI_DMA_TODEVICE)
+ memcpy (mapping->mb_sram_addr, cpu_addr, size);
+
+ return MB_SRAM_TO_PCI (mapping->mb_sram_addr);
+}
+
+/* Return to the CPU the PCI DMA memory block previously `granted' to
+ PDEV, at DMA_ADDR. */
+void pci_unmap_single (struct pci_dev *pdev, dma_addr_t dma_addr, size_t size,
+ int dir)
+{
+ void *mb_sram_addr = PCI_TO_MB_SRAM (dma_addr);
+ struct dma_mapping *mapping = deactivate_dma_mapping (mb_sram_addr);
+
+ if (size != mapping->size)
+ panic ("pci_unmap_single: size (%d) doesn't match"
+ " size of mapping at PCI DMA addr 0x%x (%d)\n",
+ size, dma_addr, mapping->size);
+
+ /* Copy back the DMA'd contents if necessary. */
+ if (dir == PCI_DMA_BIDIRECTIONAL || dir == PCI_DMA_FROMDEVICE)
+ memcpy (mapping->cpu_addr, mb_sram_addr, size);
+
+ /* Return mapping to the freelist. */
+ free_dma_mapping (mapping);
+}
+
+/* Make physical memory consistent for a single streaming mode DMA
+ translation after a transfer.
+
+ If you perform a pci_map_single() but wish to interrogate the
+ buffer using the cpu, yet do not wish to teardown the PCI dma
+ mapping, you must call this function before doing so. At the next
+ point you give the PCI dma address back to the card, you must first
+ perform a pci_dma_sync_for_device, and then the device again owns
+ the buffer. */
+void
+pci_dma_sync_single_for_cpu (struct pci_dev *pdev, dma_addr_t dma_addr, size_t size,
+ int dir)
+{
+ void *mb_sram_addr = PCI_TO_MB_SRAM (dma_addr);
+ struct dma_mapping *mapping = find_dma_mapping (mb_sram_addr);
+
+ /* Synchronize the DMA buffer with the CPU buffer if necessary. */
+ if (dir == PCI_DMA_FROMDEVICE)
+ memcpy (mapping->cpu_addr, mb_sram_addr, size);
+ else if (dir == PCI_DMA_TODEVICE)
+ ; /* nothing to do */
+ else
+ panic("pci_dma_sync_single: unsupported sync dir: %d", dir);
+}
+
+void
+pci_dma_sync_single_for_device (struct pci_dev *pdev, dma_addr_t dma_addr, size_t size,
+ int dir)
+{
+ void *mb_sram_addr = PCI_TO_MB_SRAM (dma_addr);
+ struct dma_mapping *mapping = find_dma_mapping (mb_sram_addr);
+
+ /* Synchronize the DMA buffer with the CPU buffer if necessary. */
+ if (dir == PCI_DMA_FROMDEVICE)
+ ; /* nothing to do */
+ else if (dir == PCI_DMA_TODEVICE)
+ memcpy (mb_sram_addr, mapping->cpu_addr, size);
+ else
+ panic("pci_dma_sync_single: unsupported sync dir: %d", dir);
+}
+
+
+/* Scatter-gather PCI DMA mappings. */
+
+/* Do multiple DMA mappings at once. */
+int
+pci_map_sg (struct pci_dev *pdev, struct scatterlist *sg, int sg_len, int dir)
+{
+ BUG ();
+ return 0;
+}
+
+/* Unmap multiple DMA mappings at once. */
+void
+pci_unmap_sg (struct pci_dev *pdev, struct scatterlist *sg, int sg_len,int dir)
+{
+ BUG ();
+}
+
+/* Make physical memory consistent for a set of streaming mode DMA
+ translations after a transfer. The same as pci_dma_sync_single_* but
+ for a scatter-gather list, same rules and usage. */
+
+void
+pci_dma_sync_sg_for_cpu (struct pci_dev *dev, struct scatterlist *sg, int sg_len,
+ int dir)
+{
+ BUG ();
+}
+
+void
+pci_dma_sync_sg_for_device (struct pci_dev *dev, struct scatterlist *sg, int sg_len,
+ int dir)
+{
+ BUG ();
+}
+
+
+/* PCI mem mapping. */
+
+/* Allocate and map kernel buffer using consistent mode DMA for PCI
+ device. Returns non-NULL cpu-view pointer to the buffer if
+ successful and sets *DMA_ADDR to the pci side dma address as well,
+ else DMA_ADDR is undefined. */
+void *
+pci_alloc_consistent (struct pci_dev *pdev, size_t size, dma_addr_t *dma_addr)
+{
+ void *mb_sram_mem = alloc_mb_sram (size);
+ if (mb_sram_mem)
+ *dma_addr = MB_SRAM_TO_PCI (mb_sram_mem);
+ return mb_sram_mem;
+}
+
+/* Free and unmap a consistent DMA buffer. CPU_ADDR and DMA_ADDR must
+ be values that were returned from pci_alloc_consistent. SIZE must be
+ the same as what as passed into pci_alloc_consistent. References to
+ the memory and mappings assosciated with CPU_ADDR or DMA_ADDR past
+ this call are illegal. */
+void
+pci_free_consistent (struct pci_dev *pdev, size_t size, void *cpu_addr,
+ dma_addr_t dma_addr)
+{
+ void *mb_sram_mem = PCI_TO_MB_SRAM (dma_addr);
+ free_mb_sram (mb_sram_mem, size);
+}
+
+
+/* symbol exports (for modules) */
+
+EXPORT_SYMBOL (pci_map_single);
+EXPORT_SYMBOL (pci_unmap_single);
+EXPORT_SYMBOL (pci_alloc_consistent);
+EXPORT_SYMBOL (pci_free_consistent);
+EXPORT_SYMBOL (pci_dma_sync_single_for_cpu);
+EXPORT_SYMBOL (pci_dma_sync_single_for_device);
diff --git a/arch/v850/kernel/rte_me2_cb.c b/arch/v850/kernel/rte_me2_cb.c
new file mode 100644
index 0000000..faaf3d9
--- /dev/null
+++ b/arch/v850/kernel/rte_me2_cb.c
@@ -0,0 +1,300 @@
+/*
+ * arch/v850/kernel/rte_me2_cb.c -- Midas labs RTE-V850E/ME2-CB board
+ *
+ * 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/config.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/bootmem.h>
+#include <linux/irq.h>
+#include <linux/fs.h>
+#include <linux/major.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+
+#include <asm/atomic.h>
+#include <asm/page.h>
+#include <asm/me2.h>
+#include <asm/rte_me2_cb.h>
+#include <asm/machdep.h>
+#include <asm/v850e_intc.h>
+#include <asm/v850e_cache.h>
+#include <asm/irq.h>
+
+#include "mach.h"
+
+extern unsigned long *_intv_start;
+extern unsigned long *_intv_end;
+
+/* LED access routines. */
+extern unsigned read_leds (int pos, char *buf, int len);
+extern unsigned write_leds (int pos, const char *buf, int len);
+
+
+/* SDRAM are almost contiguous (with a small hole in between;
+ see mach_reserve_bootmem for details), so just use both as one big area. */
+#define RAM_START SDRAM_ADDR
+#define RAM_END (SDRAM_ADDR + SDRAM_SIZE)
+
+
+void __init mach_get_physical_ram (unsigned long *ram_start,
+ unsigned long *ram_len)
+{
+ *ram_start = RAM_START;
+ *ram_len = RAM_END - RAM_START;
+}
+
+void mach_gettimeofday (struct timespec *tv)
+{
+ tv->tv_sec = 0;
+ tv->tv_nsec = 0;
+}
+
+/* Called before configuring an on-chip UART. */
+void rte_me2_cb_uart_pre_configure (unsigned chan,
+ unsigned cflags, unsigned baud)
+{
+ /* The RTE-V850E/ME2-CB connects some general-purpose I/O
+ pins on the CPU to the RTS/CTS lines of UARTB channel 0's
+ serial connection.
+ I/O pins P21 and P22 are RTS and CTS respectively. */
+ if (chan == 0) {
+ /* Put P21 & P22 in I/O port mode. */
+ ME2_PORT2_PMC &= ~0x6;
+ /* Make P21 and output, and P22 an input. */
+ ME2_PORT2_PM = (ME2_PORT2_PM & ~0xC) | 0x4;
+ }
+
+ me2_uart_pre_configure (chan, cflags, baud);
+}
+
+void __init mach_init_irqs (void)
+{
+ /* Initialize interrupts. */
+ me2_init_irqs ();
+ rte_me2_cb_init_irqs ();
+}
+
+#ifdef CONFIG_ROM_KERNEL
+/* Initialization for kernel in ROM. */
+static inline rom_kernel_init (void)
+{
+ /* If the kernel is in ROM, we have to copy any initialized data
+ from ROM into RAM. */
+ extern unsigned long _data_load_start, _sdata, _edata;
+ register unsigned long *src = &_data_load_start;
+ register unsigned long *dst = &_sdata, *end = &_edata;
+
+ while (dst != end)
+ *dst++ = *src++;
+}
+#endif /* CONFIG_ROM_KERNEL */
+
+static void install_interrupt_vectors (void)
+{
+ unsigned long *p1, *p2;
+
+ ME2_IRAMM = 0x03; /* V850E/ME2 iRAM write mode */
+
+ /* vector copy to iRAM */
+ p1 = (unsigned long *)0; /* v85x vector start */
+ p2 = (unsigned long *)&_intv_start;
+ while (p2 < (unsigned long *)&_intv_end)
+ *p1++ = *p2++;
+
+ ME2_IRAMM = 0x00; /* V850E/ME2 iRAM read mode */
+}
+
+/* CompactFlash */
+
+static void cf_power_on (void)
+{
+ /* CF card detected? */
+ if (CB_CF_STS0 & 0x0030)
+ return;
+
+ CB_CF_REG0 = 0x0002; /* reest on */
+ mdelay (10);
+ CB_CF_REG0 = 0x0003; /* power on */
+ mdelay (10);
+ CB_CF_REG0 = 0x0001; /* reset off */
+ mdelay (10);
+}
+
+static void cf_power_off (void)
+{
+ CB_CF_REG0 = 0x0003; /* power on */
+ mdelay (10);
+ CB_CF_REG0 = 0x0002; /* reest on */
+ mdelay (10);
+}
+
+void __init mach_early_init (void)
+{
+ install_interrupt_vectors ();
+
+ /* CS1 SDRAM instruction cache enable */
+ v850e_cache_enable (0x04, 0x03, 0);
+
+ rte_cb_early_init ();
+
+ /* CompactFlash power on */
+ cf_power_on ();
+
+#if defined (CONFIG_ROM_KERNEL)
+ rom_kernel_init ();
+#endif
+}
+
+
+/* RTE-V850E/ME2-CB Programmable Interrupt Controller. */
+
+static struct cb_pic_irq_init cb_pic_irq_inits[] = {
+ { "CB_EXTTM0", IRQ_CB_EXTTM0, 1, 1, 6 },
+ { "CB_EXTSIO", IRQ_CB_EXTSIO, 1, 1, 6 },
+ { "CB_TOVER", IRQ_CB_TOVER, 1, 1, 6 },
+ { "CB_GINT0", IRQ_CB_GINT0, 1, 1, 6 },
+ { "CB_USB", IRQ_CB_USB, 1, 1, 6 },
+ { "CB_LANC", IRQ_CB_LANC, 1, 1, 6 },
+ { "CB_USB_VBUS_ON", IRQ_CB_USB_VBUS_ON, 1, 1, 6 },
+ { "CB_USB_VBUS_OFF", IRQ_CB_USB_VBUS_OFF, 1, 1, 6 },
+ { "CB_EXTTM1", IRQ_CB_EXTTM1, 1, 1, 6 },
+ { "CB_EXTTM2", IRQ_CB_EXTTM2, 1, 1, 6 },
+ { 0 }
+};
+#define NUM_CB_PIC_IRQ_INITS \
+ ((sizeof cb_pic_irq_inits / sizeof cb_pic_irq_inits[0]) - 1)
+
+static struct hw_interrupt_type cb_pic_hw_itypes[NUM_CB_PIC_IRQ_INITS];
+static unsigned char cb_pic_active_irqs = 0;
+
+void __init rte_me2_cb_init_irqs (void)
+{
+ cb_pic_init_irq_types (cb_pic_irq_inits, cb_pic_hw_itypes);
+
+ /* Initalize on board PIC1 (not PIC0) enable */
+ CB_PIC_INT0M = 0x0000;
+ CB_PIC_INT1M = 0x0000;
+ CB_PIC_INTR = 0x0000;
+ CB_PIC_INTEN |= CB_PIC_INT1EN;
+
+ ME2_PORT2_PMC |= 0x08; /* INTP23/SCK1 mode */
+ ME2_PORT2_PFC &= ~0x08; /* INTP23 mode */
+ ME2_INTR(2) &= ~0x08; /* INTP23 falling-edge detect */
+ ME2_INTF(2) &= ~0x08; /* " */
+
+ rte_cb_init_irqs (); /* gbus &c */
+}
+
+
+/* Enable interrupt handling for interrupt IRQ. */
+void cb_pic_enable_irq (unsigned irq)
+{
+ CB_PIC_INT1M |= 1 << (irq - CB_PIC_BASE_IRQ);
+}
+
+void cb_pic_disable_irq (unsigned irq)
+{
+ CB_PIC_INT1M &= ~(1 << (irq - CB_PIC_BASE_IRQ));
+}
+
+void cb_pic_shutdown_irq (unsigned irq)
+{
+ cb_pic_disable_irq (irq);
+
+ if (--cb_pic_active_irqs == 0)
+ free_irq (IRQ_CB_PIC, 0);
+
+ CB_PIC_INT1M &= ~(1 << (irq - CB_PIC_BASE_IRQ));
+}
+
+static irqreturn_t cb_pic_handle_irq (int irq, void *dev_id,
+ struct pt_regs *regs)
+{
+ irqreturn_t rval = IRQ_NONE;
+ unsigned status = CB_PIC_INTR;
+ unsigned enable = CB_PIC_INT1M;
+
+ /* Only pay attention to enabled interrupts. */
+ status &= enable;
+
+ CB_PIC_INTEN &= ~CB_PIC_INT1EN;
+
+ if (status) {
+ unsigned mask = 1;
+
+ irq = CB_PIC_BASE_IRQ;
+ do {
+ /* There's an active interrupt, find out which one,
+ and call its handler. */
+ while (! (status & mask)) {
+ irq++;
+ mask <<= 1;
+ }
+ status &= ~mask;
+
+ CB_PIC_INTR = mask;
+
+ /* Recursively call handle_irq to handle it. */
+ handle_irq (irq, regs);
+ rval = IRQ_HANDLED;
+ } while (status);
+ }
+
+ CB_PIC_INTEN |= CB_PIC_INT1EN;
+
+ return rval;
+}
+
+
+static void irq_nop (unsigned irq) { }
+
+static unsigned cb_pic_startup_irq (unsigned irq)
+{
+ int rval;
+
+ if (cb_pic_active_irqs == 0) {
+ rval = request_irq (IRQ_CB_PIC, cb_pic_handle_irq,
+ SA_INTERRUPT, "cb_pic_handler", 0);
+ if (rval != 0)
+ return rval;
+ }
+
+ cb_pic_active_irqs++;
+
+ cb_pic_enable_irq (irq);
+
+ return 0;
+}
+
+/* Initialize HW_IRQ_TYPES for INTC-controlled irqs described in array
+ INITS (which is terminated by an entry with the name field == 0). */
+void __init cb_pic_init_irq_types (struct cb_pic_irq_init *inits,
+ struct hw_interrupt_type *hw_irq_types)
+{
+ struct cb_pic_irq_init *init;
+ for (init = inits; init->name; init++) {
+ struct hw_interrupt_type *hwit = hw_irq_types++;
+
+ hwit->typename = init->name;
+
+ hwit->startup = cb_pic_startup_irq;
+ hwit->shutdown = cb_pic_shutdown_irq;
+ hwit->enable = cb_pic_enable_irq;
+ hwit->disable = cb_pic_disable_irq;
+ hwit->ack = irq_nop;
+ hwit->end = irq_nop;
+
+ /* Initialize kernel IRQ infrastructure for this interrupt. */
+ init_irq_handlers(init->base, init->num, init->interval, hwit);
+ }
+}
diff --git a/arch/v850/kernel/rte_me2_cb.ld b/arch/v850/kernel/rte_me2_cb.ld
new file mode 100644
index 0000000..cf07660
--- /dev/null
+++ b/arch/v850/kernel/rte_me2_cb.ld
@@ -0,0 +1,30 @@
+/* Linker script for the Midas labs RTE-V850E/ME2-CB evaluation board
+ (CONFIG_RTE_CB_ME2), with kernel in SDRAM. */
+
+MEMORY {
+ /* 128Kbyte of IRAM */
+ IRAM : ORIGIN = 0x00000000, LENGTH = 0x00020000
+
+ /* 32MB of SDRAM. */
+ SDRAM : ORIGIN = SDRAM_ADDR, LENGTH = SDRAM_SIZE
+}
+
+#define KRAM SDRAM
+
+SECTIONS {
+ .text : {
+ __kram_start = . ;
+ TEXT_CONTENTS
+ INTV_CONTENTS /* copy to iRAM (0x0-0x620) */
+ } > KRAM
+
+ .data : {
+ DATA_CONTENTS
+ BSS_CONTENTS
+ RAMK_INIT_CONTENTS
+ __kram_end = . ;
+ BOOTMAP_CONTENTS
+ } > KRAM
+
+ .root ALIGN (4096) : { ROOT_FS_CONTENTS } > SDRAM
+}
diff --git a/arch/v850/kernel/rte_nb85e_cb-multi.ld b/arch/v850/kernel/rte_nb85e_cb-multi.ld
new file mode 100644
index 0000000..de347b4
--- /dev/null
+++ b/arch/v850/kernel/rte_nb85e_cb-multi.ld
@@ -0,0 +1,57 @@
+/* Linker script for the Midas labs RTE-NB85E-CB evaluation board
+ (CONFIG_RTE_CB_NB85E), with the Multi debugger ROM monitor . */
+
+MEMORY {
+ /* 1MB of SRAM; we can't use the last 96KB, because it's used by
+ the monitor scratch-RAM. This memory is mirrored 4 times. */
+ SRAM : ORIGIN = SRAM_ADDR, LENGTH = (SRAM_SIZE - MON_SCRATCH_SIZE)
+ /* Monitor scratch RAM; only the interrupt vectors should go here. */
+ MRAM : ORIGIN = MON_SCRATCH_ADDR, LENGTH = MON_SCRATCH_SIZE
+ /* 16MB of SDRAM. */
+ SDRAM : ORIGIN = SDRAM_ADDR, LENGTH = SDRAM_SIZE
+}
+
+#ifdef CONFIG_RTE_CB_NB85E_KSRAM
+# define KRAM SRAM
+#else
+# define KRAM SDRAM
+#endif
+
+SECTIONS {
+ /* We can't use RAMK_KRAM_CONTENTS because that puts the whole
+ kernel in a single ELF segment, and the Multi debugger (which
+ we use to load the kernel) appears to have bizarre problems
+ dealing with it. */
+
+ .text : {
+ __kram_start = . ;
+ TEXT_CONTENTS
+ } > KRAM
+
+ .data : {
+ DATA_CONTENTS
+ BSS_CONTENTS
+ RAMK_INIT_CONTENTS
+ __kram_end = . ;
+ BOOTMAP_CONTENTS
+
+ /* The address at which the interrupt vectors are initially
+ loaded by the loader. We can't load the interrupt vectors
+ directly into their target location, because the monitor
+ ROM for the GHS Multi debugger barfs if we try.
+ 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 have the
+ `mach_early_init' function for Midas boards do the
+ necessary copying and relocation at runtime (this section
+ basically only contains `jr' instructions, so it's not
+ that hard). */
+ . = ALIGN (0x10) ;
+ __intv_load_start = . ;
+ INTV_CONTENTS
+ } > KRAM
+
+ .root ALIGN (4096) : { ROOT_FS_CONTENTS } > SDRAM
+}
diff --git a/arch/v850/kernel/rte_nb85e_cb.c b/arch/v850/kernel/rte_nb85e_cb.c
new file mode 100644
index 0000000..990b20b
--- /dev/null
+++ b/arch/v850/kernel/rte_nb85e_cb.c
@@ -0,0 +1,82 @@
+/*
+ * arch/v850/kernel/rte_nb85e_cb.c -- Midas labs RTE-V850E/NB85E-CB board
+ *
+ * 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/config.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/mm.h>
+#include <linux/swap.h>
+#include <linux/bootmem.h>
+#include <linux/irq.h>
+
+#include <asm/atomic.h>
+#include <asm/page.h>
+#include <asm/v850e.h>
+#include <asm/rte_nb85e_cb.h>
+
+#include "mach.h"
+
+void __init mach_early_init (void)
+{
+ /* Configure caching; some possible settings:
+
+ BHC = 0x0000, DCC = 0x0000 -- all caching disabled
+ BHC = 0x0040, DCC = 0x0000 -- SDRAM: icache only
+ BHC = 0x0080, DCC = 0x0C00 -- SDRAM: write-back dcache only
+ BHC = 0x00C0, DCC = 0x0C00 -- SDRAM: icache + write-back dcache
+ BHC = 0x00C0, DCC = 0x0800 -- SDRAM: icache + write-thru dcache
+
+ We can only cache SDRAM (we can't use cache SRAM because it's in
+ the same memory region as the on-chip RAM and I/O space).
+
+ Unfortunately, the dcache seems to be buggy, so we only use the
+ icache for now. */
+ v850e_cache_enable (0x0040 /*BHC*/, 0x0003 /*ICC*/, 0x0000 /*DCC*/);
+
+ rte_cb_early_init ();
+}
+
+void __init mach_get_physical_ram (unsigned long *ram_start,
+ unsigned long *ram_len)
+{
+ /* We just use SDRAM here. */
+ *ram_start = SDRAM_ADDR;
+ *ram_len = SDRAM_SIZE;
+}
+
+void mach_gettimeofday (struct timespec *tv)
+{
+ tv->tv_sec = 0;
+ tv->tv_nsec = 0;
+}
+
+/* Called before configuring an on-chip UART. */
+void rte_nb85e_cb_uart_pre_configure (unsigned chan,
+ unsigned cflags, unsigned baud)
+{
+ /* The RTE-NB85E-CB connects some general-purpose I/O pins on the
+ CPU to the RTS/CTS lines the UART's serial connection, as follows:
+ P00 = CTS (in), P01 = DSR (in), P02 = RTS (out), P03 = DTR (out). */
+
+ TEG_PORT0_PM = 0x03; /* P00 and P01 inputs, P02 and P03 outputs */
+ TEG_PORT0_IO = 0x03; /* Accept input */
+
+ /* Do pre-configuration for the actual UART. */
+ teg_uart_pre_configure (chan, cflags, baud);
+}
+
+void __init mach_init_irqs (void)
+{
+ teg_init_irqs ();
+ rte_cb_init_irqs ();
+}
diff --git a/arch/v850/kernel/rte_nb85e_cb.ld b/arch/v850/kernel/rte_nb85e_cb.ld
new file mode 100644
index 0000000..b672f48
--- /dev/null
+++ b/arch/v850/kernel/rte_nb85e_cb.ld
@@ -0,0 +1,22 @@
+/* Linker script for the Midas labs RTE-NB85E-CB evaluation board
+ (CONFIG_RTE_CB_NB85E). */
+
+MEMORY {
+ LOW : ORIGIN = 0x0, LENGTH = 0x00100000
+ /* 1MB of SRAM This memory is mirrored 4 times. */
+ SRAM : ORIGIN = SRAM_ADDR, LENGTH = SRAM_SIZE
+ /* 16MB of SDRAM. */
+ SDRAM : ORIGIN = SDRAM_ADDR, LENGTH = SDRAM_SIZE
+}
+
+#ifdef CONFIG_RTE_CB_NB85E_KSRAM
+# define KRAM SRAM
+#else
+# define KRAM SDRAM
+#endif
+
+SECTIONS {
+ .intv : { INTV_CONTENTS } > LOW
+ .sram : { RAMK_KRAM_CONTENTS } > KRAM
+ .root : { ROOT_FS_CONTENTS } > SDRAM
+}
diff --git a/arch/v850/kernel/semaphore.c b/arch/v850/kernel/semaphore.c
new file mode 100644
index 0000000..fc89fd6
--- /dev/null
+++ b/arch/v850/kernel/semaphore.c
@@ -0,0 +1,166 @@
+/*
+ * arch/v850/kernel/semaphore.c -- Semaphore support
+ *
+ * Copyright (C) 1998-2000 IBM Corporation
+ * Copyright (C) 1999 Linus Torvalds
+ *
+ * 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.
+ *
+ * This file is a copy of the s390 version, arch/s390/kernel/semaphore.c
+ * Author(s): Martin Schwidefsky
+ * which was derived from the i386 version, linux/arch/i386/kernel/semaphore.c
+ */
+
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+
+#include <asm/semaphore.h>
+
+/*
+ * Semaphores are implemented using a two-way counter:
+ * The "count" variable is decremented for each process
+ * that tries to acquire the semaphore, while the "sleeping"
+ * variable is a count of such acquires.
+ *
+ * Notably, the inline "up()" and "down()" functions can
+ * efficiently test if they need to do any extra work (up
+ * needs to do something only if count was negative before
+ * the increment operation.
+ *
+ * "sleeping" and the contention routine ordering is
+ * protected by the semaphore spinlock.
+ *
+ * Note that these functions are only called when there is
+ * contention on the lock, and as such all this is the
+ * "non-critical" part of the whole semaphore business. The
+ * critical part is the inline stuff in <asm/semaphore.h>
+ * where we want to avoid any extra jumps and calls.
+ */
+
+/*
+ * Logic:
+ * - only on a boundary condition do we need to care. When we go
+ * from a negative count to a non-negative, we wake people up.
+ * - when we go from a non-negative count to a negative do we
+ * (a) synchronize with the "sleeper" count and (b) make sure
+ * that we're on the wakeup list before we synchronize so that
+ * we cannot lose wakeup events.
+ */
+
+void __up(struct semaphore *sem)
+{
+ wake_up(&sem->wait);
+}
+
+static DEFINE_SPINLOCK(semaphore_lock);
+
+void __sched __down(struct semaphore * sem)
+{
+ struct task_struct *tsk = current;
+ DECLARE_WAITQUEUE(wait, tsk);
+ tsk->state = TASK_UNINTERRUPTIBLE;
+ add_wait_queue_exclusive(&sem->wait, &wait);
+
+ spin_lock_irq(&semaphore_lock);
+ sem->sleepers++;
+ for (;;) {
+ int sleepers = sem->sleepers;
+
+ /*
+ * Add "everybody else" into it. They aren't
+ * playing, because we own the spinlock.
+ */
+ if (!atomic_add_negative(sleepers - 1, &sem->count)) {
+ sem->sleepers = 0;
+ break;
+ }
+ sem->sleepers = 1; /* us - see -1 above */
+ spin_unlock_irq(&semaphore_lock);
+
+ schedule();
+ tsk->state = TASK_UNINTERRUPTIBLE;
+ spin_lock_irq(&semaphore_lock);
+ }
+ spin_unlock_irq(&semaphore_lock);
+ remove_wait_queue(&sem->wait, &wait);
+ tsk->state = TASK_RUNNING;
+ wake_up(&sem->wait);
+}
+
+int __sched __down_interruptible(struct semaphore * sem)
+{
+ int retval = 0;
+ struct task_struct *tsk = current;
+ DECLARE_WAITQUEUE(wait, tsk);
+ tsk->state = TASK_INTERRUPTIBLE;
+ add_wait_queue_exclusive(&sem->wait, &wait);
+
+ spin_lock_irq(&semaphore_lock);
+ sem->sleepers ++;
+ for (;;) {
+ int sleepers = sem->sleepers;
+
+ /*
+ * With signals pending, this turns into
+ * the trylock failure case - we won't be
+ * sleeping, and we* can't get the lock as
+ * it has contention. Just correct the count
+ * and exit.
+ */
+ if (signal_pending(current)) {
+ retval = -EINTR;
+ sem->sleepers = 0;
+ atomic_add(sleepers, &sem->count);
+ break;
+ }
+
+ /*
+ * Add "everybody else" into it. They aren't
+ * playing, because we own the spinlock. The
+ * "-1" is because we're still hoping to get
+ * the lock.
+ */
+ if (!atomic_add_negative(sleepers - 1, &sem->count)) {
+ sem->sleepers = 0;
+ break;
+ }
+ sem->sleepers = 1; /* us - see -1 above */
+ spin_unlock_irq(&semaphore_lock);
+
+ schedule();
+ tsk->state = TASK_INTERRUPTIBLE;
+ spin_lock_irq(&semaphore_lock);
+ }
+ spin_unlock_irq(&semaphore_lock);
+ tsk->state = TASK_RUNNING;
+ remove_wait_queue(&sem->wait, &wait);
+ wake_up(&sem->wait);
+ return retval;
+}
+
+/*
+ * Trylock failed - make sure we correct for
+ * having decremented the count.
+ */
+int __down_trylock(struct semaphore * sem)
+{
+ unsigned long flags;
+ int sleepers;
+
+ spin_lock_irqsave(&semaphore_lock, flags);
+ sleepers = sem->sleepers + 1;
+ sem->sleepers = 0;
+
+ /*
+ * Add "everybody else" and us into it. They aren't
+ * playing, because we own the spinlock.
+ */
+ if (!atomic_add_negative(sleepers, &sem->count))
+ wake_up(&sem->wait);
+
+ spin_unlock_irqrestore(&semaphore_lock, flags);
+ return 1;
+}
diff --git a/arch/v850/kernel/setup.c b/arch/v850/kernel/setup.c
new file mode 100644
index 0000000..c41d72b
--- /dev/null
+++ b/arch/v850/kernel/setup.c
@@ -0,0 +1,286 @@
+/*
+ * arch/v850/kernel/setup.c -- Arch-dependent initialization functions
+ *
+ * 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/mm.h>
+#include <linux/bootmem.h>
+#include <linux/swap.h> /* we don't have swap, but for nr_free_pages */
+#include <linux/irq.h>
+#include <linux/reboot.h>
+#include <linux/personality.h>
+#include <linux/major.h>
+#include <linux/root_dev.h>
+#include <linux/mtd/mtd.h>
+#include <linux/init.h>
+
+#include <asm/irq.h>
+#include <asm/setup.h>
+
+#include "mach.h"
+
+/* These symbols are all defined in the linker map to delineate various
+ statically allocated regions of memory. */
+
+extern char _intv_start, _intv_end;
+/* `kram' is only used if the kernel uses part of normal user RAM. */
+extern char _kram_start __attribute__ ((__weak__));
+extern char _kram_end __attribute__ ((__weak__));
+extern char _init_start, _init_end;
+extern char _bootmap;
+extern char _stext, _etext, _sdata, _edata, _sbss, _ebss;
+/* Many platforms use an embedded root image. */
+extern char _root_fs_image_start __attribute__ ((__weak__));
+extern char _root_fs_image_end __attribute__ ((__weak__));
+
+
+char command_line[COMMAND_LINE_SIZE];
+
+/* Memory not used by the kernel. */
+static unsigned long total_ram_pages;
+
+/* System RAM. */
+static unsigned long ram_start = 0, ram_len = 0;
+
+
+#define ADDR_TO_PAGE_UP(x) ((((unsigned long)x) + PAGE_SIZE-1) >> PAGE_SHIFT)
+#define ADDR_TO_PAGE(x) (((unsigned long)x) >> PAGE_SHIFT)
+#define PAGE_TO_ADDR(x) (((unsigned long)x) << PAGE_SHIFT)
+
+static void init_mem_alloc (unsigned long ram_start, unsigned long ram_len);
+
+void set_mem_root (void *addr, size_t len, char *cmd_line);
+
+
+void __init setup_arch (char **cmdline)
+{
+ /* Keep a copy of command line */
+ *cmdline = command_line;
+ memcpy (saved_command_line, command_line, COMMAND_LINE_SIZE);
+ saved_command_line[COMMAND_LINE_SIZE - 1] = '\0';
+
+ console_verbose ();
+
+ init_mm.start_code = (unsigned long) &_stext;
+ init_mm.end_code = (unsigned long) &_etext;
+ init_mm.end_data = (unsigned long) &_edata;
+ init_mm.brk = (unsigned long) &_kram_end;
+
+ /* Find out what mem this machine has. */
+ mach_get_physical_ram (&ram_start, &ram_len);
+ /* ... and tell the kernel about it. */
+ init_mem_alloc (ram_start, ram_len);
+
+ printk (KERN_INFO "CPU: %s\nPlatform: %s\n",
+ CPU_MODEL_LONG, PLATFORM_LONG);
+
+ /* do machine-specific setups. */
+ mach_setup (cmdline);
+
+#ifdef CONFIG_MTD
+ if (!ROOT_DEV && &_root_fs_image_end > &_root_fs_image_start)
+ set_mem_root (&_root_fs_image_start,
+ &_root_fs_image_end - &_root_fs_image_start,
+ *cmdline);
+#endif
+}
+
+void __init trap_init (void)
+{
+}
+
+#ifdef CONFIG_MTD
+/* Set the root filesystem to be the given memory region.
+ Some parameter may be appended to CMD_LINE. */
+void set_mem_root (void *addr, size_t len, char *cmd_line)
+{
+ /* The only way to pass info to the MTD slram driver is via
+ the command line. */
+ if (*cmd_line) {
+ cmd_line += strlen (cmd_line);
+ *cmd_line++ = ' ';
+ }
+ sprintf (cmd_line, "slram=root,0x%x,+0x%x", (u32)addr, (u32)len);
+
+ ROOT_DEV = MKDEV (MTD_BLOCK_MAJOR, 0);
+}
+#endif
+
+
+static void irq_nop (unsigned irq) { }
+static unsigned irq_zero (unsigned irq) { return 0; }
+
+static void nmi_end (unsigned irq)
+{
+ if (irq != IRQ_NMI (0)) {
+ printk (KERN_CRIT "NMI %d is unrecoverable; restarting...",
+ irq - IRQ_NMI (0));
+ machine_restart (0);
+ }
+}
+
+static struct hw_interrupt_type nmi_irq_type = {
+ "NMI",
+ irq_zero, /* startup */
+ irq_nop, /* shutdown */
+ irq_nop, /* enable */
+ irq_nop, /* disable */
+ irq_nop, /* ack */
+ nmi_end, /* end */
+};
+
+void __init init_IRQ (void)
+{
+ init_irq_handlers (0, NUM_MACH_IRQS, 1, 0);
+ init_irq_handlers (IRQ_NMI (0), NUM_NMIS, 1, &nmi_irq_type);
+ mach_init_irqs ();
+}
+
+
+void __init mem_init (void)
+{
+ max_mapnr = MAP_NR (ram_start + ram_len);
+
+ num_physpages = ADDR_TO_PAGE (ram_len);
+
+ total_ram_pages = free_all_bootmem ();
+
+ printk (KERN_INFO
+ "Memory: %luK/%luK available"
+ " (%luK kernel code, %luK data)\n",
+ PAGE_TO_ADDR (nr_free_pages()) / 1024,
+ ram_len / 1024,
+ ((unsigned long)&_etext - (unsigned long)&_stext) / 1024,
+ ((unsigned long)&_ebss - (unsigned long)&_sdata) / 1024);
+}
+
+void free_initmem (void)
+{
+ unsigned long ram_end = ram_start + ram_len;
+ unsigned long start = PAGE_ALIGN ((unsigned long)(&_init_start));
+
+ if (start >= ram_start && start < ram_end) {
+ unsigned long addr;
+ unsigned long end = PAGE_ALIGN ((unsigned long)(&_init_end));
+
+ if (end > ram_end)
+ end = ram_end;
+
+ printk("Freeing unused kernel memory: %ldK freed\n",
+ (end - start) / 1024);
+
+ for (addr = start; addr < end; addr += PAGE_SIZE) {
+ struct page *page = virt_to_page (addr);
+ ClearPageReserved (page);
+ set_page_count (page, 1);
+ __free_page (page);
+ total_ram_pages++;
+ }
+ }
+}
+
+
+/* Initialize the `bootmem allocator'. RAM_START and RAM_LEN identify
+ what RAM may be used. */
+static void __init
+init_bootmem_alloc (unsigned long ram_start, unsigned long ram_len)
+{
+ /* The part of the kernel that's in the same managed RAM space
+ used for general allocation. */
+ unsigned long kram_start = (unsigned long)&_kram_start;
+ unsigned long kram_end = (unsigned long)&_kram_end;
+ /* End of the managed RAM space. */
+ unsigned long ram_end = ram_start + ram_len;
+ /* Address range of the interrupt vector table. */
+ unsigned long intv_start = (unsigned long)&_intv_start;
+ unsigned long intv_end = (unsigned long)&_intv_end;
+ /* True if the interrupt vectors are in the managed RAM area. */
+ int intv_in_ram = (intv_end > ram_start && intv_start < ram_end);
+ /* True if the interrupt vectors are inside the kernel's RAM. */
+ int intv_in_kram = (intv_end > kram_start && intv_start < kram_end);
+ /* A pointer to an optional function that reserves platform-specific
+ memory regions. We declare the pointer `volatile' to avoid gcc
+ turning the call into a static call (the problem is that since
+ it's a weak symbol, a static call may end up trying to reference
+ the location 0x0, which is not always reachable). */
+ void (*volatile mrb) (void) = mach_reserve_bootmem;
+ /* The bootmem allocator's allocation bitmap. */
+ unsigned long bootmap = (unsigned long)&_bootmap;
+ unsigned long bootmap_len;
+
+ /* Round bootmap location up to next page. */
+ bootmap = PAGE_TO_ADDR (ADDR_TO_PAGE_UP (bootmap));
+
+ /* Initialize bootmem allocator. */
+ bootmap_len = init_bootmem_node (NODE_DATA (0),
+ ADDR_TO_PAGE (bootmap),
+ ADDR_TO_PAGE (PAGE_OFFSET),
+ ADDR_TO_PAGE (ram_end));
+
+ /* Now make the RAM actually allocatable (it starts out `reserved'). */
+ free_bootmem (ram_start, ram_len);
+
+ if (kram_end > kram_start)
+ /* Reserve the RAM part of the kernel's address space, so it
+ doesn't get allocated. */
+ reserve_bootmem (kram_start, kram_end - kram_start);
+
+ if (intv_in_ram && !intv_in_kram)
+ /* Reserve the interrupt vector space. */
+ reserve_bootmem (intv_start, intv_end - intv_start);
+
+ if (bootmap >= ram_start && bootmap < ram_end)
+ /* Reserve the bootmap space. */
+ reserve_bootmem (bootmap, bootmap_len);
+
+ /* Reserve the memory used by the root filesystem image if it's
+ in RAM. */
+ if (&_root_fs_image_end > &_root_fs_image_start
+ && (unsigned long)&_root_fs_image_start >= ram_start
+ && (unsigned long)&_root_fs_image_start < ram_end)
+ reserve_bootmem ((unsigned long)&_root_fs_image_start,
+ &_root_fs_image_end - &_root_fs_image_start);
+
+ /* Let the platform-dependent code reserve some too. */
+ if (mrb)
+ (*mrb) ();
+}
+
+/* Tell the kernel about what RAM it may use for memory allocation. */
+static void __init
+init_mem_alloc (unsigned long ram_start, unsigned long ram_len)
+{
+ unsigned i;
+ unsigned long zones_size[MAX_NR_ZONES];
+
+ init_bootmem_alloc (ram_start, ram_len);
+
+ for (i = 0; i < MAX_NR_ZONES; i++)
+ zones_size[i] = 0;
+
+ /* We stuff all the memory into one area, which includes the
+ initial gap from PAGE_OFFSET to ram_start. */
+ zones_size[ZONE_DMA]
+ = ADDR_TO_PAGE (ram_len + (ram_start - PAGE_OFFSET));
+
+ /* The allocator is very picky about the address of the first
+ allocatable page -- it must be at least as aligned as the
+ maximum allocation -- so try to detect cases where it will get
+ confused and signal them at compile time (this is a common
+ problem when porting to a new platform with ). There is a
+ similar runtime check in free_area_init_core. */
+#if ((PAGE_OFFSET >> PAGE_SHIFT) & ((1UL << (MAX_ORDER - 1)) - 1))
+#error MAX_ORDER is too large for given PAGE_OFFSET (use CONFIG_FORCE_MAX_ZONEORDER to change it)
+#endif
+ NODE_DATA(0)->node_mem_map = NULL;
+ free_area_init_node (0, NODE_DATA(0), zones_size,
+ ADDR_TO_PAGE (PAGE_OFFSET), 0);
+}
diff --git a/arch/v850/kernel/signal.c b/arch/v850/kernel/signal.c
new file mode 100644
index 0000000..37061e3
--- /dev/null
+++ b/arch/v850/kernel/signal.c
@@ -0,0 +1,525 @@
+/*
+ * arch/v850/kernel/signal.c -- Signal handling
+ *
+ * Copyright (C) 2001,02,03 NEC Electronics Corporation
+ * Copyright (C) 2001,02,03 Miles Bader <miles@gnu.org>
+ * Copyright (C) 1999,2000,2002 Niibe Yutaka & Kaz Kojima
+ * Copyright (C) 1991,1992 Linus Torvalds
+ *
+ * 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.
+ *
+ * 1997-11-28 Modified for POSIX.1b signals by Richard Henderson
+ *
+ * This file was derived from the sh version, arch/sh/kernel/signal.c
+ */
+
+#include <linux/mm.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/errno.h>
+#include <linux/wait.h>
+#include <linux/ptrace.h>
+#include <linux/unistd.h>
+#include <linux/stddef.h>
+#include <linux/personality.h>
+#include <linux/tty.h>
+
+#include <asm/ucontext.h>
+#include <asm/uaccess.h>
+#include <asm/pgtable.h>
+#include <asm/pgalloc.h>
+#include <asm/thread_info.h>
+#include <asm/cacheflush.h>
+
+#define DEBUG_SIG 0
+
+#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
+
+asmlinkage int do_signal(struct pt_regs *regs, sigset_t *oldset);
+
+/*
+ * Atomically swap in the new signal mask, and wait for a signal.
+ */
+asmlinkage int
+sys_sigsuspend(old_sigset_t mask, struct pt_regs *regs)
+{
+ sigset_t saveset;
+
+ mask &= _BLOCKABLE;
+ spin_lock_irq(&current->sighand->siglock);
+ saveset = current->blocked;
+ siginitset(&current->blocked, mask);
+ recalc_sigpending();
+ spin_unlock_irq(&current->sighand->siglock);
+
+ regs->gpr[GPR_RVAL] = -EINTR;
+ while (1) {
+ current->state = TASK_INTERRUPTIBLE;
+ schedule();
+ if (do_signal(regs, &saveset))
+ return -EINTR;
+ }
+}
+
+asmlinkage int
+sys_rt_sigsuspend(sigset_t *unewset, size_t sigsetsize,
+ struct pt_regs *regs)
+{
+ sigset_t saveset, newset;
+
+ /* XXX: Don't preclude handling different sized sigset_t's. */
+ if (sigsetsize != sizeof(sigset_t))
+ return -EINVAL;
+
+ if (copy_from_user(&newset, unewset, sizeof(newset)))
+ return -EFAULT;
+ sigdelsetmask(&newset, ~_BLOCKABLE);
+ spin_lock_irq(&current->sighand->siglock);
+ saveset = current->blocked;
+ current->blocked = newset;
+ recalc_sigpending();
+ spin_unlock_irq(&current->sighand->siglock);
+
+ regs->gpr[GPR_RVAL] = -EINTR;
+ while (1) {
+ current->state = TASK_INTERRUPTIBLE;
+ schedule();
+ if (do_signal(regs, &saveset))
+ return -EINTR;
+ }
+}
+
+asmlinkage int
+sys_sigaction(int sig, const struct old_sigaction *act,
+ struct old_sigaction *oact)
+{
+ struct k_sigaction new_ka, old_ka;
+ int ret;
+
+ if (act) {
+ old_sigset_t mask;
+ if (!access_ok(VERIFY_READ, act, sizeof(*act)) ||
+ __get_user(new_ka.sa.sa_handler, &act->sa_handler) ||
+ __get_user(new_ka.sa.sa_restorer, &act->sa_restorer))
+ return -EFAULT;
+ __get_user(new_ka.sa.sa_flags, &act->sa_flags);
+ __get_user(mask, &act->sa_mask);
+ siginitset(&new_ka.sa.sa_mask, mask);
+ }
+
+ ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL);
+
+ if (!ret && oact) {
+ if (!access_ok(VERIFY_WRITE, oact, sizeof(*oact)) ||
+ __put_user(old_ka.sa.sa_handler, &oact->sa_handler) ||
+ __put_user(old_ka.sa.sa_restorer, &oact->sa_restorer))
+ return -EFAULT;
+ __put_user(old_ka.sa.sa_flags, &oact->sa_flags);
+ __put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask);
+ }
+
+ return ret;
+}
+
+asmlinkage int
+sys_sigaltstack(const stack_t *uss, stack_t *uoss,
+ struct pt_regs *regs)
+{
+ return do_sigaltstack(uss, uoss, regs->gpr[GPR_SP]);
+}
+
+
+/*
+ * Do a signal return; undo the signal stack.
+ */
+
+struct sigframe
+{
+ struct sigcontext sc;
+ unsigned long extramask[_NSIG_WORDS-1];
+ unsigned long tramp[2]; /* signal trampoline */
+};
+
+struct rt_sigframe
+{
+ struct siginfo info;
+ struct ucontext uc;
+ unsigned long tramp[2]; /* signal trampoline */
+};
+
+static int
+restore_sigcontext(struct pt_regs *regs, struct sigcontext *sc, int *rval_p)
+{
+ unsigned int err = 0;
+
+#define COPY(x) err |= __get_user(regs->x, &sc->regs.x)
+ COPY(gpr[0]); COPY(gpr[1]); COPY(gpr[2]); COPY(gpr[3]);
+ COPY(gpr[4]); COPY(gpr[5]); COPY(gpr[6]); COPY(gpr[7]);
+ COPY(gpr[8]); COPY(gpr[9]); COPY(gpr[10]); COPY(gpr[11]);
+ COPY(gpr[12]); COPY(gpr[13]); COPY(gpr[14]); COPY(gpr[15]);
+ COPY(gpr[16]); COPY(gpr[17]); COPY(gpr[18]); COPY(gpr[19]);
+ COPY(gpr[20]); COPY(gpr[21]); COPY(gpr[22]); COPY(gpr[23]);
+ COPY(gpr[24]); COPY(gpr[25]); COPY(gpr[26]); COPY(gpr[27]);
+ COPY(gpr[28]); COPY(gpr[29]); COPY(gpr[30]); COPY(gpr[31]);
+ COPY(pc); COPY(psw);
+ COPY(ctpc); COPY(ctpsw); COPY(ctbp);
+#undef COPY
+
+ return err;
+}
+
+asmlinkage int sys_sigreturn(struct pt_regs *regs)
+{
+ struct sigframe *frame = (struct sigframe *)regs->gpr[GPR_SP];
+ sigset_t set;
+ int rval;
+
+ if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
+ goto badframe;
+
+ if (__get_user(set.sig[0], &frame->sc.oldmask)
+ || (_NSIG_WORDS > 1
+ && __copy_from_user(&set.sig[1], &frame->extramask,
+ sizeof(frame->extramask))))
+ goto badframe;
+
+ sigdelsetmask(&set, ~_BLOCKABLE);
+ spin_lock_irq(&current->sighand->siglock);
+ current->blocked = set;
+ recalc_sigpending();
+ spin_unlock_irq(&current->sighand->siglock);
+
+ if (restore_sigcontext(regs, &frame->sc, &rval))
+ goto badframe;
+ return rval;
+
+badframe:
+ force_sig(SIGSEGV, current);
+ return 0;
+}
+
+asmlinkage int sys_rt_sigreturn(struct pt_regs *regs)
+{
+ struct rt_sigframe *frame = (struct rt_sigframe *)regs->gpr[GPR_SP];
+ sigset_t set;
+ stack_t st;
+ int rval;
+
+ if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
+ goto badframe;
+
+ if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set)))
+ goto badframe;
+
+ sigdelsetmask(&set, ~_BLOCKABLE);
+ spin_lock_irq(&current->sighand->siglock);
+ current->blocked = set;
+ recalc_sigpending();
+ spin_unlock_irq(&current->sighand->siglock);
+
+ if (restore_sigcontext(regs, &frame->uc.uc_mcontext, &rval))
+ goto badframe;
+
+ if (__copy_from_user(&st, &frame->uc.uc_stack, sizeof(st)))
+ goto badframe;
+ /* It is more difficult to avoid calling this function than to
+ call it and ignore errors. */
+ do_sigaltstack(&st, NULL, regs->gpr[GPR_SP]);
+
+ return rval;
+
+badframe:
+ force_sig(SIGSEGV, current);
+ return 0;
+}
+
+/*
+ * Set up a signal frame.
+ */
+
+static int
+setup_sigcontext(struct sigcontext *sc, struct pt_regs *regs,
+ unsigned long mask)
+{
+ int err = 0;
+
+#define COPY(x) err |= __put_user(regs->x, &sc->regs.x)
+ COPY(gpr[0]); COPY(gpr[1]); COPY(gpr[2]); COPY(gpr[3]);
+ COPY(gpr[4]); COPY(gpr[5]); COPY(gpr[6]); COPY(gpr[7]);
+ COPY(gpr[8]); COPY(gpr[9]); COPY(gpr[10]); COPY(gpr[11]);
+ COPY(gpr[12]); COPY(gpr[13]); COPY(gpr[14]); COPY(gpr[15]);
+ COPY(gpr[16]); COPY(gpr[17]); COPY(gpr[18]); COPY(gpr[19]);
+ COPY(gpr[20]); COPY(gpr[21]); COPY(gpr[22]); COPY(gpr[23]);
+ COPY(gpr[24]); COPY(gpr[25]); COPY(gpr[26]); COPY(gpr[27]);
+ COPY(gpr[28]); COPY(gpr[29]); COPY(gpr[30]); COPY(gpr[31]);
+ COPY(pc); COPY(psw);
+ COPY(ctpc); COPY(ctpsw); COPY(ctbp);
+#undef COPY
+
+ err |= __put_user(mask, &sc->oldmask);
+
+ return err;
+}
+
+/*
+ * Determine which stack to use..
+ */
+static inline void *
+get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, size_t frame_size)
+{
+ /* Default to using normal stack */
+ unsigned long sp = regs->gpr[GPR_SP];
+
+ if ((ka->sa.sa_flags & SA_ONSTACK) != 0 && ! on_sig_stack(sp))
+ sp = current->sas_ss_sp + current->sas_ss_size;
+
+ return (void *)((sp - frame_size) & -8UL);
+}
+
+static void setup_frame(int sig, struct k_sigaction *ka,
+ sigset_t *set, struct pt_regs *regs)
+{
+ struct sigframe *frame;
+ int err = 0;
+ int signal;
+
+ frame = get_sigframe(ka, regs, sizeof(*frame));
+
+ if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
+ goto give_sigsegv;
+
+ signal = current_thread_info()->exec_domain
+ && current_thread_info()->exec_domain->signal_invmap
+ && sig < 32
+ ? current_thread_info()->exec_domain->signal_invmap[sig]
+ : sig;
+
+ err |= setup_sigcontext(&frame->sc, regs, set->sig[0]);
+
+ if (_NSIG_WORDS > 1) {
+ err |= __copy_to_user(frame->extramask, &set->sig[1],
+ sizeof(frame->extramask));
+ }
+
+ /* Set up to return from userspace. If provided, use a stub
+ already in userspace. */
+ if (ka->sa.sa_flags & SA_RESTORER) {
+ regs->gpr[GPR_LP] = (unsigned long) ka->sa.sa_restorer;
+ } else {
+ /* Note, these encodings are _little endian_! */
+
+ /* addi __NR_sigreturn, r0, r12 */
+ err |= __put_user(0x6600 | (__NR_sigreturn << 16),
+ frame->tramp + 0);
+ /* trap 0 */
+ err |= __put_user(0x010007e0,
+ frame->tramp + 1);
+
+ regs->gpr[GPR_LP] = (unsigned long)frame->tramp;
+
+ flush_cache_sigtramp (regs->gpr[GPR_LP]);
+ }
+
+ if (err)
+ goto give_sigsegv;
+
+ /* Set up registers for signal handler. */
+ regs->pc = (v850_reg_t) ka->sa.sa_handler;
+ regs->gpr[GPR_SP] = (v850_reg_t)frame;
+ /* Signal handler args: */
+ regs->gpr[GPR_ARG0] = signal; /* arg 0: signum */
+ regs->gpr[GPR_ARG1] = (v850_reg_t)&frame->sc;/* arg 1: sigcontext */
+
+ set_fs(USER_DS);
+
+#if DEBUG_SIG
+ printk("SIG deliver (%s:%d): sp=%p pc=%08lx ra=%08lx\n",
+ current->comm, current->pid, frame, regs->pc, );
+#endif
+
+ return;
+
+give_sigsegv:
+ force_sigsegv(sig, current);
+}
+
+static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
+ sigset_t *set, struct pt_regs *regs)
+{
+ struct rt_sigframe *frame;
+ int err = 0;
+ int signal;
+
+ frame = get_sigframe(ka, regs, sizeof(*frame));
+
+ if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
+ goto give_sigsegv;
+
+ signal = current_thread_info()->exec_domain
+ && current_thread_info()->exec_domain->signal_invmap
+ && sig < 32
+ ? current_thread_info()->exec_domain->signal_invmap[sig]
+ : sig;
+
+ err |= copy_siginfo_to_user(&frame->info, info);
+
+ /* Create the ucontext. */
+ err |= __put_user(0, &frame->uc.uc_flags);
+ err |= __put_user(0, &frame->uc.uc_link);
+ err |= __put_user((void *)current->sas_ss_sp,
+ &frame->uc.uc_stack.ss_sp);
+ err |= __put_user(sas_ss_flags(regs->gpr[GPR_SP]),
+ &frame->uc.uc_stack.ss_flags);
+ err |= __put_user(current->sas_ss_size, &frame->uc.uc_stack.ss_size);
+ err |= setup_sigcontext(&frame->uc.uc_mcontext,
+ regs, set->sig[0]);
+ err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set));
+
+ /* Set up to return from userspace. If provided, use a stub
+ already in userspace. */
+ if (ka->sa.sa_flags & SA_RESTORER) {
+ regs->gpr[GPR_LP] = (unsigned long) ka->sa.sa_restorer;
+ } else {
+ /* Note, these encodings are _little endian_! */
+
+ /* addi __NR_sigreturn, r0, r12 */
+ err |= __put_user(0x6600 | (__NR_sigreturn << 16),
+ frame->tramp + 0);
+ /* trap 0 */
+ err |= __put_user(0x010007e0,
+ frame->tramp + 1);
+
+ regs->gpr[GPR_LP] = (unsigned long)frame->tramp;
+
+ flush_cache_sigtramp (regs->gpr[GPR_LP]);
+ }
+
+ if (err)
+ goto give_sigsegv;
+
+ /* Set up registers for signal handler. */
+ regs->pc = (v850_reg_t) ka->sa.sa_handler;
+ regs->gpr[GPR_SP] = (v850_reg_t)frame;
+ /* Signal handler args: */
+ regs->gpr[GPR_ARG0] = signal; /* arg 0: signum */
+ regs->gpr[GPR_ARG1] = (v850_reg_t)&frame->info; /* arg 1: siginfo */
+ regs->gpr[GPR_ARG2] = (v850_reg_t)&frame->uc; /* arg 2: ucontext */
+
+ set_fs(USER_DS);
+
+#if DEBUG_SIG
+ printk("SIG deliver (%s:%d): sp=%p pc=%08lx pr=%08lx\n",
+ current->comm, current->pid, frame, regs->pc, regs->pr);
+#endif
+
+ return;
+
+give_sigsegv:
+ force_sigsegv(sig, current);
+}
+
+/*
+ * OK, we're invoking a handler
+ */
+
+static void
+handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka,
+ sigset_t *oldset, struct pt_regs * regs)
+{
+ /* Are we from a system call? */
+ if (PT_REGS_SYSCALL (regs)) {
+ /* If so, check system call restarting.. */
+ switch (regs->gpr[GPR_RVAL]) {
+ case -ERESTART_RESTARTBLOCK:
+ current_thread_info()->restart_block.fn =
+ do_no_restart_syscall;
+ /* fall through */
+ case -ERESTARTNOHAND:
+ regs->gpr[GPR_RVAL] = -EINTR;
+ break;
+
+ case -ERESTARTSYS:
+ if (!(ka->sa.sa_flags & SA_RESTART)) {
+ regs->gpr[GPR_RVAL] = -EINTR;
+ break;
+ }
+ /* fallthrough */
+ case -ERESTARTNOINTR:
+ regs->gpr[12] = PT_REGS_SYSCALL (regs);
+ regs->pc -= 4; /* Size of `trap 0' insn. */
+ }
+
+ PT_REGS_SET_SYSCALL (regs, 0);
+ }
+
+ /* Set up the stack frame */
+ if (ka->sa.sa_flags & SA_SIGINFO)
+ setup_rt_frame(sig, ka, info, oldset, regs);
+ else
+ setup_frame(sig, ka, oldset, regs);
+
+ if (!(ka->sa.sa_flags & SA_NODEFER)) {
+ spin_lock_irq(&current->sighand->siglock);
+ sigorsets(&current->blocked,&current->blocked,&ka->sa.sa_mask);
+ sigaddset(&current->blocked,sig);
+ recalc_sigpending();
+ spin_unlock_irq(&current->sighand->siglock);
+ }
+}
+
+/*
+ * Note that 'init' is a special process: it doesn't get signals it doesn't
+ * want to handle. Thus you cannot kill init even with a SIGKILL even by
+ * mistake.
+ *
+ * Note that we go through the signals twice: once to check the signals that
+ * the kernel can handle, and then we build all the user-level signal handling
+ * stack-frames in one go after that.
+ */
+int do_signal(struct pt_regs *regs, sigset_t *oldset)
+{
+ siginfo_t info;
+ int signr;
+ struct k_sigaction ka;
+
+ /*
+ * We want the common case to go fast, which
+ * is why we may in certain cases get here from
+ * kernel mode. Just return without doing anything
+ * if so.
+ */
+ if (!user_mode(regs))
+ return 1;
+
+ if (!oldset)
+ oldset = &current->blocked;
+
+ signr = get_signal_to_deliver(&info, &ka, regs, NULL);
+ if (signr > 0) {
+ /* Whee! Actually deliver the signal. */
+ handle_signal(signr, &info, &ka, oldset, regs);
+ return 1;
+ }
+
+ /* Did we come from a system call? */
+ if (PT_REGS_SYSCALL (regs)) {
+ int rval = (int)regs->gpr[GPR_RVAL];
+ /* Restart the system call - no handlers present */
+ if (rval == -ERESTARTNOHAND
+ || rval == -ERESTARTSYS
+ || rval == -ERESTARTNOINTR)
+ {
+ regs->gpr[12] = PT_REGS_SYSCALL (regs);
+ regs->pc -= 4; /* Size of `trap 0' insn. */
+ }
+ else if (rval == -ERESTART_RESTARTBLOCK) {
+ regs->gpr[12] = __NR_restart_syscall;
+ regs->pc -= 4; /* Size of `trap 0' insn. */
+ }
+ }
+ return 0;
+}
diff --git a/arch/v850/kernel/sim.c b/arch/v850/kernel/sim.c
new file mode 100644
index 0000000..4f31da9
--- /dev/null
+++ b/arch/v850/kernel/sim.c
@@ -0,0 +1,179 @@
+/*
+ * arch/v850/kernel/sim.c -- Machine-specific stuff for GDB v850e simulator
+ *
+ * Copyright (C) 2001,02 NEC Corporation
+ * Copyright (C) 2001,02 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/config.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/mm.h>
+#include <linux/swap.h>
+#include <linux/bootmem.h>
+#include <linux/irq.h>
+
+#include <asm/atomic.h>
+#include <asm/page.h>
+#include <asm/machdep.h>
+#include <asm/simsyscall.h>
+
+#include "mach.h"
+
+/* The name of a file containing the root filesystem. */
+#define ROOT_FS "rootfs.image"
+
+extern void simcons_setup (void);
+extern void simcons_poll_ttys (void);
+extern void set_mem_root (void *addr, size_t len, char *cmd_line);
+
+static int read_file (const char *name,
+ unsigned long *addr, unsigned long *len,
+ const char **err);
+
+void __init mach_setup (char **cmdline)
+{
+ const char *err;
+ unsigned long root_dev_addr, root_dev_len;
+
+ simcons_setup ();
+
+ printk (KERN_INFO "Reading root filesystem: %s", ROOT_FS);
+
+ if (read_file (ROOT_FS, &root_dev_addr, &root_dev_len, &err)) {
+ printk (" (size %luK)\n", root_dev_len / 1024);
+ set_mem_root ((void *)root_dev_addr, (size_t)root_dev_len,
+ *cmdline);
+ } else
+ printk ("...%s failed!\n", err);
+}
+
+void mach_get_physical_ram (unsigned long *ram_start, unsigned long *ram_len)
+{
+ *ram_start = RAM_ADDR;
+ *ram_len = RAM_SIZE;
+}
+
+void __init mach_sched_init (struct irqaction *timer_action)
+{
+ /* ...do magic timer initialization?... */
+ mach_tick = simcons_poll_ttys;
+ setup_irq (0, timer_action);
+}
+
+
+static void irq_nop (unsigned irq) { }
+static unsigned irq_zero (unsigned irq) { return 0; }
+
+static struct hw_interrupt_type sim_irq_type = {
+ "IRQ",
+ irq_zero, /* startup */
+ irq_nop, /* shutdown */
+ irq_nop, /* enable */
+ irq_nop, /* disable */
+ irq_nop, /* ack */
+ irq_nop, /* end */
+};
+
+void __init mach_init_irqs (void)
+{
+ init_irq_handlers (0, NUM_MACH_IRQS, 1, &sim_irq_type);
+}
+
+
+void mach_gettimeofday (struct timespec *tv)
+{
+ long timeval[2], timezone[2];
+ int rval = V850_SIM_SYSCALL (gettimeofday, timeval, timezone);
+ if (rval == 0) {
+ tv->tv_sec = timeval[0];
+ tv->tv_nsec = timeval[1] * 1000;
+ }
+}
+
+void machine_restart (char *__unused)
+{
+ V850_SIM_SYSCALL (write, 1, "RESTART\n", 8);
+ V850_SIM_SYSCALL (exit, 0);
+}
+
+EXPORT_SYMBOL(machine_restart);
+
+void machine_halt (void)
+{
+ V850_SIM_SYSCALL (write, 1, "HALT\n", 5);
+ V850_SIM_SYSCALL (exit, 0);
+}
+
+EXPORT_SYMBOL(machine_halt);
+
+void machine_power_off (void)
+{
+ V850_SIM_SYSCALL (write, 1, "POWER OFF\n", 10);
+ V850_SIM_SYSCALL (exit, 0);
+}
+
+EXPORT_SYMBOL(machine_power_off);
+
+
+/* Load data from a file called NAME into ram. The address and length
+ of the data image are returned in ADDR and LEN. */
+static int __init
+read_file (const char *name,
+ unsigned long *addr, unsigned long *len,
+ const char **err)
+{
+ int rval, fd;
+ unsigned long cur, left;
+ /* Note this is not a normal stat buffer, it's an ad-hoc
+ structure defined by the simulator. */
+ unsigned long stat_buf[10];
+
+ /* Stat the file to find out the length. */
+ rval = V850_SIM_SYSCALL (stat, name, stat_buf);
+ if (rval < 0) {
+ if (err) *err = "stat";
+ return 0;
+ }
+ *len = stat_buf[4];
+
+ /* Open the file; `0' is O_RDONLY. */
+ fd = V850_SIM_SYSCALL (open, name, 0);
+ if (fd < 0) {
+ if (err) *err = "open";
+ return 0;
+ }
+
+ *addr = (unsigned long)alloc_bootmem(*len);
+ if (! *addr) {
+ V850_SIM_SYSCALL (close, fd);
+ if (err) *err = "alloc_bootmem";
+ return 0;
+ }
+
+ cur = *addr;
+ left = *len;
+ while (left > 0) {
+ int chunk = V850_SIM_SYSCALL (read, fd, cur, left);
+ if (chunk <= 0)
+ break;
+ cur += chunk;
+ left -= chunk;
+ }
+ V850_SIM_SYSCALL (close, fd);
+ if (left > 0) {
+ /* Some read failed. */
+ free_bootmem (*addr, *len);
+ if (err) *err = "read";
+ return 0;
+ }
+
+ return 1;
+}
diff --git a/arch/v850/kernel/sim.ld b/arch/v850/kernel/sim.ld
new file mode 100644
index 0000000..101885f
--- /dev/null
+++ b/arch/v850/kernel/sim.ld
@@ -0,0 +1,13 @@
+/* Linker script for the gdb v850e simulator (CONFIG_V850E_SIM). */
+
+MEMORY {
+ /* Interrupt vectors. */
+ INTV : ORIGIN = 0x0, LENGTH = 0xe0
+ /* Main RAM. */
+ RAM : ORIGIN = RAM_ADDR, LENGTH = RAM_SIZE
+}
+
+SECTIONS {
+ .intv : { INTV_CONTENTS } > INTV
+ .ram : { RAMK_KRAM_CONTENTS } > RAM
+}
diff --git a/arch/v850/kernel/sim85e2.c b/arch/v850/kernel/sim85e2.c
new file mode 100644
index 0000000..93a722b
--- /dev/null
+++ b/arch/v850/kernel/sim85e2.c
@@ -0,0 +1,201 @@
+/*
+ * arch/v850/kernel/sim85e2.c -- Machine-specific stuff for
+ * V850E2 RTL simulator
+ *
+ * Copyright (C) 2002,03 NEC Electronics Corporation
+ * Copyright (C) 2002,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/config.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/mm.h>
+#include <linux/swap.h>
+#include <linux/bootmem.h>
+#include <linux/irq.h>
+
+#include <asm/atomic.h>
+#include <asm/page.h>
+#include <asm/machdep.h>
+
+#include "mach.h"
+
+
+/* There are 4 possible areas we can use:
+
+ IRAM (1MB) is fast for instruction fetches, but slow for data
+ DRAM (1020KB) is fast for data, but slow for instructions
+ ERAM is cached, so should be fast for both insns and data
+ SDRAM is external DRAM, similar to ERAM
+*/
+
+#define INIT_MEMC_FOR_SDRAM
+#define USE_SDRAM_AREA
+#define KERNEL_IN_SDRAM_AREA
+
+#define DCACHE_MODE V850E2_CACHE_BTSC_DCM_WT
+/*#define DCACHE_MODE V850E2_CACHE_BTSC_DCM_WB_ALLOC*/
+
+#ifdef USE_SDRAM_AREA
+#define RAM_START SDRAM_ADDR
+#define RAM_END (SDRAM_ADDR + SDRAM_SIZE)
+#else
+/* When we use DRAM, we need to account for the fact that the end of it is
+ used for R0_RAM. */
+#define RAM_START DRAM_ADDR
+#define RAM_END R0_RAM_ADDR
+#endif
+
+
+extern void memcons_setup (void);
+
+
+#ifdef KERNEL_IN_SDRAM_AREA
+#define EARLY_INIT_SECTION_ATTR __attribute__ ((section (".early.text")))
+#else
+#define EARLY_INIT_SECTION_ATTR __init
+#endif
+
+void EARLY_INIT_SECTION_ATTR mach_early_init (void)
+{
+ /* The sim85e2 simulator tracks `undefined' values, so to make
+ debugging easier, we begin by zeroing out all otherwise
+ undefined registers. This is not strictly necessary.
+
+ The registers we zero are:
+ Every GPR except:
+ stack-pointer (r3)
+ task-pointer (r16)
+ our return addr (r31)
+ Every system register (SPR) that we know about except for
+ the PSW (SPR 5), which we zero except for the
+ disable-interrupts bit.
+ */
+
+ /* GPRs */
+ asm volatile (" mov r0, r1 ; mov r0, r2 ");
+ asm volatile ("mov r0, r4 ; mov r0, r5 ; mov r0, r6 ; mov r0, r7 ");
+ asm volatile ("mov r0, r8 ; mov r0, r9 ; mov r0, r10; mov r0, r11");
+ asm volatile ("mov r0, r12; mov r0, r13; mov r0, r14; mov r0, r15");
+ asm volatile (" mov r0, r17; mov r0, r18; mov r0, r19");
+ asm volatile ("mov r0, r20; mov r0, r21; mov r0, r22; mov r0, r23");
+ asm volatile ("mov r0, r24; mov r0, r25; mov r0, r26; mov r0, r27");
+ asm volatile ("mov r0, r28; mov r0, r29; mov r0, r30");
+
+ /* SPRs */
+ asm volatile ("ldsr r0, 0; ldsr r0, 1; ldsr r0, 2; ldsr r0, 3");
+ asm volatile ("ldsr r0, 4");
+ asm volatile ("addi 0x20, r0, r1; ldsr r1, 5"); /* PSW */
+ asm volatile ("ldsr r0, 16; ldsr r0, 17; ldsr r0, 18; ldsr r0, 19");
+ asm volatile ("ldsr r0, 20");
+
+
+#ifdef INIT_MEMC_FOR_SDRAM
+ /* Settings for SDRAM controller. */
+ V850E2_VSWC = 0x0042;
+ V850E2_BSC = 0x9286;
+ V850E2_BCT(0) = 0xb000; /* was: 0 */
+ V850E2_BCT(1) = 0x000b;
+ V850E2_ASC = 0;
+ V850E2_LBS = 0xa9aa; /* was: 0xaaaa */
+ V850E2_LBC(0) = 0;
+ V850E2_LBC(1) = 0; /* was: 0x3 */
+ V850E2_BCC = 0;
+ V850E2_RFS(4) = 0x800a; /* was: 0xf109 */
+ V850E2_SCR(4) = 0x2091; /* was: 0x20a1 */
+ V850E2_RFS(3) = 0x800c;
+ V850E2_SCR(3) = 0x20a1;
+ V850E2_DWC(0) = 0;
+ V850E2_DWC(1) = 0;
+#endif
+
+#if 0
+#ifdef CONFIG_V850E2_SIM85E2S
+ /* Turn on the caches. */
+ V850E2_CACHE_BTSC = V850E2_CACHE_BTSC_ICM | DCACHE_MODE;
+ V850E2_BHC = 0x1010;
+#elif CONFIG_V850E2_SIM85E2C
+ V850E2_CACHE_BTSC |= (V850E2_CACHE_BTSC_ICM | V850E2_CACHE_BTSC_DCM0);
+ V850E2_BUSM_BHC = 0xFFFF;
+#endif
+#else
+ V850E2_BHC = 0;
+#endif
+
+ /* Don't stop the simulator at `halt' instructions. */
+ SIM85E2_NOTHAL = 1;
+
+ /* Ensure that the simulator halts on a panic, instead of going
+ into an infinite loop inside the panic function. */
+ panic_timeout = -1;
+}
+
+void __init mach_setup (char **cmdline)
+{
+ memcons_setup ();
+}
+
+void mach_get_physical_ram (unsigned long *ram_start, unsigned long *ram_len)
+{
+ *ram_start = RAM_START;
+ *ram_len = RAM_END - RAM_START;
+}
+
+void __init mach_sched_init (struct irqaction *timer_action)
+{
+ /* The simulator actually cycles through all interrupts
+ periodically. We just pay attention to IRQ0, which gives us
+ 1/64 the rate of the periodic interrupts. */
+ setup_irq (0, timer_action);
+}
+
+void mach_gettimeofday (struct timespec *tv)
+{
+ tv->tv_sec = 0;
+ tv->tv_nsec = 0;
+}
+
+/* Interrupts */
+
+struct v850e_intc_irq_init irq_inits[] = {
+ { "IRQ", 0, NUM_MACH_IRQS, 1, 7 },
+ { 0 }
+};
+struct hw_interrupt_type hw_itypes[1];
+
+/* Initialize interrupts. */
+void __init mach_init_irqs (void)
+{
+ v850e_intc_init_irq_types (irq_inits, hw_itypes);
+}
+
+
+void machine_halt (void) __attribute__ ((noreturn));
+void machine_halt (void)
+{
+ SIM85E2_SIMFIN = 0; /* Halt immediately. */
+ for (;;) {}
+}
+
+EXPORT_SYMBOL(machine_halt);
+
+void machine_restart (char *__unused)
+{
+ machine_halt ();
+}
+
+EXPORT_SYMBOL(machine_restart);
+
+void machine_power_off (void)
+{
+ machine_halt ();
+}
+
+EXPORT_SYMBOL(machine_power_off);
diff --git a/arch/v850/kernel/sim85e2.ld b/arch/v850/kernel/sim85e2.ld
new file mode 100644
index 0000000..7470fd2
--- /dev/null
+++ b/arch/v850/kernel/sim85e2.ld
@@ -0,0 +1,36 @@
+/* Linker script for the sim85e2c simulator, which is a verilog simulation of
+ the V850E2 NA85E2C cpu core (CONFIG_V850E2_SIM85E2C). */
+
+MEMORY {
+ /* 1MB of `instruction RAM', starting at 0.
+ Instruction fetches are much faster from IRAM than from DRAM. */
+ IRAM : ORIGIN = IRAM_ADDR, LENGTH = IRAM_SIZE
+
+ /* 1MB of `data RAM', below and contiguous with the I/O space.
+ Data fetches are much faster from DRAM than from IRAM. */
+ DRAM : ORIGIN = DRAM_ADDR, LENGTH = DRAM_SIZE
+
+ /* `external ram' (CS1 area), comes after IRAM. */
+ ERAM : ORIGIN = ERAM_ADDR, LENGTH = ERAM_SIZE
+
+ /* Dynamic RAM; uses memory controller. */
+ SDRAM : ORIGIN = SDRAM_ADDR, LENGTH = SDRAM_SIZE
+}
+
+SECTIONS {
+ .iram : {
+ INTV_CONTENTS
+ *arch/v850/kernel/head.o
+ *(.early.text)
+ } > IRAM
+ .dram : {
+ _memcons_output = . ;
+ . = . + 0x8000 ;
+ _memcons_output_end = . ;
+ } > DRAM
+ .sdram : {
+ /* We stick console output into a buffer here. */
+ RAMK_KRAM_CONTENTS
+ ROOT_FS_CONTENTS
+ } > SDRAM
+}
diff --git a/arch/v850/kernel/simcons.c b/arch/v850/kernel/simcons.c
new file mode 100644
index 0000000..7f0efaa
--- /dev/null
+++ b/arch/v850/kernel/simcons.c
@@ -0,0 +1,166 @@
+/*
+ * arch/v850/kernel/simcons.c -- Console I/O for GDB v850e simulator
+ *
+ * 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/kernel.h>
+#include <linux/console.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/tty_driver.h>
+#include <linux/init.h>
+
+#include <asm/poll.h>
+#include <asm/string.h>
+#include <asm/simsyscall.h>
+
+
+/* Low-level console. */
+
+static void simcons_write (struct console *co, const char *buf, unsigned len)
+{
+ V850_SIM_SYSCALL (write, 1, buf, len);
+}
+
+static int simcons_read (struct console *co, char *buf, unsigned len)
+{
+ return V850_SIM_SYSCALL (read, 0, buf, len);
+}
+
+static struct tty_driver *tty_driver;
+static struct tty_driver *simcons_device (struct console *c, int *index)
+{
+ *index = c->index;
+ return tty_driver;
+}
+
+static struct console simcons =
+{
+ .name = "simcons",
+ .write = simcons_write,
+ .read = simcons_read,
+ .device = simcons_device,
+ .flags = CON_PRINTBUFFER,
+ .index = -1,
+};
+
+/* Higher level TTY interface. */
+
+int simcons_tty_open (struct tty_struct *tty, struct file *filp)
+{
+ return 0;
+}
+
+int simcons_tty_write (struct tty_struct *tty,
+ const unsigned char *buf, int count)
+{
+ return V850_SIM_SYSCALL (write, 1, buf, count);
+}
+
+int simcons_tty_write_room (struct tty_struct *tty)
+{
+ /* Completely arbitrary. */
+ return 0x100000;
+}
+
+int simcons_tty_chars_in_buffer (struct tty_struct *tty)
+{
+ /* We have no buffer. */
+ return 0;
+}
+
+static struct tty_operations ops = {
+ .open = simcons_tty_open,
+ .write = simcons_tty_write,
+ .write_room = simcons_tty_write_room,
+ .chars_in_buffer = simcons_tty_chars_in_buffer,
+};
+
+int __init simcons_tty_init (void)
+{
+ struct tty_driver *driver = alloc_tty_driver(1);
+ int err;
+ if (!driver)
+ return -ENOMEM;
+ driver->name = "simcons";
+ driver->major = TTY_MAJOR;
+ driver->minor_start = 64;
+ driver->type = TTY_DRIVER_TYPE_SYSCONS;
+ driver->init_termios = tty_std_termios;
+ tty_set_operations(driver, &ops);
+ err = tty_register_driver(driver);
+ if (err) {
+ put_tty_driver(driver);
+ return err;
+ }
+ tty_driver = driver;
+ return 0;
+}
+/* We use `late_initcall' instead of just `__initcall' as a workaround for
+ the fact that (1) simcons_tty_init can't be called before tty_init,
+ (2) tty_init is called via `module_init', (3) if statically linked,
+ module_init == device_init, and (4) there's no ordering of init lists.
+ We can do this easily because simcons is always statically linked, but
+ other tty drivers that depend on tty_init and which must use
+ `module_init' to declare their init routines are likely to be broken. */
+late_initcall(simcons_tty_init);
+
+/* Poll for input on the console, and if there's any, deliver it to the
+ tty driver. */
+void simcons_poll_tty (struct tty_struct *tty)
+{
+ int flip = 0, send_break = 0;
+ struct pollfd pfd;
+ pfd.fd = 0;
+ pfd.events = POLLIN;
+
+ if (V850_SIM_SYSCALL (poll, &pfd, 1, 0) > 0) {
+ if (pfd.revents & POLLIN) {
+ int left = TTY_FLIPBUF_SIZE - tty->flip.count;
+
+ if (left > 0) {
+ unsigned char *buf = tty->flip.char_buf_ptr;
+ int rd = V850_SIM_SYSCALL (read, 0, buf, left);
+
+ if (rd > 0) {
+ tty->flip.count += rd;
+ tty->flip.char_buf_ptr += rd;
+ memset (tty->flip.flag_buf_ptr, 0, rd);
+ tty->flip.flag_buf_ptr += rd;
+ flip = 1;
+ } else
+ send_break = 1;
+ }
+ } else if (pfd.revents & POLLERR)
+ send_break = 1;
+ }
+
+ if (send_break) {
+ tty_insert_flip_char (tty, 0, TTY_BREAK);
+ flip = 1;
+ }
+
+ if (flip)
+ tty_schedule_flip (tty);
+}
+
+void simcons_poll_ttys (void)
+{
+ if (tty_driver && tty_driver->ttys[0])
+ simcons_poll_tty (tty_driver->ttys[0]);
+}
+
+void simcons_setup (void)
+{
+ V850_SIM_SYSCALL (make_raw, 0);
+ register_console (&simcons);
+ printk (KERN_INFO "Console: GDB V850E simulator stdio\n");
+}
diff --git a/arch/v850/kernel/syscalls.c b/arch/v850/kernel/syscalls.c
new file mode 100644
index 0000000..9224cb6
--- /dev/null
+++ b/arch/v850/kernel/syscalls.c
@@ -0,0 +1,197 @@
+/*
+ * arch/v850/kernel/syscalls.c -- Various system-call definitions not
+ * defined in machine-independent code
+ *
+ * Copyright (C) 2001,02 NEC Corporation
+ * Copyright (C) 2001,02 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.
+ *
+ * This file was derived the ppc version, arch/ppc/kernel/syscalls.c
+ * ... which was derived from "arch/i386/kernel/sys_i386.c" by Gary Thomas;
+ * modified by Cort Dougan (cort@cs.nmt.edu)
+ * and Paul Mackerras (paulus@cs.anu.edu.au).
+ */
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+#include <linux/syscalls.h>
+#include <linux/sem.h>
+#include <linux/msg.h>
+#include <linux/shm.h>
+#include <linux/stat.h>
+#include <linux/mman.h>
+#include <linux/sys.h>
+#include <linux/ipc.h>
+#include <linux/utsname.h>
+#include <linux/file.h>
+
+#include <asm/uaccess.h>
+#include <asm/ipc.h>
+#include <asm/semaphore.h>
+
+/*
+ * sys_ipc() is the de-multiplexer for the SysV IPC calls..
+ *
+ * This is really horribly ugly.
+ */
+int
+sys_ipc (uint call, int first, int second, int third, void *ptr, long fifth)
+{
+ int version, ret;
+
+ version = call >> 16; /* hack for backward compatibility */
+ call &= 0xffff;
+
+ ret = -EINVAL;
+ switch (call) {
+ case SEMOP:
+ ret = sys_semop (first, (struct sembuf *)ptr, second);
+ break;
+ case SEMGET:
+ ret = sys_semget (first, second, third);
+ break;
+ case SEMCTL:
+ {
+ union semun fourth;
+
+ if (!ptr)
+ break;
+ if ((ret = access_ok(VERIFY_READ, ptr, sizeof(long)) ? 0 : -EFAULT)
+ || (ret = get_user(fourth.__pad, (void **)ptr)))
+ break;
+ ret = sys_semctl (first, second, third, fourth);
+ break;
+ }
+ case MSGSND:
+ ret = sys_msgsnd (first, (struct msgbuf *) ptr, second, third);
+ break;
+ case MSGRCV:
+ switch (version) {
+ case 0: {
+ struct ipc_kludge tmp;
+
+ if (!ptr)
+ break;
+ if ((ret = access_ok(VERIFY_READ, ptr, sizeof(tmp)) ? 0 : -EFAULT)
+ || (ret = copy_from_user(&tmp,
+ (struct ipc_kludge *) ptr,
+ sizeof (tmp))))
+ break;
+ ret = sys_msgrcv (first, tmp.msgp, second, tmp.msgtyp,
+ third);
+ break;
+ }
+ default:
+ ret = sys_msgrcv (first, (struct msgbuf *) ptr,
+ second, fifth, third);
+ break;
+ }
+ break;
+ case MSGGET:
+ ret = sys_msgget ((key_t) first, second);
+ break;
+ case MSGCTL:
+ ret = sys_msgctl (first, second, (struct msqid_ds *) ptr);
+ break;
+ case SHMAT:
+ switch (version) {
+ default: {
+ ulong raddr;
+
+ if ((ret = access_ok(VERIFY_WRITE, (ulong*) third,
+ sizeof(ulong)) ? 0 : -EFAULT))
+ break;
+ ret = do_shmat (first, (char *) ptr, second, &raddr);
+ if (ret)
+ break;
+ ret = put_user (raddr, (ulong *) third);
+ break;
+ }
+ case 1: /* iBCS2 emulator entry point */
+ if (!segment_eq(get_fs(), get_ds()))
+ break;
+ ret = do_shmat (first, (char *) ptr, second,
+ (ulong *) third);
+ break;
+ }
+ break;
+ case SHMDT:
+ ret = sys_shmdt ((char *)ptr);
+ break;
+ case SHMGET:
+ ret = sys_shmget (first, second, third);
+ break;
+ case SHMCTL:
+ ret = sys_shmctl (first, second, (struct shmid_ds *) ptr);
+ break;
+ }
+
+ return ret;
+}
+
+/*
+ * sys_pipe() is the normal C calling standard for creating
+ * a pipe. It's not the way unix traditionally does this, though.
+ */
+int sys_pipe (int *fildes)
+{
+ int fd[2];
+ int error;
+
+ error = do_pipe (fd);
+ if (!error) {
+ if (copy_to_user (fildes, fd, 2*sizeof (int)))
+ error = -EFAULT;
+ }
+ return error;
+}
+
+static inline unsigned long
+do_mmap2 (unsigned long addr, size_t len,
+ unsigned long prot, unsigned long flags,
+ unsigned long fd, unsigned long pgoff)
+{
+ struct file * file = NULL;
+ int ret = -EBADF;
+
+ flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
+ if (! (flags & MAP_ANONYMOUS)) {
+ if (!(file = fget (fd)))
+ goto out;
+ }
+
+ down_write (&current->mm->mmap_sem);
+ ret = do_mmap_pgoff (file, addr, len, prot, flags, pgoff);
+ up_write (&current->mm->mmap_sem);
+ if (file)
+ fput (file);
+out:
+ return ret;
+}
+
+unsigned long sys_mmap2 (unsigned long addr, size_t len,
+ unsigned long prot, unsigned long flags,
+ unsigned long fd, unsigned long pgoff)
+{
+ return do_mmap2 (addr, len, prot, flags, fd, pgoff);
+}
+
+unsigned long sys_mmap (unsigned long addr, size_t len,
+ unsigned long prot, unsigned long flags,
+ unsigned long fd, off_t offset)
+{
+ int err = -EINVAL;
+
+ if (offset & ~PAGE_MASK)
+ goto out;
+
+ err = do_mmap2 (addr, len, prot, flags, fd, offset >> PAGE_SHIFT);
+out:
+ return err;
+}
diff --git a/arch/v850/kernel/teg.c b/arch/v850/kernel/teg.c
new file mode 100644
index 0000000..495cf8f
--- /dev/null
+++ b/arch/v850/kernel/teg.c
@@ -0,0 +1,63 @@
+/*
+ * arch/v850/kernel/teg.c -- NB85E-TEG cpu chip
+ *
+ * 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/config.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/mm.h>
+#include <linux/swap.h>
+#include <linux/bootmem.h>
+#include <linux/irq.h>
+
+#include <asm/atomic.h>
+#include <asm/page.h>
+#include <asm/machdep.h>
+#include <asm/v850e_timer_d.h>
+
+#include "mach.h"
+
+void __init mach_sched_init (struct irqaction *timer_action)
+{
+ /* Select timer interrupt instead of external pin. */
+ TEG_ISS |= 0x1;
+ /* Start hardware timer. */
+ v850e_timer_d_configure (0, HZ);
+ /* Install timer interrupt handler. */
+ setup_irq (IRQ_INTCMD(0), timer_action);
+}
+
+static struct v850e_intc_irq_init irq_inits[] = {
+ { "IRQ", 0, NUM_CPU_IRQS, 1, 7 },
+ { "CMD", IRQ_INTCMD(0), IRQ_INTCMD_NUM, 1, 5 },
+ { "SER", IRQ_INTSER(0), IRQ_INTSER_NUM, 1, 3 },
+ { "SR", IRQ_INTSR(0), IRQ_INTSR_NUM, 1, 4 },
+ { "ST", IRQ_INTST(0), IRQ_INTST_NUM, 1, 5 },
+ { 0 }
+};
+#define NUM_IRQ_INITS ((sizeof irq_inits / sizeof irq_inits[0]) - 1)
+
+static struct hw_interrupt_type hw_itypes[NUM_IRQ_INITS];
+
+/* Initialize MA chip interrupts. */
+void __init teg_init_irqs (void)
+{
+ v850e_intc_init_irq_types (irq_inits, hw_itypes);
+}
+
+/* Called before configuring an on-chip UART. */
+void teg_uart_pre_configure (unsigned chan, unsigned cflags, unsigned baud)
+{
+ /* Enable UART I/O pins instead of external interrupt pins, and
+ UART interrupts instead of external pin interrupts. */
+ TEG_ISS |= 0x4E;
+}
diff --git a/arch/v850/kernel/time.c b/arch/v850/kernel/time.c
new file mode 100644
index 0000000..f722a26
--- /dev/null
+++ b/arch/v850/kernel/time.c
@@ -0,0 +1,198 @@
+/*
+ * linux/arch/v850/kernel/time.c -- Arch-dependent timer functions
+ *
+ * Copyright (C) 1991, 1992, 1995, 2001, 2002 Linus Torvalds
+ *
+ * This file contains the v850-specific time handling details.
+ * Most of the stuff is located in the machine specific files.
+ *
+ * 1997-09-10 Updated NTP code according to technical memorandum Jan '96
+ * "A Kernel Model for Precision Timekeeping" by Dave Mills
+ */
+
+#include <linux/config.h> /* CONFIG_HEARTBEAT */
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/param.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/time.h>
+#include <linux/timex.h>
+#include <linux/profile.h>
+
+#include <asm/io.h>
+
+#include "mach.h"
+
+u64 jiffies_64 = INITIAL_JIFFIES;
+
+EXPORT_SYMBOL(jiffies_64);
+
+#define TICK_SIZE (tick_nsec / 1000)
+
+/*
+ * Scheduler clock - returns current time in nanosec units.
+ */
+unsigned long long sched_clock(void)
+{
+ return (unsigned long long)jiffies * (1000000000 / HZ);
+}
+
+/*
+ * timer_interrupt() needs to keep up the real-time clock,
+ * as well as call the "do_timer()" routine every clocktick
+ */
+static irqreturn_t timer_interrupt (int irq, void *dummy, struct pt_regs *regs)
+{
+#if 0
+ /* last time the cmos clock got updated */
+ static long last_rtc_update=0;
+#endif
+
+ /* may need to kick the hardware timer */
+ if (mach_tick)
+ mach_tick ();
+
+ do_timer (regs);
+#ifndef CONFIG_SMP
+ update_process_times(user_mode(regs));
+#endif
+ profile_tick(CPU_PROFILING, regs);
+#if 0
+ /*
+ * 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_status & STA_UNSYNC) == 0 &&
+ xtime.tv_sec > last_rtc_update + 660 &&
+ (xtime.tv_nsec / 1000) >= 500000 - ((unsigned) TICK_SIZE) / 2 &&
+ (xtime.tv_nsec / 1000) <= 500000 + ((unsigned) TICK_SIZE) / 2) {
+ 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 */
+ }
+#ifdef CONFIG_HEARTBEAT
+ /* use power LED as a heartbeat instead -- much more useful
+ for debugging -- based on the version for PReP by Cort */
+ /* acts like an actual heart beat -- ie thump-thump-pause... */
+ if (mach_heartbeat) {
+ static unsigned cnt = 0, period = 0, dist = 0;
+
+ if (cnt == 0 || cnt == dist)
+ mach_heartbeat ( 1 );
+ else if (cnt == 7 || cnt == dist+7)
+ mach_heartbeat ( 0 );
+
+ if (++cnt > period) {
+ cnt = 0;
+ /* The hyperbolic function below modifies the heartbeat period
+ * length in dependency of the current (5min) load. It goes
+ * through the points f(0)=126, f(1)=86, f(5)=51,
+ * f(inf)->30. */
+ period = ((672<<FSHIFT)/(5*avenrun[0]+(7<<FSHIFT))) + 30;
+ dist = period / 4;
+ }
+ }
+#endif /* CONFIG_HEARTBEAT */
+#endif /* 0 */
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * This version of gettimeofday has near microsecond resolution.
+ */
+void do_gettimeofday (struct timeval *tv)
+{
+#if 0 /* DAVIDM later if possible */
+ extern volatile unsigned long lost_ticks;
+ unsigned long lost;
+#endif
+ unsigned long flags;
+ unsigned long usec, sec;
+ unsigned long seq;
+
+ do {
+ seq = read_seqbegin_irqsave(&xtime_lock, flags);
+
+#if 0
+ usec = mach_gettimeoffset ? mach_gettimeoffset () : 0;
+#else
+ usec = 0;
+#endif
+#if 0 /* DAVIDM later if possible */
+ lost = lost_ticks;
+ if (lost)
+ usec += lost * (1000000/HZ);
+#endif
+ sec = xtime.tv_sec;
+ usec += xtime.tv_nsec / 1000;
+ } while (read_seqretry_irqrestore(&xtime_lock, seq, flags));
+
+ while (usec >= 1000000) {
+ usec -= 1000000;
+ sec++;
+ }
+
+ tv->tv_sec = sec;
+ tv->tv_usec = usec;
+}
+
+EXPORT_SYMBOL(do_gettimeofday);
+
+int do_settimeofday(struct timespec *tv)
+{
+ if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC)
+ return -EINVAL;
+
+ write_seqlock_irq (&xtime_lock);
+
+ /* This is revolting. We need to set the xtime.tv_nsec
+ * 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!
+ */
+#if 0
+ tv->tv_nsec -= mach_gettimeoffset() * 1000;
+#endif
+
+ while (tv->tv_nsec < 0) {
+ tv->tv_nsec += NSEC_PER_SEC;
+ tv->tv_sec--;
+ }
+
+ xtime.tv_sec = tv->tv_sec;
+ xtime.tv_nsec = tv->tv_nsec;
+
+ time_adjust = 0; /* stop active adjtime () */
+ time_status |= STA_UNSYNC;
+ time_maxerror = NTP_PHASE_LIMIT;
+ time_esterror = NTP_PHASE_LIMIT;
+
+ write_sequnlock_irq (&xtime_lock);
+ clock_was_set();
+ return 0;
+}
+
+EXPORT_SYMBOL(do_settimeofday);
+
+static int timer_dev_id;
+static struct irqaction timer_irqaction = {
+ timer_interrupt,
+ SA_INTERRUPT,
+ CPU_MASK_NONE,
+ "timer",
+ &timer_dev_id,
+ NULL
+};
+
+void time_init (void)
+{
+ mach_gettimeofday (&xtime);
+ mach_sched_init (&timer_irqaction);
+}
diff --git a/arch/v850/kernel/v850_ksyms.c b/arch/v850/kernel/v850_ksyms.c
new file mode 100644
index 0000000..0ca6490
--- /dev/null
+++ b/arch/v850/kernel/v850_ksyms.c
@@ -0,0 +1,78 @@
+#include <linux/module.h>
+#include <linux/linkage.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/user.h>
+#include <linux/elfcore.h>
+#include <linux/in6.h>
+#include <linux/interrupt.h>
+#include <linux/config.h>
+
+#include <asm/pgalloc.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+#include <asm/semaphore.h>
+#include <asm/checksum.h>
+#include <asm/current.h>
+
+
+extern void *trap_table;
+EXPORT_SYMBOL (trap_table);
+
+/* platform dependent support */
+extern void dump_thread (struct pt_regs *, struct user *);
+EXPORT_SYMBOL (dump_thread);
+EXPORT_SYMBOL (kernel_thread);
+EXPORT_SYMBOL (enable_irq);
+EXPORT_SYMBOL (disable_irq);
+EXPORT_SYMBOL (disable_irq_nosync);
+EXPORT_SYMBOL (__bug);
+
+/* Networking helper routines. */
+EXPORT_SYMBOL (csum_partial_copy);
+EXPORT_SYMBOL (csum_partial_copy_from_user);
+EXPORT_SYMBOL (ip_compute_csum);
+EXPORT_SYMBOL (ip_fast_csum);
+
+/* string / mem functions */
+EXPORT_SYMBOL (strcpy);
+EXPORT_SYMBOL (strncpy);
+EXPORT_SYMBOL (strcat);
+EXPORT_SYMBOL (strncat);
+EXPORT_SYMBOL (strcmp);
+EXPORT_SYMBOL (strncmp);
+EXPORT_SYMBOL (strchr);
+EXPORT_SYMBOL (strlen);
+EXPORT_SYMBOL (strnlen);
+EXPORT_SYMBOL (strpbrk);
+EXPORT_SYMBOL (strrchr);
+EXPORT_SYMBOL (strstr);
+EXPORT_SYMBOL (memset);
+EXPORT_SYMBOL (memcpy);
+EXPORT_SYMBOL (memmove);
+EXPORT_SYMBOL (memcmp);
+EXPORT_SYMBOL (memscan);
+
+/* semaphores */
+EXPORT_SYMBOL (__down);
+EXPORT_SYMBOL (__down_interruptible);
+EXPORT_SYMBOL (__down_trylock);
+EXPORT_SYMBOL (__up);
+
+/*
+ * libgcc functions - functions that are used internally by the
+ * compiler... (prototypes are not correct though, but that
+ * doesn't really matter since they're not versioned).
+ */
+extern void __ashldi3 (void);
+extern void __ashrdi3 (void);
+extern void __lshrdi3 (void);
+extern void __muldi3 (void);
+extern void __negdi2 (void);
+
+EXPORT_SYMBOL (__ashldi3);
+EXPORT_SYMBOL (__ashrdi3);
+EXPORT_SYMBOL (__lshrdi3);
+EXPORT_SYMBOL (__muldi3);
+EXPORT_SYMBOL (__negdi2);
diff --git a/arch/v850/kernel/v850e2_cache.c b/arch/v850/kernel/v850e2_cache.c
new file mode 100644
index 0000000..4570312
--- /dev/null
+++ b/arch/v850/kernel/v850e2_cache.c
@@ -0,0 +1,127 @@
+/*
+ * arch/v850/kernel/v850e2_cache.c -- Cache control for V850E2 cache
+ * memories
+ *
+ * Copyright (C) 2003 NEC Electronics Corporation
+ * Copyright (C) 2003 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/mm.h>
+
+#include <asm/v850e2_cache.h>
+
+/* Cache operations we can do. The encoding corresponds directly to the
+ value we need to write into the COPR register. */
+enum cache_op {
+ OP_SYNC_IF_DIRTY = V850E2_CACHE_COPR_CFC(0), /* 000 */
+ OP_SYNC_IF_VALID = V850E2_CACHE_COPR_CFC(1), /* 001 */
+ OP_SYNC_IF_VALID_AND_CLEAR = V850E2_CACHE_COPR_CFC(3), /* 011 */
+ OP_WAY_CLEAR = V850E2_CACHE_COPR_CFC(4), /* 100 */
+ OP_FILL = V850E2_CACHE_COPR_CFC(5), /* 101 */
+ OP_CLEAR = V850E2_CACHE_COPR_CFC(6), /* 110 */
+ OP_CREATE_DIRTY = V850E2_CACHE_COPR_CFC(7) /* 111 */
+};
+
+/* Which cache to use. This encoding also corresponds directly to the
+ value we need to write into the COPR register. */
+enum cache {
+ ICACHE = 0,
+ DCACHE = V850E2_CACHE_COPR_LBSL
+};
+
+/* Returns ADDR rounded down to the beginning of its cache-line. */
+#define CACHE_LINE_ADDR(addr) \
+ ((addr) & ~(V850E2_CACHE_LINE_SIZE - 1))
+/* Returns END_ADDR rounded up to the `limit' of its cache-line. */
+#define CACHE_LINE_END_ADDR(end_addr) \
+ CACHE_LINE_ADDR(end_addr + (V850E2_CACHE_LINE_SIZE - 1))
+
+
+/* Low-level cache ops. */
+
+/* Apply cache-op OP to all entries in CACHE. */
+static inline void cache_op_all (enum cache_op op, enum cache cache)
+{
+ int cmd = op | cache | V850E2_CACHE_COPR_WSLE | V850E2_CACHE_COPR_STRT;
+
+ if (op != OP_WAY_CLEAR) {
+ /* The WAY_CLEAR operation does the whole way, but other
+ ops take begin-index and count params; we just indicate
+ the entire cache. */
+ V850E2_CACHE_CADL = 0;
+ V850E2_CACHE_CADH = 0;
+ V850E2_CACHE_CCNT = V850E2_CACHE_WAY_SIZE - 1;
+ }
+
+ V850E2_CACHE_COPR = cmd | V850E2_CACHE_COPR_WSL(0); /* way 0 */
+ V850E2_CACHE_COPR = cmd | V850E2_CACHE_COPR_WSL(1); /* way 1 */
+ V850E2_CACHE_COPR = cmd | V850E2_CACHE_COPR_WSL(2); /* way 2 */
+ V850E2_CACHE_COPR = cmd | V850E2_CACHE_COPR_WSL(3); /* way 3 */
+}
+
+/* Apply cache-op OP to all entries in CACHE covering addresses ADDR
+ through ADDR+LEN. */
+static inline void cache_op_range (enum cache_op op, u32 addr, u32 len,
+ enum cache cache)
+{
+ u32 start = CACHE_LINE_ADDR (addr);
+ u32 end = CACHE_LINE_END_ADDR (addr + len);
+ u32 num_lines = (end - start) >> V850E2_CACHE_LINE_SIZE_BITS;
+
+ V850E2_CACHE_CADL = start & 0xFFFF;
+ V850E2_CACHE_CADH = start >> 16;
+ V850E2_CACHE_CCNT = num_lines - 1;
+
+ V850E2_CACHE_COPR = op | cache | V850E2_CACHE_COPR_STRT;
+}
+
+
+/* High-level ops. */
+
+static void cache_exec_after_store_all (void)
+{
+ cache_op_all (OP_SYNC_IF_DIRTY, DCACHE);
+ cache_op_all (OP_WAY_CLEAR, ICACHE);
+}
+
+static void cache_exec_after_store_range (u32 start, u32 len)
+{
+ cache_op_range (OP_SYNC_IF_DIRTY, start, len, DCACHE);
+ cache_op_range (OP_CLEAR, start, len, ICACHE);
+}
+
+
+/* Exported functions. */
+
+void flush_icache (void)
+{
+ cache_exec_after_store_all ();
+}
+
+void flush_icache_range (unsigned long start, unsigned long end)
+{
+ cache_exec_after_store_range (start, end - start);
+}
+
+void flush_icache_page (struct vm_area_struct *vma, struct page *page)
+{
+ cache_exec_after_store_range (page_to_virt (page), PAGE_SIZE);
+}
+
+void flush_icache_user_range (struct vm_area_struct *vma, struct page *page,
+ unsigned long addr, int len)
+{
+ cache_exec_after_store_range (addr, len);
+}
+
+void flush_cache_sigtramp (unsigned long addr)
+{
+ /* For the exact size, see signal.c, but 16 bytes should be enough. */
+ cache_exec_after_store_range (addr, 16);
+}
diff --git a/arch/v850/kernel/v850e_cache.c b/arch/v850/kernel/v850e_cache.c
new file mode 100644
index 0000000..ea3e51c
--- /dev/null
+++ b/arch/v850/kernel/v850e_cache.c
@@ -0,0 +1,174 @@
+/*
+ * arch/v850/kernel/v850e_cache.c -- Cache control for V850E cache memories
+ *
+ * Copyright (C) 2003 NEC Electronics Corporation
+ * Copyright (C) 2003 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>
+ */
+
+/* This file implements cache control for the rather simple cache used on
+ some V850E CPUs, specifically the NB85E/TEG CPU-core and the V850E/ME2
+ CPU. V850E2 processors have their own (better) cache
+ implementation. */
+
+#include <asm/entry.h>
+#include <asm/cacheflush.h>
+#include <asm/v850e_cache.h>
+
+#define WAIT_UNTIL_CLEAR(value) while (value) {}
+
+/* Set caching params via the BHC and DCC registers. */
+void v850e_cache_enable (u16 bhc, u16 icc, u16 dcc)
+{
+ unsigned long *r0_ram = (unsigned long *)R0_RAM_ADDR;
+ register u16 bhc_val asm ("r6") = bhc;
+
+ /* Read the instruction cache control register (ICC) and confirm
+ that bits 0 and 1 (TCLR0, TCLR1) are all cleared. */
+ WAIT_UNTIL_CLEAR (V850E_CACHE_ICC & 0x3);
+ V850E_CACHE_ICC = icc;
+
+#ifdef V850E_CACHE_DCC
+ /* Configure data-cache. */
+ V850E_CACHE_DCC = dcc;
+#endif /* V850E_CACHE_DCC */
+
+ /* Configure caching for various memory regions by writing the BHC
+ register. The documentation says that an instruction _cannot_
+ enable/disable caching for the memory region in which the
+ instruction itself exists; to work around this, we store
+ appropriate instructions into the on-chip RAM area (which is never
+ cached), and briefly jump there to do the work. */
+#ifdef V850E_CACHE_WRITE_IBS
+ *r0_ram++ = 0xf0720760; /* st.h r0, 0xfffff072[r0] */
+#endif
+ *r0_ram++ = 0xf06a3760; /* st.h r6, 0xfffff06a[r0] */
+ *r0_ram = 0x5640006b; /* jmp [r11] */
+
+ asm ("mov hilo(1f), r11; jmp [%1]; 1:;"
+ :: "r" (bhc_val), "r" (R0_RAM_ADDR) : "r11");
+}
+
+static void clear_icache (void)
+{
+ /* 1. Read the instruction cache control register (ICC) and confirm
+ that bits 0 and 1 (TCLR0, TCLR1) are all cleared. */
+ WAIT_UNTIL_CLEAR (V850E_CACHE_ICC & 0x3);
+
+ /* 2. Read the ICC register and confirm that bit 12 (LOCK0) is
+ cleared. Bit 13 of the ICC register is always cleared. */
+ WAIT_UNTIL_CLEAR (V850E_CACHE_ICC & 0x1000);
+
+ /* 3. Set the TCLR0 and TCLR1 bits of the ICC register as follows,
+ when clearing way 0 and way 1 at the same time:
+ (a) Set the TCLR0 and TCLR1 bits.
+ (b) Read the TCLR0 and TCLR1 bits to confirm that these bits
+ are cleared.
+ (c) Perform (a) and (b) above again. */
+ V850E_CACHE_ICC |= 0x3;
+ WAIT_UNTIL_CLEAR (V850E_CACHE_ICC & 0x3);
+
+#ifdef V850E_CACHE_REPEAT_ICC_WRITE
+ /* Do it again. */
+ V850E_CACHE_ICC |= 0x3;
+ WAIT_UNTIL_CLEAR (V850E_CACHE_ICC & 0x3);
+#endif
+}
+
+#ifdef V850E_CACHE_DCC
+/* Flush or clear (or both) the data cache, depending on the value of FLAGS;
+ the procedure is the same for both, just the control bits used differ (and
+ both may be performed simultaneously). */
+static void dcache_op (unsigned short flags)
+{
+ /* 1. Read the data cache control register (DCC) and confirm that bits
+ 0, 1, 4, and 5 (DC00, DC01, DC04, DC05) are all cleared. */
+ WAIT_UNTIL_CLEAR (V850E_CACHE_DCC & 0x33);
+
+ /* 2. Clear DCC register bit 12 (DC12), bit 13 (DC13), or both
+ depending on the way for which tags are to be cleared. */
+ V850E_CACHE_DCC &= ~0xC000;
+
+ /* 3. Set DCC register bit 0 (DC00), bit 1 (DC01) or both depending on
+ the way for which tags are to be cleared.
+ ...
+ Set DCC register bit 4 (DC04), bit 5 (DC05), or both depending
+ on the way to be data flushed. */
+ V850E_CACHE_DCC |= flags;
+
+ /* 4. Read DCC register bit DC00, DC01 [DC04, DC05], or both depending
+ on the way for which tags were cleared [flushed] and confirm
+ that that bit is cleared. */
+ WAIT_UNTIL_CLEAR (V850E_CACHE_DCC & flags);
+}
+#endif /* V850E_CACHE_DCC */
+
+/* Flushes the contents of the dcache to memory. */
+static inline void flush_dcache (void)
+{
+#ifdef V850E_CACHE_DCC
+ /* We only need to do something if in write-back mode. */
+ if (V850E_CACHE_DCC & 0x0400)
+ dcache_op (0x30);
+#endif /* V850E_CACHE_DCC */
+}
+
+/* Flushes the contents of the dcache to memory, and then clears it. */
+static inline void clear_dcache (void)
+{
+#ifdef V850E_CACHE_DCC
+ /* We only need to do something if the dcache is enabled. */
+ if (V850E_CACHE_DCC & 0x0C00)
+ dcache_op (0x33);
+#endif /* V850E_CACHE_DCC */
+}
+
+/* Clears the dcache without flushing to memory first. */
+static inline void clear_dcache_no_flush (void)
+{
+#ifdef V850E_CACHE_DCC
+ /* We only need to do something if the dcache is enabled. */
+ if (V850E_CACHE_DCC & 0x0C00)
+ dcache_op (0x3);
+#endif /* V850E_CACHE_DCC */
+}
+
+static inline void cache_exec_after_store (void)
+{
+ flush_dcache ();
+ clear_icache ();
+}
+
+
+/* Exported functions. */
+
+void flush_icache (void)
+{
+ cache_exec_after_store ();
+}
+
+void flush_icache_range (unsigned long start, unsigned long end)
+{
+ cache_exec_after_store ();
+}
+
+void flush_icache_page (struct vm_area_struct *vma, struct page *page)
+{
+ cache_exec_after_store ();
+}
+
+void flush_icache_user_range (struct vm_area_struct *vma, struct page *page,
+ unsigned long adr, int len)
+{
+ cache_exec_after_store ();
+}
+
+void flush_cache_sigtramp (unsigned long addr)
+{
+ cache_exec_after_store ();
+}
diff --git a/arch/v850/kernel/v850e_intc.c b/arch/v850/kernel/v850e_intc.c
new file mode 100644
index 0000000..8d39a52
--- /dev/null
+++ b/arch/v850/kernel/v850e_intc.c
@@ -0,0 +1,104 @@
+/*
+ * arch/v850/kernel/v850e_intc.c -- V850E interrupt controller (INTC)
+ *
+ * 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/kernel.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+
+#include <asm/v850e_intc.h>
+
+static void irq_nop (unsigned irq) { }
+
+static unsigned v850e_intc_irq_startup (unsigned irq)
+{
+ v850e_intc_clear_pending_irq (irq);
+ v850e_intc_enable_irq (irq);
+ return 0;
+}
+
+static void v850e_intc_end_irq (unsigned irq)
+{
+ unsigned long psw, temp;
+
+ /* Clear the highest-level bit in the In-service priority register
+ (ISPR), to allow this interrupt (or another of the same or
+ lesser priority) to happen again.
+
+ The `reti' instruction normally does this automatically when the
+ PSW bits EP and NP are zero, but we can't always rely on reti
+ being used consistently to return after an interrupt (another
+ process can be scheduled, for instance, which can delay the
+ associated reti for a long time, or this process may be being
+ single-stepped, which uses the `dbret' instruction to return
+ from the kernel).
+
+ We also set the PSW EP bit, which prevents reti from also
+ trying to modify the ISPR itself. */
+
+ /* Get PSW and disable interrupts. */
+ asm volatile ("stsr psw, %0; di" : "=r" (psw));
+ /* We don't want to do anything for NMIs (they don't use the ISPR). */
+ if (! (psw & 0xC0)) {
+ /* Transition to `trap' state, so that an eventual real
+ reti instruction won't modify the ISPR. */
+ psw |= 0x40;
+ /* Fake an interrupt return, which automatically clears the
+ appropriate bit in the ISPR. */
+ asm volatile ("mov hilo(1f), %0;"
+ "ldsr %0, eipc; ldsr %1, eipsw;"
+ "reti;"
+ "1:"
+ : "=&r" (temp) : "r" (psw));
+ }
+}
+
+/* Initialize HW_IRQ_TYPES for INTC-controlled irqs described in array
+ INITS (which is terminated by an entry with the name field == 0). */
+void __init v850e_intc_init_irq_types (struct v850e_intc_irq_init *inits,
+ struct hw_interrupt_type *hw_irq_types)
+{
+ struct v850e_intc_irq_init *init;
+ for (init = inits; init->name; init++) {
+ unsigned i;
+ struct hw_interrupt_type *hwit = hw_irq_types++;
+
+ hwit->typename = init->name;
+
+ hwit->startup = v850e_intc_irq_startup;
+ hwit->shutdown = v850e_intc_disable_irq;
+ hwit->enable = v850e_intc_enable_irq;
+ hwit->disable = v850e_intc_disable_irq;
+ hwit->ack = irq_nop;
+ hwit->end = v850e_intc_end_irq;
+
+ /* Initialize kernel IRQ infrastructure for this interrupt. */
+ init_irq_handlers(init->base, init->num, init->interval, hwit);
+
+ /* Set the interrupt priorities. */
+ for (i = 0; i < init->num; i++) {
+ unsigned irq = init->base + i * init->interval;
+
+ /* If the interrupt is currently enabled (all
+ interrupts are initially disabled), then
+ assume whoever enabled it has set things up
+ properly, and avoid messing with it. */
+ if (! v850e_intc_irq_enabled (irq))
+ /* This write also (1) disables the
+ interrupt, and (2) clears any pending
+ interrupts. */
+ V850E_INTC_IC (irq)
+ = (V850E_INTC_IC_PR (init->priority)
+ | V850E_INTC_IC_MK);
+ }
+ }
+}
diff --git a/arch/v850/kernel/v850e_timer_d.c b/arch/v850/kernel/v850e_timer_d.c
new file mode 100644
index 0000000..d2a4ece
--- /dev/null
+++ b/arch/v850/kernel/v850e_timer_d.c
@@ -0,0 +1,54 @@
+/*
+ * include/asm-v850/v850e_timer_d.c -- `Timer D' component often used
+ * with V850E CPUs
+ *
+ * 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/kernel.h>
+
+#include <asm/v850e_utils.h>
+#include <asm/v850e_timer_d.h>
+
+/* Start interval timer TIMER (0-3). The timer will issue the
+ corresponding INTCMD interrupt RATE times per second.
+ This function does not enable the interrupt. */
+void v850e_timer_d_configure (unsigned timer, unsigned rate)
+{
+ unsigned divlog2, count;
+
+ /* Calculate params for timer. */
+ if (! calc_counter_params (
+ V850E_TIMER_D_BASE_FREQ, rate,
+ V850E_TIMER_D_TMCD_CS_MIN, V850E_TIMER_D_TMCD_CS_MAX, 16,
+ &divlog2, &count))
+ printk (KERN_WARNING
+ "Cannot find interval timer %d setting suitable"
+ " for rate of %dHz.\n"
+ "Using rate of %dHz instead.\n",
+ timer, rate,
+ (V850E_TIMER_D_BASE_FREQ >> divlog2) >> 16);
+
+ /* Do the actual hardware timer initialization: */
+
+ /* Enable timer. */
+ V850E_TIMER_D_TMCD(timer) = V850E_TIMER_D_TMCD_CAE;
+ /* Set clock divider. */
+ V850E_TIMER_D_TMCD(timer)
+ = V850E_TIMER_D_TMCD_CAE
+ | V850E_TIMER_D_TMCD_CS(divlog2);
+ /* Set timer compare register. */
+ V850E_TIMER_D_CMD(timer) = count;
+ /* Start counting. */
+ V850E_TIMER_D_TMCD(timer)
+ = V850E_TIMER_D_TMCD_CAE
+ | V850E_TIMER_D_TMCD_CS(divlog2)
+ | V850E_TIMER_D_TMCD_CE;
+}
diff --git a/arch/v850/kernel/v850e_utils.c b/arch/v850/kernel/v850e_utils.c
new file mode 100644
index 0000000..e6807ef
--- /dev/null
+++ b/arch/v850/kernel/v850e_utils.c
@@ -0,0 +1,62 @@
+/*
+ * include/asm-v850/v850e_utils.h -- Utility functions associated with
+ * V850E CPUs
+ *
+ * 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 <asm/v850e_utils.h>
+
+/* Calculate counter clock-divider and count values to attain the
+ desired frequency RATE from the base frequency BASE_FREQ. The
+ counter is expected to have a clock-divider, which can divide the
+ system cpu clock by a power of two value from MIN_DIVLOG2 to
+ MAX_DIV_LOG2, and a word-size of COUNTER_SIZE bits (the counter
+ counts up and resets whenever it's equal to the compare register,
+ generating an interrupt or whatever when it does so). The returned
+ values are: *DIVLOG2 -- log2 of the desired clock divider and *COUNT
+ -- the counter compare value to use. Returns true if it was possible
+ to find a reasonable value, otherwise false (and the other return
+ values will be set to be as good as possible). */
+int calc_counter_params (unsigned long base_freq,
+ unsigned long rate,
+ unsigned min_divlog2, unsigned max_divlog2,
+ unsigned counter_size,
+ unsigned *divlog2, unsigned *count)
+{
+ unsigned _divlog2;
+ int ok = 0;
+
+ /* Find the lowest clock divider setting that can represent RATE. */
+ for (_divlog2 = min_divlog2; _divlog2 <= max_divlog2; _divlog2++) {
+ /* Minimum interrupt rate possible using this divider. */
+ unsigned min_int_rate
+ = (base_freq >> _divlog2) >> counter_size;
+
+ if (min_int_rate <= rate) {
+ /* This setting is the highest resolution
+ setting that's slow enough enough to attain
+ RATE interrupts per second, so use it. */
+ ok = 1;
+ break;
+ }
+ }
+
+ if (_divlog2 > max_divlog2)
+ /* Can't find correct setting. */
+ _divlog2 = max_divlog2;
+
+ if (divlog2)
+ *divlog2 = _divlog2;
+ if (count)
+ *count = ((base_freq >> _divlog2) + rate/2) / rate;
+
+ return ok;
+}
diff --git a/arch/v850/kernel/vmlinux.lds.S b/arch/v850/kernel/vmlinux.lds.S
new file mode 100644
index 0000000..bbd3429
--- /dev/null
+++ b/arch/v850/kernel/vmlinux.lds.S
@@ -0,0 +1,285 @@
+/*
+ * arch/v850/vmlinux.lds.S -- kernel linker script for v850 platforms
+ *
+ * Copyright (C) 2002,03,04 NEC Electronics Corporation
+ * Copyright (C) 2002,03,04 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/config.h>
+#define VMLINUX_SYMBOL(_sym_) _##_sym_
+#include <asm-generic/vmlinux.lds.h>
+
+/* For most platforms, this will define useful things like RAM addr/size. */
+#include <asm/machdep.h>
+
+
+/* The following macros contain the usual definitions for various data areas.
+ The prefix `RAMK_' is used to indicate macros suitable for kernels loaded
+ into RAM, and similarly `ROMK_' for ROM-resident kernels. Note that all
+ symbols are prefixed with an extra `_' for compatibility with the v850
+ toolchain. */
+
+
+/* Interrupt vectors. */
+#define INTV_CONTENTS \
+ . = ALIGN (0x10) ; \
+ __intv_start = . ; \
+ *(.intv.reset) /* Reset vector */ \
+ . = __intv_start + 0x10 ; \
+ *(.intv.common) /* Vectors common to all v850e proc */\
+ . = __intv_start + 0x80 ; \
+ *(.intv.mach) /* Machine-specific int. vectors. */ \
+ __intv_end = . ;
+
+#define RODATA_CONTENTS \
+ . = ALIGN (16) ; \
+ *(.rodata) *(.rodata.*) \
+ *(__vermagic) /* Kernel version magic */ \
+ *(.rodata1) \
+ /* Kernel symbol table: Normal symbols */ \
+ ___start___ksymtab = .; \
+ *(__ksymtab) \
+ ___stop___ksymtab = .; \
+ /* Kernel symbol table: GPL-only symbols */ \
+ ___start___ksymtab_gpl = .; \
+ *(__ksymtab_gpl) \
+ ___stop___ksymtab_gpl = .; \
+ /* Kernel symbol table: strings */ \
+ *(__ksymtab_strings) \
+ /* Kernel symbol table: Normal symbols */ \
+ ___start___kcrctab = .; \
+ *(__kcrctab) \
+ ___stop___kcrctab = .; \
+ /* Kernel symbol table: GPL-only symbols */ \
+ ___start___kcrctab_gpl = .; \
+ *(__kcrctab_gpl) \
+ ___stop___kcrctab_gpl = .; \
+ /* Built-in module parameters */ \
+ ___start___param = .; \
+ *(__param) \
+ ___stop___param = .;
+
+
+/* Kernel text segment, and some constant data areas. */
+#define TEXT_CONTENTS \
+ __stext = . ; \
+ *(.text) \
+ SCHED_TEXT \
+ *(.exit.text) /* 2.5 convention */ \
+ *(.text.exit) /* 2.4 convention */ \
+ *(.text.lock) \
+ *(.exitcall.exit) \
+ __real_etext = . ; /* There may be data after here. */ \
+ RODATA_CONTENTS \
+ . = ALIGN (4) ; \
+ *(.call_table_data) \
+ *(.call_table_text) \
+ . = ALIGN (16) ; /* Exception table. */ \
+ ___start___ex_table = . ; \
+ *(__ex_table) \
+ ___stop___ex_table = . ; \
+ . = ALIGN (4) ; \
+ __etext = . ;
+
+/* Kernel data segment. */
+#define DATA_CONTENTS \
+ __sdata = . ; \
+ *(.data) \
+ *(.exit.data) /* 2.5 convention */ \
+ *(.data.exit) /* 2.4 convention */ \
+ . = ALIGN (16) ; \
+ *(.data.cacheline_aligned) \
+ . = ALIGN (0x2000) ; \
+ *(.data.init_task) \
+ . = ALIGN (0x2000) ; \
+ __edata = . ;
+
+/* Kernel BSS segment. */
+#define BSS_CONTENTS \
+ __sbss = . ; \
+ *(.bss) \
+ *(COMMON) \
+ . = ALIGN (4) ; \
+ __init_stack_end = . ; \
+ __ebss = . ;
+
+/* `initcall' tables. */
+#define INITCALL_CONTENTS \
+ . = ALIGN (16) ; \
+ ___setup_start = . ; \
+ *(.init.setup) /* 2.5 convention */ \
+ *(.setup.init) /* 2.4 convention */ \
+ ___setup_end = . ; \
+ ___initcall_start = . ; \
+ *(.initcall.init) \
+ *(.initcall1.init) \
+ *(.initcall2.init) \
+ *(.initcall3.init) \
+ *(.initcall4.init) \
+ *(.initcall5.init) \
+ *(.initcall6.init) \
+ *(.initcall7.init) \
+ . = ALIGN (4) ; \
+ ___initcall_end = . ; \
+ ___con_initcall_start = .; \
+ *(.con_initcall.init) \
+ ___con_initcall_end = .;
+
+/* Contents of `init' section for a kernel that's loaded into RAM. */
+#define RAMK_INIT_CONTENTS \
+ RAMK_INIT_CONTENTS_NO_END \
+ __init_end = . ;
+/* Same as RAMK_INIT_CONTENTS, but doesn't define the `__init_end' symbol. */
+#define RAMK_INIT_CONTENTS_NO_END \
+ . = ALIGN (4096) ; \
+ __init_start = . ; \
+ __sinittext = .; \
+ *(.init.text) /* 2.5 convention */ \
+ __einittext = .; \
+ *(.init.data) \
+ *(.text.init) /* 2.4 convention */ \
+ *(.data.init) \
+ INITCALL_CONTENTS \
+ INITRAMFS_CONTENTS
+
+/* The contents of `init' section for a ROM-resident kernel which
+ should go into RAM. */
+#define ROMK_INIT_RAM_CONTENTS \
+ . = ALIGN (4096) ; \
+ __init_start = . ; \
+ *(.init.data) /* 2.5 convention */ \
+ *(.data.init) /* 2.4 convention */ \
+ __init_end = . ; \
+ . = ALIGN (4096) ;
+
+/* The contents of `init' section for a ROM-resident kernel which
+ should go into ROM. */
+#define ROMK_INIT_ROM_CONTENTS \
+ _sinittext = .; \
+ *(.init.text) /* 2.5 convention */ \
+ _einittext = .; \
+ *(.text.init) /* 2.4 convention */ \
+ INITCALL_CONTENTS \
+ INITRAMFS_CONTENTS
+
+/* A root filesystem image, for kernels with an embedded root filesystem. */
+#define ROOT_FS_CONTENTS \
+ __root_fs_image_start = . ; \
+ *(.root) \
+ __root_fs_image_end = . ;
+/* The initramfs archive. */
+#define INITRAMFS_CONTENTS \
+ . = ALIGN (4) ; \
+ ___initramfs_start = . ; \
+ *(.init.ramfs) \
+ ___initramfs_end = . ;
+/* Where the initial bootmap (bitmap for the boot-time memory allocator)
+ should be place. */
+#define BOOTMAP_CONTENTS \
+ . = ALIGN (4096) ; \
+ __bootmap = . ; \
+ . = . + 4096 ; /* enough for 128MB. */
+
+/* The contents of a `typical' kram area for a kernel in RAM. */
+#define RAMK_KRAM_CONTENTS \
+ __kram_start = . ; \
+ TEXT_CONTENTS \
+ DATA_CONTENTS \
+ BSS_CONTENTS \
+ RAMK_INIT_CONTENTS \
+ __kram_end = . ; \
+ BOOTMAP_CONTENTS
+
+
+/* Define output sections normally used for a ROM-resident kernel.
+ ROM and RAM should be appropriate memory areas to use for kernel
+ ROM and RAM data. This assumes that ROM starts at 0 (and thus can
+ hold the interrupt vectors). */
+#define ROMK_SECTIONS(ROM, RAM) \
+ .rom : { \
+ INTV_CONTENTS \
+ TEXT_CONTENTS \
+ ROMK_INIT_ROM_CONTENTS \
+ ROOT_FS_CONTENTS \
+ } > ROM \
+ \
+ __rom_copy_src_start = . ; \
+ \
+ .data : { \
+ __kram_start = . ; \
+ __rom_copy_dst_start = . ; \
+ DATA_CONTENTS \
+ ROMK_INIT_RAM_CONTENTS \
+ __rom_copy_dst_end = . ; \
+ } > RAM AT> ROM \
+ \
+ .bss ALIGN (4) : { \
+ BSS_CONTENTS \
+ __kram_end = . ; \
+ BOOTMAP_CONTENTS \
+ } > RAM
+
+
+/* The 32-bit variable `jiffies' is just the lower 32-bits of `jiffies_64'. */
+_jiffies = _jiffies_64 ;
+
+
+/* Include an appropriate platform-dependent linker-script (which
+ usually should use the above macros to do most of the work). */
+
+#ifdef CONFIG_V850E_SIM
+# include "sim.ld"
+#endif
+
+#ifdef CONFIG_V850E2_SIM85E2
+# include "sim85e2.ld"
+#endif
+
+#ifdef CONFIG_V850E2_FPGA85E2C
+# include "fpga85e2c.ld"
+#endif
+
+#ifdef CONFIG_V850E2_ANNA
+# ifdef CONFIG_ROM_KERNEL
+# include "anna-rom.ld"
+# else
+# include "anna.ld"
+# endif
+#endif
+
+#ifdef CONFIG_V850E_AS85EP1
+# ifdef CONFIG_ROM_KERNEL
+# include "as85ep1-rom.ld"
+# else
+# include "as85ep1.ld"
+# endif
+#endif
+
+#ifdef CONFIG_RTE_CB_MA1
+# ifdef CONFIG_ROM_KERNEL
+# include "rte_ma1_cb-rom.ld"
+# else
+# include "rte_ma1_cb.ld"
+# endif
+#endif
+
+#ifdef CONFIG_RTE_CB_NB85E
+# ifdef CONFIG_ROM_KERNEL
+# include "rte_nb85e_cb-rom.ld"
+# elif defined(CONFIG_RTE_CB_MULTI)
+# include "rte_nb85e_cb-multi.ld"
+# else
+# include "rte_nb85e_cb.ld"
+# endif
+#endif
+
+#ifdef CONFIG_RTE_CB_ME2
+# include "rte_me2_cb.ld"
+#endif
+
OpenPOWER on IntegriCloud