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:
316
arch/v850/Kconfig
Normal file
316
arch/v850/Kconfig
Normal 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
10
arch/v850/Kconfig.debug
Normal 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
63
arch/v850/Makefile
Normal 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
32
arch/v850/README
Normal 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
40
arch/v850/kernel/Makefile
Normal 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
|
16
arch/v850/kernel/anna-rom.ld
Normal file
16
arch/v850/kernel/anna-rom.ld
Normal 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
208
arch/v850/kernel/anna.c
Normal 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
20
arch/v850/kernel/anna.ld
Normal 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
|
||||
}
|
21
arch/v850/kernel/as85ep1-rom.ld
Normal file
21
arch/v850/kernel/as85ep1-rom.ld
Normal 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
240
arch/v850/kernel/as85ep1.c
Normal 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;
|
||||
}
|
||||
}
|
49
arch/v850/kernel/as85ep1.ld
Normal file
49
arch/v850/kernel/as85ep1.ld
Normal 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
|
||||
}
|
61
arch/v850/kernel/asm-consts.c
Normal file
61
arch/v850/kernel/asm-consts.c
Normal 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
142
arch/v850/kernel/bug.c
Normal 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
1121
arch/v850/kernel/entry.S
Normal file
File diff suppressed because it is too large
Load Diff
171
arch/v850/kernel/fpga85e2c.c
Normal file
171
arch/v850/kernel/fpga85e2c.c
Normal 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), ®_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", ®_snap_dev_id, 0
|
||||
};
|
62
arch/v850/kernel/fpga85e2c.ld
Normal file
62
arch/v850/kernel/fpga85e2c.ld
Normal 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
271
arch/v850/kernel/gbus_int.c
Normal 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
128
arch/v850/kernel/head.S
Normal 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)
|
132
arch/v850/kernel/highres_timer.c
Normal file
132
arch/v850/kernel/highres_timer.c
Normal 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);
|
||||
}
|
49
arch/v850/kernel/init_task.c
Normal file
49
arch/v850/kernel/init_task.c
Normal 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
87
arch/v850/kernel/intv.S
Normal 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
744
arch/v850/kernel/irq.c
Normal 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
70
arch/v850/kernel/ma.c
Normal 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
17
arch/v850/kernel/mach.c
Normal 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
56
arch/v850/kernel/mach.h
Normal 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
74
arch/v850/kernel/me2.c
Normal 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
135
arch/v850/kernel/memcons.c
Normal 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
237
arch/v850/kernel/module.c
Normal 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
236
arch/v850/kernel/process.c
Normal 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
67
arch/v850/kernel/procfs.c
Normal 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
282
arch/v850/kernel/ptrace.c
Normal 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
200
arch/v850/kernel/rte_cb.c
Normal 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 */
|
||||
}
|
138
arch/v850/kernel/rte_cb_leds.c
Normal file
138
arch/v850/kernel/rte_cb_leds.c
Normal 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);
|
121
arch/v850/kernel/rte_cb_multi.c
Normal file
121
arch/v850/kernel/rte_cb_multi.c
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
14
arch/v850/kernel/rte_ma1_cb-rom.ld
Normal file
14
arch/v850/kernel/rte_ma1_cb-rom.ld
Normal 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)
|
||||
}
|
106
arch/v850/kernel/rte_ma1_cb.c
Normal file
106
arch/v850/kernel/rte_ma1_cb.c
Normal 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.*/
|
||||
}
|
57
arch/v850/kernel/rte_ma1_cb.ld
Normal file
57
arch/v850/kernel/rte_ma1_cb.ld
Normal 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
|
||||
}
|
796
arch/v850/kernel/rte_mb_a_pci.c
Normal file
796
arch/v850/kernel/rte_mb_a_pci.c
Normal 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);
|
300
arch/v850/kernel/rte_me2_cb.c
Normal file
300
arch/v850/kernel/rte_me2_cb.c
Normal 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);
|
||||
}
|
||||
}
|
30
arch/v850/kernel/rte_me2_cb.ld
Normal file
30
arch/v850/kernel/rte_me2_cb.ld
Normal 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
|
||||
}
|
57
arch/v850/kernel/rte_nb85e_cb-multi.ld
Normal file
57
arch/v850/kernel/rte_nb85e_cb-multi.ld
Normal 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
|
||||
}
|
82
arch/v850/kernel/rte_nb85e_cb.c
Normal file
82
arch/v850/kernel/rte_nb85e_cb.c
Normal 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 ();
|
||||
}
|
22
arch/v850/kernel/rte_nb85e_cb.ld
Normal file
22
arch/v850/kernel/rte_nb85e_cb.ld
Normal 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
|
||||
}
|
166
arch/v850/kernel/semaphore.c
Normal file
166
arch/v850/kernel/semaphore.c
Normal 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
286
arch/v850/kernel/setup.c
Normal 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
525
arch/v850/kernel/signal.c
Normal 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(¤t->sighand->siglock);
|
||||
saveset = current->blocked;
|
||||
siginitset(¤t->blocked, mask);
|
||||
recalc_sigpending();
|
||||
spin_unlock_irq(¤t->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(¤t->sighand->siglock);
|
||||
saveset = current->blocked;
|
||||
current->blocked = newset;
|
||||
recalc_sigpending();
|
||||
spin_unlock_irq(¤t->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(¤t->sighand->siglock);
|
||||
current->blocked = set;
|
||||
recalc_sigpending();
|
||||
spin_unlock_irq(¤t->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(¤t->sighand->siglock);
|
||||
current->blocked = set;
|
||||
recalc_sigpending();
|
||||
spin_unlock_irq(¤t->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(¤t->sighand->siglock);
|
||||
sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask);
|
||||
sigaddset(¤t->blocked,sig);
|
||||
recalc_sigpending();
|
||||
spin_unlock_irq(¤t->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 = ¤t->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
179
arch/v850/kernel/sim.c
Normal 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
13
arch/v850/kernel/sim.ld
Normal 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
201
arch/v850/kernel/sim85e2.c
Normal 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);
|
36
arch/v850/kernel/sim85e2.ld
Normal file
36
arch/v850/kernel/sim85e2.ld
Normal 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
166
arch/v850/kernel/simcons.c
Normal 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
197
arch/v850/kernel/syscalls.c
Normal 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 (¤t->mm->mmap_sem);
|
||||
ret = do_mmap_pgoff (file, addr, len, prot, flags, pgoff);
|
||||
up_write (¤t->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
63
arch/v850/kernel/teg.c
Normal 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
198
arch/v850/kernel/time.c
Normal 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);
|
||||
}
|
78
arch/v850/kernel/v850_ksyms.c
Normal file
78
arch/v850/kernel/v850_ksyms.c
Normal 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);
|
127
arch/v850/kernel/v850e2_cache.c
Normal file
127
arch/v850/kernel/v850e2_cache.c
Normal 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);
|
||||
}
|
174
arch/v850/kernel/v850e_cache.c
Normal file
174
arch/v850/kernel/v850e_cache.c
Normal 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 ();
|
||||
}
|
104
arch/v850/kernel/v850e_intc.c
Normal file
104
arch/v850/kernel/v850e_intc.c
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
54
arch/v850/kernel/v850e_timer_d.c
Normal file
54
arch/v850/kernel/v850e_timer_d.c
Normal 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;
|
||||
}
|
62
arch/v850/kernel/v850e_utils.c
Normal file
62
arch/v850/kernel/v850e_utils.c
Normal 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;
|
||||
}
|
285
arch/v850/kernel/vmlinux.lds.S
Normal file
285
arch/v850/kernel/vmlinux.lds.S
Normal 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
6
arch/v850/lib/Makefile
Normal 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
62
arch/v850/lib/ashldi3.c
Normal 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
63
arch/v850/lib/ashrdi3.c
Normal 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
154
arch/v850/lib/checksum.c
Normal 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
62
arch/v850/lib/lshrdi3.c
Normal 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
92
arch/v850/lib/memcpy.c
Normal 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
68
arch/v850/lib/memset.c
Normal 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
61
arch/v850/lib/muldi3.c
Normal 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
25
arch/v850/lib/negdi2.c
Normal 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");
|
||||
}
|
Reference in New Issue
Block a user