Linux-2.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!
This commit is contained in:
Linus Torvalds
2005-04-16 15:20:36 -07:00
commit 1da177e4c3
17291 changed files with 6718755 additions and 0 deletions

316
arch/v850/Kconfig Normal file
View File

@@ -0,0 +1,316 @@
#############################################################################
#
# For a description of the syntax of this configuration file,
# see Documentation/kbuild/kconfig-language.txt.
#
#############################################################################
mainmenu "uClinux/v850 (w/o MMU) Kernel Configuration"
config MMU
bool
default n
config UID16
bool
default n
config RWSEM_GENERIC_SPINLOCK
bool
default y
config RWSEM_XCHGADD_ALGORITHM
bool
default n
config GENERIC_CALIBRATE_DELAY
bool
default y
# Turn off some random 386 crap that can affect device config
config ISA
bool
default n
config ISAPNP
bool
default n
config EISA
bool
default n
config MCA
bool
default n
#############################################################################
#### v850-specific config
# Define the architecture
config V850
bool
default y
menu "Processor type and features"
choice
prompt "Platform"
default GDB
config V850E_SIM
bool "GDB"
config RTE_CB_MA1
bool "RTE-V850E/MA1-CB"
config RTE_CB_NB85E
bool "RTE-V850E/NB85E-CB"
config RTE_CB_ME2
bool "RTE-V850E/ME2-CB"
config V850E_AS85EP1
bool "AS85EP1"
config V850E2_SIM85E2C
bool "sim85e2c"
config V850E2_SIM85E2S
bool "sim85e2s"
config V850E2_FPGA85E2C
bool "NA85E2C-FPGA"
config V850E2_ANNA
bool "Anna"
endchoice
#### V850E processor-specific config
# All CPUs currently supported use the v850e architecture
config V850E
bool
default y
# The RTE-V850E/MA1-CB is the only type of V850E/MA1 platform we
# currently support
config V850E_MA1
bool
depends RTE_CB_MA1
default y
# Similarly for the RTE-V850E/NB85E-CB - V850E/TEG
config V850E_TEG
bool
depends RTE_CB_NB85E
default y
# ... and the RTE-V850E/ME2-CB - V850E/ME2
config V850E_ME2
bool
depends RTE_CB_ME2
default y
#### sim85e2-specific config
config V850E2_SIM85E2
bool
depends V850E2_SIM85E2C || V850E2_SIM85E2S
default y
#### V850E2 processor-specific config
# V850E2 processors
config V850E2
bool
depends V850E2_SIM85E2 || V850E2_FPGA85E2C || V850E2_ANNA
default y
#### RTE-CB platform-specific config
# Boards in the RTE-x-CB series
config RTE_CB
bool
depends RTE_CB_MA1 || RTE_CB_NB85E || RTE_CB_ME2
default y
config RTE_CB_MULTI
bool
# RTE_CB_NB85E can either have multi ROM support or not, but
# other platforms (currently only RTE_CB_MA1) require it.
prompt "Multi monitor ROM support" if RTE_CB_NB85E
depends RTE_CB_MA1 || RTE_CB_NB85E
default y
config RTE_CB_MULTI_DBTRAP
bool "Pass illegal insn trap / dbtrap to kernel"
depends RTE_CB_MULTI
default n
config RTE_CB_MA1_KSRAM
bool "Kernel in SRAM (limits size of kernel)"
depends RTE_CB_MA1 && RTE_CB_MULTI
default n
config RTE_MB_A_PCI
bool "Mother-A PCI support"
depends RTE_CB
default y
# The GBUS is used to talk to the RTE-MOTHER-A board
config RTE_GBUS_INT
bool
depends RTE_MB_A_PCI
default y
# The only PCI bus we support is on the RTE-MOTHER-A board
config PCI
bool
default RTE_MB_A_PCI
#### Some feature-specific configs
# Everything except for the GDB simulator uses the same interrupt controller
config V850E_INTC
bool
default !V850E_SIM
# Everything except for the various simulators uses the "Timer D" unit
config V850E_TIMER_D
bool
default !V850E_SIM && !V850E2_SIM85E2
# Cache control used on some v850e1 processors
config V850E_CACHE
bool
default V850E_TEG || V850E_ME2
# Cache control used on v850e2 processors; I think this should
# actually apply to more, but currently only the SIM85E2S uses it
config V850E2_CACHE
bool
default V850E2_SIM85E2S
config NO_CACHE
bool
default !V850E_CACHE && !V850E2_CACHE
#### Misc config
config ROM_KERNEL
bool "Kernel in ROM"
depends V850E2_ANNA || V850E_AS85EP1 || RTE_CB_ME2
# Some platforms pre-zero memory, in which case the kernel doesn't need to
config ZERO_BSS
bool
depends !V850E2_SIM85E2C
default y
# The crappy-ass zone allocator requires that the start of allocatable
# memory be aligned to the largest possible allocation.
config FORCE_MAX_ZONEORDER
int
default 8 if V850E2_SIM85E2C || V850E2_FPGA85E2C
config V850E_HIGHRES_TIMER
bool "High resolution timer support"
depends V850E_TIMER_D
config TIME_BOOTUP
bool "Time bootup"
depends V850E_HIGHRES_TIMER
config RESET_GUARD
bool "Reset Guard"
config LARGE_ALLOCS
bool "Allow allocating large blocks (> 1MB) of memory"
help
Allow the slab memory allocator to keep chains for very large
memory sizes - upto 32MB. You may need this if your system has
a lot of RAM, and you need to able to allocate very large
contiguous chunks. If unsure, say N.
endmenu
#############################################################################
source init/Kconfig
#############################################################################
menu "Bus options (PCI, PCMCIA, EISA, MCA, ISA)"
# config PCI
# bool "PCI support"
# help
# Support for PCI bus.
source "drivers/pci/Kconfig"
source "drivers/pcmcia/Kconfig"
source "drivers/pci/hotplug/Kconfig"
endmenu
menu "Executable file formats"
source "fs/Kconfig.binfmt"
endmenu
#############################################################################
source "drivers/base/Kconfig"
source drivers/mtd/Kconfig
source drivers/parport/Kconfig
#source drivers/pnp/Kconfig
source drivers/block/Kconfig
#############################################################################
menu "Disk device support"
source "drivers/ide/Kconfig"
source "drivers/scsi/Kconfig"
endmenu
#############################################################################
source "drivers/md/Kconfig"
source "drivers/message/fusion/Kconfig"
source "drivers/ieee1394/Kconfig"
source "drivers/message/i2o/Kconfig"
source "net/Kconfig"
source "drivers/isdn/Kconfig"
#source "drivers/telephony/Kconfig"
#
# input before char - char/joystick depends on it. As does USB.
#
source "drivers/input/Kconfig"
source "drivers/char/Kconfig"
#source drivers/misc/Config.in
source "drivers/media/Kconfig"
source "fs/Kconfig"
source "drivers/video/Kconfig"
source "sound/Kconfig"
source "drivers/usb/Kconfig"
source "arch/v850/Kconfig.debug"
source "security/Kconfig"
source "crypto/Kconfig"
source "lib/Kconfig"
#############################################################################

10
arch/v850/Kconfig.debug Normal file
View File

@@ -0,0 +1,10 @@
menu "Kernel hacking"
source "lib/Kconfig.debug"
config NO_KERNEL_MSG
bool "Suppress Kernel BUG Messages"
help
Do not output any debug BUG messages within the kernel.
endmenu

63
arch/v850/Makefile Normal file
View File

@@ -0,0 +1,63 @@
#
# arch/v850/Makefile
#
# Copyright (C) 2001,02,03 NEC Corporation
# Copyright (C) 2001,02,03 Miles Bader <miles@gnu.org>
#
# This file is included by the global makefile so that you can add your own
# architecture-specific flags and dependencies. Remember to do have actions
# for "archclean" and "archdep" for cleaning up and making dependencies for
# this architecture
#
# 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.
#
arch_dir = arch/v850
CFLAGS += -mv850e
# r16 is a fixed pointer to the current task
CFLAGS += -ffixed-r16 -mno-prolog-function
CFLAGS += -fno-builtin
CFLAGS += -D__linux__ -DUTS_SYSNAME=\"uClinux\"
# This prevents the linker from consolidating the .gnu.linkonce.this_module
# section into .text (which the v850 default linker script for -r does for
# some reason)
LDFLAGS_MODULE += --unique=.gnu.linkonce.this_module
OBJCOPY_FLAGS_BLOB := -I binary -O elf32-little -B v850e
head-y := $(arch_dir)/kernel/head.o $(arch_dir)/kernel/init_task.o
core-y += $(arch_dir)/kernel/
libs-y += $(arch_dir)/lib/
# Deal with the initial contents of the root device
ifdef ROOT_FS_IMAGE
core-y += root_fs_image.o
# Because the kernel build-system erases all explicit .o build rules, we
# have to use an intermediate target to fool it into building for us.
# This results in it being built anew each time, but that's alright.
root_fs_image.o: root_fs_image_force
root_fs_image_force: $(ROOT_FS_IMAGE)
$(OBJCOPY) $(OBJCOPY_FLAGS_BLOB) --rename-section .data=.root,alloc,load,readonly,data,contents $< root_fs_image.o
endif
prepare: include/asm-$(ARCH)/asm-consts.h
# Generate constants from C code for use by asm files
arch/$(ARCH)/kernel/asm-consts.s: include/asm include/linux/version.h \
include/config/MARKER
include/asm-$(ARCH)/asm-consts.h: arch/$(ARCH)/kernel/asm-consts.s
$(call filechk,gen-asm-offsets)
CLEAN_FILES += include/asm-$(ARCH)/asm-consts.h \
arch/$(ARCH)/kernel/asm-consts.s \
root_fs_image.o

32
arch/v850/README Normal file
View File

@@ -0,0 +1,32 @@
This port to the NEC V850E processor supports the following platforms:
+ The gdb v850e simulator (CONFIG_V850E_SIM).
+ The Midas labs RTE-V850E/MA1-CB and RTE-V850E/NB85E-CB evaluation boards
(CONFIG_RTE_CB_MA1 and CONFIG_RTE_CB_NB85E). This support has only been
tested when running with the Multi-debugger monitor ROM (for the Green
Hills Multi debugger). The optional NEC Solution Gear RTE-MOTHER-A
motherboard is also supported, which allows PCI boards to be used
(CONFIG_RTE_MB_A_PCI).
+ The Midas labs RTE-V850E/ME2-CB evaluation board (CONFIG_RTE_CB_ME2).
This has only been tested using a kernel downloaded via an ICE connection
using the Multi debugger. Support for the RTE-MOTHER-A is present, but
hasn't been tested (unlike the other Midas labs cpu boards, the
RTE-V850E/ME2-CB includes an ethernet adaptor).
+ The NEC AS85EP1 V850E evaluation chip/board (CONFIG_V850E_AS85EP1).
+ The NEC `Anna' (board/chip) implementation of the V850E2 processor
(CONFIG_V850E2_ANNA).
+ The sim85e2c and sim85e2s simulators, which are verilog simulations of
the V850E2 NA85E2C/NA85E2S cpu cores (CONFIG_V850E2_SIM85E2C and
CONFIG_V850E2_SIM85E2S).
+ A FPGA implementation of the V850E2 NA85E2C cpu core
(CONFIG_V850E2_FPGA85E2C).
Porting to anything with a V850E/MA1 or MA2 processor should be simple.
See the file <asm-v850/machdep.h> and the files it includes for an example of
how to add platform/chip-specific support.

40
arch/v850/kernel/Makefile Normal file
View File

@@ -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

View File

@@ -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)
}

208
arch/v850/kernel/anna.c Normal file
View File

@@ -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;
}
}

20
arch/v850/kernel/anna.ld Normal file
View File

@@ -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
}

View File

@@ -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)
}

240
arch/v850/kernel/as85ep1.c Normal file
View File

@@ -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;
}
}

View File

@@ -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
}

View File

@@ -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;
}

142
arch/v850/kernel/bug.c Normal file
View File

@@ -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);

1121
arch/v850/kernel/entry.S Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -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
};

View File

@@ -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
}

271
arch/v850/kernel/gbus_int.c Normal file
View File

@@ -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;
}

128
arch/v850/kernel/head.S Normal file
View File

@@ -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)

View File

@@ -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);
}

View File

@@ -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) };

87
arch/v850/kernel/intv.S Normal file
View File

@@ -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 */

744
arch/v850/kernel/irq.c Normal file
View File

@@ -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 */

70
arch/v850/kernel/ma.c Normal file
View File

@@ -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;
}
}

17
arch/v850/kernel/mach.c Normal file
View File

@@ -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;

56
arch/v850/kernel/mach.h Normal file
View File

@@ -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__ */

74
arch/v850/kernel/me2.c Normal file
View File

@@ -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;
}
}

135
arch/v850/kernel/memcons.c Normal file
View File

@@ -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);

237
arch/v850/kernel/module.c Normal file
View File

@@ -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)
{
}

236
arch/v850/kernel/process.c Normal file
View File

@@ -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;
}

67
arch/v850/kernel/procfs.c Normal file
View File

@@ -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
};

282
arch/v850/kernel/ptrace.c Normal file
View File

@@ -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 */
}

200
arch/v850/kernel/rte_cb.c Normal file
View File

@@ -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 */
}

View File

@@ -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);

View File

@@ -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;
}
}
}

View File

@@ -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)
}

View File

@@ -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.*/
}

View File

@@ -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
}

View File

@@ -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);

View File

@@ -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);
}
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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 ();
}

View File

@@ -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
}

View File

@@ -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;
}

286
arch/v850/kernel/setup.c Normal file
View File

@@ -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);
}

525
arch/v850/kernel/signal.c Normal file
View File

@@ -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;
}

179
arch/v850/kernel/sim.c Normal file
View File

@@ -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;
}

13
arch/v850/kernel/sim.ld Normal file
View File

@@ -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
}

201
arch/v850/kernel/sim85e2.c Normal file
View File

@@ -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);

View File

@@ -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
}

166
arch/v850/kernel/simcons.c Normal file
View File

@@ -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");
}

197
arch/v850/kernel/syscalls.c Normal file
View File

@@ -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;
}

63
arch/v850/kernel/teg.c Normal file
View File

@@ -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;
}

198
arch/v850/kernel/time.c Normal file
View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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 ();
}

View File

@@ -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);
}
}
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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

6
arch/v850/lib/Makefile Normal file
View File

@@ -0,0 +1,6 @@
#
# arch/v850/lib/Makefile
#
lib-y = ashrdi3.o ashldi3.o lshrdi3.o muldi3.o negdi2.o \
checksum.o memcpy.o memset.o

62
arch/v850/lib/ashldi3.c Normal file
View File

@@ -0,0 +1,62 @@
/* ashldi3.c extracted from gcc-2.95.2/libgcc2.c which is: */
/* Copyright (C) 1989, 92-98, 1999 Free Software Foundation, Inc.
This file is part of GNU CC.
GNU CC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
GNU CC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GNU CC; see the file COPYING. If not, write to
the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
#define BITS_PER_UNIT 8
typedef int SItype __attribute__ ((mode (SI)));
typedef unsigned int USItype __attribute__ ((mode (SI)));
typedef int DItype __attribute__ ((mode (DI)));
typedef int word_type __attribute__ ((mode (__word__)));
struct DIstruct {SItype high, low;};
typedef union
{
struct DIstruct s;
DItype ll;
} DIunion;
DItype
__ashldi3 (DItype u, word_type b)
{
DIunion w;
word_type bm;
DIunion uu;
if (b == 0)
return u;
uu.ll = u;
bm = (sizeof (SItype) * BITS_PER_UNIT) - b;
if (bm <= 0)
{
w.s.low = 0;
w.s.high = (USItype)uu.s.low << -bm;
}
else
{
USItype carries = (USItype)uu.s.low >> bm;
w.s.low = (USItype)uu.s.low << b;
w.s.high = ((USItype)uu.s.high << b) | carries;
}
return w.ll;
}

63
arch/v850/lib/ashrdi3.c Normal file
View File

@@ -0,0 +1,63 @@
/* ashrdi3.c extracted from gcc-2.7.2/libgcc2.c which is: */
/* Copyright (C) 1989, 1992, 1993, 1994, 1995 Free Software Foundation, Inc.
This file is part of GNU CC.
GNU CC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
GNU CC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GNU CC; see the file COPYING. If not, write to
the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
#define BITS_PER_UNIT 8
typedef int SItype __attribute__ ((mode (SI)));
typedef unsigned int USItype __attribute__ ((mode (SI)));
typedef int DItype __attribute__ ((mode (DI)));
typedef int word_type __attribute__ ((mode (__word__)));
struct DIstruct {SItype high, low;};
typedef union
{
struct DIstruct s;
DItype ll;
} DIunion;
DItype
__ashrdi3 (DItype u, word_type b)
{
DIunion w;
word_type bm;
DIunion uu;
if (b == 0)
return u;
uu.ll = u;
bm = (sizeof (SItype) * BITS_PER_UNIT) - b;
if (bm <= 0)
{
/* w.s.high = 1..1 or 0..0 */
w.s.high = uu.s.high >> (sizeof (SItype) * BITS_PER_UNIT - 1);
w.s.low = uu.s.high >> -bm;
}
else
{
USItype carries = (USItype)uu.s.high << bm;
w.s.high = uu.s.high >> b;
w.s.low = ((USItype)uu.s.low >> b) | carries;
}
return w.ll;
}

154
arch/v850/lib/checksum.c Normal file
View File

@@ -0,0 +1,154 @@
/*
* INET An implementation of the TCP/IP protocol suite for the LINUX
* operating system. INET is implemented using the BSD Socket
* interface as the means of communication with the user level.
*
* MIPS specific IP/TCP/UDP checksumming routines
*
* Authors: Ralf Baechle, <ralf@waldorf-gmbh.de>
* Lots of code moved from tcp.c and ip.c; see those files
* for more names.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
* $Id: checksum.c,v 1.1 2002/09/28 14:58:40 gerg Exp $
*/
#include <net/checksum.h>
#include <linux/module.h>
#include <linux/types.h>
#include <asm/byteorder.h>
#include <asm/string.h>
#include <asm/uaccess.h>
static inline unsigned short from32to16 (unsigned long sum)
{
unsigned int result;
/*
%0 %1
hsw %1, %0 H L L H
add %1, %0 H L H+L+C H+L
*/
asm ("hsw %1, %0; add %1, %0" : "=&r" (result) : "r" (sum));
return result >> 16;
}
static inline unsigned int do_csum(const unsigned char * buff, int len)
{
int odd, count;
unsigned int result = 0;
if (len <= 0)
goto out;
odd = 1 & (unsigned long) buff;
if (odd) {
result = be16_to_cpu(*buff);
len--;
buff++;
}
count = len >> 1; /* nr of 16-bit words.. */
if (count) {
if (2 & (unsigned long) buff) {
result += *(unsigned short *) buff;
count--;
len -= 2;
buff += 2;
}
count >>= 1; /* nr of 32-bit words.. */
if (count) {
unsigned int carry = 0;
do {
unsigned int w = *(unsigned int *) buff;
count--;
buff += 4;
result += carry;
result += w;
carry = (w > result);
} while (count);
result += carry;
result = (result & 0xffff) + (result >> 16);
}
if (len & 2) {
result += *(unsigned short *) buff;
buff += 2;
}
}
if (len & 1)
result += le16_to_cpu(*buff);
result = from32to16(result);
if (odd)
result = ((result >> 8) & 0xff) | ((result & 0xff) << 8);
out:
return result;
}
/*
* This is a version of ip_compute_csum() optimized for IP headers,
* which always checksum on 4 octet boundaries.
*/
unsigned short ip_fast_csum(unsigned char * iph, unsigned int ihl)
{
return ~do_csum(iph,ihl*4);
}
/*
* this routine is used for miscellaneous IP-like checksums, mainly
* in icmp.c
*/
unsigned short ip_compute_csum(const unsigned char * buff, int len)
{
return ~do_csum(buff,len);
}
/*
* computes a partial checksum, e.g. for TCP/UDP fragments
*/
unsigned int csum_partial(const unsigned char *buff, int len, unsigned int sum)
{
unsigned int result = do_csum(buff, len);
/* add in old sum, and carry.. */
result += sum;
if(sum > result)
result += 1;
return result;
}
EXPORT_SYMBOL(csum_partial);
/*
* copy while checksumming, otherwise like csum_partial
*/
unsigned int csum_partial_copy(const unsigned char *src, unsigned char *dst,
int len, unsigned int sum)
{
/*
* It's 2:30 am and I don't feel like doing it real ...
* This is lots slower than the real thing (tm)
*/
sum = csum_partial(src, len, sum);
memcpy(dst, src, len);
return sum;
}
/*
* Copy from userspace and compute checksum. If we catch an exception
* then zero the rest of the buffer.
*/
unsigned int csum_partial_copy_from_user (const unsigned char *src, unsigned char *dst,
int len, unsigned int sum,
int *err_ptr)
{
int missing;
missing = copy_from_user(dst, src, len);
if (missing) {
memset(dst + len - missing, 0, missing);
*err_ptr = -EFAULT;
}
return csum_partial(dst, len, sum);
}

62
arch/v850/lib/lshrdi3.c Normal file
View File

@@ -0,0 +1,62 @@
/* lshrdi3.c extracted from gcc-2.7.2/libgcc2.c which is: */
/* Copyright (C) 1989, 1992, 1993, 1994, 1995 Free Software Foundation, Inc.
This file is part of GNU CC.
GNU CC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
GNU CC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GNU CC; see the file COPYING. If not, write to
the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
#define BITS_PER_UNIT 8
typedef int SItype __attribute__ ((mode (SI)));
typedef unsigned int USItype __attribute__ ((mode (SI)));
typedef int DItype __attribute__ ((mode (DI)));
typedef int word_type __attribute__ ((mode (__word__)));
struct DIstruct {SItype high, low;};
typedef union
{
struct DIstruct s;
DItype ll;
} DIunion;
DItype
__lshrdi3 (DItype u, word_type b)
{
DIunion w;
word_type bm;
DIunion uu;
if (b == 0)
return u;
uu.ll = u;
bm = (sizeof (SItype) * BITS_PER_UNIT) - b;
if (bm <= 0)
{
w.s.high = 0;
w.s.low = (USItype)uu.s.high >> -bm;
}
else
{
USItype carries = (USItype)uu.s.high << bm;
w.s.high = (USItype)uu.s.high >> b;
w.s.low = ((USItype)uu.s.low >> b) | carries;
}
return w.ll;
}

92
arch/v850/lib/memcpy.c Normal file
View File

@@ -0,0 +1,92 @@
/*
* arch/v850/lib/memcpy.c -- Memory copying
*
* 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/types.h>
#include <asm/string.h>
#define CHUNK_SIZE 32 /* bytes */
#define CHUNK_ALIGNED(addr) (((unsigned long)addr & 0x3) == 0)
/* Note that this macro uses 8 call-clobbered registers (not including
R1), which are few enough so that the following functions don't need
to spill anything to memory. It also uses R1, which is nominally
reserved for the assembler, but here it should be OK. */
#define COPY_CHUNK(src, dst) \
asm ("mov %0, ep;" \
"sld.w 0[ep], r1; sld.w 4[ep], r12;" \
"sld.w 8[ep], r13; sld.w 12[ep], r14;" \
"sld.w 16[ep], r15; sld.w 20[ep], r17;" \
"sld.w 24[ep], r18; sld.w 28[ep], r19;" \
"mov %1, ep;" \
"sst.w r1, 0[ep]; sst.w r12, 4[ep];" \
"sst.w r13, 8[ep]; sst.w r14, 12[ep];" \
"sst.w r15, 16[ep]; sst.w r17, 20[ep];" \
"sst.w r18, 24[ep]; sst.w r19, 28[ep]" \
:: "r" (src), "r" (dst) \
: "r1", "r12", "r13", "r14", "r15", \
"r17", "r18", "r19", "ep", "memory");
void *memcpy (void *dst, const void *src, __kernel_size_t size)
{
char *_dst = dst;
const char *_src = src;
if (size >= CHUNK_SIZE && CHUNK_ALIGNED(_src) && CHUNK_ALIGNED(_dst)) {
/* Copy large blocks efficiently. */
unsigned count;
for (count = size / CHUNK_SIZE; count; count--) {
COPY_CHUNK (_src, _dst);
_src += CHUNK_SIZE;
_dst += CHUNK_SIZE;
}
size %= CHUNK_SIZE;
}
if (size > 0)
do
*_dst++ = *_src++;
while (--size);
return dst;
}
void *memmove (void *dst, const void *src, __kernel_size_t size)
{
if ((unsigned long)dst < (unsigned long)src
|| (unsigned long)src + size < (unsigned long)dst)
return memcpy (dst, src, size);
else {
char *_dst = dst + size;
const char *_src = src + size;
if (size >= CHUNK_SIZE
&& CHUNK_ALIGNED (_src) && CHUNK_ALIGNED (_dst))
{
/* Copy large blocks efficiently. */
unsigned count;
for (count = size / CHUNK_SIZE; count; count--) {
_src -= CHUNK_SIZE;
_dst -= CHUNK_SIZE;
COPY_CHUNK (_src, _dst);
}
size %= CHUNK_SIZE;
}
if (size > 0)
do
*--_dst = *--_src;
while (--size);
return _dst;
}
}

68
arch/v850/lib/memset.c Normal file
View File

@@ -0,0 +1,68 @@
/*
* arch/v850/lib/memset.c -- Memory initialization
*
* Copyright (C) 2001,02,04 NEC Corporation
* Copyright (C) 2001,02,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/types.h>
void *memset (void *dst, int val, __kernel_size_t count)
{
if (count) {
register unsigned loop;
register void *ptr asm ("ep") = dst;
/* replicate VAL into a long. */
val &= 0xff;
val |= val << 8;
val |= val << 16;
/* copy initial unaligned bytes. */
if ((long)ptr & 1) {
*(char *)ptr = val;
ptr = (void *)((char *)ptr + 1);
count--;
}
if (count > 2 && ((long)ptr & 2)) {
*(short *)ptr = val;
ptr = (void *)((short *)ptr + 1);
count -= 2;
}
/* 32-byte copying loop. */
for (loop = count / 32; loop; loop--) {
asm ("sst.w %0, 0[ep]; sst.w %0, 4[ep];"
"sst.w %0, 8[ep]; sst.w %0, 12[ep];"
"sst.w %0, 16[ep]; sst.w %0, 20[ep];"
"sst.w %0, 24[ep]; sst.w %0, 28[ep]"
:: "r" (val) : "memory");
ptr += 32;
}
count %= 32;
/* long copying loop. */
for (loop = count / 4; loop; loop--) {
*(long *)ptr = val;
ptr = (void *)((long *)ptr + 1);
}
count %= 4;
/* finish up with any trailing bytes. */
if (count & 2) {
*(short *)ptr = val;
ptr = (void *)((short *)ptr + 1);
}
if (count & 1) {
*(char *)ptr = val;
}
}
return dst;
}

61
arch/v850/lib/muldi3.c Normal file
View File

@@ -0,0 +1,61 @@
/* muldi3.c extracted from gcc-2.7.2.3/libgcc2.c and
gcc-2.7.2.3/longlong.h which is: */
/* Copyright (C) 1989, 1992, 1993, 1994, 1995, 2001 Free Software Foundation, Inc.
This file is part of GNU CC.
GNU CC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
GNU CC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GNU CC; see the file COPYING. If not, write to
the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
#define umul_ppmm(w1, w0, u, v) \
__asm__ ("mulu %3, %0, %1" \
: "=r" ((USItype)(w0)), \
"=r" ((USItype)(w1)) \
: "%0" ((USItype)(u)), \
"r" ((USItype)(v)))
#define __umulsidi3(u, v) \
({DIunion __w; \
umul_ppmm (__w.s.high, __w.s.low, u, v); \
__w.ll; })
typedef int SItype __attribute__ ((mode (SI)));
typedef unsigned int USItype __attribute__ ((mode (SI)));
typedef int DItype __attribute__ ((mode (DI)));
typedef int word_type __attribute__ ((mode (__word__)));
struct DIstruct {SItype high, low;};
typedef union
{
struct DIstruct s;
DItype ll;
} DIunion;
DItype
__muldi3 (DItype u, DItype v)
{
DIunion w;
DIunion uu, vv;
uu.ll = u,
vv.ll = v;
w.ll = __umulsidi3 (uu.s.low, vv.s.low);
w.s.high += ((USItype) uu.s.low * (USItype) vv.s.high
+ (USItype) uu.s.high * (USItype) vv.s.low);
return w.ll;
}

25
arch/v850/lib/negdi2.c Normal file
View File

@@ -0,0 +1,25 @@
/*
* arch/v850/lib/negdi2.c -- 64-bit negation
*
* 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>
*/
typedef int DItype __attribute__ ((mode (DI)));
DItype __negdi2 (DItype x)
{
__asm__ __volatile__
("not r6, r10;"
"add 1, r10;"
"setf c, r6;"
"not r7, r11;"
"add r6, r11"
::: "r6", "r7", "r10", "r11");
}