aboutsummaryrefslogtreecommitdiff
path: root/arch/sh/kernel
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /arch/sh/kernel
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!
Diffstat (limited to 'arch/sh/kernel')
-rw-r--r--arch/sh/kernel/Makefile22
-rw-r--r--arch/sh/kernel/asm-offsets.c32
-rw-r--r--arch/sh/kernel/cf-enabler.c158
-rw-r--r--arch/sh/kernel/cpu/Makefile16
-rw-r--r--arch/sh/kernel/cpu/adc.c36
-rw-r--r--arch/sh/kernel/cpu/bus.c195
-rw-r--r--arch/sh/kernel/cpu/init.c222
-rw-r--r--arch/sh/kernel/cpu/irq_imask.c116
-rw-r--r--arch/sh/kernel/cpu/irq_ipr.c339
-rw-r--r--arch/sh/kernel/cpu/rtc.c136
-rw-r--r--arch/sh/kernel/cpu/sh2/Makefile6
-rw-r--r--arch/sh/kernel/cpu/sh2/probe.c39
-rw-r--r--arch/sh/kernel/cpu/sh3/Makefile6
-rw-r--r--arch/sh/kernel/cpu/sh3/ex.S199
-rw-r--r--arch/sh/kernel/cpu/sh3/probe.c97
-rw-r--r--arch/sh/kernel/cpu/sh4/Makefile10
-rw-r--r--arch/sh/kernel/cpu/sh4/ex.S384
-rw-r--r--arch/sh/kernel/cpu/sh4/fpu.c335
-rw-r--r--arch/sh/kernel/cpu/sh4/irq_intc2.c222
-rw-r--r--arch/sh/kernel/cpu/sh4/probe.c138
-rw-r--r--arch/sh/kernel/cpu/sh4/sq.c453
-rw-r--r--arch/sh/kernel/cpu/ubc.S59
-rw-r--r--arch/sh/kernel/cpufreq.c218
-rw-r--r--arch/sh/kernel/early_printk.c137
-rw-r--r--arch/sh/kernel/entry.S1149
-rw-r--r--arch/sh/kernel/head.S76
-rw-r--r--arch/sh/kernel/init_task.c36
-rw-r--r--arch/sh/kernel/io.c59
-rw-r--r--arch/sh/kernel/io_generic.c243
-rw-r--r--arch/sh/kernel/irq.c106
-rw-r--r--arch/sh/kernel/kgdb_jmp.S33
-rw-r--r--arch/sh/kernel/kgdb_stub.c1491
-rw-r--r--arch/sh/kernel/module.c146
-rw-r--r--arch/sh/kernel/process.c531
-rw-r--r--arch/sh/kernel/ptrace.c320
-rw-r--r--arch/sh/kernel/semaphore.c139
-rw-r--r--arch/sh/kernel/setup.c649
-rw-r--r--arch/sh/kernel/sh_bios.c75
-rw-r--r--arch/sh/kernel/sh_ksyms.c126
-rw-r--r--arch/sh/kernel/signal.c607
-rw-r--r--arch/sh/kernel/smp.c199
-rw-r--r--arch/sh/kernel/sys_sh.c289
-rw-r--r--arch/sh/kernel/time.c657
-rw-r--r--arch/sh/kernel/traps.c712
-rw-r--r--arch/sh/kernel/vmlinux.lds.S155
45 files changed, 11373 insertions, 0 deletions
diff --git a/arch/sh/kernel/Makefile b/arch/sh/kernel/Makefile
new file mode 100644
index 00000000000..8b819698df1
--- /dev/null
+++ b/arch/sh/kernel/Makefile
@@ -0,0 +1,22 @@
+#
+# Makefile for the Linux/SuperH kernel.
+#
+
+extra-y := head.o init_task.o vmlinux.lds
+
+obj-y := process.o signal.o entry.o traps.o irq.o \
+ ptrace.o setup.o time.o sys_sh.o semaphore.o \
+ io.o io_generic.o sh_ksyms.o
+
+obj-y += cpu/
+
+obj-$(CONFIG_SMP) += smp.o
+obj-$(CONFIG_CF_ENABLER) += cf-enabler.o
+obj-$(CONFIG_SH_STANDARD_BIOS) += sh_bios.o
+obj-$(CONFIG_SH_KGDB) += kgdb_stub.o kgdb_jmp.o
+obj-$(CONFIG_SH_CPU_FREQ) += cpufreq.o
+obj-$(CONFIG_MODULES) += module.o
+obj-$(CONFIG_EARLY_PRINTK) += early_printk.o
+
+USE_STANDARD_AS_RULE := true
+
diff --git a/arch/sh/kernel/asm-offsets.c b/arch/sh/kernel/asm-offsets.c
new file mode 100644
index 00000000000..dc6725c51a8
--- /dev/null
+++ b/arch/sh/kernel/asm-offsets.c
@@ -0,0 +1,32 @@
+/*
+ * 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/types.h>
+#include <linux/mm.h>
+#include <asm/thread_info.h>
+
+#define DEFINE(sym, val) \
+ asm volatile("\n->" #sym " %0 " #val : : "i" (val))
+
+#define BLANK() asm volatile("\n->" : : )
+
+int main(void)
+{
+ /* offsets into the thread_info struct */
+ DEFINE(TI_TASK, offsetof(struct thread_info, task));
+ DEFINE(TI_EXEC_DOMAIN, offsetof(struct thread_info, exec_domain));
+ DEFINE(TI_FLAGS, offsetof(struct thread_info, flags));
+ DEFINE(TI_CPU, offsetof(struct thread_info, cpu));
+ DEFINE(TI_PRE_COUNT, offsetof(struct thread_info, preempt_count));
+ DEFINE(TI_RESTART_BLOCK,offsetof(struct thread_info, restart_block));
+
+ return 0;
+}
diff --git a/arch/sh/kernel/cf-enabler.c b/arch/sh/kernel/cf-enabler.c
new file mode 100644
index 00000000000..7a3b18faa27
--- /dev/null
+++ b/arch/sh/kernel/cf-enabler.c
@@ -0,0 +1,158 @@
+/* $Id: cf-enabler.c,v 1.4 2004/02/22 22:44:36 kkojima Exp $
+ *
+ * linux/drivers/block/cf-enabler.c
+ *
+ * Copyright (C) 1999 Niibe Yutaka
+ * Copyright (C) 2000 Toshiharu Nozawa
+ * Copyright (C) 2001 A&D Co., Ltd.
+ *
+ * Enable the CF configuration.
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+
+/*
+ * You can connect Compact Flash directly to the bus of SuperH.
+ * This is the enabler for that.
+ *
+ * SIM: How generic is this really? It looks pretty board, or at
+ * least SH sub-type, specific to me.
+ * I know it doesn't work on the Overdrive!
+ */
+
+/*
+ * 0xB8000000 : Attribute
+ * 0xB8001000 : Common Memory
+ * 0xBA000000 : I/O
+ */
+#if defined(CONFIG_IDE) && defined(CONFIG_CPU_SH4)
+/* SH4 can't access PCMCIA interface through P2 area.
+ * we must remap it with appropreate attribute bit of the page set.
+ * this part is based on Greg Banks' hd64465_ss.c implementation - Masahiro Abe */
+#include <linux/mm.h>
+#include <linux/vmalloc.h>
+
+#if defined(CONFIG_CF_AREA6)
+#define slot_no 0
+#else
+#define slot_no 1
+#endif
+
+/* defined in mm/ioremap.c */
+extern void * p3_ioremap(unsigned long phys_addr, unsigned long size, unsigned long flags);
+
+/* use this pointer to access to directly connected compact flash io area*/
+void *cf_io_base;
+
+static int __init allocate_cf_area(void)
+{
+ pgprot_t prot;
+ unsigned long paddrbase, psize;
+
+ /* open I/O area window */
+ paddrbase = virt_to_phys((void*)CONFIG_CF_BASE_ADDR);
+ psize = PAGE_SIZE;
+ prot = PAGE_KERNEL_PCC(slot_no, _PAGE_PCC_IO16);
+ cf_io_base = p3_ioremap(paddrbase, psize, prot.pgprot);
+ if (!cf_io_base) {
+ printk("allocate_cf_area : can't open CF I/O window!\n");
+ return -ENOMEM;
+ }
+/* printk("p3_ioremap(paddr=0x%08lx, psize=0x%08lx, prot=0x%08lx)=0x%08lx\n",
+ paddrbase, psize, prot.pgprot, cf_io_base);*/
+
+ /* XXX : do we need attribute and common-memory area also? */
+
+ return 0;
+}
+#endif
+
+static int __init cf_init_default(void)
+{
+/* You must have enabled the card, and set the level interrupt
+ * before reaching this point. Possibly in boot ROM or boot loader.
+ */
+#if defined(CONFIG_IDE) && defined(CONFIG_CPU_SH4)
+ allocate_cf_area();
+#endif
+#if defined(CONFIG_SH_UNKNOWN)
+ /* This should be done in each board's init_xxx_irq. */
+ make_imask_irq(14);
+ disable_irq(14);
+#endif
+ return 0;
+}
+
+#if defined(CONFIG_SH_SOLUTION_ENGINE)
+#include <asm/se/se.h>
+
+/*
+ * SolutionEngine
+ *
+ * 0xB8400000 : Common Memory
+ * 0xB8500000 : Attribute
+ * 0xB8600000 : I/O
+ */
+
+static int __init cf_init_se(void)
+{
+ if ((ctrl_inw(MRSHPC_CSR) & 0x000c) != 0)
+ return 0; /* Not detected */
+
+ if ((ctrl_inw(MRSHPC_CSR) & 0x0080) == 0) {
+ ctrl_outw(0x0674, MRSHPC_CPWCR); /* Card Vcc is 3.3v? */
+ } else {
+ ctrl_outw(0x0678, MRSHPC_CPWCR); /* Card Vcc is 5V */
+ }
+
+ /*
+ * PC-Card window open
+ * flag == COMMON/ATTRIBUTE/IO
+ */
+ /* common window open */
+ ctrl_outw(0x8a84, MRSHPC_MW0CR1);/* window 0xb8400000 */
+ if((ctrl_inw(MRSHPC_CSR) & 0x4000) != 0)
+ /* common mode & bus width 16bit SWAP = 1*/
+ ctrl_outw(0x0b00, MRSHPC_MW0CR2);
+ else
+ /* common mode & bus width 16bit SWAP = 0*/
+ ctrl_outw(0x0300, MRSHPC_MW0CR2);
+
+ /* attribute window open */
+ ctrl_outw(0x8a85, MRSHPC_MW1CR1);/* window 0xb8500000 */
+ if ((ctrl_inw(MRSHPC_CSR) & 0x4000) != 0)
+ /* attribute mode & bus width 16bit SWAP = 1*/
+ ctrl_outw(0x0a00, MRSHPC_MW1CR2);
+ else
+ /* attribute mode & bus width 16bit SWAP = 0*/
+ ctrl_outw(0x0200, MRSHPC_MW1CR2);
+
+ /* I/O window open */
+ ctrl_outw(0x8a86, MRSHPC_IOWCR1);/* I/O window 0xb8600000 */
+ ctrl_outw(0x0008, MRSHPC_CDCR); /* I/O card mode */
+ if ((ctrl_inw(MRSHPC_CSR) & 0x4000) != 0)
+ ctrl_outw(0x0a00, MRSHPC_IOWCR2); /* bus width 16bit SWAP = 1*/
+ else
+ ctrl_outw(0x0200, MRSHPC_IOWCR2); /* bus width 16bit SWAP = 0*/
+
+ ctrl_outw(0x2000, MRSHPC_ICR);
+ ctrl_outb(0x00, PA_MRSHPC_MW2 + 0x206);
+ ctrl_outb(0x42, PA_MRSHPC_MW2 + 0x200);
+ return 0;
+}
+#endif
+
+int __init cf_init(void)
+{
+#if defined(CONFIG_SH_SOLUTION_ENGINE)
+ if (MACH_SE)
+ return cf_init_se();
+#endif
+ return cf_init_default();
+}
+
+__initcall (cf_init);
diff --git a/arch/sh/kernel/cpu/Makefile b/arch/sh/kernel/cpu/Makefile
new file mode 100644
index 00000000000..cd43714df61
--- /dev/null
+++ b/arch/sh/kernel/cpu/Makefile
@@ -0,0 +1,16 @@
+#
+# Makefile for the Linux/SuperH CPU-specifc backends.
+#
+
+obj-y := irq_ipr.o irq_imask.o init.o bus.o
+
+obj-$(CONFIG_CPU_SH2) += sh2/
+obj-$(CONFIG_CPU_SH3) += sh3/
+obj-$(CONFIG_CPU_SH4) += sh4/
+
+obj-$(CONFIG_SH_RTC) += rtc.o
+obj-$(CONFIG_UBC_WAKEUP) += ubc.o
+obj-$(CONFIG_SH_ADC) += adc.o
+
+USE_STANDARD_AS_RULE := true
+
diff --git a/arch/sh/kernel/cpu/adc.c b/arch/sh/kernel/cpu/adc.c
new file mode 100644
index 00000000000..da3d6877f93
--- /dev/null
+++ b/arch/sh/kernel/cpu/adc.c
@@ -0,0 +1,36 @@
+/*
+ * linux/arch/sh/kernel/adc.c -- SH3 on-chip ADC support
+ *
+ * Copyright (C) 2004 Andriy Skulysh <askulysh@image.kiev.ua>
+ */
+
+#include <linux/module.h>
+#include <asm/adc.h>
+#include <asm/io.h>
+
+
+int adc_single(unsigned int channel)
+{
+ int off;
+ unsigned char csr;
+
+ if (channel >= 8) return -1;
+
+ off = (channel & 0x03) << 2;
+
+ csr = ctrl_inb(ADCSR);
+ csr = channel | ADCSR_ADST | ADCSR_CKS;
+ ctrl_outb(csr, ADCSR);
+
+ do {
+ csr = ctrl_inb(ADCSR);
+ } while ((csr & ADCSR_ADF) == 0);
+
+ csr &= ~(ADCSR_ADF | ADCSR_ADST);
+ ctrl_outb(csr, ADCSR);
+
+ return (((ctrl_inb(ADDRAH + off) << 8) |
+ ctrl_inb(ADDRAL + off)) >> 6);
+}
+
+EXPORT_SYMBOL(adc_single);
diff --git a/arch/sh/kernel/cpu/bus.c b/arch/sh/kernel/cpu/bus.c
new file mode 100644
index 00000000000..ace82f4b4a5
--- /dev/null
+++ b/arch/sh/kernel/cpu/bus.c
@@ -0,0 +1,195 @@
+/*
+ * arch/sh/kernel/cpu/bus.c
+ *
+ * Virtual bus for SuperH.
+ *
+ * Copyright (C) 2004 Paul Mundt
+ *
+ * Shamelessly cloned from arch/arm/mach-omap/bus.c, which was written
+ * by:
+ *
+ * Copyright (C) 2003 - 2004 Nokia Corporation
+ * Written by Tony Lindgren <tony@atomide.com>
+ * Portions of code based on sa1111.c.
+ *
+ * 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.
+ */
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <asm/bus-sh.h>
+
+static int sh_bus_match(struct device *dev, struct device_driver *drv)
+{
+ struct sh_driver *shdrv = to_sh_driver(drv);
+ struct sh_dev *shdev = to_sh_dev(dev);
+
+ return shdev->dev_id == shdrv->dev_id;
+}
+
+static int sh_bus_suspend(struct device *dev, u32 state)
+{
+ struct sh_dev *shdev = to_sh_dev(dev);
+ struct sh_driver *shdrv = to_sh_driver(dev->driver);
+
+ if (shdrv && shdrv->suspend)
+ return shdrv->suspend(shdev, state);
+
+ return 0;
+}
+
+static int sh_bus_resume(struct device *dev)
+{
+ struct sh_dev *shdev = to_sh_dev(dev);
+ struct sh_driver *shdrv = to_sh_driver(dev->driver);
+
+ if (shdrv && shdrv->resume)
+ return shdrv->resume(shdev);
+
+ return 0;
+}
+
+static struct device sh_bus_devices[SH_NR_BUSES] = {
+ {
+ .bus_id = SH_BUS_NAME_VIRT,
+ },
+};
+
+struct bus_type sh_bus_types[SH_NR_BUSES] = {
+ {
+ .name = SH_BUS_NAME_VIRT,
+ .match = sh_bus_match,
+ .suspend = sh_bus_suspend,
+ .resume = sh_bus_resume,
+ },
+};
+
+static int sh_device_probe(struct device *dev)
+{
+ struct sh_dev *shdev = to_sh_dev(dev);
+ struct sh_driver *shdrv = to_sh_driver(dev->driver);
+
+ if (shdrv && shdrv->probe)
+ return shdrv->probe(shdev);
+
+ return -ENODEV;
+}
+
+static int sh_device_remove(struct device *dev)
+{
+ struct sh_dev *shdev = to_sh_dev(dev);
+ struct sh_driver *shdrv = to_sh_driver(dev->driver);
+
+ if (shdrv && shdrv->remove)
+ return shdrv->remove(shdev);
+
+ return 0;
+}
+
+int sh_device_register(struct sh_dev *dev)
+{
+ if (!dev)
+ return -EINVAL;
+
+ if (dev->bus_id < 0 || dev->bus_id >= SH_NR_BUSES) {
+ printk(KERN_ERR "%s: bus_id invalid: %s bus: %d\n",
+ __FUNCTION__, dev->name, dev->bus_id);
+ return -EINVAL;
+ }
+
+ dev->dev.parent = &sh_bus_devices[dev->bus_id];
+ dev->dev.bus = &sh_bus_types[dev->bus_id];
+
+ /* This is needed for USB OHCI to work */
+ if (dev->dma_mask)
+ dev->dev.dma_mask = dev->dma_mask;
+
+ snprintf(dev->dev.bus_id, BUS_ID_SIZE, "%s%u",
+ dev->name, dev->dev_id);
+
+ printk(KERN_INFO "Registering SH device '%s'. Parent at %s\n",
+ dev->dev.bus_id, dev->dev.parent->bus_id);
+
+ return device_register(&dev->dev);
+}
+
+void sh_device_unregister(struct sh_dev *dev)
+{
+ device_unregister(&dev->dev);
+}
+
+int sh_driver_register(struct sh_driver *drv)
+{
+ if (!drv)
+ return -EINVAL;
+
+ if (drv->bus_id < 0 || drv->bus_id >= SH_NR_BUSES) {
+ printk(KERN_ERR "%s: bus_id invalid: bus: %d device %d\n",
+ __FUNCTION__, drv->bus_id, drv->dev_id);
+ return -EINVAL;
+ }
+
+ drv->drv.probe = sh_device_probe;
+ drv->drv.remove = sh_device_remove;
+ drv->drv.bus = &sh_bus_types[drv->bus_id];
+
+ return driver_register(&drv->drv);
+}
+
+void sh_driver_unregister(struct sh_driver *drv)
+{
+ driver_unregister(&drv->drv);
+}
+
+static int __init sh_bus_init(void)
+{
+ int i, ret = 0;
+
+ for (i = 0; i < SH_NR_BUSES; i++) {
+ ret = device_register(&sh_bus_devices[i]);
+ if (ret != 0) {
+ printk(KERN_ERR "Unable to register bus device %s\n",
+ sh_bus_devices[i].bus_id);
+ continue;
+ }
+
+ ret = bus_register(&sh_bus_types[i]);
+ if (ret != 0) {
+ printk(KERN_ERR "Unable to register bus %s\n",
+ sh_bus_types[i].name);
+ device_unregister(&sh_bus_devices[i]);
+ }
+ }
+
+ printk(KERN_INFO "SH Virtual Bus initialized\n");
+
+ return ret;
+}
+
+static void __exit sh_bus_exit(void)
+{
+ int i;
+
+ for (i = 0; i < SH_NR_BUSES; i++) {
+ bus_unregister(&sh_bus_types[i]);
+ device_unregister(&sh_bus_devices[i]);
+ }
+}
+
+module_init(sh_bus_init);
+module_exit(sh_bus_exit);
+
+MODULE_AUTHOR("Paul Mundt <lethal@linux-sh.org>");
+MODULE_DESCRIPTION("SH Virtual Bus");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(sh_bus_types);
+EXPORT_SYMBOL(sh_device_register);
+EXPORT_SYMBOL(sh_device_unregister);
+EXPORT_SYMBOL(sh_driver_register);
+EXPORT_SYMBOL(sh_driver_unregister);
+
diff --git a/arch/sh/kernel/cpu/init.c b/arch/sh/kernel/cpu/init.c
new file mode 100644
index 00000000000..cf94e8ef17c
--- /dev/null
+++ b/arch/sh/kernel/cpu/init.c
@@ -0,0 +1,222 @@
+/*
+ * arch/sh/kernel/cpu/init.c
+ *
+ * CPU init code
+ *
+ * Copyright (C) 2002, 2003 Paul Mundt
+ *
+ * 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/init.h>
+#include <linux/kernel.h>
+#include <asm/processor.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <asm/cacheflush.h>
+#include <asm/cache.h>
+#include <asm/io.h>
+
+extern void detect_cpu_and_cache_system(void);
+
+/*
+ * Generic wrapper for command line arguments to disable on-chip
+ * peripherals (nofpu, nodsp, and so forth).
+ */
+#define onchip_setup(x) \
+static int x##_disabled __initdata = 0; \
+ \
+static int __init x##_setup(char *opts) \
+{ \
+ x##_disabled = 1; \
+ return 0; \
+} \
+__setup("no" __stringify(x), x##_setup);
+
+onchip_setup(fpu);
+onchip_setup(dsp);
+
+/*
+ * Generic first-level cache init
+ */
+static void __init cache_init(void)
+{
+ unsigned long ccr, flags;
+
+ if (cpu_data->type == CPU_SH_NONE)
+ panic("Unknown CPU");
+
+ jump_to_P2();
+ ccr = ctrl_inl(CCR);
+
+ /*
+ * If the cache is already enabled .. flush it.
+ */
+ if (ccr & CCR_CACHE_ENABLE) {
+ unsigned long ways, waysize, addrstart;
+
+ waysize = cpu_data->dcache.sets;
+
+ /*
+ * If the OC is already in RAM mode, we only have
+ * half of the entries to flush..
+ */
+ if (ccr & CCR_CACHE_ORA)
+ waysize >>= 1;
+
+ waysize <<= cpu_data->dcache.entry_shift;
+
+#ifdef CCR_CACHE_EMODE
+ /* If EMODE is not set, we only have 1 way to flush. */
+ if (!(ccr & CCR_CACHE_EMODE))
+ ways = 1;
+ else
+#endif
+ ways = cpu_data->dcache.ways;
+
+ addrstart = CACHE_OC_ADDRESS_ARRAY;
+ do {
+ unsigned long addr;
+
+ for (addr = addrstart;
+ addr < addrstart + waysize;
+ addr += cpu_data->dcache.linesz)
+ ctrl_outl(0, addr);
+
+ addrstart += cpu_data->dcache.way_incr;
+ } while (--ways);
+ }
+
+ /*
+ * Default CCR values .. enable the caches
+ * and invalidate them immediately..
+ */
+ flags = CCR_CACHE_ENABLE | CCR_CACHE_INVALIDATE;
+
+#ifdef CCR_CACHE_EMODE
+ /* Force EMODE if possible */
+ if (cpu_data->dcache.ways > 1)
+ flags |= CCR_CACHE_EMODE;
+#endif
+
+#ifdef CONFIG_SH_WRITETHROUGH
+ /* Turn on Write-through caching */
+ flags |= CCR_CACHE_WT;
+#else
+ /* .. or default to Write-back */
+ flags |= CCR_CACHE_CB;
+#endif
+
+#ifdef CONFIG_SH_OCRAM
+ /* Turn on OCRAM -- halve the OC */
+ flags |= CCR_CACHE_ORA;
+ cpu_data->dcache.sets >>= 1;
+#endif
+
+ ctrl_outl(flags, CCR);
+ back_to_P1();
+}
+
+#ifdef CONFIG_SH_DSP
+static void __init release_dsp(void)
+{
+ unsigned long sr;
+
+ /* Clear SR.DSP bit */
+ __asm__ __volatile__ (
+ "stc\tsr, %0\n\t"
+ "and\t%1, %0\n\t"
+ "ldc\t%0, sr\n\t"
+ : "=&r" (sr)
+ : "r" (~SR_DSP)
+ );
+}
+
+static void __init dsp_init(void)
+{
+ unsigned long sr;
+
+ /*
+ * Set the SR.DSP bit, wait for one instruction, and then read
+ * back the SR value.
+ */
+ __asm__ __volatile__ (
+ "stc\tsr, %0\n\t"
+ "or\t%1, %0\n\t"
+ "ldc\t%0, sr\n\t"
+ "nop\n\t"
+ "stc\tsr, %0\n\t"
+ : "=&r" (sr)
+ : "r" (SR_DSP)
+ );
+
+ /* If the DSP bit is still set, this CPU has a DSP */
+ if (sr & SR_DSP)
+ cpu_data->flags |= CPU_HAS_DSP;
+
+ /* Now that we've determined the DSP status, clear the DSP bit. */
+ release_dsp();
+}
+#endif /* CONFIG_SH_DSP */
+
+/**
+ * sh_cpu_init
+ *
+ * This is our initial entry point for each CPU, and is invoked on the boot
+ * CPU prior to calling start_kernel(). For SMP, a combination of this and
+ * start_secondary() will bring up each processor to a ready state prior
+ * to hand forking the idle loop.
+ *
+ * We do all of the basic processor init here, including setting up the
+ * caches, FPU, DSP, kicking the UBC, etc. By the time start_kernel() is
+ * hit (and subsequently platform_setup()) things like determining the
+ * CPU subtype and initial configuration will all be done.
+ *
+ * Each processor family is still responsible for doing its own probing
+ * and cache configuration in detect_cpu_and_cache_system().
+ */
+asmlinkage void __init sh_cpu_init(void)
+{
+ /* First, probe the CPU */
+ detect_cpu_and_cache_system();
+
+ /* Init the cache */
+ cache_init();
+
+ /* Disable the FPU */
+ if (fpu_disabled) {
+ printk("FPU Disabled\n");
+ cpu_data->flags &= ~CPU_HAS_FPU;
+ disable_fpu();
+ }
+
+ /* FPU initialization */
+ if ((cpu_data->flags & CPU_HAS_FPU)) {
+ clear_thread_flag(TIF_USEDFPU);
+ clear_used_math();
+ }
+
+#ifdef CONFIG_SH_DSP
+ /* Probe for DSP */
+ dsp_init();
+
+ /* Disable the DSP */
+ if (dsp_disabled) {
+ printk("DSP Disabled\n");
+ cpu_data->flags &= ~CPU_HAS_DSP;
+ release_dsp();
+ }
+#endif
+
+#ifdef CONFIG_UBC_WAKEUP
+ /*
+ * Some brain-damaged loaders decided it would be a good idea to put
+ * the UBC to sleep. This causes some issues when it comes to things
+ * like PTRACE_SINGLESTEP or doing hardware watchpoints in GDB. So ..
+ * we wake it up and hope that all is well.
+ */
+ ubc_wakeup();
+#endif
+}
+
diff --git a/arch/sh/kernel/cpu/irq_imask.c b/arch/sh/kernel/cpu/irq_imask.c
new file mode 100644
index 00000000000..f76901e732f
--- /dev/null
+++ b/arch/sh/kernel/cpu/irq_imask.c
@@ -0,0 +1,116 @@
+/* $Id: irq_imask.c,v 1.1.2.1 2002/11/17 10:53:43 mrbrown Exp $
+ *
+ * linux/arch/sh/kernel/irq_imask.c
+ *
+ * Copyright (C) 1999, 2000 Niibe Yutaka
+ *
+ * Simple interrupt handling using IMASK of SR register.
+ *
+ */
+
+/* NOTE: Will not work on level 15 */
+
+
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/kernel_stat.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/bitops.h>
+
+#include <asm/system.h>
+#include <asm/irq.h>
+
+#include <linux/spinlock.h>
+#include <linux/cache.h>
+#include <linux/irq.h>
+
+/* Bitmap of IRQ masked */
+static unsigned long imask_mask = 0x7fff;
+static int interrupt_priority = 0;
+
+static void enable_imask_irq(unsigned int irq);
+static void disable_imask_irq(unsigned int irq);
+static void shutdown_imask_irq(unsigned int irq);
+static void mask_and_ack_imask(unsigned int);
+static void end_imask_irq(unsigned int irq);
+
+#define IMASK_PRIORITY 15
+
+static unsigned int startup_imask_irq(unsigned int irq)
+{
+ /* Nothing to do */
+ return 0; /* never anything pending */
+}
+
+static struct hw_interrupt_type imask_irq_type = {
+ "SR.IMASK",
+ startup_imask_irq,
+ shutdown_imask_irq,
+ enable_imask_irq,
+ disable_imask_irq,
+ mask_and_ack_imask,
+ end_imask_irq
+};
+
+void static inline set_interrupt_registers(int ip)
+{
+ unsigned long __dummy;
+
+ asm volatile("ldc %2, r6_bank\n\t"
+ "stc sr, %0\n\t"
+ "and #0xf0, %0\n\t"
+ "shlr2 %0\n\t"
+ "cmp/eq #0x3c, %0\n\t"
+ "bt/s 1f ! CLI-ed\n\t"
+ " stc sr, %0\n\t"
+ "and %1, %0\n\t"
+ "or %2, %0\n\t"
+ "ldc %0, sr\n"
+ "1:"
+ : "=&z" (__dummy)
+ : "r" (~0xf0), "r" (ip << 4)
+ : "t");
+}
+
+static void disable_imask_irq(unsigned int irq)
+{
+ clear_bit(irq, &imask_mask);
+ if (interrupt_priority < IMASK_PRIORITY - irq)
+ interrupt_priority = IMASK_PRIORITY - irq;
+
+ set_interrupt_registers(interrupt_priority);
+}
+
+static void enable_imask_irq(unsigned int irq)
+{
+ set_bit(irq, &imask_mask);
+ interrupt_priority = IMASK_PRIORITY - ffz(imask_mask);
+
+ set_interrupt_registers(interrupt_priority);
+}
+
+static void mask_and_ack_imask(unsigned int irq)
+{
+ disable_imask_irq(irq);
+}
+
+static void end_imask_irq(unsigned int irq)
+{
+ if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
+ enable_imask_irq(irq);
+}
+
+static void shutdown_imask_irq(unsigned int irq)
+{
+ /* Nothing to do */
+}
+
+void make_imask_irq(unsigned int irq)
+{
+ disable_irq_nosync(irq);
+ irq_desc[irq].handler = &imask_irq_type;
+ enable_irq(irq);
+}
diff --git a/arch/sh/kernel/cpu/irq_ipr.c b/arch/sh/kernel/cpu/irq_ipr.c
new file mode 100644
index 00000000000..7ea3d2d030e
--- /dev/null
+++ b/arch/sh/kernel/cpu/irq_ipr.c
@@ -0,0 +1,339 @@
+/* $Id: irq_ipr.c,v 1.1.2.1 2002/11/17 10:53:43 mrbrown Exp $
+ *
+ * linux/arch/sh/kernel/irq_ipr.c
+ *
+ * Copyright (C) 1999 Niibe Yutaka & Takeshi Yaegashi
+ * Copyright (C) 2000 Kazumoto Kojima
+ * Copyright (C) 2003 Takashi Kusuda <kusuda-takashi@hitachi-ul.co.jp>
+ *
+ * Interrupt handling for IPR-based IRQ.
+ *
+ * Supported system:
+ * On-chip supporting modules (TMU, RTC, etc.).
+ * On-chip supporting modules for SH7709/SH7709A/SH7729/SH7300.
+ * Hitachi SolutionEngine external I/O:
+ * MS7709SE01, MS7709ASE01, and MS7750SE01
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/machvec.h>
+
+struct ipr_data {
+ unsigned int addr; /* Address of Interrupt Priority Register */
+ int shift; /* Shifts of the 16-bit data */
+ int priority; /* The priority */
+};
+static struct ipr_data ipr_data[NR_IRQS];
+
+static void enable_ipr_irq(unsigned int irq);
+static void disable_ipr_irq(unsigned int irq);
+
+/* shutdown is same as "disable" */
+#define shutdown_ipr_irq disable_ipr_irq
+
+static void mask_and_ack_ipr(unsigned int);
+static void end_ipr_irq(unsigned int irq);
+
+static unsigned int startup_ipr_irq(unsigned int irq)
+{
+ enable_ipr_irq(irq);
+ return 0; /* never anything pending */
+}
+
+static struct hw_interrupt_type ipr_irq_type = {
+ "IPR-IRQ",
+ startup_ipr_irq,
+ shutdown_ipr_irq,
+ enable_ipr_irq,
+ disable_ipr_irq,
+ mask_and_ack_ipr,
+ end_ipr_irq
+};
+
+static void disable_ipr_irq(unsigned int irq)
+{
+ unsigned long val, flags;
+ unsigned int addr = ipr_data[irq].addr;
+ unsigned short mask = 0xffff ^ (0x0f << ipr_data[irq].shift);
+
+ /* Set the priority in IPR to 0 */
+ local_irq_save(flags);
+ val = ctrl_inw(addr);
+ val &= mask;
+ ctrl_outw(val, addr);
+ local_irq_restore(flags);
+}
+
+static void enable_ipr_irq(unsigned int irq)
+{
+ unsigned long val, flags;
+ unsigned int addr = ipr_data[irq].addr;
+ int priority = ipr_data[irq].priority;
+ unsigned short value = (priority << ipr_data[irq].shift);
+
+ /* Set priority in IPR back to original value */
+ local_irq_save(flags);
+ val = ctrl_inw(addr);
+ val |= value;
+ ctrl_outw(val, addr);
+ local_irq_restore(flags);
+}
+
+static void mask_and_ack_ipr(unsigned int irq)
+{
+ disable_ipr_irq(irq);
+
+#if defined(CONFIG_CPU_SUBTYPE_SH7707) || defined(CONFIG_CPU_SUBTYPE_SH7709) || \
+ defined(CONFIG_CPU_SUBTYPE_SH7300) || defined(CONFIG_CPU_SUBTYPE_SH7705)
+ /* This is needed when we use edge triggered setting */
+ /* XXX: Is it really needed? */
+ if (IRQ0_IRQ <= irq && irq <= IRQ5_IRQ) {
+ /* Clear external interrupt request */
+ int a = ctrl_inb(INTC_IRR0);
+ a &= ~(1 << (irq - IRQ0_IRQ));
+ ctrl_outb(a, INTC_IRR0);
+ }
+#endif
+}
+
+static void end_ipr_irq(unsigned int irq)
+{
+ if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
+ enable_ipr_irq(irq);
+}
+
+void make_ipr_irq(unsigned int irq, unsigned int addr, int pos, int priority)
+{
+ disable_irq_nosync(irq);
+ ipr_data[irq].addr = addr;
+ ipr_data[irq].shift = pos*4; /* POSition (0-3) x 4 means shift */
+ ipr_data[irq].priority = priority;
+
+ irq_desc[irq].handler = &ipr_irq_type;
+ disable_ipr_irq(irq);
+}
+
+#if defined(CONFIG_CPU_SUBTYPE_SH7705) || \
+ defined(CONFIG_CPU_SUBTYPE_SH7707) || \
+ defined(CONFIG_CPU_SUBTYPE_SH7709)
+static unsigned char pint_map[256];
+static unsigned long portcr_mask = 0;
+
+static void enable_pint_irq(unsigned int irq);
+static void disable_pint_irq(unsigned int irq);
+
+/* shutdown is same as "disable" */
+#define shutdown_pint_irq disable_pint_irq
+
+static void mask_and_ack_pint(unsigned int);
+static void end_pint_irq(unsigned int irq);
+
+static unsigned int startup_pint_irq(unsigned int irq)
+{
+ enable_pint_irq(irq);
+ return 0; /* never anything pending */
+}
+
+static struct hw_interrupt_type pint_irq_type = {
+ "PINT-IRQ",
+ startup_pint_irq,
+ shutdown_pint_irq,
+ enable_pint_irq,
+ disable_pint_irq,
+ mask_and_ack_pint,
+ end_pint_irq
+};
+
+static void disable_pint_irq(unsigned int irq)
+{
+ unsigned long val, flags;
+
+ local_irq_save(flags);
+ val = ctrl_inw(INTC_INTER);
+ val &= ~(1 << (irq - PINT_IRQ_BASE));
+ ctrl_outw(val, INTC_INTER); /* disable PINTn */
+ portcr_mask &= ~(3 << (irq - PINT_IRQ_BASE)*2);
+ local_irq_restore(flags);
+}
+
+static void enable_pint_irq(unsigned int irq)
+{
+ unsigned long val, flags;
+
+ local_irq_save(flags);
+ val = ctrl_inw(INTC_INTER);
+ val |= 1 << (irq - PINT_IRQ_BASE);
+ ctrl_outw(val, INTC_INTER); /* enable PINTn */
+ portcr_mask |= 3 << (irq - PINT_IRQ_BASE)*2;
+ local_irq_restore(flags);
+}
+
+static void mask_and_ack_pint(unsigned int irq)
+{
+ disable_pint_irq(irq);
+}
+
+static void end_pint_irq(unsigned int irq)
+{
+ if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
+ enable_pint_irq(irq);
+}
+
+void make_pint_irq(unsigned int irq)
+{
+ disable_irq_nosync(irq);
+ irq_desc[irq].handler = &pint_irq_type;
+ disable_pint_irq(irq);
+}
+#endif
+
+void __init init_IRQ(void)
+{
+#if defined(CONFIG_CPU_SUBTYPE_SH7705) || \
+ defined(CONFIG_CPU_SUBTYPE_SH7707) || \
+ defined(CONFIG_CPU_SUBTYPE_SH7709)
+ int i;
+#endif
+
+ make_ipr_irq(TIMER_IRQ, TIMER_IPR_ADDR, TIMER_IPR_POS, TIMER_PRIORITY);
+ make_ipr_irq(TIMER1_IRQ, TIMER1_IPR_ADDR, TIMER1_IPR_POS, TIMER1_PRIORITY);
+#if defined(CONFIG_SH_RTC)
+ make_ipr_irq(RTC_IRQ, RTC_IPR_ADDR, RTC_IPR_POS, RTC_PRIORITY);
+#endif
+
+#ifdef SCI_ERI_IRQ
+ make_ipr_irq(SCI_ERI_IRQ, SCI_IPR_ADDR, SCI_IPR_POS, SCI_PRIORITY);
+ make_ipr_irq(SCI_RXI_IRQ, SCI_IPR_ADDR, SCI_IPR_POS, SCI_PRIORITY);
+ make_ipr_irq(SCI_TXI_IRQ, SCI_IPR_ADDR, SCI_IPR_POS, SCI_PRIORITY);
+#endif
+
+#ifdef SCIF1_ERI_IRQ
+ make_ipr_irq(SCIF1_ERI_IRQ, SCIF1_IPR_ADDR, SCIF1_IPR_POS, SCIF1_PRIORITY);
+ make_ipr_irq(SCIF1_RXI_IRQ, SCIF1_IPR_ADDR, SCIF1_IPR_POS, SCIF1_PRIORITY);
+ make_ipr_irq(SCIF1_BRI_IRQ, SCIF1_IPR_ADDR, SCIF1_IPR_POS, SCIF1_PRIORITY);
+ make_ipr_irq(SCIF1_TXI_IRQ, SCIF1_IPR_ADDR, SCIF1_IPR_POS, SCIF1_PRIORITY);
+#endif
+
+#if defined(CONFIG_CPU_SUBTYPE_SH7300)
+ make_ipr_irq(SCIF0_IRQ, SCIF0_IPR_ADDR, SCIF0_IPR_POS, SCIF0_PRIORITY);
+ make_ipr_irq(DMTE2_IRQ, DMA1_IPR_ADDR, DMA1_IPR_POS, DMA1_PRIORITY);
+ make_ipr_irq(DMTE3_IRQ, DMA1_IPR_ADDR, DMA1_IPR_POS, DMA1_PRIORITY);
+ make_ipr_irq(VIO_IRQ, VIO_IPR_ADDR, VIO_IPR_POS, VIO_PRIORITY);
+#endif
+
+#ifdef SCIF_ERI_IRQ
+ make_ipr_irq(SCIF_ERI_IRQ, SCIF_IPR_ADDR, SCIF_IPR_POS, SCIF_PRIORITY);
+ make_ipr_irq(SCIF_RXI_IRQ, SCIF_IPR_ADDR, SCIF_IPR_POS, SCIF_PRIORITY);
+ make_ipr_irq(SCIF_BRI_IRQ, SCIF_IPR_ADDR, SCIF_IPR_POS, SCIF_PRIORITY);
+ make_ipr_irq(SCIF_TXI_IRQ, SCIF_IPR_ADDR, SCIF_IPR_POS, SCIF_PRIORITY);
+#endif
+
+#ifdef IRDA_ERI_IRQ
+ make_ipr_irq(IRDA_ERI_IRQ, IRDA_IPR_ADDR, IRDA_IPR_POS, IRDA_PRIORITY);
+ make_ipr_irq(IRDA_RXI_IRQ, IRDA_IPR_ADDR, IRDA_IPR_POS, IRDA_PRIORITY);
+ make_ipr_irq(IRDA_BRI_IRQ, IRDA_IPR_ADDR, IRDA_IPR_POS, IRDA_PRIORITY);
+ make_ipr_irq(IRDA_TXI_IRQ, IRDA_IPR_ADDR, IRDA_IPR_POS, IRDA_PRIORITY);
+#endif
+
+#if defined(CONFIG_CPU_SUBTYPE_SH7707) || defined(CONFIG_CPU_SUBTYPE_SH7709) || \
+ defined(CONFIG_CPU_SUBTYPE_SH7300) || defined(CONFIG_CPU_SUBTYPE_SH7705)
+ /*
+ * Initialize the Interrupt Controller (INTC)
+ * registers to their power on values
+ */
+
+ /*
+ * Enable external irq (INTC IRQ mode).
+ * You should set corresponding bits of PFC to "00"
+ * to enable these interrupts.
+ */
+ make_ipr_irq(IRQ0_IRQ, IRQ0_IPR_ADDR, IRQ0_IPR_POS, IRQ0_PRIORITY);
+ make_ipr_irq(IRQ1_IRQ, IRQ1_IPR_ADDR, IRQ1_IPR_POS, IRQ1_PRIORITY);
+ make_ipr_irq(IRQ2_IRQ, IRQ2_IPR_ADDR, IRQ2_IPR_POS, IRQ2_PRIORITY);
+ make_ipr_irq(IRQ3_IRQ, IRQ3_IPR_ADDR, IRQ3_IPR_POS, IRQ3_PRIORITY);
+ make_ipr_irq(IRQ4_IRQ, IRQ4_IPR_ADDR, IRQ4_IPR_POS, IRQ4_PRIORITY);
+ make_ipr_irq(IRQ5_IRQ, IRQ5_IPR_ADDR, IRQ5_IPR_POS, IRQ5_PRIORITY);
+#if !defined(CONFIG_CPU_SUBTYPE_SH7300)
+ make_ipr_irq(PINT0_IRQ, PINT0_IPR_ADDR, PINT0_IPR_POS, PINT0_PRIORITY);
+ make_ipr_irq(PINT8_IRQ, PINT8_IPR_ADDR, PINT8_IPR_POS, PINT8_PRIORITY);
+ enable_ipr_irq(PINT0_IRQ);
+ enable_ipr_irq(PINT8_IRQ);
+
+ for(i = 0; i < 16; i++)
+ make_pint_irq(PINT_IRQ_BASE + i);
+ for(i = 0; i < 256; i++)
+ {
+ if(i & 1) pint_map[i] = 0;
+ else if(i & 2) pint_map[i] = 1;
+ else if(i & 4) pint_map[i] = 2;
+ else if(i & 8) pint_map[i] = 3;
+ else if(i & 0x10) pint_map[i] = 4;
+ else if(i & 0x20) pint_map[i] = 5;
+ else if(i & 0x40) pint_map[i] = 6;
+ else if(i & 0x80) pint_map[i] = 7;
+ }
+#endif /* !CONFIG_CPU_SUBTYPE_SH7300 */
+#endif /* CONFIG_CPU_SUBTYPE_SH7707 || CONFIG_CPU_SUBTYPE_SH7709 || CONFIG_CPU_SUBTYPE_SH7300*/
+
+#ifdef CONFIG_CPU_SUBTYPE_ST40
+ init_IRQ_intc2();
+#endif
+
+ /* Perform the machine specific initialisation */
+ if (sh_mv.mv_init_irq != NULL) {
+ sh_mv.mv_init_irq();
+ }
+}
+#if defined(CONFIG_CPU_SUBTYPE_SH7707) || defined(CONFIG_CPU_SUBTYPE_SH7709) || \
+ defined(CONFIG_CPU_SUBTYPE_SH7300) || defined(CONFIG_CPU_SUBTYPE_SH7705)
+int ipr_irq_demux(int irq)
+{
+#if !defined(CONFIG_CPU_SUBTYPE_SH7300)
+ unsigned long creg, dreg, d, sav;
+
+ if(irq == PINT0_IRQ)
+ {
+#if defined(CONFIG_CPU_SUBTYPE_SH7707)
+ creg = PORT_PACR;
+ dreg = PORT_PADR;
+#else
+ creg = PORT_PCCR;
+ dreg = PORT_PCDR;
+#endif
+ sav = ctrl_inw(creg);
+ ctrl_outw(sav | portcr_mask, creg);
+ d = (~ctrl_inb(dreg) ^ ctrl_inw(INTC_ICR2)) & ctrl_inw(INTC_INTER) & 0xff;
+ ctrl_outw(sav, creg);
+ if(d == 0) return irq;
+ return PINT_IRQ_BASE + pint_map[d];
+ }
+ else if(irq == PINT8_IRQ)
+ {
+#if defined(CONFIG_CPU_SUBTYPE_SH7707)
+ creg = PORT_PBCR;
+ dreg = PORT_PBDR;
+#else
+ creg = PORT_PFCR;
+ dreg = PORT_PFDR;
+#endif
+ sav = ctrl_inw(creg);
+ ctrl_outw(sav | (portcr_mask >> 16), creg);
+ d = (~ctrl_inb(dreg) ^ (ctrl_inw(INTC_ICR2) >> 8)) & (ctrl_inw(INTC_INTER) >> 8) & 0xff;
+ ctrl_outw(sav, creg);
+ if(d == 0) return irq;
+ return PINT_IRQ_BASE + 8 + pint_map[d];
+ }
+#endif
+ return irq;
+}
+#endif
+
+EXPORT_SYMBOL(make_ipr_irq);
+
diff --git a/arch/sh/kernel/cpu/rtc.c b/arch/sh/kernel/cpu/rtc.c
new file mode 100644
index 00000000000..f8361f5e788
--- /dev/null
+++ b/arch/sh/kernel/cpu/rtc.c
@@ -0,0 +1,136 @@
+/*
+ * linux/arch/sh/kernel/rtc.c -- SH3 / SH4 on-chip RTC support
+ *
+ * Copyright (C) 2000 Philipp Rumpf <prumpf@tux.org>
+ * Copyright (C) 1999 Tetsuya Okada & Niibe Yutaka
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/time.h>
+
+#include <asm/io.h>
+#include <asm/rtc.h>
+
+#ifndef BCD_TO_BIN
+#define BCD_TO_BIN(val) ((val)=((val)&15) + ((val)>>4)*10)
+#endif
+
+#ifndef BIN_TO_BCD
+#define BIN_TO_BCD(val) ((val)=(((val)/10)<<4) + (val)%10)
+#endif
+
+void sh_rtc_gettimeofday(struct timespec *ts)
+{
+ unsigned int sec128, sec, sec2, min, hr, wk, day, mon, yr, yr100, cf_bit;
+ unsigned long flags;
+
+ again:
+ do {
+ local_irq_save(flags);
+ ctrl_outb(0, RCR1); /* Clear CF-bit */
+ sec128 = ctrl_inb(R64CNT);
+ sec = ctrl_inb(RSECCNT);
+ min = ctrl_inb(RMINCNT);
+ hr = ctrl_inb(RHRCNT);
+ wk = ctrl_inb(RWKCNT);
+ day = ctrl_inb(RDAYCNT);
+ mon = ctrl_inb(RMONCNT);
+#if defined(CONFIG_CPU_SH4)
+ yr = ctrl_inw(RYRCNT);
+ yr100 = (yr >> 8);
+ yr &= 0xff;
+#else
+ yr = ctrl_inb(RYRCNT);
+ yr100 = (yr == 0x99) ? 0x19 : 0x20;
+#endif
+ sec2 = ctrl_inb(R64CNT);
+ cf_bit = ctrl_inb(RCR1) & RCR1_CF;
+ local_irq_restore(flags);
+ } while (cf_bit != 0 || ((sec128 ^ sec2) & RTC_BIT_INVERTED) != 0);
+
+ BCD_TO_BIN(yr100);
+ BCD_TO_BIN(yr);
+ BCD_TO_BIN(mon);
+ BCD_TO_BIN(day);
+ BCD_TO_BIN(hr);
+ BCD_TO_BIN(min);
+ BCD_TO_BIN(sec);
+
+ if (yr > 99 || mon < 1 || mon > 12 || day > 31 || day < 1 ||
+ hr > 23 || min > 59 || sec > 59) {
+ printk(KERN_ERR
+ "SH RTC: invalid value, resetting to 1 Jan 2000\n");
+ local_irq_save(flags);
+ ctrl_outb(RCR2_RESET, RCR2); /* Reset & Stop */
+ ctrl_outb(0, RSECCNT);
+ ctrl_outb(0, RMINCNT);
+ ctrl_outb(0, RHRCNT);
+ ctrl_outb(6, RWKCNT);
+ ctrl_outb(1, RDAYCNT);
+ ctrl_outb(1, RMONCNT);
+#if defined(CONFIG_CPU_SH4)
+ ctrl_outw(0x2000, RYRCNT);
+#else
+ ctrl_outb(0, RYRCNT);
+#endif
+ ctrl_outb(RCR2_RTCEN|RCR2_START, RCR2); /* Start */
+ goto again;
+ }
+
+#if RTC_BIT_INVERTED != 0
+ if ((sec128 & RTC_BIT_INVERTED))
+ sec--;
+#endif
+
+ ts->tv_sec = mktime(yr100 * 100 + yr, mon, day, hr, min, sec);
+ ts->tv_nsec = ((sec128 * 1000000) / 128) * 1000;
+}
+
+/*
+ * Changed to only care about tv_sec, and not the full timespec struct
+ * (i.e. tv_nsec). It can easily be switched to timespec for future cpus
+ * that support setting usec or nsec RTC values.
+ */
+int sh_rtc_settimeofday(const time_t secs)
+{
+ int retval = 0;
+ int real_seconds, real_minutes, cmos_minutes;
+ unsigned long flags;
+
+ local_irq_save(flags);
+ ctrl_outb(RCR2_RESET, RCR2); /* Reset pre-scaler & stop RTC */
+
+ cmos_minutes = ctrl_inb(RMINCNT);
+ BCD_TO_BIN(cmos_minutes);
+
+ /*
+ * since we're only adjusting minutes and seconds,
+ * don't interfere with hour overflow. This avoids
+ * messing with unknown time zones but requires your
+ * RTC not to be off by more than 15 minutes
+ */
+ real_seconds = secs % 60;
+ real_minutes = secs / 60;
+ if (((abs(real_minutes - cmos_minutes) + 15)/30) & 1)
+ real_minutes += 30; /* correct for half hour time zone */
+ real_minutes %= 60;
+
+ if (abs(real_minutes - cmos_minutes) < 30) {
+ BIN_TO_BCD(real_seconds);
+ BIN_TO_BCD(real_minutes);
+ ctrl_outb(real_seconds, RSECCNT);
+ ctrl_outb(real_minutes, RMINCNT);
+ } else {
+ printk(KERN_WARNING
+ "set_rtc_time: can't update from %d to %d\n",
+ cmos_minutes, real_minutes);
+ retval = -1;
+ }
+
+ ctrl_outb(RCR2_RTCEN|RCR2_START, RCR2); /* Start RTC */
+ local_irq_restore(flags);
+
+ return retval;
+}
diff --git a/arch/sh/kernel/cpu/sh2/Makefile b/arch/sh/kernel/cpu/sh2/Makefile
new file mode 100644
index 00000000000..389353fba60
--- /dev/null
+++ b/arch/sh/kernel/cpu/sh2/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for the Linux/SuperH SH-2 backends.
+#
+
+obj-y := probe.o
+
diff --git a/arch/sh/kernel/cpu/sh2/probe.c b/arch/sh/kernel/cpu/sh2/probe.c
new file mode 100644
index 00000000000..f17a2a0d588
--- /dev/null
+++ b/arch/sh/kernel/cpu/sh2/probe.c
@@ -0,0 +1,39 @@
+/*
+ * arch/sh/kernel/cpu/sh2/probe.c
+ *
+ * CPU Subtype Probing for SH-2.
+ *
+ * Copyright (C) 2002 Paul Mundt
+ *
+ * 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/init.h>
+#include <asm/processor.h>
+#include <asm/cache.h>
+
+int __init detect_cpu_and_cache_system(void)
+{
+ /*
+ * For now, assume SH7604 .. fix this later.
+ */
+ cpu_data->type = CPU_SH7604;
+ cpu_data->dcache.ways = 4;
+ cpu_data->dcache.way_shift = 6;
+ cpu_data->dcache.sets = 64;
+ cpu_data->dcache.entry_shift = 4;
+ cpu_data->dcache.linesz = L1_CACHE_BYTES;
+ cpu_data->dcache.flags = 0;
+
+ /*
+ * SH-2 doesn't have separate caches
+ */
+ cpu_data->dcache.flags |= SH_CACHE_COMBINED;
+ cpu_data->icache = cpu_data->dcache;
+
+ return 0;
+}
+
diff --git a/arch/sh/kernel/cpu/sh3/Makefile b/arch/sh/kernel/cpu/sh3/Makefile
new file mode 100644
index 00000000000..a64532e4dc6
--- /dev/null
+++ b/arch/sh/kernel/cpu/sh3/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for the Linux/SuperH SH-3 backends.
+#
+
+obj-y := ex.o probe.o
+
diff --git a/arch/sh/kernel/cpu/sh3/ex.S b/arch/sh/kernel/cpu/sh3/ex.S
new file mode 100644
index 00000000000..966c0858b71
--- /dev/null
+++ b/arch/sh/kernel/cpu/sh3/ex.S
@@ -0,0 +1,199 @@
+/*
+ * arch/sh/kernel/cpu/sh3/ex.S
+ *
+ * The SH-3 exception vector table.
+
+ * Copyright (C) 1999, 2000, 2002 Niibe Yutaka
+ * Copyright (C) 2003 Paul Mundt
+ *
+ * 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/linkage.h>
+#include <linux/config.h>
+
+ .align 2
+ .data
+
+ENTRY(exception_handling_table)
+ .long exception_error /* 000 */
+ .long exception_error
+#if defined(CONFIG_MMU)
+ .long tlb_miss_load /* 040 */
+ .long tlb_miss_store
+ .long initial_page_write
+ .long tlb_protection_violation_load
+ .long tlb_protection_violation_store
+ .long address_error_load
+ .long address_error_store /* 100 */
+#else
+ .long exception_error ! tlb miss load /* 040 */
+ .long exception_error ! tlb miss store
+ .long exception_error ! initial page write
+ .long exception_error ! tlb prot violation load
+ .long exception_error ! tlb prot violation store
+ .long exception_error ! address error load
+ .long exception_error ! address error store /* 100 */
+#endif
+ .long exception_error ! fpu_exception /* 120 */
+ .long exception_error /* 140 */
+ .long system_call ! Unconditional Trap /* 160 */
+ .long exception_error ! reserved_instruction (filled by trap_init) /* 180 */
+ .long exception_error ! illegal_slot_instruction (filled by trap_init) /*1A0*/
+ENTRY(nmi_slot)
+#if defined (CONFIG_KGDB_NMI)
+ .long debug_enter /* 1C0 */ ! Allow trap to debugger
+#else
+ .long exception_none /* 1C0 */ ! Not implemented yet
+#endif
+ENTRY(user_break_point_trap)
+ .long break_point_trap /* 1E0 */
+ENTRY(interrupt_table)
+ ! external hardware
+ .long do_IRQ ! 0000 /* 200 */
+ .long do_IRQ ! 0001
+ .long do_IRQ ! 0010
+ .long do_IRQ ! 0011
+ .long do_IRQ ! 0100
+ .long do_IRQ ! 0101
+ .long do_IRQ ! 0110
+ .long do_IRQ ! 0111
+ .long do_IRQ ! 1000 /* 300 */
+ .long do_IRQ ! 1001
+ .long do_IRQ ! 1010
+ .long do_IRQ ! 1011
+ .long do_IRQ ! 1100
+ .long do_IRQ ! 1101
+ .long do_IRQ ! 1110
+ .long exception_error
+ ! Internal hardware
+ .long do_IRQ ! TMU0 tuni0 /* 400 */
+ .long do_IRQ ! TMU1 tuni1
+ .long do_IRQ ! TMU2 tuni2
+ .long do_IRQ ! ticpi2
+ .long do_IRQ ! RTC ati
+ .long do_IRQ ! pri
+ .long do_IRQ ! cui
+ .long do_IRQ ! SCI eri
+ .long do_IRQ ! rxi /* 500 */
+ .long do_IRQ ! txi
+ .long do_IRQ ! tei
+ .long do_IRQ ! WDT iti /* 560 */
+ .long do_IRQ ! REF rcmi
+ .long do_IRQ ! rovi
+ .long do_IRQ
+ .long do_IRQ /* 5E0 */
+#if defined(CONFIG_CPU_SUBTYPE_SH7707) || defined(CONFIG_CPU_SUBTYPE_SH7709) || \
+ defined(CONFIG_CPU_SUBTYPE_SH7300) || defined(CONFIG_CPU_SUBTYPE_SH7705)
+ .long do_IRQ ! 32 IRQ irq0 /* 600 */
+ .long do_IRQ ! 33 irq1
+ .long do_IRQ ! 34 irq2
+ .long do_IRQ ! 35 irq3
+ .long do_IRQ ! 36 irq4
+ .long do_IRQ ! 37 irq5
+ .long do_IRQ ! 38
+ .long do_IRQ ! 39
+ .long do_IRQ ! 40 PINT pint0-7 /* 700 */
+ .long do_IRQ ! 41 pint8-15
+ .long do_IRQ ! 42
+ .long do_IRQ ! 43
+ .long do_IRQ ! 44
+ .long do_IRQ ! 45
+ .long do_IRQ ! 46
+ .long do_IRQ ! 47
+ .long do_IRQ ! 48 DMAC dei0 /* 800 */
+ .long do_IRQ ! 49 dei1
+ .long do_IRQ ! 50 dei2
+ .long do_IRQ ! 51 dei3
+ .long do_IRQ ! 52 IrDA eri1
+ .long do_IRQ ! 53 rxi1
+ .long do_IRQ ! 54 bri1
+ .long do_IRQ ! 55 txi1
+ .long do_IRQ ! 56 SCIF eri2
+ .long do_IRQ ! 57 rxi2
+ .long do_IRQ ! 58 bri2
+ .long do_IRQ ! 59 txi2
+ .long do_IRQ ! 60 ADC adi /* 980 */
+#if defined(CONFIG_CPU_SUBTYPE_SH7705)
+ .long exception_none ! 61 /* 9A0 */
+ .long exception_none ! 62
+ .long exception_none ! 63
+ .long exception_none ! 64 /* A00 */
+ .long do_IRQ ! 65 USB usi0
+ .long do_IRQ ! 66 usi1
+ .long exception_none ! 67
+ .long exception_none ! 68
+ .long exception_none ! 69
+ .long exception_none ! 70
+ .long exception_none ! 71
+ .long exception_none ! 72 /* B00 */
+ .long exception_none ! 73
+ .long exception_none ! 74
+ .long exception_none ! 75
+ .long exception_none ! 76
+ .long exception_none ! 77
+ .long exception_none ! 78
+ .long exception_none ! 79
+ .long do_IRQ ! 80 TPU0 tpi0 /* C00 */
+ .long do_IRQ ! 81 TPU1 tpi1
+ .long exception_none ! 82
+ .long exception_none ! 83
+ .long do_IRQ ! 84 TPU2 tpi2
+ .long do_IRQ ! 85 TPU3 tpi3 /* CA0 */
+#endif
+#if defined(CONFIG_CPU_SUBTYPE_SH7707) || defined(CONFIG_CPU_SUBTYPE_SH7300)
+ .long do_IRQ ! 61 LCDC lcdi /* 9A0 */
+ .long do_IRQ ! 62 PCC pcc0i
+ .long do_IRQ ! 63 pcc1i /* 9E0 */
+#endif
+#if defined(CONFIG_CPU_SUBTYPE_SH7300)
+ .long do_IRQ ! 64
+ .long do_IRQ ! 65
+ .long do_IRQ ! 66
+ .long do_IRQ ! 67
+ .long do_IRQ ! 68
+ .long do_IRQ ! 69
+ .long do_IRQ ! 70
+ .long do_IRQ ! 71
+ .long do_IRQ ! 72
+ .long do_IRQ ! 73
+ .long do_IRQ ! 74
+ .long do_IRQ ! 75
+ .long do_IRQ ! 76
+ .long do_IRQ ! 77
+ .long do_IRQ ! 78
+ .long do_IRQ ! 79
+ .long do_IRQ ! 80 SCIF0(SH7300)
+ .long do_IRQ ! 81
+ .long do_IRQ ! 82
+ .long do_IRQ ! 83
+ .long do_IRQ ! 84
+ .long do_IRQ ! 85
+ .long do_IRQ ! 86
+ .long do_IRQ ! 87
+ .long do_IRQ ! 88
+ .long do_IRQ ! 89
+ .long do_IRQ ! 90
+ .long do_IRQ ! 91
+ .long do_IRQ ! 92
+ .long do_IRQ ! 93
+ .long do_IRQ ! 94
+ .long do_IRQ ! 95
+ .long do_IRQ ! 96
+ .long do_IRQ ! 97
+ .long do_IRQ ! 98
+ .long do_IRQ ! 99
+ .long do_IRQ ! 100
+ .long do_IRQ ! 101
+ .long do_IRQ ! 102
+ .long do_IRQ ! 103
+ .long do_IRQ ! 104
+ .long do_IRQ ! 105
+ .long do_IRQ ! 106
+ .long do_IRQ ! 107
+ .long do_IRQ ! 108
+#endif
+#endif
+
diff --git a/arch/sh/kernel/cpu/sh3/probe.c b/arch/sh/kernel/cpu/sh3/probe.c
new file mode 100644
index 00000000000..5cdc8863860
--- /dev/null
+++ b/arch/sh/kernel/cpu/sh3/probe.c
@@ -0,0 +1,97 @@
+/*
+ * arch/sh/kernel/cpu/sh3/probe.c
+ *
+ * CPU Subtype Probing for SH-3.
+ *
+ * Copyright (C) 1999, 2000 Niibe Yutaka
+ * Copyright (C) 2002 Paul Mundt
+ *
+ * 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/init.h>
+#include <asm/processor.h>
+#include <asm/cache.h>
+#include <asm/io.h>
+
+int __init detect_cpu_and_cache_system(void)
+{
+ unsigned long addr0, addr1, data0, data1, data2, data3;
+
+ jump_to_P2();
+ /*
+ * Check if the entry shadows or not.
+ * When shadowed, it's 128-entry system.
+ * Otherwise, it's 256-entry system.
+ */
+ addr0 = CACHE_OC_ADDRESS_ARRAY + (3 << 12);
+ addr1 = CACHE_OC_ADDRESS_ARRAY + (1 << 12);
+
+ /* First, write back & invalidate */
+ data0 = ctrl_inl(addr0);
+ ctrl_outl(data0&~(SH_CACHE_VALID|SH_CACHE_UPDATED), addr0);
+ data1 = ctrl_inl(addr1);
+ ctrl_outl(data1&~(SH_CACHE_VALID|SH_CACHE_UPDATED), addr1);
+
+ /* Next, check if there's shadow or not */
+ data0 = ctrl_inl(addr0);
+ data0 ^= SH_CACHE_VALID;
+ ctrl_outl(data0, addr0);
+ data1 = ctrl_inl(addr1);
+ data2 = data1 ^ SH_CACHE_VALID;
+ ctrl_outl(data2, addr1);
+ data3 = ctrl_inl(addr0);
+
+ /* Lastly, invaliate them. */
+ ctrl_outl(data0&~SH_CACHE_VALID, addr0);
+ ctrl_outl(data2&~SH_CACHE_VALID, addr1);
+
+ back_to_P1();
+
+ cpu_data->dcache.ways = 4;
+ cpu_data->dcache.entry_shift = 4;
+ cpu_data->dcache.linesz = L1_CACHE_BYTES;
+ cpu_data->dcache.flags = 0;
+
+ /*
+ * 7709A/7729 has 16K cache (256-entry), while 7702 has only
+ * 2K(direct) 7702 is not supported (yet)
+ */
+ if (data0 == data1 && data2 == data3) { /* Shadow */
+ cpu_data->dcache.way_incr = (1 << 11);
+ cpu_data->dcache.entry_mask = 0x7f0;
+ cpu_data->dcache.sets = 128;
+ cpu_data->type = CPU_SH7708;
+
+ cpu_data->flags |= CPU_HAS_MMU_PAGE_ASSOC;
+ } else { /* 7709A or 7729 */
+ cpu_data->dcache.way_incr = (1 << 12);
+ cpu_data->dcache.entry_mask = 0xff0;
+ cpu_data->dcache.sets = 256;
+ cpu_data->type = CPU_SH7729;
+
+#if defined(CONFIG_CPU_SUBTYPE_SH7705)
+ cpu_data->type = CPU_SH7705;
+
+#if defined(CONFIG_SH7705_CACHE_32KB)
+ cpu_data->dcache.way_incr = (1 << 13);
+ cpu_data->dcache.entry_mask = 0x1ff0;
+ cpu_data->dcache.sets = 512;
+ ctrl_outl(CCR_CACHE_32KB, CCR3);
+#else
+ ctrl_outl(CCR_CACHE_16KB, CCR3);
+#endif
+#endif
+ }
+
+ /*
+ * SH-3 doesn't have separate caches
+ */
+ cpu_data->dcache.flags |= SH_CACHE_COMBINED;
+ cpu_data->icache = cpu_data->dcache;
+
+ return 0;
+}
+
diff --git a/arch/sh/kernel/cpu/sh4/Makefile b/arch/sh/kernel/cpu/sh4/Makefile
new file mode 100644
index 00000000000..ead1071eac7
--- /dev/null
+++ b/arch/sh/kernel/cpu/sh4/Makefile
@@ -0,0 +1,10 @@
+#
+# Makefile for the Linux/SuperH SH-4 backends.
+#
+
+obj-y := ex.o probe.o
+
+obj-$(CONFIG_SH_FPU) += fpu.o
+obj-$(CONFIG_CPU_SUBTYPE_ST40STB1) += irq_intc2.o
+obj-$(CONFIG_SH_STORE_QUEUES) += sq.o
+
diff --git a/arch/sh/kernel/cpu/sh4/ex.S b/arch/sh/kernel/cpu/sh4/ex.S
new file mode 100644
index 00000000000..8221e9d1551
--- /dev/null
+++ b/arch/sh/kernel/cpu/sh4/ex.S
@@ -0,0 +1,384 @@
+/*
+ * arch/sh/kernel/cpu/sh4/ex.S
+ *
+ * The SH-4 exception vector table.
+
+ * Copyright (C) 1999, 2000, 2002 Niibe Yutaka
+ * Copyright (C) 2003 Paul Mundt
+ *
+ * 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/linkage.h>
+#include <linux/config.h>
+
+ .align 2
+ .data
+
+ENTRY(exception_handling_table)
+ .long exception_error /* 000 */
+ .long exception_error
+#if defined(CONFIG_MMU)
+ .long tlb_miss_load /* 040 */
+ .long tlb_miss_store
+ .long initial_page_write
+ .long tlb_protection_violation_load
+ .long tlb_protection_violation_store
+ .long address_error_load
+ .long address_error_store /* 100 */
+#else
+ .long exception_error ! tlb miss load /* 040 */
+ .long exception_error ! tlb miss store
+ .long exception_error ! initial page write
+ .long exception_error ! tlb prot violation load
+ .long exception_error ! tlb prot violation store
+ .long exception_error ! address error load
+ .long exception_error ! address error store /* 100 */
+#endif
+#if defined(CONFIG_SH_FPU)
+ .long do_fpu_error /* 120 */
+#else
+ .long exception_error /* 120 */
+#endif
+ .long exception_error /* 140 */
+ .long system_call ! Unconditional Trap /* 160 */
+ .long exception_error ! reserved_instruction (filled by trap_init) /* 180 */
+ .long exception_error ! illegal_slot_instruction (filled by trap_init) /*1A0*/
+ENTRY(nmi_slot)
+#if defined (CONFIG_KGDB_NMI)
+ .long debug_enter /* 1C0 */ ! Allow trap to debugger
+#else
+ .long exception_none /* 1C0 */ ! Not implemented yet
+#endif
+ENTRY(user_break_point_trap)
+ .long break_point_trap /* 1E0 */
+ENTRY(interrupt_table)
+ ! external hardware
+ .long do_IRQ ! 0000 /* 200 */
+ .long do_IRQ ! 0001
+ .long do_IRQ ! 0010
+ .long do_IRQ ! 0011
+ .long do_IRQ ! 0100
+ .long do_IRQ ! 0101
+ .long do_IRQ ! 0110
+ .long do_IRQ ! 0111
+ .long do_IRQ ! 1000 /* 300 */
+ .long do_IRQ ! 1001
+ .long do_IRQ ! 1010
+ .long do_IRQ ! 1011
+ .long do_IRQ ! 1100
+ .long do_IRQ ! 1101
+ .long do_IRQ ! 1110
+ .long exception_error
+ ! Internal hardware
+ .long do_IRQ ! TMU0 tuni0 /* 400 */
+ .long do_IRQ ! TMU1 tuni1
+ .long do_IRQ ! TMU2 tuni2
+ .long do_IRQ ! ticpi2
+#if defined(CONFIG_CPU_SUBTYPE_SH7760)
+ .long exception_error
+ .long exception_error
+ .long exception_error
+ .long exception_error
+ .long exception_error /* 500 */
+ .long exception_error
+ .long exception_error
+#else
+ .long do_IRQ ! RTC ati
+ .long do_IRQ ! pri
+ .long do_IRQ ! cui
+ .long do_IRQ ! SCI eri
+ .long do_IRQ ! rxi /* 500 */
+ .long do_IRQ ! txi
+ .long do_IRQ ! tei
+#endif
+ .long do_IRQ ! WDT iti /* 560 */
+ .long do_IRQ ! REF rcmi
+ .long do_IRQ ! rovi
+ .long do_IRQ
+ .long do_IRQ /* 5E0 */
+ .long do_IRQ ! 32 Hitachi UDI /* 600 */
+ .long do_IRQ ! 33 GPIO
+ .long do_IRQ ! 34 DMAC dmte0
+ .long do_IRQ ! 35 dmte1
+ .long do_IRQ ! 36 dmte2
+ .long do_IRQ ! 37 dmte3
+ .long do_IRQ ! 38 dmae
+ .long exception_error ! 39 /* 6E0 */
+#if defined(CONFIG_CPU_SUBTYPE_SH7760)
+ .long exception_error /* 700 */
+ .long exception_error
+ .long exception_error
+ .long exception_error /* 760 */
+#else
+ .long do_IRQ ! 40 SCIF eri /* 700 */
+ .long do_IRQ ! 41 rxi
+ .long do_IRQ ! 42 bri
+ .long do_IRQ ! 43 txi
+#endif
+#if CONFIG_NR_ONCHIP_DMA_CHANNELS == 8
+ .long do_IRQ ! 44 DMAC dmte4 /* 780 */
+ .long do_IRQ ! 45 dmte5
+ .long do_IRQ ! 46 dmte6
+ .long do_IRQ ! 47 dmte7 /* 7E0 */
+#else
+ .long exception_error ! 44 /* 780 */
+ .long exception_error ! 45
+ .long exception_error ! 46
+ .long exception_error ! 47
+#endif
+#if defined(CONFIG_SH_FPU)
+ .long do_fpu_state_restore ! 48 /* 800 */
+ .long do_fpu_state_restore ! 49 /* 820 */
+#else
+ .long exception_error
+ .long exception_error
+#endif
+#if defined(CONFIG_CPU_SUBTYPE_SH7751)
+ .long exception_error /* 840 */
+ .long exception_error
+ .long exception_error
+ .long exception_error
+ .long exception_error
+ .long exception_error
+ .long exception_error /* 900 */
+ .long exception_error
+ .long exception_error
+ .long exception_error
+ .long exception_error
+ .long exception_error
+ .long exception_error
+ .long exception_error
+ .long do_IRQ ! PCI serr /* A00 */
+ .long do_IRQ ! dma3
+ .long do_IRQ ! dma2
+ .long do_IRQ ! dma1
+ .long do_IRQ ! dma0
+ .long do_IRQ ! pwon
+ .long do_IRQ ! pwdwn
+ .long do_IRQ ! err
+ .long do_IRQ ! TMU3 tuni3 /* B00 */
+ .long exception_error
+ .long exception_error
+ .long exception_error
+ .long do_IRQ ! TMU4 tuni4 /* B80 */
+#elif defined(CONFIG_CPU_SUBTYPE_SH7760)
+ .long do_IRQ ! IRQ irq6 /* 840 */
+ .long do_IRQ ! irq7
+ .long do_IRQ ! SCIF eri0
+ .long do_IRQ ! rxi0
+ .long do_IRQ ! bri0
+ .long do_IRQ ! txi0
+ .long do_IRQ ! HCAN2 cani0 /* 900 */
+ .long do_IRQ ! cani1
+ .long do_IRQ ! SSI ssii0
+ .long do_IRQ ! ssii1
+ .long do_IRQ ! HAC haci0
+ .long do_IRQ ! haci1
+ .long do_IRQ ! IIC iici0
+ .long do_IRQ ! iici1
+ .long do_IRQ ! USB usbi /* A00 */
+ .long do_IRQ ! LCDC vint
+ .long exception_error
+ .long exception_error
+ .long do_IRQ ! DMABRG dmabrgi0
+ .long do_IRQ ! dmabrgi1
+ .long do_IRQ ! dmabrgi2
+ .long exception_error
+ .long do_IRQ ! SCIF eri1 /* B00 */
+ .long do_IRQ ! rxi1
+ .long do_IRQ ! bri1
+ .long do_IRQ ! txi1
+ .long do_IRQ ! eri2
+ .long do_IRQ ! rxi2
+ .long do_IRQ ! bri2
+ .long do_IRQ ! txi2
+ .long do_IRQ ! SIM simeri /* C00 */
+ .long do_IRQ ! simrxi
+ .long do_IRQ ! simtxi
+ .long do_IRQ ! simtei
+ .long do_IRQ ! HSPI spii
+ .long exception_error
+ .long exception_error
+ .long exception_error
+ .long do_IRQ ! MMCIF mmci0 /* D00 */
+ .long do_IRQ ! mmci1
+ .long do_IRQ ! mmci2
+ .long do_IRQ ! mmci3
+ .long exception_error
+ .long exception_error
+ .long exception_error
+ .long exception_error
+ .long exception_error /* E00 */
+ .long exception_error
+ .long exception_error
+ .long exception_error
+ .long do_IRQ ! MFI mfii
+ .long exception_error
+ .long exception_error
+ .long exception_error
+ .long exception_error /* F00 */
+ .long exception_error
+ .long exception_error
+ .long exception_error
+ .long do_IRQ ! ADC adi
+ .long do_IRQ ! CMT cmti /* FA0 */
+#elif defined(CONFIG_CPU_SUBTYPE_SH73180)
+ .long do_IRQ ! 50 0x840
+ .long do_IRQ ! 51 0x860
+ .long do_IRQ ! 52 0x880
+ .long do_IRQ ! 53 0x8a0
+ .long do_IRQ ! 54 0x8c0
+ .long do_IRQ ! 55 0x8e0
+ .long do_IRQ ! 56 0x900
+ .long do_IRQ ! 57 0x920
+ .long do_IRQ ! 58 0x940
+ .long do_IRQ ! 59 0x960
+ .long do_IRQ ! 60 0x980
+ .long do_IRQ ! 61 0x9a0
+ .long do_IRQ ! 62 0x9c0
+ .long do_IRQ ! 63 0x9e0
+ .long do_IRQ ! 64 0xa00
+ .long do_IRQ ! 65 0xa20
+ .long do_IRQ ! 66 0xa40
+ .long do_IRQ ! 67 0xa60
+ .long do_IRQ ! 68 0xa80
+ .long do_IRQ ! 69 0xaa0
+ .long do_IRQ ! 70 0xac0
+ .long do_IRQ ! 71 0xae0
+ .long do_IRQ ! 72 0xb00
+ .long do_IRQ ! 73 0xb20
+ .long do_IRQ ! 74 0xb40
+ .long do_IRQ ! 75 0xb60
+ .long do_IRQ ! 76 0xb80
+ .long do_IRQ ! 77 0xba0
+ .long do_IRQ ! 78 0xbc0
+ .long do_IRQ ! 79 0xbe0
+ .long do_IRQ ! 80 0xc00
+ .long do_IRQ ! 81 0xc20
+ .long do_IRQ ! 82 0xc40
+ .long do_IRQ ! 83 0xc60
+ .long do_IRQ ! 84 0xc80
+ .long do_IRQ ! 85 0xca0
+ .long do_IRQ ! 86 0xcc0
+ .long do_IRQ ! 87 0xce0
+ .long do_IRQ ! 88 0xd00
+ .long do_IRQ ! 89 0xd20
+ .long do_IRQ ! 90 0xd40
+ .long do_IRQ ! 91 0xd60
+ .long do_IRQ ! 92 0xd80
+ .long do_IRQ ! 93 0xda0
+ .long do_IRQ ! 94 0xdc0
+ .long do_IRQ ! 95 0xde0
+ .long do_IRQ ! 96 0xe00
+ .long do_IRQ ! 97 0xe20
+ .long do_IRQ ! 98 0xe40
+ .long do_IRQ ! 99 0xe60
+ .long do_IRQ ! 100 0xe80
+ .long do_IRQ ! 101 0xea0
+ .long do_IRQ ! 102 0xec0
+ .long do_IRQ ! 103 0xee0
+ .long do_IRQ ! 104 0xf00
+ .long do_IRQ ! 105 0xf20
+ .long do_IRQ ! 106 0xf40
+ .long do_IRQ ! 107 0xf60
+ .long do_IRQ ! 108 0xf80
+#elif defined(CONFIG_CPU_SUBTYPE_ST40STB1)
+ .long exception_error ! 50 0x840
+ .long exception_error ! 51 0x860
+ .long exception_error ! 52 0x880
+ .long exception_error ! 53 0x8a0
+ .long exception_error ! 54 0x8c0
+ .long exception_error ! 55 0x8e0
+ .long exception_error ! 56 0x900
+ .long exception_error ! 57 0x920
+ .long exception_error ! 58 0x940
+ .long exception_error ! 59 0x960
+ .long exception_error ! 60 0x980
+ .long exception_error ! 61 0x9a0
+ .long exception_error ! 62 0x9c0
+ .long exception_error ! 63 0x9e0
+ .long do_IRQ ! 64 0xa00 PCI serr
+ .long do_IRQ ! 65 0xa20 err
+ .long do_IRQ ! 66 0xa40 ad
+ .long do_IRQ ! 67 0xa60 pwr_dwn
+ .long exception_error ! 68 0xa80
+ .long exception_error ! 69 0xaa0
+ .long exception_error ! 70 0xac0
+ .long exception_error ! 71 0xae0
+ .long do_IRQ ! 72 0xb00 DMA INT0
+ .long do_IRQ ! 73 0xb20 INT1
+ .long do_IRQ ! 74 0xb40 INT2
+ .long do_IRQ ! 75 0xb60 INT3
+ .long do_IRQ ! 76 0xb80 INT4
+ .long exception_error ! 77 0xba0
+ .long do_IRQ ! 78 0xbc0 DMA ERR
+ .long exception_error ! 79 0xbe0
+ .long do_IRQ ! 80 0xc00 PIO0
+ .long do_IRQ ! 81 0xc20 PIO1
+ .long do_IRQ ! 82 0xc40 PIO2
+ .long exception_error ! 83 0xc60
+ .long exception_error ! 84 0xc80
+ .long exception_error ! 85 0xca0
+ .long exception_error ! 86 0xcc0
+ .long exception_error ! 87 0xce0
+ .long exception_error ! 88 0xd00
+ .long exception_error ! 89 0xd20
+ .long exception_error ! 90 0xd40
+ .long exception_error ! 91 0xd60
+ .long exception_error ! 92 0xd80
+ .long exception_error ! 93 0xda0
+ .long exception_error ! 94 0xdc0
+ .long exception_error ! 95 0xde0
+ .long exception_error ! 96 0xe00
+ .long exception_error ! 97 0xe20
+ .long exception_error ! 98 0xe40
+ .long exception_error ! 99 0xe60
+ .long exception_error ! 100 0xe80
+ .long exception_error ! 101 0xea0
+ .long exception_error ! 102 0xec0
+ .long exception_error ! 103 0xee0
+ .long exception_error ! 104 0xf00
+ .long exception_error ! 105 0xf20
+ .long exception_error ! 106 0xf40
+ .long exception_error ! 107 0xf60
+ .long exception_error ! 108 0xf80
+ .long exception_error ! 109 0xfa0
+ .long exception_error ! 110 0xfc0
+ .long exception_error ! 111 0xfe0
+ .long do_IRQ ! 112 0x1000 Mailbox
+ .long exception_error ! 113 0x1020
+ .long exception_error ! 114 0x1040
+ .long exception_error ! 115 0x1060
+ .long exception_error ! 116 0x1080
+ .long exception_error ! 117 0x10a0
+ .long exception_error ! 118 0x10c0
+ .long exception_error ! 119 0x10e0
+ .long exception_error ! 120 0x1100
+ .long exception_error ! 121 0x1120
+ .long exception_error ! 122 0x1140
+ .long exception_error ! 123 0x1160
+ .long exception_error ! 124 0x1180
+ .long exception_error ! 125 0x11a0
+ .long exception_error ! 126 0x11c0
+ .long exception_error ! 127 0x11e0
+ .long exception_error ! 128 0x1200
+ .long exception_error ! 129 0x1220
+ .long exception_error ! 130 0x1240
+ .long exception_error ! 131 0x1260
+ .long exception_error ! 132 0x1280
+ .long exception_error ! 133 0x12a0
+ .long exception_error ! 134 0x12c0
+ .long exception_error ! 135 0x12e0
+ .long exception_error ! 136 0x1300
+ .long exception_error ! 137 0x1320
+ .long exception_error ! 138 0x1340
+ .long exception_error ! 139 0x1360
+ .long do_IRQ ! 140 0x1380 EMPI INV_ADDR
+ .long exception_error ! 141 0x13a0
+ .long exception_error ! 142 0x13c0
+ .long exception_error ! 143 0x13e0
+#endif
+
diff --git a/arch/sh/kernel/cpu/sh4/fpu.c b/arch/sh/kernel/cpu/sh4/fpu.c
new file mode 100644
index 00000000000..f486c07e10e
--- /dev/null
+++ b/arch/sh/kernel/cpu/sh4/fpu.c
@@ -0,0 +1,335 @@
+/* $Id: fpu.c,v 1.4 2004/01/13 05:52:11 kkojima Exp $
+ *
+ * linux/arch/sh/kernel/fpu.c
+ *
+ * Save/restore floating point context for signal handlers.
+ *
+ * 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.
+ *
+ * Copyright (C) 1999, 2000 Kaz Kojima & Niibe Yutaka
+ *
+ * FIXME! These routines can be optimized in big endian case.
+ */
+
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <asm/processor.h>
+#include <asm/io.h>
+
+/* The PR (precision) bit in the FP Status Register must be clear when
+ * an frchg instruction is executed, otherwise the instruction is undefined.
+ * Executing frchg with PR set causes a trap on some SH4 implementations.
+ */
+
+#define FPSCR_RCHG 0x00000000
+
+
+/*
+ * Save FPU registers onto task structure.
+ * Assume called with FPU enabled (SR.FD=0).
+ */
+void
+save_fpu(struct task_struct *tsk, struct pt_regs *regs)
+{
+ unsigned long dummy;
+
+ clear_tsk_thread_flag(tsk, TIF_USEDFPU);
+ enable_fpu();
+ asm volatile("sts.l fpul, @-%0\n\t"
+ "sts.l fpscr, @-%0\n\t"
+ "lds %2, fpscr\n\t"
+ "frchg\n\t"
+ "fmov.s fr15, @-%0\n\t"
+ "fmov.s fr14, @-%0\n\t"
+ "fmov.s fr13, @-%0\n\t"
+ "fmov.s fr12, @-%0\n\t"
+ "fmov.s fr11, @-%0\n\t"
+ "fmov.s fr10, @-%0\n\t"
+ "fmov.s fr9, @-%0\n\t"
+ "fmov.s fr8, @-%0\n\t"
+ "fmov.s fr7, @-%0\n\t"
+ "fmov.s fr6, @-%0\n\t"
+ "fmov.s fr5, @-%0\n\t"
+ "fmov.s fr4, @-%0\n\t"
+ "fmov.s fr3, @-%0\n\t"
+ "fmov.s fr2, @-%0\n\t"
+ "fmov.s fr1, @-%0\n\t"
+ "fmov.s fr0, @-%0\n\t"
+ "frchg\n\t"
+ "fmov.s fr15, @-%0\n\t"
+ "fmov.s fr14, @-%0\n\t"
+ "fmov.s fr13, @-%0\n\t"
+ "fmov.s fr12, @-%0\n\t"
+ "fmov.s fr11, @-%0\n\t"
+ "fmov.s fr10, @-%0\n\t"
+ "fmov.s fr9, @-%0\n\t"
+ "fmov.s fr8, @-%0\n\t"
+ "fmov.s fr7, @-%0\n\t"
+ "fmov.s fr6, @-%0\n\t"
+ "fmov.s fr5, @-%0\n\t"
+ "fmov.s fr4, @-%0\n\t"
+ "fmov.s fr3, @-%0\n\t"
+ "fmov.s fr2, @-%0\n\t"
+ "fmov.s fr1, @-%0\n\t"
+ "fmov.s fr0, @-%0\n\t"
+ "lds %3, fpscr\n\t"
+ : "=r" (dummy)
+ : "0" ((char *)(&tsk->thread.fpu.hard.status)),
+ "r" (FPSCR_RCHG),
+ "r" (FPSCR_INIT)
+ : "memory");
+
+ disable_fpu();
+ release_fpu(regs);
+}
+
+static void
+restore_fpu(struct task_struct *tsk)
+{
+ unsigned long dummy;
+
+ enable_fpu();
+ asm volatile("lds %2, fpscr\n\t"
+ "fmov.s @%0+, fr0\n\t"
+ "fmov.s @%0+, fr1\n\t"
+ "fmov.s @%0+, fr2\n\t"
+ "fmov.s @%0+, fr3\n\t"
+ "fmov.s @%0+, fr4\n\t"
+ "fmov.s @%0+, fr5\n\t"
+ "fmov.s @%0+, fr6\n\t"
+ "fmov.s @%0+, fr7\n\t"
+ "fmov.s @%0+, fr8\n\t"
+ "fmov.s @%0+, fr9\n\t"
+ "fmov.s @%0+, fr10\n\t"
+ "fmov.s @%0+, fr11\n\t"
+ "fmov.s @%0+, fr12\n\t"
+ "fmov.s @%0+, fr13\n\t"
+ "fmov.s @%0+, fr14\n\t"
+ "fmov.s @%0+, fr15\n\t"
+ "frchg\n\t"
+ "fmov.s @%0+, fr0\n\t"
+ "fmov.s @%0+, fr1\n\t"
+ "fmov.s @%0+, fr2\n\t"
+ "fmov.s @%0+, fr3\n\t"
+ "fmov.s @%0+, fr4\n\t"
+ "fmov.s @%0+, fr5\n\t"
+ "fmov.s @%0+, fr6\n\t"
+ "fmov.s @%0+, fr7\n\t"
+ "fmov.s @%0+, fr8\n\t"
+ "fmov.s @%0+, fr9\n\t"
+ "fmov.s @%0+, fr10\n\t"
+ "fmov.s @%0+, fr11\n\t"
+ "fmov.s @%0+, fr12\n\t"
+ "fmov.s @%0+, fr13\n\t"
+ "fmov.s @%0+, fr14\n\t"
+ "fmov.s @%0+, fr15\n\t"
+ "frchg\n\t"
+ "lds.l @%0+, fpscr\n\t"
+ "lds.l @%0+, fpul\n\t"
+ : "=r" (dummy)
+ : "0" (&tsk->thread.fpu), "r" (FPSCR_RCHG)
+ : "memory");
+ disable_fpu();
+}
+
+/*
+ * Load the FPU with signalling NANS. This bit pattern we're using
+ * has the property that no matter wether considered as single or as
+ * double precission represents signaling NANS.
+ */
+
+static void
+fpu_init(void)
+{
+ enable_fpu();
+ asm volatile("lds %0, fpul\n\t"
+ "lds %1, fpscr\n\t"
+ "fsts fpul, fr0\n\t"
+ "fsts fpul, fr1\n\t"
+ "fsts fpul, fr2\n\t"
+ "fsts fpul, fr3\n\t"
+ "fsts fpul, fr4\n\t"
+ "fsts fpul, fr5\n\t"
+ "fsts fpul, fr6\n\t"
+ "fsts fpul, fr7\n\t"
+ "fsts fpul, fr8\n\t"
+ "fsts fpul, fr9\n\t"
+ "fsts fpul, fr10\n\t"
+ "fsts fpul, fr11\n\t"
+ "fsts fpul, fr12\n\t"
+ "fsts fpul, fr13\n\t"
+ "fsts fpul, fr14\n\t"
+ "fsts fpul, fr15\n\t"
+ "frchg\n\t"
+ "fsts fpul, fr0\n\t"
+ "fsts fpul, fr1\n\t"
+ "fsts fpul, fr2\n\t"
+ "fsts fpul, fr3\n\t"
+ "fsts fpul, fr4\n\t"
+ "fsts fpul, fr5\n\t"
+ "fsts fpul, fr6\n\t"
+ "fsts fpul, fr7\n\t"
+ "fsts fpul, fr8\n\t"
+ "fsts fpul, fr9\n\t"
+ "fsts fpul, fr10\n\t"
+ "fsts fpul, fr11\n\t"
+ "fsts fpul, fr12\n\t"
+ "fsts fpul, fr13\n\t"
+ "fsts fpul, fr14\n\t"
+ "fsts fpul, fr15\n\t"
+ "frchg\n\t"
+ "lds %2, fpscr\n\t"
+ : /* no output */
+ : "r" (0), "r" (FPSCR_RCHG), "r" (FPSCR_INIT));
+ disable_fpu();
+}
+
+/**
+ * denormal_to_double - Given denormalized float number,
+ * store double float
+ *
+ * @fpu: Pointer to sh_fpu_hard structure
+ * @n: Index to FP register
+ */
+static void
+denormal_to_double (struct sh_fpu_hard_struct *fpu, int n)
+{
+ unsigned long du, dl;
+ unsigned long x = fpu->fpul;
+ int exp = 1023 - 126;
+
+ if (x != 0 && (x & 0x7f800000) == 0) {
+ du = (x & 0x80000000);
+ while ((x & 0x00800000) == 0) {
+ x <<= 1;
+ exp--;
+ }
+ x &= 0x007fffff;
+ du |= (exp << 20) | (x >> 3);
+ dl = x << 29;
+
+ fpu->fp_regs[n] = du;
+ fpu->fp_regs[n+1] = dl;
+ }
+}
+
+/**
+ * ieee_fpe_handler - Handle denormalized number exception
+ *
+ * @regs: Pointer to register structure
+ *
+ * Returns 1 when it's handled (should not cause exception).
+ */
+static int
+ieee_fpe_handler (struct pt_regs *regs)
+{
+ unsigned short insn = *(unsigned short *) regs->pc;
+ unsigned short finsn;
+ unsigned long nextpc;
+ int nib[4] = {
+ (insn >> 12) & 0xf,
+ (insn >> 8) & 0xf,
+ (insn >> 4) & 0xf,
+ insn & 0xf};
+
+ if (nib[0] == 0xb ||
+ (nib[0] == 0x4 && nib[2] == 0x0 && nib[3] == 0xb)) /* bsr & jsr */
+ regs->pr = regs->pc + 4;
+
+ if (nib[0] == 0xa || nib[0] == 0xb) { /* bra & bsr */
+ nextpc = regs->pc + 4 + ((short) ((insn & 0xfff) << 4) >> 3);
+ finsn = *(unsigned short *) (regs->pc + 2);
+ } else if (nib[0] == 0x8 && nib[1] == 0xd) { /* bt/s */
+ if (regs->sr & 1)
+ nextpc = regs->pc + 4 + ((char) (insn & 0xff) << 1);
+ else
+ nextpc = regs->pc + 4;
+ finsn = *(unsigned short *) (regs->pc + 2);
+ } else if (nib[0] == 0x8 && nib[1] == 0xf) { /* bf/s */
+ if (regs->sr & 1)
+ nextpc = regs->pc + 4;
+ else
+ nextpc = regs->pc + 4 + ((char) (insn & 0xff) << 1);
+ finsn = *(unsigned short *) (regs->pc + 2);
+ } else if (nib[0] == 0x4 && nib[3] == 0xb &&
+ (nib[2] == 0x0 || nib[2] == 0x2)) { /* jmp & jsr */
+ nextpc = regs->regs[nib[1]];
+ finsn = *(unsigned short *) (regs->pc + 2);
+ } else if (nib[0] == 0x0 && nib[3] == 0x3 &&
+ (nib[2] == 0x0 || nib[2] == 0x2)) { /* braf & bsrf */
+ nextpc = regs->pc + 4 + regs->regs[nib[1]];
+ finsn = *(unsigned short *) (regs->pc + 2);
+ } else if (insn == 0x000b) { /* rts */
+ nextpc = regs->pr;
+ finsn = *(unsigned short *) (regs->pc + 2);
+ } else {
+ nextpc = regs->pc + 2;
+ finsn = insn;
+ }
+
+ if ((finsn & 0xf1ff) == 0xf0ad) { /* fcnvsd */
+ struct task_struct *tsk = current;
+
+ save_fpu(tsk, regs);
+ if ((tsk->thread.fpu.hard.fpscr & (1 << 17))) {
+ /* FPU error */
+ denormal_to_double (&tsk->thread.fpu.hard,
+ (finsn >> 8) & 0xf);
+ tsk->thread.fpu.hard.fpscr &=
+ ~(FPSCR_CAUSE_MASK | FPSCR_FLAG_MASK);
+ grab_fpu(regs);
+ restore_fpu(tsk);
+ set_tsk_thread_flag(tsk, TIF_USEDFPU);
+ } else {
+ tsk->thread.trap_no = 11;
+ tsk->thread.error_code = 0;
+ force_sig(SIGFPE, tsk);
+ }
+
+ regs->pc = nextpc;
+ return 1;
+ }
+
+ return 0;
+}
+
+asmlinkage void
+do_fpu_error(unsigned long r4, unsigned long r5, unsigned long r6, unsigned long r7,
+ struct pt_regs regs)
+{
+ struct task_struct *tsk = current;
+
+ if (ieee_fpe_handler (&regs))
+ return;
+
+ regs.pc += 2;
+ save_fpu(tsk, &regs);
+ tsk->thread.trap_no = 11;
+ tsk->thread.error_code = 0;
+ force_sig(SIGFPE, tsk);
+}
+
+asmlinkage void
+do_fpu_state_restore(unsigned long r4, unsigned long r5, unsigned long r6,
+ unsigned long r7, struct pt_regs regs)
+{
+ struct task_struct *tsk = current;
+
+ grab_fpu(&regs);
+ if (!user_mode(&regs)) {
+ printk(KERN_ERR "BUG: FPU is used in kernel mode.\n");
+ return;
+ }
+
+ if (used_math()) {
+ /* Using the FPU again. */
+ restore_fpu(tsk);
+ } else {
+ /* First time FPU user. */
+ fpu_init();
+ set_used_math();
+ }
+ set_tsk_thread_flag(tsk, TIF_USEDFPU);
+}
diff --git a/arch/sh/kernel/cpu/sh4/irq_intc2.c b/arch/sh/kernel/cpu/sh4/irq_intc2.c
new file mode 100644
index 00000000000..099ebbf8974
--- /dev/null
+++ b/arch/sh/kernel/cpu/sh4/irq_intc2.c
@@ -0,0 +1,222 @@
+/*
+ * linux/arch/sh/kernel/irq_intc2.c
+ *
+ * Copyright (C) 2001 David J. Mckay (david.mckay@st.com)
+ *
+ * May be copied or modified under the terms of the GNU General Public
+ * License. See linux/COPYING for more information.
+ *
+ * Interrupt handling for INTC2-based IRQ.
+ *
+ * These are the "new Hitachi style" interrupts, as present on the
+ * Hitachi 7751 and the STM ST40 STB1.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/machvec.h>
+
+
+struct intc2_data {
+ unsigned char msk_offset;
+ unsigned char msk_shift;
+#ifdef CONFIG_CPU_SUBTYPE_ST40
+ int (*clear_irq) (int);
+#endif
+};
+
+
+static struct intc2_data intc2_data[NR_INTC2_IRQS];
+
+static void enable_intc2_irq(unsigned int irq);
+static void disable_intc2_irq(unsigned int irq);
+
+/* shutdown is same as "disable" */
+#define shutdown_intc2_irq disable_intc2_irq
+
+static void mask_and_ack_intc2(unsigned int);
+static void end_intc2_irq(unsigned int irq);
+
+static unsigned int startup_intc2_irq(unsigned int irq)
+{
+ enable_intc2_irq(irq);
+ return 0; /* never anything pending */
+}
+
+static struct hw_interrupt_type intc2_irq_type = {
+ "INTC2-IRQ",
+ startup_intc2_irq,
+ shutdown_intc2_irq,
+ enable_intc2_irq,
+ disable_intc2_irq,
+ mask_and_ack_intc2,
+ end_intc2_irq
+};
+
+static void disable_intc2_irq(unsigned int irq)
+{
+ int irq_offset = irq - INTC2_FIRST_IRQ;
+ int msk_shift, msk_offset;
+
+ // Sanity check
+ if((irq_offset<0) || (irq_offset>=NR_INTC2_IRQS))
+ return;
+
+ msk_shift = intc2_data[irq_offset].msk_shift;
+ msk_offset = intc2_data[irq_offset].msk_offset;
+
+ ctrl_outl(1<<msk_shift,
+ INTC2_BASE+INTC2_INTMSK_OFFSET+msk_offset);
+}
+
+static void enable_intc2_irq(unsigned int irq)
+{
+ int irq_offset = irq - INTC2_FIRST_IRQ;
+ int msk_shift, msk_offset;
+
+ /* Sanity check */
+ if((irq_offset<0) || (irq_offset>=NR_INTC2_IRQS))
+ return;
+
+ msk_shift = intc2_data[irq_offset].msk_shift;
+ msk_offset = intc2_data[irq_offset].msk_offset;
+
+ ctrl_outl(1<<msk_shift,
+ INTC2_BASE+INTC2_INTMSKCLR_OFFSET+msk_offset);
+}
+
+static void mask_and_ack_intc2(unsigned int irq)
+{
+ disable_intc2_irq(irq);
+}
+
+static void end_intc2_irq(unsigned int irq)
+{
+ if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
+ enable_intc2_irq(irq);
+
+#ifdef CONFIG_CPU_SUBTYPE_ST40
+ if (intc2_data[irq - INTC2_FIRST_IRQ].clear_irq)
+ intc2_data[irq - INTC2_FIRST_IRQ].clear_irq (irq);
+#endif
+}
+
+/*
+ * Setup an INTC2 style interrupt.
+ * NOTE: Unlike IPR interrupts, parameters are not shifted by this code,
+ * allowing the use of the numbers straight out of the datasheet.
+ * For example:
+ * PIO1 which is INTPRI00[19,16] and INTMSK00[13]
+ * would be: ^ ^ ^ ^
+ * | | | |
+ * make_intc2_irq(84, 0, 16, 0, 13);
+ */
+void make_intc2_irq(unsigned int irq,
+ unsigned int ipr_offset, unsigned int ipr_shift,
+ unsigned int msk_offset, unsigned int msk_shift,
+ unsigned int priority)
+{
+ int irq_offset = irq - INTC2_FIRST_IRQ;
+ unsigned int flags;
+ unsigned long ipr;
+
+ if((irq_offset<0) || (irq_offset>=NR_INTC2_IRQS))
+ return;
+
+ disable_irq_nosync(irq);
+
+ /* Fill the data we need */
+ intc2_data[irq_offset].msk_offset = msk_offset;
+ intc2_data[irq_offset].msk_shift = msk_shift;
+#ifdef CONFIG_CPU_SUBTYPE_ST40
+ intc2_data[irq_offset].clear_irq = NULL;
+#endif
+
+ /* Set the priority level */
+ local_irq_save(flags);
+
+ ipr=ctrl_inl(INTC2_BASE+INTC2_INTPRI_OFFSET+ipr_offset);
+ ipr&=~(0xf<<ipr_shift);
+ ipr|=(priority)<<ipr_shift;
+ ctrl_outl(ipr, INTC2_BASE+INTC2_INTPRI_OFFSET+ipr_offset);
+
+ local_irq_restore(flags);
+
+ irq_desc[irq].handler=&intc2_irq_type;
+
+ disable_intc2_irq(irq);
+}
+
+#ifdef CONFIG_CPU_SUBTYPE_ST40
+
+struct intc2_init {
+ unsigned short irq;
+ unsigned char ipr_offset, ipr_shift;
+ unsigned char msk_offset, msk_shift;
+};
+
+static struct intc2_init intc2_init_data[] __initdata = {
+ {64, 0, 0, 0, 0}, /* PCI serr */
+ {65, 0, 4, 0, 1}, /* PCI err */
+ {66, 0, 4, 0, 2}, /* PCI ad */
+ {67, 0, 4, 0, 3}, /* PCI pwd down */
+ {72, 0, 8, 0, 5}, /* DMAC INT0 */
+ {73, 0, 8, 0, 6}, /* DMAC INT1 */
+ {74, 0, 8, 0, 7}, /* DMAC INT2 */
+ {75, 0, 8, 0, 8}, /* DMAC INT3 */
+ {76, 0, 8, 0, 9}, /* DMAC INT4 */
+ {78, 0, 8, 0, 11}, /* DMAC ERR */
+ {80, 0, 12, 0, 12}, /* PIO0 */
+ {84, 0, 16, 0, 13}, /* PIO1 */
+ {88, 0, 20, 0, 14}, /* PIO2 */
+ {112, 4, 0, 4, 0}, /* Mailbox */
+#ifdef CONFIG_CPU_SUBTYPE_ST40GX1
+ {116, 4, 4, 4, 4}, /* SSC0 */
+ {120, 4, 8, 4, 8}, /* IR Blaster */
+ {124, 4, 12, 4, 12}, /* USB host */
+ {128, 4, 16, 4, 16}, /* Video processor BLITTER */
+ {132, 4, 20, 4, 20}, /* UART0 */
+ {134, 4, 20, 4, 22}, /* UART2 */
+ {136, 4, 24, 4, 24}, /* IO_PIO0 */
+ {140, 4, 28, 4, 28}, /* EMPI */
+ {144, 8, 0, 8, 0}, /* MAFE */
+ {148, 8, 4, 8, 4}, /* PWM */
+ {152, 8, 8, 8, 8}, /* SSC1 */
+ {156, 8, 12, 8, 12}, /* IO_PIO1 */
+ {160, 8, 16, 8, 16}, /* USB target */
+ {164, 8, 20, 8, 20}, /* UART1 */
+ {168, 8, 24, 8, 24}, /* Teletext */
+ {172, 8, 28, 8, 28}, /* VideoSync VTG */
+ {173, 8, 28, 8, 29}, /* VideoSync DVP0 */
+ {174, 8, 28, 8, 30}, /* VideoSync DVP1 */
+#endif
+};
+
+void __init init_IRQ_intc2(void)
+{
+ struct intc2_init *p;
+
+ printk(KERN_ALERT "init_IRQ_intc2\n");
+
+ for (p = intc2_init_data;
+ p<intc2_init_data+ARRAY_SIZE(intc2_init_data);
+ p++) {
+ make_intc2_irq(p->irq, p->ipr_offset, p->ipr_shift,
+ p-> msk_offset, p->msk_shift, 13);
+ }
+}
+
+/* Adds a termination callback to the interrupt */
+void intc2_add_clear_irq(int irq, int (*fn)(int))
+{
+ if (irq < INTC2_FIRST_IRQ)
+ return;
+
+ intc2_data[irq - INTC2_FIRST_IRQ].clear_irq = fn;
+}
+
+#endif /* CONFIG_CPU_SUBTYPE_ST40 */
diff --git a/arch/sh/kernel/cpu/sh4/probe.c b/arch/sh/kernel/cpu/sh4/probe.c
new file mode 100644
index 00000000000..42427b79697
--- /dev/null
+++ b/arch/sh/kernel/cpu/sh4/probe.c
@@ -0,0 +1,138 @@
+/*
+ * arch/sh/kernel/cpu/sh4/probe.c
+ *
+ * CPU Subtype Probing for SH-4.
+ *
+ * Copyright (C) 2001, 2002, 2003, 2004 Paul Mundt
+ * Copyright (C) 2003 Richard Curnow
+ *
+ * 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/init.h>
+#include <asm/processor.h>
+#include <asm/cache.h>
+#include <asm/io.h>
+
+int __init detect_cpu_and_cache_system(void)
+{
+ unsigned long pvr, prr, cvr;
+ unsigned long size;
+
+ static unsigned long sizes[16] = {
+ [1] = (1 << 12),
+ [2] = (1 << 13),
+ [4] = (1 << 14),
+ [8] = (1 << 15),
+ [9] = (1 << 16)
+ };
+
+ pvr = (ctrl_inl(CCN_PVR) >> 8) & 0xffff;
+ prr = (ctrl_inl(CCN_PRR) >> 4) & 0xff;
+ cvr = (ctrl_inl(CCN_CVR));
+
+ /*
+ * Setup some sane SH-4 defaults for the icache
+ */
+ cpu_data->icache.way_incr = (1 << 13);
+ cpu_data->icache.entry_shift = 5;
+ cpu_data->icache.entry_mask = 0x1fe0;
+ cpu_data->icache.sets = 256;
+ cpu_data->icache.ways = 1;
+ cpu_data->icache.linesz = L1_CACHE_BYTES;
+
+ /*
+ * And again for the dcache ..
+ */
+ cpu_data->dcache.way_incr = (1 << 14);
+ cpu_data->dcache.entry_shift = 5;
+ cpu_data->dcache.entry_mask = 0x3fe0;
+ cpu_data->dcache.sets = 512;
+ cpu_data->dcache.ways = 1;
+ cpu_data->dcache.linesz = L1_CACHE_BYTES;
+
+ /* Set the FPU flag, virtually all SH-4's have one */
+ cpu_data->flags |= CPU_HAS_FPU;
+
+ /*
+ * Probe the underlying processor version/revision and
+ * adjust cpu_data setup accordingly.
+ */
+ switch (pvr) {
+ case 0x205:
+ cpu_data->type = CPU_SH7750;
+ cpu_data->flags |= CPU_HAS_P2_FLUSH_BUG | CPU_HAS_PERF_COUNTER;
+ break;
+ case 0x206:
+ cpu_data->type = CPU_SH7750S;
+ cpu_data->flags |= CPU_HAS_P2_FLUSH_BUG | CPU_HAS_PERF_COUNTER;
+ break;
+ case 0x1100:
+ cpu_data->type = CPU_SH7751;
+ break;
+ case 0x2000:
+ cpu_data->type = CPU_SH73180;
+ cpu_data->icache.ways = 4;
+ cpu_data->dcache.ways = 4;
+ cpu_data->flags &= ~CPU_HAS_FPU;
+ break;
+ case 0x8000:
+ cpu_data->type = CPU_ST40RA;
+ break;
+ case 0x8100:
+ cpu_data->type = CPU_ST40GX1;
+ break;
+ case 0x700:
+ cpu_data->type = CPU_SH4_501;
+ cpu_data->icache.ways = 2;
+ cpu_data->dcache.ways = 2;
+
+ /* No FPU on the SH4-500 series.. */
+ cpu_data->flags &= ~CPU_HAS_FPU;
+ break;
+ case 0x600:
+ cpu_data->type = CPU_SH4_202;
+ cpu_data->icache.ways = 2;
+ cpu_data->dcache.ways = 2;
+ break;
+ case 0x500 ... 0x501:
+ switch (prr) {
+ case 0x10: cpu_data->type = CPU_SH7750R; break;
+ case 0x11: cpu_data->type = CPU_SH7751R; break;
+ case 0x50: cpu_data->type = CPU_SH7760; break;
+ }
+
+ cpu_data->icache.ways = 2;
+ cpu_data->dcache.ways = 2;
+
+ break;
+ default:
+ cpu_data->type = CPU_SH_NONE;
+ break;
+ }
+
+ /*
+ * On anything that's not a direct-mapped cache, look to the CVR
+ * for I/D-cache specifics.
+ */
+ if (cpu_data->icache.ways > 1) {
+ size = sizes[(cvr >> 20) & 0xf];
+ cpu_data->icache.way_incr = (size >> 1);
+ cpu_data->icache.sets = (size >> 6);
+ cpu_data->icache.entry_mask =
+ (cpu_data->icache.way_incr - (1 << 5));
+ }
+
+ if (cpu_data->dcache.ways > 1) {
+ size = sizes[(cvr >> 16) & 0xf];
+ cpu_data->dcache.way_incr = (size >> 1);
+ cpu_data->dcache.sets = (size >> 6);
+ cpu_data->dcache.entry_mask =
+ (cpu_data->dcache.way_incr - (1 << 5));
+ }
+
+ return 0;
+}
+
diff --git a/arch/sh/kernel/cpu/sh4/sq.c b/arch/sh/kernel/cpu/sh4/sq.c
new file mode 100644
index 00000000000..8437ea7430f
--- /dev/null
+++ b/arch/sh/kernel/cpu/sh4/sq.c
@@ -0,0 +1,453 @@
+/*
+ * arch/sh/kernel/cpu/sq.c
+ *
+ * General management API for SH-4 integrated Store Queues
+ *
+ * Copyright (C) 2001, 2002, 2003, 2004 Paul Mundt
+ * Copyright (C) 2001, 2002 M. R. Brown
+ *
+ * Some of this code has been adopted directly from the old arch/sh/mm/sq.c
+ * hack that was part of the LinuxDC project. For all intents and purposes,
+ * this is a completely new interface that really doesn't have much in common
+ * with the old zone-based approach at all. In fact, it's only listed here for
+ * general completeness.
+ *
+ * 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/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/proc_fs.h>
+#include <linux/miscdevice.h>
+#include <linux/vmalloc.h>
+
+#include <asm/io.h>
+#include <asm/page.h>
+#include <asm/mmu_context.h>
+#include <asm/cpu/sq.h>
+
+static LIST_HEAD(sq_mapping_list);
+static DEFINE_SPINLOCK(sq_mapping_lock);
+
+/**
+ * sq_flush - Flush (prefetch) the store queue cache
+ * @addr: the store queue address to flush
+ *
+ * Executes a prefetch instruction on the specified store queue cache,
+ * so that the cached data is written to physical memory.
+ */
+inline void sq_flush(void *addr)
+{
+ __asm__ __volatile__ ("pref @%0" : : "r" (addr) : "memory");
+}
+
+/**
+ * sq_flush_range - Flush (prefetch) a specific SQ range
+ * @start: the store queue address to start flushing from
+ * @len: the length to flush
+ *
+ * Flushes the store queue cache from @start to @start + @len in a
+ * linear fashion.
+ */
+void sq_flush_range(unsigned long start, unsigned int len)
+{
+ volatile unsigned long *sq = (unsigned long *)start;
+ unsigned long dummy;
+
+ /* Flush the queues */
+ for (len >>= 5; len--; sq += 8)
+ sq_flush((void *)sq);
+
+ /* Wait for completion */
+ dummy = ctrl_inl(P4SEG_STORE_QUE);
+
+ ctrl_outl(0, P4SEG_STORE_QUE + 0);
+ ctrl_outl(0, P4SEG_STORE_QUE + 8);
+}
+
+static struct sq_mapping *__sq_alloc_mapping(unsigned long virt, unsigned long phys, unsigned long size, const char *name)
+{
+ struct sq_mapping *map;
+
+ if (virt + size > SQ_ADDRMAX)
+ return ERR_PTR(-ENOSPC);
+
+ map = kmalloc(sizeof(struct sq_mapping), GFP_KERNEL);
+ if (!map)
+ return ERR_PTR(-ENOMEM);
+
+ INIT_LIST_HEAD(&map->list);
+
+ map->sq_addr = virt;
+ map->addr = phys;
+ map->size = size + 1;
+ map->name = name;
+
+ list_add(&map->list, &sq_mapping_list);
+
+ return map;
+}
+
+static unsigned long __sq_get_next_addr(void)
+{
+ if (!list_empty(&sq_mapping_list)) {
+ struct list_head *pos, *tmp;
+
+ /*
+ * Read one off the list head, as it will have the highest
+ * mapped allocation. Set the next one up right above it.
+ *
+ * This is somewhat sub-optimal, as we don't look at
+ * gaps between allocations or anything lower then the
+ * highest-level allocation.
+ *
+ * However, in the interest of performance and the general
+ * lack of desire to do constant list rebalancing, we don't
+ * worry about it.
+ */
+ list_for_each_safe(pos, tmp, &sq_mapping_list) {
+ struct sq_mapping *entry;
+
+ entry = list_entry(pos, typeof(*entry), list);
+
+ return entry->sq_addr + entry->size;
+ }
+ }
+
+ return P4SEG_STORE_QUE;
+}
+
+/**
+ * __sq_remap - Perform a translation from the SQ to a phys addr
+ * @map: sq mapping containing phys and store queue addresses.
+ *
+ * Maps the store queue address specified in the mapping to the physical
+ * address specified in the mapping.
+ */
+static struct sq_mapping *__sq_remap(struct sq_mapping *map)
+{
+ unsigned long flags, pteh, ptel;
+ struct vm_struct *vma;
+ pgprot_t pgprot;
+
+ /*
+ * Without an MMU (or with it turned off), this is much more
+ * straightforward, as we can just load up each queue's QACR with
+ * the physical address appropriately masked.
+ */
+
+ ctrl_outl(((map->addr >> 26) << 2) & 0x1c, SQ_QACR0);
+ ctrl_outl(((map->addr >> 26) << 2) & 0x1c, SQ_QACR1);
+
+#ifdef CONFIG_MMU
+ /*
+ * With an MMU on the other hand, things are slightly more involved.
+ * Namely, we have to have a direct mapping between the SQ addr and
+ * the associated physical address in the UTLB by way of setting up
+ * a virt<->phys translation by hand. We do this by simply specifying
+ * the SQ addr in UTLB.VPN and the associated physical address in
+ * UTLB.PPN.
+ *
+ * Notably, even though this is a special case translation, and some
+ * of the configuration bits are meaningless, we're still required
+ * to have a valid ASID context in PTEH.
+ *
+ * We could also probably get by without explicitly setting PTEA, but
+ * we do it here just for good measure.
+ */
+ spin_lock_irqsave(&sq_mapping_lock, flags);
+
+ pteh = map->sq_addr;
+ ctrl_outl((pteh & MMU_VPN_MASK) | get_asid(), MMU_PTEH);
+
+ ptel = map->addr & PAGE_MASK;
+ ctrl_outl(((ptel >> 28) & 0xe) | (ptel & 0x1), MMU_PTEA);
+
+ pgprot = pgprot_noncached(PAGE_KERNEL);
+
+ ptel &= _PAGE_FLAGS_HARDWARE_MASK;
+ ptel |= pgprot_val(pgprot);
+ ctrl_outl(ptel, MMU_PTEL);
+
+ __asm__ __volatile__ ("ldtlb" : : : "memory");
+
+ spin_unlock_irqrestore(&sq_mapping_lock, flags);
+
+ /*
+ * Next, we need to map ourselves in the kernel page table, so that
+ * future accesses after a TLB flush will be handled when we take a
+ * page fault.
+ *
+ * Theoretically we could just do this directly and not worry about
+ * setting up the translation by hand ahead of time, but for the
+ * cases where we want a one-shot SQ mapping followed by a quick
+ * writeout before we hit the TLB flush, we do it anyways. This way
+ * we at least save ourselves the initial page fault overhead.
+ */
+ vma = __get_vm_area(map->size, VM_ALLOC, map->sq_addr, SQ_ADDRMAX);
+ if (!vma)
+ return ERR_PTR(-ENOMEM);
+
+ vma->phys_addr = map->addr;
+
+ if (remap_area_pages((unsigned long)vma->addr, vma->phys_addr,
+ map->size, pgprot_val(pgprot))) {
+ vunmap(vma->addr);
+ return NULL;
+ }
+#endif /* CONFIG_MMU */
+
+ return map;
+}
+
+/**
+ * sq_remap - Map a physical address through the Store Queues
+ * @phys: Physical address of mapping.
+ * @size: Length of mapping.
+ * @name: User invoking mapping.
+ *
+ * Remaps the physical address @phys through the next available store queue
+ * address of @size length. @name is logged at boot time as well as through
+ * the procfs interface.
+ *
+ * A pre-allocated and filled sq_mapping pointer is returned, and must be
+ * cleaned up with a call to sq_unmap() when the user is done with the
+ * mapping.
+ */
+struct sq_mapping *sq_remap(unsigned long phys, unsigned int size, const char *name)
+{
+ struct sq_mapping *map;
+ unsigned long virt, end;
+ unsigned int psz;
+
+ /* Don't allow wraparound or zero size */
+ end = phys + size - 1;
+ if (!size || end < phys)
+ return NULL;
+ /* Don't allow anyone to remap normal memory.. */
+ if (phys < virt_to_phys(high_memory))
+ return NULL;
+
+ phys &= PAGE_MASK;
+
+ size = PAGE_ALIGN(end + 1) - phys;
+ virt = __sq_get_next_addr();
+ psz = (size + (PAGE_SIZE - 1)) / PAGE_SIZE;
+ map = __sq_alloc_mapping(virt, phys, size, name);
+
+ printk("sqremap: %15s [%4d page%s] va 0x%08lx pa 0x%08lx\n",
+ map->name ? map->name : "???",
+ psz, psz == 1 ? " " : "s",
+ map->sq_addr, map->addr);
+
+ return __sq_remap(map);
+}
+
+/**
+ * sq_unmap - Unmap a Store Queue allocation
+ * @map: Pre-allocated Store Queue mapping.
+ *
+ * Unmaps the store queue allocation @map that was previously created by
+ * sq_remap(). Also frees up the pte that was previously inserted into
+ * the kernel page table and discards the UTLB translation.
+ */
+void sq_unmap(struct sq_mapping *map)
+{
+ if (map->sq_addr > (unsigned long)high_memory)
+ vfree((void *)(map->sq_addr & PAGE_MASK));
+
+ list_del(&map->list);
+ kfree(map);
+}
+
+/**
+ * sq_clear - Clear a store queue range
+ * @addr: Address to start clearing from.
+ * @len: Length to clear.
+ *
+ * A quick zero-fill implementation for clearing out memory that has been
+ * remapped through the store queues.
+ */
+void sq_clear(unsigned long addr, unsigned int len)
+{
+ int i;
+
+ /* Clear out both queues linearly */
+ for (i = 0; i < 8; i++) {
+ ctrl_outl(0, addr + i + 0);
+ ctrl_outl(0, addr + i + 8);
+ }
+
+ sq_flush_range(addr, len);
+}
+
+/**
+ * sq_vma_unmap - Unmap a VMA range
+ * @area: VMA containing range.
+ * @addr: Start of range.
+ * @len: Length of range.
+ *
+ * Searches the sq_mapping_list for a mapping matching the sq addr @addr,
+ * and subsequently frees up the entry. Further cleanup is done by generic
+ * code.
+ */
+static void sq_vma_unmap(struct vm_area_struct *area,
+ unsigned long addr, size_t len)
+{
+ struct list_head *pos, *tmp;
+
+ list_for_each_safe(pos, tmp, &sq_mapping_list) {
+ struct sq_mapping *entry;
+
+ entry = list_entry(pos, typeof(*entry), list);
+
+ if (entry->sq_addr == addr) {
+ /*
+ * We could probably get away without doing the tlb flush
+ * here, as generic code should take care of most of this
+ * when unmapping the rest of the VMA range for us. Leave
+ * it in for added sanity for the time being..
+ */
+ __flush_tlb_page(get_asid(), entry->sq_addr & PAGE_MASK);
+
+ list_del(&entry->list);
+ kfree(entry);
+
+ return;
+ }
+ }
+}
+
+/**
+ * sq_vma_sync - Sync a VMA range
+ * @area: VMA containing range.
+ * @start: Start of range.
+ * @len: Length of range.
+ * @flags: Additional flags.
+ *
+ * Synchronizes an sq mapped range by flushing the store queue cache for
+ * the duration of the mapping.
+ *
+ * Used internally for user mappings, which must use msync() to prefetch
+ * the store queue cache.
+ */
+static int sq_vma_sync(struct vm_area_struct *area,
+ unsigned long start, size_t len, unsigned int flags)
+{
+ sq_flush_range(start, len);
+
+ return 0;
+}
+
+static struct vm_operations_struct sq_vma_ops = {
+ .unmap = sq_vma_unmap,
+ .sync = sq_vma_sync,
+};
+
+/**
+ * sq_mmap - mmap() for /dev/cpu/sq
+ * @file: unused.
+ * @vma: VMA to remap.
+ *
+ * Remap the specified vma @vma through the store queues, and setup associated
+ * information for the new mapping. Also build up the page tables for the new
+ * area.
+ */
+static int sq_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
+ unsigned long size = vma->vm_end - vma->vm_start;
+ struct sq_mapping *map;
+
+ /*
+ * We're not interested in any arbitrary virtual address that has
+ * been stuck in the VMA, as we already know what addresses we
+ * want. Save off the size, and reposition the VMA to begin at
+ * the next available sq address.
+ */
+ vma->vm_start = __sq_get_next_addr();
+ vma->vm_end = vma->vm_start + size;
+
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+
+ vma->vm_flags |= VM_IO | VM_RESERVED;
+
+ map = __sq_alloc_mapping(vma->vm_start, offset, size, "Userspace");
+
+ if (io_remap_pfn_range(vma, map->sq_addr, map->addr >> PAGE_SHIFT,
+ size, vma->vm_page_prot))
+ return -EAGAIN;
+
+ vma->vm_ops = &sq_vma_ops;
+
+ return 0;
+}
+
+#ifdef CONFIG_PROC_FS
+static int sq_mapping_read_proc(char *buf, char **start, off_t off,
+ int len, int *eof, void *data)
+{
+ struct list_head *pos;
+ char *p = buf;
+
+ list_for_each_prev(pos, &sq_mapping_list) {
+ struct sq_mapping *entry;
+
+ entry = list_entry(pos, typeof(*entry), list);
+
+ p += sprintf(p, "%08lx-%08lx [%08lx]: %s\n", entry->sq_addr,
+ entry->sq_addr + entry->size - 1, entry->addr,
+ entry->name);
+ }
+
+ return p - buf;
+}
+#endif
+
+static struct file_operations sq_fops = {
+ .owner = THIS_MODULE,
+ .mmap = sq_mmap,
+};
+
+static struct miscdevice sq_dev = {
+ .minor = STORE_QUEUE_MINOR,
+ .name = "sq",
+ .devfs_name = "cpu/sq",
+ .fops = &sq_fops,
+};
+
+static int __init sq_api_init(void)
+{
+ printk(KERN_NOTICE "sq: Registering store queue API.\n");
+
+#ifdef CONFIG_PROC_FS
+ create_proc_read_entry("sq_mapping", 0, 0, sq_mapping_read_proc, 0);
+#endif
+
+ return misc_register(&sq_dev);
+}
+
+static void __exit sq_api_exit(void)
+{
+ misc_deregister(&sq_dev);
+}
+
+module_init(sq_api_init);
+module_exit(sq_api_exit);
+
+MODULE_AUTHOR("Paul Mundt <lethal@linux-sh.org>, M. R. Brown <mrbrown@0xd6.org>");
+MODULE_DESCRIPTION("Simple API for SH-4 integrated Store Queues");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(STORE_QUEUE_MINOR);
+
+EXPORT_SYMBOL(sq_remap);
+EXPORT_SYMBOL(sq_unmap);
+EXPORT_SYMBOL(sq_clear);
+EXPORT_SYMBOL(sq_flush);
+EXPORT_SYMBOL(sq_flush_range);
+
diff --git a/arch/sh/kernel/cpu/ubc.S b/arch/sh/kernel/cpu/ubc.S
new file mode 100644
index 00000000000..0c569b20e1c
--- /dev/null
+++ b/arch/sh/kernel/cpu/ubc.S
@@ -0,0 +1,59 @@
+/*
+ * arch/sh/kernel/ubc.S
+ *
+ * Set of management routines for the User Break Controller (UBC)
+ *
+ * Copyright (C) 2002 Paul Mundt
+ *
+ * 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.
+ */
+#include <linux/linkage.h>
+#include <asm/ubc.h>
+
+#define STBCR2 0xffc00010
+
+ENTRY(ubc_sleep)
+ mov #0, r0
+
+ mov.l 1f, r1 ! Zero out UBC_BBRA ..
+ mov.w r0, @r1
+
+ mov.l 2f, r1 ! .. same for BBRB ..
+ mov.w r0, @r1
+
+ mov.l 3f, r1 ! .. and again for BRCR.
+ mov.w r0, @r1
+
+ mov.w @r1, r0 ! Dummy read BRCR
+
+ mov.l 4f, r1 ! Set MSTP5 in STBCR2
+ mov.b @r1, r0
+ or #0x01, r0
+ mov.b r0, @r1
+
+ mov.b @r1, r0 ! Two dummy reads ..
+ mov.b @r1, r0
+
+ rts
+ nop
+
+ENTRY(ubc_wakeup)
+ mov.l 4f, r1 ! Clear MSTP5
+ mov.b @r1, r0
+ and #0xfe, r0
+ mov.b r0, @r1
+
+ mov.b @r1, r0 ! Two more dummy reads ..
+ mov.b @r1, r0
+
+ rts
+ nop
+
+1: .long UBC_BBRA
+2: .long UBC_BBRB
+3: .long UBC_BRCR
+4: .long STBCR2
+
diff --git a/arch/sh/kernel/cpufreq.c b/arch/sh/kernel/cpufreq.c
new file mode 100644
index 00000000000..e0b384bef55
--- /dev/null
+++ b/arch/sh/kernel/cpufreq.c
@@ -0,0 +1,218 @@
+/*
+ * arch/sh/kernel/cpufreq.c
+ *
+ * cpufreq driver for the SuperH processors.
+ *
+ * Copyright (C) 2002, 2003, 2004, 2005 Paul Mundt
+ * Copyright (C) 2002 M. R. Brown
+ *
+ * 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.
+ */
+#include <linux/types.h>
+#include <linux/cpufreq.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/cpumask.h>
+#include <linux/smp.h>
+
+#include <asm/processor.h>
+#include <asm/watchdog.h>
+#include <asm/freq.h>
+#include <asm/io.h>
+
+/*
+ * For SuperH, each policy change requires that we change the IFC, BFC, and
+ * PFC at the same time. Here we define sane values that won't trash the
+ * system.
+ *
+ * Note the max set is computed at runtime, we use the divisors that we booted
+ * with to setup our maximum operating frequencies.
+ */
+struct clock_set {
+ unsigned int ifc;
+ unsigned int bfc;
+ unsigned int pfc;
+} clock_sets[] = {
+#if defined(CONFIG_CPU_SH3) || defined(CONFIG_CPU_SH2)
+ { 0, 0, 0 }, /* not implemented yet */
+#elif defined(CONFIG_CPU_SH4)
+ { 4, 8, 8 }, /* min - IFC: 1/4, BFC: 1/8, PFC: 1/8 */
+ { 1, 2, 2 }, /* max - IFC: 1, BFC: 1/2, PFC: 1/2 */
+#endif
+};
+
+#define MIN_CLOCK_SET 0
+#define MAX_CLOCK_SET (ARRAY_SIZE(clock_sets) - 1)
+
+/*
+ * For the time being, we only support two frequencies, which in turn are
+ * aimed at the POWERSAVE and PERFORMANCE policies, which in turn are derived
+ * directly from the respective min/max clock sets. Technically we could
+ * support a wider range of frequencies, but these vary far too much for each
+ * CPU subtype (and we'd have to construct a frequency table for each subtype).
+ *
+ * Maybe something to implement in the future..
+ */
+#define SH_FREQ_MAX 0
+#define SH_FREQ_MIN 1
+
+static struct cpufreq_frequency_table sh_freqs[] = {
+ { SH_FREQ_MAX, 0 },
+ { SH_FREQ_MIN, 0 },
+ { 0, CPUFREQ_TABLE_END },
+};
+
+static void sh_cpufreq_update_clocks(unsigned int set)
+{
+ current_cpu_data.cpu_clock = current_cpu_data.master_clock / clock_sets[set].ifc;
+ current_cpu_data.bus_clock = current_cpu_data.master_clock / clock_sets[set].bfc;
+ current_cpu_data.module_clock = current_cpu_data.master_clock / clock_sets[set].pfc;
+ current_cpu_data.loops_per_jiffy = loops_per_jiffy;
+}
+
+/* XXX: This needs to be split out per CPU and CPU subtype. */
+/*
+ * Here we notify other drivers of the proposed change and the final change.
+ */
+static int sh_cpufreq_setstate(unsigned int cpu, unsigned int set)
+{
+ unsigned short frqcr = ctrl_inw(FRQCR);
+ cpumask_t cpus_allowed;
+ struct cpufreq_freqs freqs;
+
+ if (!cpu_online(cpu))
+ return -ENODEV;
+
+ cpus_allowed = current->cpus_allowed;
+ set_cpus_allowed(current, cpumask_of_cpu(cpu));
+
+ BUG_ON(smp_processor_id() != cpu);
+
+ freqs.cpu = cpu;
+ freqs.old = current_cpu_data.cpu_clock / 1000;
+ freqs.new = (current_cpu_data.master_clock / clock_sets[set].ifc) / 1000;
+
+ cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+#if defined(CONFIG_CPU_SH3)
+ frqcr |= (newstate & 0x4000) << 14;
+ frqcr |= (newstate & 0x000c) << 2;
+#elif defined(CONFIG_CPU_SH4)
+ /*
+ * FRQCR.PLL2EN is 1, we need to allow the PLL to stabilize by
+ * initializing the WDT.
+ */
+ if (frqcr & (1 << 9)) {
+ __u8 csr;
+
+ /*
+ * Set the overflow period to the highest available,
+ * in this case a 1/4096 division ratio yields a 5.25ms
+ * overflow period. See asm-sh/watchdog.h for more
+ * information and a range of other divisors.
+ */
+ csr = sh_wdt_read_csr();
+ csr |= WTCSR_CKS_4096;
+ sh_wdt_write_csr(csr);
+
+ sh_wdt_write_cnt(0);
+ }
+ frqcr &= 0x0e00; /* Clear ifc, bfc, pfc */
+ frqcr |= get_ifc_value(clock_sets[set].ifc) << 6;
+ frqcr |= get_bfc_value(clock_sets[set].bfc) << 3;
+ frqcr |= get_pfc_value(clock_sets[set].pfc);
+#endif
+ ctrl_outw(frqcr, FRQCR);
+ sh_cpufreq_update_clocks(set);
+
+ set_cpus_allowed(current, cpus_allowed);
+ cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+
+ return 0;
+}
+
+static int sh_cpufreq_cpu_init(struct cpufreq_policy *policy)
+{
+ unsigned int min_freq, max_freq;
+ unsigned int ifc, bfc, pfc;
+
+ if (!cpu_online(policy->cpu))
+ return -ENODEV;
+
+ /* Update our maximum clock set */
+ get_current_frequency_divisors(&ifc, &bfc, &pfc);
+ clock_sets[MAX_CLOCK_SET].ifc = ifc;
+ clock_sets[MAX_CLOCK_SET].bfc = bfc;
+ clock_sets[MAX_CLOCK_SET].pfc = pfc;
+
+ /* Convert from Hz to kHz */
+ max_freq = current_cpu_data.cpu_clock / 1000;
+ min_freq = (current_cpu_data.master_clock / clock_sets[MIN_CLOCK_SET].ifc) / 1000;
+
+ sh_freqs[SH_FREQ_MAX].frequency = max_freq;
+ sh_freqs[SH_FREQ_MIN].frequency = min_freq;
+
+ /* cpuinfo and default policy values */
+ policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
+ policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL;
+ policy->cur = max_freq;
+
+ return cpufreq_frequency_table_cpuinfo(policy, &sh_freqs[0]);
+}
+
+static int sh_cpufreq_verify(struct cpufreq_policy *policy)
+{
+ return cpufreq_frequency_table_verify(policy, &sh_freqs[0]);
+}
+
+static int sh_cpufreq_target(struct cpufreq_policy *policy,
+ unsigned int target_freq,
+ unsigned int relation)
+{
+ unsigned int set, idx = 0;
+
+ if (cpufreq_frequency_table_target(policy, &sh_freqs[0], target_freq, relation, &idx))
+ return -EINVAL;
+
+ set = (idx == SH_FREQ_MIN) ? MIN_CLOCK_SET : MAX_CLOCK_SET;
+
+ sh_cpufreq_setstate(policy->cpu, set);
+
+ return 0;
+}
+
+static struct cpufreq_driver sh_cpufreq_driver = {
+ .owner = THIS_MODULE,
+ .name = "SH cpufreq",
+ .init = sh_cpufreq_cpu_init,
+ .verify = sh_cpufreq_verify,
+ .target = sh_cpufreq_target,
+};
+
+static int __init sh_cpufreq_init(void)
+{
+ if (!current_cpu_data.cpu_clock)
+ return -EINVAL;
+ if (cpufreq_register_driver(&sh_cpufreq_driver))
+ return -EINVAL;
+
+ return 0;
+}
+
+static void __exit sh_cpufreq_exit(void)
+{
+ cpufreq_unregister_driver(&sh_cpufreq_driver);
+}
+
+module_init(sh_cpufreq_init);
+module_exit(sh_cpufreq_exit);
+
+MODULE_AUTHOR("Paul Mundt <lethal@linux-sh.org>");
+MODULE_DESCRIPTION("cpufreq driver for SuperH");
+MODULE_LICENSE("GPL");
+
diff --git a/arch/sh/kernel/early_printk.c b/arch/sh/kernel/early_printk.c
new file mode 100644
index 00000000000..1378db375e1
--- /dev/null
+++ b/arch/sh/kernel/early_printk.c
@@ -0,0 +1,137 @@
+/*
+ * arch/sh/kernel/early_printk.c
+ *
+ * Copyright (C) 1999, 2000 Niibe Yutaka
+ * Copyright (C) 2002 M. R. Brown
+ * Copyright (C) 2004 Paul Mundt
+ *
+ * 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/console.h>
+#include <linux/tty.h>
+#include <linux/init.h>
+#include <asm/io.h>
+
+#ifdef CONFIG_SH_STANDARD_BIOS
+#include <asm/sh_bios.h>
+
+/*
+ * Print a string through the BIOS
+ */
+static void sh_console_write(struct console *co, const char *s,
+ unsigned count)
+{
+ sh_bios_console_write(s, count);
+}
+
+/*
+ * Setup initial baud/bits/parity. We do two things here:
+ * - construct a cflag setting for the first rs_open()
+ * - initialize the serial port
+ * Return non-zero if we didn't find a serial port.
+ */
+static int __init sh_console_setup(struct console *co, char *options)
+{
+ int cflag = CREAD | HUPCL | CLOCAL;
+
+ /*
+ * Now construct a cflag setting.
+ * TODO: this is a totally bogus cflag, as we have
+ * no idea what serial settings the BIOS is using, or
+ * even if its using the serial port at all.
+ */
+ cflag |= B115200 | CS8 | /*no parity*/0;
+
+ co->cflag = cflag;
+
+ return 0;
+}
+
+static struct console early_console = {
+ .name = "bios",
+ .write = sh_console_write,
+ .setup = sh_console_setup,
+ .flags = CON_PRINTBUFFER,
+ .index = -1,
+};
+#endif
+
+#ifdef CONFIG_EARLY_SCIF_CONSOLE
+#define SCIF_REG 0xffe80000
+
+static void scif_sercon_putc(int c)
+{
+ while (!(ctrl_inw(SCIF_REG + 0x10) & 0x20)) ;
+
+ ctrl_outb(c, SCIF_REG + 12);
+ ctrl_outw((ctrl_inw(SCIF_REG + 0x10) & 0x9f), SCIF_REG + 0x10);
+
+ if (c == '\n')
+ scif_sercon_putc('\r');
+}
+
+static void scif_sercon_flush(void)
+{
+ ctrl_outw((ctrl_inw(SCIF_REG + 0x10) & 0xbf), SCIF_REG + 0x10);
+
+ while (!(ctrl_inw(SCIF_REG + 0x10) & 0x40)) ;
+
+ ctrl_outw((ctrl_inw(SCIF_REG + 0x10) & 0xbf), SCIF_REG + 0x10);
+}
+
+static void scif_sercon_write(struct console *con, const char *s, unsigned count)
+{
+ while (count-- > 0)
+ scif_sercon_putc(*s++);
+
+ scif_sercon_flush();
+}
+
+static int __init scif_sercon_setup(struct console *con, char *options)
+{
+ con->cflag = CREAD | HUPCL | CLOCAL | B115200 | CS8;
+
+ return 0;
+}
+
+static struct console early_console = {
+ .name = "sercon",
+ .write = scif_sercon_write,
+ .setup = scif_sercon_setup,
+ .flags = CON_PRINTBUFFER,
+ .index = -1,
+};
+
+void scif_sercon_init(int baud)
+{
+ ctrl_outw(0, SCIF_REG + 8);
+ ctrl_outw(0, SCIF_REG);
+
+ /* Set baud rate */
+ ctrl_outb((CONFIG_SH_PCLK_FREQ + 16 * baud) /
+ (32 * baud) - 1, SCIF_REG + 4);
+
+ ctrl_outw(12, SCIF_REG + 24);
+ ctrl_outw(8, SCIF_REG + 24);
+ ctrl_outw(0, SCIF_REG + 32);
+ ctrl_outw(0x60, SCIF_REG + 16);
+ ctrl_outw(0, SCIF_REG + 36);
+ ctrl_outw(0x30, SCIF_REG + 8);
+}
+#endif
+
+void __init enable_early_printk(void)
+{
+#ifdef CONFIG_EARLY_SCIF_CONSOLE
+ scif_sercon_init(115200);
+#endif
+ register_console(&early_console);
+}
+
+void disable_early_printk(void)
+{
+ unregister_console(&early_console);
+}
+
diff --git a/arch/sh/kernel/entry.S b/arch/sh/kernel/entry.S
new file mode 100644
index 00000000000..6615e4838ee
--- /dev/null
+++ b/arch/sh/kernel/entry.S
@@ -0,0 +1,1149 @@
+/* $Id: entry.S,v 1.37 2004/06/11 13:02:46 doyu Exp $
+ *
+ * linux/arch/sh/entry.S
+ *
+ * Copyright (C) 1999, 2000, 2002 Niibe Yutaka
+ * Copyright (C) 2003 Paul Mundt
+ *
+ * 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/sys.h>
+#include <linux/linkage.h>
+#include <linux/config.h>
+#include <asm/asm-offsets.h>
+#include <asm/thread_info.h>
+#include <asm/unistd.h>
+
+#if !defined(CONFIG_NFSD) && !defined(CONFIG_NFSD_MODULE)
+#define sys_nfsservctl sys_ni_syscall
+#endif
+
+#if !defined(CONFIG_MMU)
+#define sys_madvise sys_ni_syscall
+#define sys_readahead sys_ni_syscall
+#define sys_mprotect sys_ni_syscall
+#define sys_msync sys_ni_syscall
+#define sys_mlock sys_ni_syscall
+#define sys_munlock sys_ni_syscall
+#define sys_mlockall sys_ni_syscall
+#define sys_munlockall sys_ni_syscall
+#define sys_mremap sys_ni_syscall
+#define sys_mincore sys_ni_syscall
+#define sys_remap_file_pages sys_ni_syscall
+#endif
+
+! NOTE:
+! GNU as (as of 2.9.1) changes bf/s into bt/s and bra, when the address
+! to be jumped is too far, but it causes illegal slot exception.
+
+/*
+ * entry.S contains the system-call and fault low-level handling routines.
+ * This also contains the timer-interrupt handler, as well as all interrupts
+ * and faults that can result in a task-switch.
+ *
+ * NOTE: This code handles signal-recognition, which happens every time
+ * after a timer-interrupt and after each system call.
+ *
+ * NOTE: This code uses a convention that instructions in the delay slot
+ * of a transfer-control instruction are indented by an extra space, thus:
+ *
+ * jmp @k0 ! control-transfer instruction
+ * ldc k1, ssr ! delay slot
+ *
+ * Stack layout in 'ret_from_syscall':
+ * ptrace needs to have all regs on the stack.
+ * if the order here is changed, it needs to be
+ * updated in ptrace.c and ptrace.h
+ *
+ * r0
+ * ...
+ * r15 = stack pointer
+ * spc
+ * pr
+ * ssr
+ * gbr
+ * mach
+ * macl
+ * syscall #
+ *
+ */
+
+ENOSYS = 38
+EINVAL = 22
+
+#if defined(CONFIG_CPU_SH3)
+TRA = 0xffffffd0
+EXPEVT = 0xffffffd4
+#if defined(CONFIG_CPU_SUBTYPE_SH7707) || defined(CONFIG_CPU_SUBTYPE_SH7709) || \
+ defined(CONFIG_CPU_SUBTYPE_SH7300) || defined(CONFIG_CPU_SUBTYPE_SH7705)
+INTEVT = 0xa4000000 ! INTEVTE2(0xa4000000)
+#else
+INTEVT = 0xffffffd8
+#endif
+MMU_TEA = 0xfffffffc ! TLB Exception Address Register
+#elif defined(CONFIG_CPU_SH4)
+TRA = 0xff000020
+EXPEVT = 0xff000024
+INTEVT = 0xff000028
+MMU_TEA = 0xff00000c ! TLB Exception Address Register
+#endif
+
+#if defined(CONFIG_KGDB_NMI)
+NMI_VEC = 0x1c0 ! Must catch early for debounce
+#endif
+
+/* Offsets to the stack */
+OFF_R0 = 0 /* Return value. New ABI also arg4 */
+OFF_R1 = 4 /* New ABI: arg5 */
+OFF_R2 = 8 /* New ABI: arg6 */
+OFF_R3 = 12 /* New ABI: syscall_nr */
+OFF_R4 = 16 /* New ABI: arg0 */
+OFF_R5 = 20 /* New ABI: arg1 */
+OFF_R6 = 24 /* New ABI: arg2 */
+OFF_R7 = 28 /* New ABI: arg3 */
+OFF_SP = (15*4)
+OFF_PC = (16*4)
+OFF_SR = (16*4+8)
+OFF_TRA = (16*4+6*4)
+
+
+#define k0 r0
+#define k1 r1
+#define k2 r2
+#define k3 r3
+#define k4 r4
+
+#define k_ex_code r2_bank /* r2_bank1 */
+#define g_imask r6 /* r6_bank1 */
+#define k_g_imask r6_bank /* r6_bank1 */
+#define current r7 /* r7_bank1 */
+
+/*
+ * Kernel mode register usage:
+ * k0 scratch
+ * k1 scratch
+ * k2 scratch (Exception code)
+ * k3 scratch (Return address)
+ * k4 scratch
+ * k5 reserved
+ * k6 Global Interrupt Mask (0--15 << 4)
+ * k7 CURRENT_THREAD_INFO (pointer to current thread info)
+ */
+
+!
+! TLB Miss / Initial Page write exception handling
+! _and_
+! TLB hits, but the access violate the protection.
+! It can be valid access, such as stack grow and/or C-O-W.
+!
+!
+! Find the pmd/pte entry and loadtlb
+! If it's not found, cause address error (SEGV)
+!
+! Although this could be written in assembly language (and it'd be faster),
+! this first version depends *much* on C implementation.
+!
+
+#define CLI() \
+ stc sr, r0; \
+ or #0xf0, r0; \
+ ldc r0, sr
+
+#define STI() \
+ mov.l __INV_IMASK, r11; \
+ stc sr, r10; \
+ and r11, r10; \
+ stc k_g_imask, r11; \
+ or r11, r10; \
+ ldc r10, sr
+
+#if defined(CONFIG_PREEMPT)
+# define preempt_stop() CLI()
+#else
+# define preempt_stop()
+# define resume_kernel restore_all
+#endif
+
+#if defined(CONFIG_MMU)
+ .align 2
+ENTRY(tlb_miss_load)
+ bra call_dpf
+ mov #0, r5
+
+ .align 2
+ENTRY(tlb_miss_store)
+ bra call_dpf
+ mov #1, r5
+
+ .align 2
+ENTRY(initial_page_write)
+ bra call_dpf
+ mov #1, r5
+
+ .align 2
+ENTRY(tlb_protection_violation_load)
+ bra call_dpf
+ mov #0, r5
+
+ .align 2
+ENTRY(tlb_protection_violation_store)
+ bra call_dpf
+ mov #1, r5
+
+call_dpf:
+ mov.l 1f, r0
+ mov r5, r8
+ mov.l @r0, r6
+ mov r6, r9
+ mov.l 2f, r0
+ sts pr, r10
+ jsr @r0
+ mov r15, r4
+ !
+ tst r0, r0
+ bf/s 0f
+ lds r10, pr
+ rts
+ nop
+0: STI()
+ mov.l 3f, r0
+ mov r9, r6
+ mov r8, r5
+ jmp @r0
+ mov r15, r4
+
+ .align 2
+1: .long MMU_TEA
+2: .long __do_page_fault
+3: .long do_page_fault
+
+ .align 2
+ENTRY(address_error_load)
+ bra call_dae
+ mov #0,r5 ! writeaccess = 0
+
+ .align 2
+ENTRY(address_error_store)
+ bra call_dae
+ mov #1,r5 ! writeaccess = 1
+
+ .align 2
+call_dae:
+ mov.l 1f, r0
+ mov.l @r0, r6 ! address
+ mov.l 2f, r0
+ jmp @r0
+ mov r15, r4 ! regs
+
+ .align 2
+1: .long MMU_TEA
+2: .long do_address_error
+#endif /* CONFIG_MMU */
+
+#if defined(CONFIG_SH_STANDARD_BIOS) || defined(CONFIG_SH_KGDB)
+! Handle kernel debug if either kgdb (SW) or gdb-stub (FW) is present.
+! If both are configured, handle the debug traps (breakpoints) in SW,
+! but still allow BIOS traps to FW.
+
+ .align 2
+debug_kernel:
+#if defined(CONFIG_SH_STANDARD_BIOS) && defined(CONFIG_SH_KGDB)
+ /* Force BIOS call to FW (debug_trap put TRA in r8) */
+ mov r8,r0
+ shlr2 r0
+ cmp/eq #0x3f,r0
+ bt debug_kernel_fw
+#endif /* CONFIG_SH_STANDARD_BIOS && CONFIG_SH_KGDB */
+
+debug_enter:
+#if defined(CONFIG_SH_KGDB)
+ /* Jump to kgdb, pass stacked regs as arg */
+debug_kernel_sw:
+ mov.l 3f, r0
+ jmp @r0
+ mov r15, r4
+ .align 2
+3: .long kgdb_handle_exception
+#endif /* CONFIG_SH_KGDB */
+
+#if defined(CONFIG_SH_STANDARD_BIOS)
+ /* Unwind the stack and jmp to the debug entry */
+debug_kernel_fw:
+ mov.l @r15+, r0
+ mov.l @r15+, r1
+ mov.l @r15+, r2
+ mov.l @r15+, r3
+ mov.l @r15+, r4
+ mov.l @r15+, r5
+ mov.l @r15+, r6
+ mov.l @r15+, r7
+ stc sr, r8
+ mov.l 1f, r9 ! BL =1, RB=1, IMASK=0x0F
+ or r9, r8
+ ldc r8, sr ! here, change the register bank
+ mov.l @r15+, r8
+ mov.l @r15+, r9
+ mov.l @r15+, r10
+ mov.l @r15+, r11
+ mov.l @r15+, r12
+ mov.l @r15+, r13
+ mov.l @r15+, r14
+ mov.l @r15+, k0
+ ldc.l @r15+, spc
+ lds.l @r15+, pr
+ mov.l @r15+, k1
+ ldc.l @r15+, gbr
+ lds.l @r15+, mach
+ lds.l @r15+, macl
+ mov k0, r15
+ !
+ mov.l 2f, k0
+ mov.l @k0, k0
+ jmp @k0
+ ldc k1, ssr
+ .align 2
+1: .long 0x300000f0
+2: .long gdb_vbr_vector
+#endif /* CONFIG_SH_STANDARD_BIOS */
+
+#endif /* CONFIG_SH_STANDARD_BIOS || CONFIG_SH_KGDB */
+
+
+ .align 2
+debug_trap:
+#if defined(CONFIG_SH_STANDARD_BIOS) || defined(CONFIG_SH_KGDB)
+ mov #OFF_SR, r0
+ mov.l @(r0,r15), r0 ! get status register
+ shll r0
+ shll r0 ! kernel space?
+ bt/s debug_kernel
+#endif
+ mov.l @r15, r0 ! Restore R0 value
+ mov.l 1f, r8
+ jmp @r8
+ nop
+
+ .align 2
+ENTRY(exception_error)
+ !
+ STI()
+ mov.l 2f, r0
+ jmp @r0
+ nop
+
+!
+ .align 2
+1: .long break_point_trap_software
+2: .long do_exception_error
+
+ .align 2
+ret_from_exception:
+ preempt_stop()
+ret_from_irq:
+ !
+ mov #OFF_SR, r0
+ mov.l @(r0,r15), r0 ! get status register
+ shll r0
+ shll r0 ! kernel space?
+ bt/s resume_kernel ! Yes, it's from kernel, go back soon
+ GET_THREAD_INFO(r8)
+
+#ifdef CONFIG_PREEMPT
+ bra resume_userspace
+ nop
+ENTRY(resume_kernel)
+ mov.l @(TI_PRE_COUNT,r8), r0 ! current_thread_info->preempt_count
+ tst r0, r0
+ bf noresched
+need_resched:
+ mov.l @(TI_FLAGS,r8), r0 ! current_thread_info->flags
+ tst #_TIF_NEED_RESCHED, r0 ! need_resched set?
+ bt noresched
+
+ mov #OFF_SR, r0
+ mov.l @(r0,r15), r0 ! get status register
+ and #0xf0, r0 ! interrupts off (exception path)?
+ cmp/eq #0xf0, r0
+ bt noresched
+
+ mov.l 1f, r0
+ mov.l r0, @(TI_PRE_COUNT,r8)
+
+ STI()
+ mov.l 2f, r0
+ jsr @r0
+ nop
+ mov #0, r0
+ mov.l r0, @(TI_PRE_COUNT,r8)
+ CLI()
+
+ bra need_resched
+ nop
+noresched:
+ bra restore_all
+ nop
+
+ .align 2
+1: .long PREEMPT_ACTIVE
+2: .long schedule
+#endif
+
+ENTRY(resume_userspace)
+ ! r8: current_thread_info
+ CLI()
+ mov.l @(TI_FLAGS,r8), r0 ! current_thread_info->flags
+ tst #_TIF_WORK_MASK, r0
+ bt/s restore_all
+ tst #_TIF_NEED_RESCHED, r0
+
+ .align 2
+work_pending:
+ ! r0: current_thread_info->flags
+ ! r8: current_thread_info
+ ! t: result of "tst #_TIF_NEED_RESCHED, r0"
+ bf/s work_resched
+ tst #_TIF_SIGPENDING, r0
+work_notifysig:
+ bt/s restore_all
+ mov r15, r4
+ mov #0, r5
+ mov.l 2f, r1
+ mova restore_all, r0
+ jmp @r1
+ lds r0, pr
+work_resched:
+#ifndef CONFIG_PREEMPT
+ ! gUSA handling
+ mov.l @(OFF_SP,r15), r0 ! get user space stack pointer
+ mov r0, r1
+ shll r0
+ bf/s 1f
+ shll r0
+ bf/s 1f
+ mov #OFF_PC, r0
+ ! SP >= 0xc0000000 : gUSA mark
+ mov.l @(r0,r15), r2 ! get user space PC (program counter)
+ mov.l @(OFF_R0,r15), r3 ! end point
+ cmp/hs r3, r2 ! r2 >= r3?
+ bt 1f
+ add r3, r1 ! rewind point #2
+ mov.l r1, @(r0,r15) ! reset PC to rewind point #2
+ !
+1:
+#endif
+ mov.l 1f, r1
+ jsr @r1 ! schedule
+ nop
+ CLI()
+ !
+ mov.l @(TI_FLAGS,r8), r0 ! current_thread_info->flags
+ tst #_TIF_WORK_MASK, r0
+ bt restore_all
+ bra work_pending
+ tst #_TIF_NEED_RESCHED, r0
+
+ .align 2
+1: .long schedule
+2: .long do_signal
+
+ .align 2
+syscall_exit_work:
+ ! r0: current_thread_info->flags
+ ! r8: current_thread_info
+ tst #_TIF_SYSCALL_TRACE, r0
+ bt/s work_pending
+ tst #_TIF_NEED_RESCHED, r0
+ STI()
+ ! XXX setup arguments...
+ mov.l 4f, r0 ! do_syscall_trace
+ jsr @r0
+ nop
+ bra resume_userspace
+ nop
+
+ .align 2
+syscall_trace_entry:
+ ! Yes it is traced.
+ ! XXX setup arguments...
+ mov.l 4f, r11 ! Call do_syscall_trace which notifies
+ jsr @r11 ! superior (will chomp R[0-7])
+ nop
+ ! Reload R0-R4 from kernel stack, where the
+ ! parent may have modified them using
+ ! ptrace(POKEUSR). (Note that R0-R2 are
+ ! used by the system call handler directly
+ ! from the kernel stack anyway, so don't need
+ ! to be reloaded here.) This allows the parent
+ ! to rewrite system calls and args on the fly.
+ mov.l @(OFF_R4,r15), r4 ! arg0
+ mov.l @(OFF_R5,r15), r5
+ mov.l @(OFF_R6,r15), r6
+ mov.l @(OFF_R7,r15), r7 ! arg3
+ mov.l @(OFF_R3,r15), r3 ! syscall_nr
+ ! Arrange for do_syscall_trace to be called
+ ! again as the system call returns.
+ mov.l 2f, r10 ! Number of syscalls
+ cmp/hs r10, r3
+ bf syscall_call
+ mov #-ENOSYS, r0
+ bra syscall_exit
+ mov.l r0, @(OFF_R0,r15) ! Return value
+
+/*
+ * Syscall interface:
+ *
+ * Syscall #: R3
+ * Arguments #0 to #3: R4--R7
+ * Arguments #4 to #6: R0, R1, R2
+ * TRA: (number of arguments + 0x10) x 4
+ *
+ * This code also handles delegating other traps to the BIOS/gdb stub
+ * according to:
+ *
+ * Trap number
+ * (TRA>>2) Purpose
+ * -------- -------
+ * 0x0-0xf old syscall ABI
+ * 0x10-0x1f new syscall ABI
+ * 0x20-0xff delegated through debug_trap to BIOS/gdb stub.
+ *
+ * Note: When we're first called, the TRA value must be shifted
+ * right 2 bits in order to get the value that was used as the "trapa"
+ * argument.
+ */
+
+ .align 2
+ .globl ret_from_fork
+ret_from_fork:
+ mov.l 1f, r8
+ jsr @r8
+ mov r0, r4
+ bra syscall_exit
+ nop
+ .align 2
+1: .long schedule_tail
+ !
+ENTRY(system_call)
+ mov.l 1f, r9
+ mov.l @r9, r8 ! Read from TRA (Trap Address) Register
+ !
+ ! Is the trap argument >= 0x20? (TRA will be >= 0x80)
+ mov #0x7f, r9
+ cmp/hi r9, r8
+ bt/s 0f
+ mov #OFF_TRA, r9
+ add r15, r9
+ !
+ mov.l r8, @r9 ! set TRA value to tra
+ STI()
+ ! Call the system call handler through the table.
+ ! First check for bad syscall number
+ mov r3, r9
+ mov.l 2f, r8 ! Number of syscalls
+ cmp/hs r8, r9
+ bf/s good_system_call
+ GET_THREAD_INFO(r8)
+syscall_badsys: ! Bad syscall number
+ mov #-ENOSYS, r0
+ bra resume_userspace
+ mov.l r0, @(OFF_R0,r15) ! Return value
+ !
+0:
+ bra debug_trap
+ nop
+ !
+good_system_call: ! Good syscall number
+ mov.l @(TI_FLAGS,r8), r8
+ mov #_TIF_SYSCALL_TRACE, r10
+ tst r10, r8
+ bf syscall_trace_entry
+ !
+syscall_call:
+ shll2 r9 ! x4
+ mov.l 3f, r8 ! Load the address of sys_call_table
+ add r8, r9
+ mov.l @r9, r8
+ jsr @r8 ! jump to specific syscall handler
+ nop
+ mov.l r0, @(OFF_R0,r15) ! save the return value
+ !
+syscall_exit:
+ CLI()
+ !
+ GET_THREAD_INFO(r8)
+ mov.l @(TI_FLAGS,r8), r0 ! current_thread_info->flags
+ tst #_TIF_ALLWORK_MASK, r0
+ bf syscall_exit_work
+restore_all:
+ mov.l @r15+, r0
+ mov.l @r15+, r1
+ mov.l @r15+, r2
+ mov.l @r15+, r3
+ mov.l @r15+, r4
+ mov.l @r15+, r5
+ mov.l @r15+, r6
+ mov.l @r15+, r7
+ !
+ stc sr, r8
+ mov.l 7f, r9
+ or r9, r8 ! BL =1, RB=1
+ ldc r8, sr ! here, change the register bank
+ !
+ mov.l @r15+, r8
+ mov.l @r15+, r9
+ mov.l @r15+, r10
+ mov.l @r15+, r11
+ mov.l @r15+, r12
+ mov.l @r15+, r13
+ mov.l @r15+, r14
+ mov.l @r15+, k4 ! original stack pointer
+ ldc.l @r15+, spc
+ lds.l @r15+, pr
+ mov.l @r15+, k3 ! original SR
+ ldc.l @r15+, gbr
+ lds.l @r15+, mach
+ lds.l @r15+, macl
+ add #4, r15 ! Skip syscall number
+ !
+#ifdef CONFIG_SH_DSP
+ mov.l @r15+, k0 ! DSP mode marker
+ mov.l 5f, k1
+ cmp/eq k0, k1 ! Do we have a DSP stack frame?
+ bf skip_restore
+
+ stc sr, k0 ! Enable CPU DSP mode
+ or k1, k0 ! (within kernel it may be disabled)
+ ldc k0, sr
+ mov r2, k0 ! Backup r2
+
+ ! Restore DSP registers from stack
+ mov r15, r2
+ movs.l @r2+, a1
+ movs.l @r2+, a0g
+ movs.l @r2+, a1g
+ movs.l @r2+, m0
+ movs.l @r2+, m1
+ mov r2, r15
+
+ lds.l @r15+, a0
+ lds.l @r15+, x0
+ lds.l @r15+, x1
+ lds.l @r15+, y0
+ lds.l @r15+, y1
+ lds.l @r15+, dsr
+ ldc.l @r15+, rs
+ ldc.l @r15+, re
+ ldc.l @r15+, mod
+
+ mov k0, r2 ! Restore r2
+skip_restore:
+#endif
+ !
+ ! Calculate new SR value
+ mov k3, k2 ! original SR value
+ mov.l 9f, k1
+ and k1, k2 ! Mask orignal SR value
+ !
+ mov k3, k0 ! Calculate IMASK-bits
+ shlr2 k0
+ and #0x3c, k0
+ cmp/eq #0x3c, k0
+ bt/s 6f
+ shll2 k0
+ mov g_imask, k0
+ !
+6: or k0, k2 ! Set the IMASK-bits
+ ldc k2, ssr
+ !
+#if defined(CONFIG_KGDB_NMI)
+ ! Clear in_nmi
+ mov.l 4f, k0
+ mov #0, k1
+ mov.b k1, @k0
+#endif
+ mov.l @r15+, k2 ! restore EXPEVT
+ mov k4, r15
+ rte
+ nop
+
+ .align 2
+1: .long TRA
+2: .long NR_syscalls
+3: .long sys_call_table
+4: .long do_syscall_trace
+5: .long 0x00001000 ! DSP
+7: .long 0x30000000
+9:
+__INV_IMASK:
+ .long 0xffffff0f ! ~(IMASK)
+
+! Exception Vector Base
+!
+! Should be aligned page boundary.
+!
+ .balign 4096,0,4096
+ENTRY(vbr_base)
+ .long 0
+!
+ .balign 256,0,256
+general_exception:
+ mov.l 1f, k2
+ mov.l 2f, k3
+ bra handle_exception
+ mov.l @k2, k2
+ .align 2
+1: .long EXPEVT
+2: .long ret_from_exception
+!
+!
+ .balign 1024,0,1024
+tlb_miss:
+ mov.l 1f, k2
+ mov.l 4f, k3
+ bra handle_exception
+ mov.l @k2, k2
+!
+ .balign 512,0,512
+interrupt:
+ mov.l 2f, k2
+ mov.l 3f, k3
+#if defined(CONFIG_KGDB_NMI)
+ ! Debounce (filter nested NMI)
+ mov.l @k2, k0
+ mov.l 5f, k1
+ cmp/eq k1, k0
+ bf 0f
+ mov.l 6f, k1
+ tas.b @k1
+ bt 0f
+ rte
+ nop
+ .align 2
+5: .long NMI_VEC
+6: .long in_nmi
+0:
+#endif /* defined(CONFIG_KGDB_NMI) */
+ bra handle_exception
+ mov.l @k2, k2
+
+ .align 2
+1: .long EXPEVT
+2: .long INTEVT
+3: .long ret_from_irq
+4: .long ret_from_exception
+
+!
+!
+ .align 2
+handle_exception:
+ ! Using k0, k1 for scratch registers (r0_bank1, r1_bank),
+ ! save all registers onto stack.
+ !
+ stc ssr, k0 ! Is it from kernel space?
+ shll k0 ! Check MD bit (bit30) by shifting it into...
+ shll k0 ! ...the T bit
+ bt/s 1f ! It's a kernel to kernel transition.
+ mov r15, k0 ! save original stack to k0
+ /* User space to kernel */
+ mov #0x20, k1
+ shll8 k1 ! k1 := 8192 (== THREAD_SIZE)
+ add current, k1
+ mov k1, r15 ! change to kernel stack
+ !
+1: mov #-1, k4
+ mov.l 2f, k1
+ !
+#ifdef CONFIG_SH_DSP
+ mov.l r2, @-r15 ! Save r2, we need another reg
+ stc sr, k4
+ mov.l 1f, r2
+ tst r2, k4 ! Check if in DSP mode
+ mov.l @r15+, r2 ! Restore r2 now
+ bt/s skip_save
+ mov #0, k4 ! Set marker for no stack frame
+
+ mov r2, k4 ! Backup r2 (in k4) for later
+
+ ! Save DSP registers on stack
+ stc.l mod, @-r15
+ stc.l re, @-r15
+ stc.l rs, @-r15
+ sts.l dsr, @-r15
+ sts.l y1, @-r15
+ sts.l y0, @-r15
+ sts.l x1, @-r15
+ sts.l x0, @-r15
+ sts.l a0, @-r15
+
+ ! GAS is broken, does not generate correct "movs.l Ds,@-As" instr.
+
+ ! FIXME: Make sure that this is still the case with newer toolchains,
+ ! as we're not at all interested in supporting ancient toolchains at
+ ! this point. -- PFM.
+
+ mov r15, r2
+ .word 0xf653 ! movs.l a1, @-r2
+ .word 0xf6f3 ! movs.l a0g, @-r2
+ .word 0xf6d3 ! movs.l a1g, @-r2
+ .word 0xf6c3 ! movs.l m0, @-r2
+ .word 0xf6e3 ! movs.l m1, @-r2
+ mov r2, r15
+
+ mov k4, r2 ! Restore r2
+ mov.l 1f, k4 ! Force DSP stack frame
+skip_save:
+ mov.l k4, @-r15 ! Push DSP mode marker onto stack
+#endif
+ ! Save the user registers on the stack.
+ mov.l k2, @-r15 ! EXPEVT
+ mov.l k4, @-r15 ! set TRA (default: -1)
+ !
+ sts.l macl, @-r15
+ sts.l mach, @-r15
+ stc.l gbr, @-r15
+ stc.l ssr, @-r15
+ sts.l pr, @-r15
+ stc.l spc, @-r15
+ !
+ lds k3, pr ! Set the return address to pr
+ !
+ mov.l k0, @-r15 ! save orignal stack
+ mov.l r14, @-r15
+ mov.l r13, @-r15
+ mov.l r12, @-r15
+ mov.l r11, @-r15
+ mov.l r10, @-r15
+ mov.l r9, @-r15
+ mov.l r8, @-r15
+ !
+ stc sr, r8 ! Back to normal register bank, and
+ or k1, r8 ! Block all interrupts
+ mov.l 3f, k1
+ and k1, r8 ! ...
+ ldc r8, sr ! ...changed here.
+ !
+ mov.l r7, @-r15
+ mov.l r6, @-r15
+ mov.l r5, @-r15
+ mov.l r4, @-r15
+ mov.l r3, @-r15
+ mov.l r2, @-r15
+ mov.l r1, @-r15
+ mov.l r0, @-r15
+ ! Then, dispatch to the handler, according to the exception code.
+ stc k_ex_code, r8
+ shlr2 r8
+ shlr r8
+ mov.l 4f, r9
+ add r8, r9
+ mov.l @r9, r9
+ jmp @r9
+ nop
+
+ .align 2
+1: .long 0x00001000 ! DSP=1
+2: .long 0x000080f0 ! FD=1, IMASK=15
+3: .long 0xcfffffff ! RB=0, BL=0
+4: .long exception_handling_table
+
+ .align 2
+ENTRY(exception_none)
+ rts
+ nop
+
+ .data
+ENTRY(sys_call_table)
+ .long sys_ni_syscall /* 0 - old "setup()" system call*/
+ .long sys_exit
+ .long sys_fork
+ .long sys_read
+ .long sys_write
+ .long sys_open /* 5 */
+ .long sys_close
+ .long sys_waitpid
+ .long sys_creat
+ .long sys_link
+ .long sys_unlink /* 10 */
+ .long sys_execve
+ .long sys_chdir
+ .long sys_time
+ .long sys_mknod
+ .long sys_chmod /* 15 */
+ .long sys_lchown16
+ .long sys_ni_syscall /* old break syscall holder */
+ .long sys_stat
+ .long sys_lseek
+ .long sys_getpid /* 20 */
+ .long sys_mount
+ .long sys_oldumount
+ .long sys_setuid16
+ .long sys_getuid16
+ .long sys_stime /* 25 */
+ .long sys_ptrace
+ .long sys_alarm
+ .long sys_fstat
+ .long sys_pause
+ .long sys_utime /* 30 */
+ .long sys_ni_syscall /* old stty syscall holder */
+ .long sys_ni_syscall /* old gtty syscall holder */
+ .long sys_access
+ .long sys_nice
+ .long sys_ni_syscall /* 35 */ /* old ftime syscall holder */
+ .long sys_sync
+ .long sys_kill
+ .long sys_rename
+ .long sys_mkdir
+ .long sys_rmdir /* 40 */
+ .long sys_dup
+ .long sys_pipe
+ .long sys_times
+ .long sys_ni_syscall /* old prof syscall holder */
+ .long sys_brk /* 45 */
+ .long sys_setgid16
+ .long sys_getgid16
+ .long sys_signal
+ .long sys_geteuid16
+ .long sys_getegid16 /* 50 */
+ .long sys_acct
+ .long sys_umount /* recycled never used phys() */
+ .long sys_ni_syscall /* old lock syscall holder */
+ .long sys_ioctl
+ .long sys_fcntl /* 55 */
+ .long sys_ni_syscall /* old mpx syscall holder */
+ .long sys_setpgid
+ .long sys_ni_syscall /* old ulimit syscall holder */
+ .long sys_ni_syscall /* sys_olduname */
+ .long sys_umask /* 60 */
+ .long sys_chroot
+ .long sys_ustat
+ .long sys_dup2
+ .long sys_getppid
+ .long sys_getpgrp /* 65 */
+ .long sys_setsid
+ .long sys_sigaction
+ .long sys_sgetmask
+ .long sys_ssetmask
+ .long sys_setreuid16 /* 70 */
+ .long sys_setregid16
+ .long sys_sigsuspend
+ .long sys_sigpending
+ .long sys_sethostname
+ .long sys_setrlimit /* 75 */
+ .long sys_old_getrlimit
+ .long sys_getrusage
+ .long sys_gettimeofday
+ .long sys_settimeofday
+ .long sys_getgroups16 /* 80 */
+ .long sys_setgroups16
+ .long sys_ni_syscall /* sys_oldselect */
+ .long sys_symlink
+ .long sys_lstat
+ .long sys_readlink /* 85 */
+ .long sys_uselib
+ .long sys_swapon
+ .long sys_reboot
+ .long old_readdir
+ .long old_mmap /* 90 */
+ .long sys_munmap
+ .long sys_truncate
+ .long sys_ftruncate
+ .long sys_fchmod
+ .long sys_fchown16 /* 95 */
+ .long sys_getpriority
+ .long sys_setpriority
+ .long sys_ni_syscall /* old profil syscall holder */
+ .long sys_statfs
+ .long sys_fstatfs /* 100 */
+ .long sys_ni_syscall /* ioperm */
+ .long sys_socketcall
+ .long sys_syslog
+ .long sys_setitimer
+ .long sys_getitimer /* 105 */
+ .long sys_newstat
+ .long sys_newlstat
+ .long sys_newfstat
+ .long sys_uname
+ .long sys_ni_syscall /* 110 */ /* iopl */
+ .long sys_vhangup
+ .long sys_ni_syscall /* idle */
+ .long sys_ni_syscall /* vm86old */
+ .long sys_wait4
+ .long sys_swapoff /* 115 */
+ .long sys_sysinfo
+ .long sys_ipc
+ .long sys_fsync
+ .long sys_sigreturn
+ .long sys_clone /* 120 */
+ .long sys_setdomainname
+ .long sys_newuname
+ .long sys_ni_syscall /* sys_modify_ldt */
+ .long sys_adjtimex
+ .long sys_mprotect /* 125 */
+ .long sys_sigprocmask
+ .long sys_ni_syscall /* old "create_module" */
+ .long sys_init_module
+ .long sys_delete_module
+ .long sys_ni_syscall /* 130: old "get_kernel_syms" */
+ .long sys_quotactl
+ .long sys_getpgid
+ .long sys_fchdir
+ .long sys_bdflush
+ .long sys_sysfs /* 135 */
+ .long sys_personality
+ .long sys_ni_syscall /* for afs_syscall */
+ .long sys_setfsuid16
+ .long sys_setfsgid16
+ .long sys_llseek /* 140 */
+ .long sys_getdents
+ .long sys_select
+ .long sys_flock
+ .long sys_msync
+ .long sys_readv /* 145 */
+ .long sys_writev
+ .long sys_getsid
+ .long sys_fdatasync
+ .long sys_sysctl
+ .long sys_mlock /* 150 */
+ .long sys_munlock
+ .long sys_mlockall
+ .long sys_munlockall
+ .long sys_sched_setparam
+ .long sys_sched_getparam /* 155 */
+ .long sys_sched_setscheduler
+ .long sys_sched_getscheduler
+ .long sys_sched_yield
+ .long sys_sched_get_priority_max
+ .long sys_sched_get_priority_min /* 160 */
+ .long sys_sched_rr_get_interval
+ .long sys_nanosleep
+ .long sys_mremap
+ .long sys_setresuid16
+ .long sys_getresuid16 /* 165 */
+ .long sys_ni_syscall /* vm86 */
+ .long sys_ni_syscall /* old "query_module" */
+ .long sys_poll
+ .long sys_nfsservctl
+ .long sys_setresgid16 /* 170 */
+ .long sys_getresgid16
+ .long sys_prctl
+ .long sys_rt_sigreturn
+ .long sys_rt_sigaction
+ .long sys_rt_sigprocmask /* 175 */
+ .long sys_rt_sigpending
+ .long sys_rt_sigtimedwait
+ .long sys_rt_sigqueueinfo
+ .long sys_rt_sigsuspend
+ .long sys_pread_wrapper /* 180 */
+ .long sys_pwrite_wrapper
+ .long sys_chown16
+ .long sys_getcwd
+ .long sys_capget
+ .long sys_capset /* 185 */
+ .long sys_sigaltstack
+ .long sys_sendfile
+ .long sys_ni_syscall /* streams1 */
+ .long sys_ni_syscall /* streams2 */
+ .long sys_vfork /* 190 */
+ .long sys_getrlimit
+ .long sys_mmap2
+ .long sys_truncate64
+ .long sys_ftruncate64
+ .long sys_stat64 /* 195 */
+ .long sys_lstat64
+ .long sys_fstat64
+ .long sys_lchown
+ .long sys_getuid
+ .long sys_getgid /* 200 */
+ .long sys_geteuid
+ .long sys_getegid
+ .long sys_setreuid
+ .long sys_setregid
+ .long sys_getgroups /* 205 */
+ .long sys_setgroups
+ .long sys_fchown
+ .long sys_setresuid
+ .long sys_getresuid
+ .long sys_setresgid /* 210 */
+ .long sys_getresgid
+ .long sys_chown
+ .long sys_setuid
+ .long sys_setgid
+ .long sys_setfsuid /* 215 */
+ .long sys_setfsgid
+ .long sys_pivot_root
+ .long sys_mincore
+ .long sys_madvise
+ .long sys_getdents64 /* 220 */
+ .long sys_fcntl64
+ .long sys_ni_syscall /* reserved for TUX */
+ .long sys_ni_syscall /* Reserved for Security */
+ .long sys_gettid
+ .long sys_readahead /* 225 */
+ .long sys_setxattr
+ .long sys_lsetxattr
+ .long sys_fsetxattr
+ .long sys_getxattr
+ .long sys_lgetxattr /* 230 */
+ .long sys_fgetxattr
+ .long sys_listxattr
+ .long sys_llistxattr
+ .long sys_flistxattr
+ .long sys_removexattr /* 235 */
+ .long sys_lremovexattr
+ .long sys_fremovexattr
+ .long sys_tkill
+ .long sys_sendfile64
+ .long sys_futex /* 240 */
+ .long sys_sched_setaffinity
+ .long sys_sched_getaffinity
+ .long sys_ni_syscall
+ .long sys_ni_syscall
+ .long sys_io_setup /* 245 */
+ .long sys_io_destroy
+ .long sys_io_getevents
+ .long sys_io_submit
+ .long sys_io_cancel
+ .long sys_fadvise64 /* 250 */
+ .long sys_ni_syscall
+ .long sys_exit_group
+ .long sys_lookup_dcookie
+ .long sys_epoll_create
+ .long sys_epoll_ctl /* 255 */
+ .long sys_epoll_wait
+ .long sys_remap_file_pages
+ .long sys_set_tid_address
+ .long sys_timer_create
+ .long sys_timer_settime /* 260 */
+ .long sys_timer_gettime
+ .long sys_timer_getoverrun
+ .long sys_timer_delete
+ .long sys_clock_settime
+ .long sys_clock_gettime /* 265 */
+ .long sys_clock_getres
+ .long sys_clock_nanosleep
+ .long sys_statfs64
+ .long sys_fstatfs64
+ .long sys_tgkill /* 270 */
+ .long sys_utimes
+ .long sys_fadvise64_64_wrapper
+ .long sys_ni_syscall /* Reserved for vserver */
+ .long sys_ni_syscall /* Reserved for mbind */
+ .long sys_ni_syscall /* 275 - get_mempolicy */
+ .long sys_ni_syscall /* set_mempolicy */
+ .long sys_mq_open
+ .long sys_mq_unlink
+ .long sys_mq_timedsend
+ .long sys_mq_timedreceive /* 280 */
+ .long sys_mq_notify
+ .long sys_mq_getsetattr
+ .long sys_ni_syscall /* Reserved for kexec */
+ .long sys_waitid
+ .long sys_add_key /* 285 */
+ .long sys_request_key
+ .long sys_keyctl
+
+/* End of entry.S */
diff --git a/arch/sh/kernel/head.S b/arch/sh/kernel/head.S
new file mode 100644
index 00000000000..9b9e6ef626c
--- /dev/null
+++ b/arch/sh/kernel/head.S
@@ -0,0 +1,76 @@
+/* $Id: head.S,v 1.7 2003/09/01 17:58:19 lethal Exp $
+ *
+ * arch/sh/kernel/head.S
+ *
+ * Copyright (C) 1999, 2000 Niibe Yutaka & Kaz Kojima
+ *
+ * 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.
+ *
+ * Head.S contains the SH exception handlers and startup code.
+ */
+#include <linux/linkage.h>
+
+ .section .empty_zero_page, "aw"
+ENTRY(empty_zero_page)
+ .long 1 /* MOUNT_ROOT_RDONLY */
+ .long 0 /* RAMDISK_FLAGS */
+ .long 0x0200 /* ORIG_ROOT_DEV */
+ .long 1 /* LOADER_TYPE */
+ .long 0x00360000 /* INITRD_START */
+ .long 0x000a0000 /* INITRD_SIZE */
+ .long 0
+ .balign 4096,0,4096
+
+ .text
+/*
+ * Condition at the entry of _stext:
+ *
+ * BSC has already been initialized.
+ * INTC may or may not be initialized.
+ * VBR may or may not be initialized.
+ * MMU may or may not be initialized.
+ * Cache may or may not be initialized.
+ * Hardware (including on-chip modules) may or may not be initialized.
+ *
+ */
+ENTRY(_stext)
+ ! Initialize Status Register
+ mov.l 1f, r0 ! MD=1, RB=0, BL=0, IMASK=0xF
+ ldc r0, sr
+ ! Initialize global interrupt mask
+ mov #0, r0
+ ldc r0, r6_bank
+ !
+ mov.l 2f, r0
+ mov r0, r15 ! Set initial r15 (stack pointer)
+ mov #0x20, r1 !
+ shll8 r1 ! r1 = 8192
+ sub r1, r0 !
+ ldc r0, r7_bank ! ... and initial thread_info
+ !
+ ! Additional CPU initialization
+ mov.l 6f, r0
+ jsr @r0
+ nop
+ ! Clear BSS area
+ mov.l 3f, r1
+ add #4, r1
+ mov.l 4f, r2
+ mov #0, r0
+9: cmp/hs r2, r1
+ bf/s 9b ! while (r1 < r2)
+ mov.l r0,@-r2
+ ! Start kernel
+ mov.l 5f, r0
+ jmp @r0
+ nop
+
+ .balign 4
+1: .long 0x400080F0 ! MD=1, RB=0, BL=0, FD=1, IMASK=0xF
+2: .long stack
+3: .long __bss_start
+4: .long _end
+5: .long start_kernel
+6: .long sh_cpu_init
diff --git a/arch/sh/kernel/init_task.c b/arch/sh/kernel/init_task.c
new file mode 100644
index 00000000000..44053ea9293
--- /dev/null
+++ b/arch/sh/kernel/init_task.c
@@ -0,0 +1,36 @@
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/init_task.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 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) };
+
+/*
+ * 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);
diff --git a/arch/sh/kernel/io.c b/arch/sh/kernel/io.c
new file mode 100644
index 00000000000..d9932f25993
--- /dev/null
+++ b/arch/sh/kernel/io.c
@@ -0,0 +1,59 @@
+/*
+ * linux/arch/sh/kernel/io.c
+ *
+ * Copyright (C) 2000 Stuart Menefy
+ *
+ * Provide real functions which expand to whatever the header file defined.
+ * Also definitions of machine independent IO functions.
+ */
+
+#include <asm/io.h>
+#include <linux/module.h>
+
+/*
+ * Copy data from IO memory space to "real" memory space.
+ * This needs to be optimized.
+ */
+void memcpy_fromio(void * to, unsigned long from, unsigned long count)
+{
+ char *p = to;
+ while (count) {
+ count--;
+ *p = readb(from);
+ p++;
+ from++;
+ }
+}
+
+/*
+ * Copy data from "real" memory space to IO memory space.
+ * This needs to be optimized.
+ */
+void memcpy_toio(unsigned long to, const void * from, unsigned long count)
+{
+ const char *p = from;
+ while (count) {
+ count--;
+ writeb(*p, to);
+ p++;
+ to++;
+ }
+}
+
+/*
+ * "memset" on IO memory space.
+ * This needs to be optimized.
+ */
+void memset_io(unsigned long dst, int c, unsigned long count)
+{
+ while (count) {
+ count--;
+ writeb(c, dst);
+ dst++;
+ }
+}
+
+EXPORT_SYMBOL(memcpy_fromio);
+EXPORT_SYMBOL(memcpy_toio);
+EXPORT_SYMBOL(memset_io);
+
diff --git a/arch/sh/kernel/io_generic.c b/arch/sh/kernel/io_generic.c
new file mode 100644
index 00000000000..a911b0149d1
--- /dev/null
+++ b/arch/sh/kernel/io_generic.c
@@ -0,0 +1,243 @@
+/* $Id: io_generic.c,v 1.2 2003/05/04 19:29:53 lethal Exp $
+ *
+ * linux/arch/sh/kernel/io_generic.c
+ *
+ * Copyright (C) 2000 Niibe Yutaka
+ *
+ * Generic I/O routine. These can be used where a machine specific version
+ * is not required.
+ *
+ * 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 <asm/io.h>
+#include <asm/machvec.h>
+#include <linux/module.h>
+
+#if defined(CONFIG_CPU_SH3)
+/* I'm not sure SH7709 has this kind of bug */
+#define SH3_PCMCIA_BUG_WORKAROUND 1
+#define DUMMY_READ_AREA6 0xba000000
+#endif
+
+#define PORT2ADDR(x) (sh_mv.mv_isa_port2addr(x))
+
+unsigned long generic_io_base;
+
+static inline void delay(void)
+{
+ ctrl_inw(0xa0000000);
+}
+
+unsigned char generic_inb(unsigned long port)
+{
+ return *(volatile unsigned char*)PORT2ADDR(port);
+}
+
+unsigned short generic_inw(unsigned long port)
+{
+ return *(volatile unsigned short*)PORT2ADDR(port);
+}
+
+unsigned int generic_inl(unsigned long port)
+{
+ return *(volatile unsigned long*)PORT2ADDR(port);
+}
+
+unsigned char generic_inb_p(unsigned long port)
+{
+ unsigned long v = *(volatile unsigned char*)PORT2ADDR(port);
+
+ delay();
+ return v;
+}
+
+unsigned short generic_inw_p(unsigned long port)
+{
+ unsigned long v = *(volatile unsigned short*)PORT2ADDR(port);
+
+ delay();
+ return v;
+}
+
+unsigned int generic_inl_p(unsigned long port)
+{
+ unsigned long v = *(volatile unsigned long*)PORT2ADDR(port);
+
+ delay();
+ return v;
+}
+
+/*
+ * insb/w/l all read a series of bytes/words/longs from a fixed port
+ * address. However as the port address doesn't change we only need to
+ * convert the port address to real address once.
+ */
+
+void generic_insb(unsigned long port, void *buffer, unsigned long count)
+{
+ volatile unsigned char *port_addr;
+ unsigned char *buf=buffer;
+
+ port_addr = (volatile unsigned char *)PORT2ADDR(port);
+
+ while(count--)
+ *buf++ = *port_addr;
+}
+
+void generic_insw(unsigned long port, void *buffer, unsigned long count)
+{
+ volatile unsigned short *port_addr;
+ unsigned short *buf=buffer;
+
+ port_addr = (volatile unsigned short *)PORT2ADDR(port);
+
+ while(count--)
+ *buf++ = *port_addr;
+#ifdef SH3_PCMCIA_BUG_WORKAROUND
+ ctrl_inb (DUMMY_READ_AREA6);
+#endif
+}
+
+void generic_insl(unsigned long port, void *buffer, unsigned long count)
+{
+ volatile unsigned long *port_addr;
+ unsigned long *buf=buffer;
+
+ port_addr = (volatile unsigned long *)PORT2ADDR(port);
+
+ while(count--)
+ *buf++ = *port_addr;
+#ifdef SH3_PCMCIA_BUG_WORKAROUND
+ ctrl_inb (DUMMY_READ_AREA6);
+#endif
+}
+
+void generic_outb(unsigned char b, unsigned long port)
+{
+ *(volatile unsigned char*)PORT2ADDR(port) = b;
+}
+
+void generic_outw(unsigned short b, unsigned long port)
+{
+ *(volatile unsigned short*)PORT2ADDR(port) = b;
+}
+
+void generic_outl(unsigned int b, unsigned long port)
+{
+ *(volatile unsigned long*)PORT2ADDR(port) = b;
+}
+
+void generic_outb_p(unsigned char b, unsigned long port)
+{
+ *(volatile unsigned char*)PORT2ADDR(port) = b;
+ delay();
+}
+
+void generic_outw_p(unsigned short b, unsigned long port)
+{
+ *(volatile unsigned short*)PORT2ADDR(port) = b;
+ delay();
+}
+
+void generic_outl_p(unsigned int b, unsigned long port)
+{
+ *(volatile unsigned long*)PORT2ADDR(port) = b;
+ delay();
+}
+
+/*
+ * outsb/w/l all write a series of bytes/words/longs to a fixed port
+ * address. However as the port address doesn't change we only need to
+ * convert the port address to real address once.
+ */
+
+void generic_outsb(unsigned long port, const void *buffer, unsigned long count)
+{
+ volatile unsigned char *port_addr;
+ const unsigned char *buf=buffer;
+
+ port_addr = (volatile unsigned char *)PORT2ADDR(port);
+
+ while(count--)
+ *port_addr = *buf++;
+}
+
+void generic_outsw(unsigned long port, const void *buffer, unsigned long count)
+{
+ volatile unsigned short *port_addr;
+ const unsigned short *buf=buffer;
+
+ port_addr = (volatile unsigned short *)PORT2ADDR(port);
+
+ while(count--)
+ *port_addr = *buf++;
+
+#ifdef SH3_PCMCIA_BUG_WORKAROUND
+ ctrl_inb (DUMMY_READ_AREA6);
+#endif
+}
+
+void generic_outsl(unsigned long port, const void *buffer, unsigned long count)
+{
+ volatile unsigned long *port_addr;
+ const unsigned long *buf=buffer;
+
+ port_addr = (volatile unsigned long *)PORT2ADDR(port);
+
+ while(count--)
+ *port_addr = *buf++;
+
+#ifdef SH3_PCMCIA_BUG_WORKAROUND
+ ctrl_inb (DUMMY_READ_AREA6);
+#endif
+}
+
+unsigned char generic_readb(unsigned long addr)
+{
+ return *(volatile unsigned char*)addr;
+}
+
+unsigned short generic_readw(unsigned long addr)
+{
+ return *(volatile unsigned short*)addr;
+}
+
+unsigned int generic_readl(unsigned long addr)
+{
+ return *(volatile unsigned long*)addr;
+}
+
+void generic_writeb(unsigned char b, unsigned long addr)
+{
+ *(volatile unsigned char*)addr = b;
+}
+
+void generic_writew(unsigned short b, unsigned long addr)
+{
+ *(volatile unsigned short*)addr = b;
+}
+
+void generic_writel(unsigned int b, unsigned long addr)
+{
+ *(volatile unsigned long*)addr = b;
+}
+
+void * generic_ioremap(unsigned long offset, unsigned long size)
+{
+ return (void *) P2SEGADDR(offset);
+}
+EXPORT_SYMBOL(generic_ioremap);
+
+void generic_iounmap(void *addr)
+{
+}
+EXPORT_SYMBOL(generic_iounmap);
+
+unsigned long generic_isa_port2addr(unsigned long offset)
+{
+ return offset + generic_io_base;
+}
diff --git a/arch/sh/kernel/irq.c b/arch/sh/kernel/irq.c
new file mode 100644
index 00000000000..54c171225b7
--- /dev/null
+++ b/arch/sh/kernel/irq.c
@@ -0,0 +1,106 @@
+/* $Id: irq.c,v 1.20 2004/01/13 05:52:11 kkojima Exp $
+ *
+ * linux/arch/sh/kernel/irq.c
+ *
+ * Copyright (C) 1992, 1998 Linus Torvalds, Ingo Molnar
+ *
+ *
+ * SuperH version: Copyright (C) 1999 Niibe Yutaka
+ */
+
+/*
+ * IRQs are in fact implemented a bit like signal handlers for the kernel.
+ * Naturally it's not a 1:1 relation, but there are similarities.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/kernel_stat.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/timex.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/random.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+#include <linux/init.h>
+#include <linux/seq_file.h>
+#include <linux/kallsyms.h>
+#include <linux/bitops.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/pgalloc.h>
+#include <asm/delay.h>
+#include <asm/irq.h>
+#include <linux/irq.h>
+
+
+/*
+ * '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.
+ */
+void ack_bad_irq(unsigned int irq)
+{
+ printk("unexpected IRQ trap at vector %02x\n", irq);
+}
+
+#if defined(CONFIG_PROC_FS)
+int show_interrupts(struct seq_file *p, void *v)
+{
+ int i = *(loff_t *) v, j;
+ struct irqaction * action;
+ unsigned long flags;
+
+ if (i == 0) {
+ seq_puts(p, " ");
+ for (j=0; j<NR_CPUS; j++)
+ if (cpu_online(j))
+ seq_printf(p, "CPU%d ",j);
+ seq_putc(p, '\n');
+ }
+
+ if (i < ACTUAL_NR_IRQS) {
+ spin_lock_irqsave(&irq_desc[i].lock, flags);
+ action = irq_desc[i].action;
+ if (!action)
+ goto unlock;
+ seq_printf(p, "%3d: ",i);
+ seq_printf(p, "%10u ", kstat_irqs(i));
+ seq_printf(p, " %14s", irq_desc[i].handler->typename);
+ seq_printf(p, " %s", action->name);
+
+ for (action=action->next; action; action = action->next)
+ seq_printf(p, ", %s", action->name);
+ seq_putc(p, '\n');
+unlock:
+ spin_unlock_irqrestore(&irq_desc[i].lock, flags);
+ }
+ return 0;
+}
+#endif
+
+asmlinkage int do_IRQ(unsigned long r4, unsigned long r5,
+ unsigned long r6, unsigned long r7,
+ struct pt_regs regs)
+{
+ int irq;
+
+ irq_enter();
+ asm volatile("stc r2_bank, %0\n\t"
+ "shlr2 %0\n\t"
+ "shlr2 %0\n\t"
+ "shlr %0\n\t"
+ "add #-16, %0\n\t"
+ :"=z" (irq));
+ irq = irq_demux(irq);
+ __do_IRQ(irq, &regs);
+ irq_exit();
+ return 1;
+}
diff --git a/arch/sh/kernel/kgdb_jmp.S b/arch/sh/kernel/kgdb_jmp.S
new file mode 100644
index 00000000000..339bb1d7ff0
--- /dev/null
+++ b/arch/sh/kernel/kgdb_jmp.S
@@ -0,0 +1,33 @@
+#include <linux/linkage.h>
+
+ENTRY(setjmp)
+ add #(9*4), r4
+ sts.l pr, @-r4
+ mov.l r15, @-r4
+ mov.l r14, @-r4
+ mov.l r13, @-r4
+ mov.l r12, @-r4
+ mov.l r11, @-r4
+ mov.l r10, @-r4
+ mov.l r9, @-r4
+ mov.l r8, @-r4
+ rts
+ mov #0, r0
+
+ENTRY(longjmp)
+ mov.l @r4+, r8
+ mov.l @r4+, r9
+ mov.l @r4+, r10
+ mov.l @r4+, r11
+ mov.l @r4+, r12
+ mov.l @r4+, r13
+ mov.l @r4+, r14
+ mov.l @r4+, r15
+ lds.l @r4+, pr
+ mov r5, r0
+ tst r0, r0
+ bf 1f
+ mov #1, r0 ! in case val==0
+1: rts
+ nop
+
diff --git a/arch/sh/kernel/kgdb_stub.c b/arch/sh/kernel/kgdb_stub.c
new file mode 100644
index 00000000000..42638b92b51
--- /dev/null
+++ b/arch/sh/kernel/kgdb_stub.c
@@ -0,0 +1,1491 @@
+/*
+ * May be copied or modified under the terms of the GNU General Public
+ * License. See linux/COPYING for more information.
+ *
+ * Containes extracts from code by Glenn Engel, Jim Kingdon,
+ * David Grothe <dave@gcom.com>, Tigran Aivazian <tigran@sco.com>,
+ * Amit S. Kale <akale@veritas.com>, William Gatliff <bgat@open-widgets.com>,
+ * Ben Lee, Steve Chamberlain and Benoit Miller <fulg@iname.com>.
+ *
+ * This version by Henry Bell <henry.bell@st.com>
+ * Minor modifications by Jeremy Siegel <jsiegel@mvista.com>
+ *
+ * Contains low-level support for remote debug using GDB.
+ *
+ * To enable debugger support, two things need to happen. A call to
+ * set_debug_traps() is necessary in order to allow any breakpoints
+ * or error conditions to be properly intercepted and reported to gdb.
+ * A breakpoint also needs to be generated to begin communication. This
+ * is most easily accomplished by a call to breakpoint() which does
+ * a trapa if the initialisation phase has been successfully completed.
+ *
+ * In this case, set_debug_traps() is not used to "take over" exceptions;
+ * other kernel code is modified instead to enter the kgdb functions here
+ * when appropriate (see entry.S for breakpoint traps and NMI interrupts,
+ * see traps.c for kernel error exceptions).
+ *
+ * The following gdb commands are supported:
+ *
+ * Command Function Return value
+ *
+ * g return the value of the CPU registers hex data or ENN
+ * G set the value of the CPU registers OK or ENN
+ *
+ * mAA..AA,LLLL Read LLLL bytes at address AA..AA hex data or ENN
+ * MAA..AA,LLLL: Write LLLL bytes at address AA.AA OK or ENN
+ * XAA..AA,LLLL: Same, but data is binary (not hex) OK or ENN
+ *
+ * c Resume at current address SNN ( signal NN)
+ * cAA..AA Continue at address AA..AA SNN
+ * CNN; Resume at current address with signal SNN
+ * CNN;AA..AA Resume at address AA..AA with signal SNN
+ *
+ * s Step one instruction SNN
+ * sAA..AA Step one instruction from AA..AA SNN
+ * SNN; Step one instruction with signal SNN
+ * SNNAA..AA Step one instruction from AA..AA w/NN SNN
+ *
+ * k kill (Detach GDB)
+ *
+ * d Toggle debug flag
+ * D Detach GDB
+ *
+ * Hct Set thread t for operations, OK or ENN
+ * c = 'c' (step, cont), c = 'g' (other
+ * operations)
+ *
+ * qC Query current thread ID QCpid
+ * qfThreadInfo Get list of current threads (first) m<id>
+ * qsThreadInfo " " " " " (subsequent)
+ * qOffsets Get section offsets Text=x;Data=y;Bss=z
+ *
+ * TXX Find if thread XX is alive OK or ENN
+ * ? What was the last sigval ? SNN (signal NN)
+ * O Output to GDB console
+ *
+ * Remote communication protocol.
+ *
+ * A debug packet whose contents are <data> is encapsulated for
+ * transmission in the form:
+ *
+ * $ <data> # CSUM1 CSUM2
+ *
+ * <data> must be ASCII alphanumeric and cannot include characters
+ * '$' or '#'. If <data> starts with two characters followed by
+ * ':', then the existing stubs interpret this as a sequence number.
+ *
+ * CSUM1 and CSUM2 are ascii hex representation of an 8-bit
+ * checksum of <data>, the most significant nibble is sent first.
+ * the hex digits 0-9,a-f are used.
+ *
+ * Receiver responds with:
+ *
+ * + - if CSUM is correct and ready for next packet
+ * - - if CSUM is incorrect
+ *
+ * Responses can be run-length encoded to save space. A '*' means that
+ * the next character is an ASCII encoding giving a repeat count which
+ * stands for that many repititions of the character preceding the '*'.
+ * The encoding is n+29, yielding a printable character where n >=3
+ * (which is where RLE starts to win). Don't use an n > 126.
+ *
+ * So "0* " means the same as "0000".
+ */
+
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/smp.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/linkage.h>
+#include <linux/init.h>
+
+#include <asm/system.h>
+#include <asm/current.h>
+#include <asm/signal.h>
+#include <asm/pgtable.h>
+#include <asm/ptrace.h>
+#include <asm/kgdb.h>
+
+#ifdef CONFIG_SH_KGDB_CONSOLE
+#include <linux/console.h>
+#endif
+
+/* Function pointers for linkage */
+kgdb_debug_hook_t *kgdb_debug_hook;
+kgdb_bus_error_hook_t *kgdb_bus_err_hook;
+
+int (*kgdb_getchar)(void);
+void (*kgdb_putchar)(int);
+
+static void put_debug_char(int c)
+{
+ if (!kgdb_putchar)
+ return;
+ (*kgdb_putchar)(c);
+}
+static int get_debug_char(void)
+{
+ if (!kgdb_getchar)
+ return -1;
+ return (*kgdb_getchar)();
+}
+
+/* Num chars in in/out bound buffers, register packets need NUMREGBYTES * 2 */
+#define BUFMAX 1024
+#define NUMREGBYTES (MAXREG*4)
+#define OUTBUFMAX (NUMREGBYTES*2+512)
+
+enum regs {
+ R0 = 0, R1, R2, R3, R4, R5, R6, R7,
+ R8, R9, R10, R11, R12, R13, R14, R15,
+ PC, PR, GBR, VBR, MACH, MACL, SR,
+ /* */
+ MAXREG
+};
+
+static unsigned int registers[MAXREG];
+struct kgdb_regs trap_registers;
+
+char kgdb_in_gdb_mode;
+char in_nmi; /* Set during NMI to prevent reentry */
+int kgdb_nofault; /* Boolean to ignore bus errs (i.e. in GDB) */
+int kgdb_enabled = 1; /* Default to enabled, cmdline can disable */
+int kgdb_halt;
+
+/* Exposed for user access */
+struct task_struct *kgdb_current;
+unsigned int kgdb_g_imask;
+int kgdb_trapa_val;
+int kgdb_excode;
+
+/* Default values for SCI (can override via kernel args in setup.c) */
+#ifndef CONFIG_KGDB_DEFPORT
+#define CONFIG_KGDB_DEFPORT 1
+#endif
+
+#ifndef CONFIG_KGDB_DEFBAUD
+#define CONFIG_KGDB_DEFBAUD 115200
+#endif
+
+#if defined(CONFIG_KGDB_DEFPARITY_E)
+#define CONFIG_KGDB_DEFPARITY 'E'
+#elif defined(CONFIG_KGDB_DEFPARITY_O)
+#define CONFIG_KGDB_DEFPARITY 'O'
+#else /* CONFIG_KGDB_DEFPARITY_N */
+#define CONFIG_KGDB_DEFPARITY 'N'
+#endif
+
+#ifdef CONFIG_KGDB_DEFBITS_7
+#define CONFIG_KGDB_DEFBITS '7'
+#else /* CONFIG_KGDB_DEFBITS_8 */
+#define CONFIG_KGDB_DEFBITS '8'
+#endif
+
+/* SCI/UART settings, used in kgdb_console_setup() */
+int kgdb_portnum = CONFIG_KGDB_DEFPORT;
+int kgdb_baud = CONFIG_KGDB_DEFBAUD;
+char kgdb_parity = CONFIG_KGDB_DEFPARITY;
+char kgdb_bits = CONFIG_KGDB_DEFBITS;
+
+/* Jump buffer for setjmp/longjmp */
+static jmp_buf rem_com_env;
+
+/* TRA differs sh3/4 */
+#if defined(CONFIG_CPU_SH3)
+#define TRA 0xffffffd0
+#elif defined(CONFIG_CPU_SH4)
+#define TRA 0xff000020
+#endif
+
+/* Macros for single step instruction identification */
+#define OPCODE_BT(op) (((op) & 0xff00) == 0x8900)
+#define OPCODE_BF(op) (((op) & 0xff00) == 0x8b00)
+#define OPCODE_BTF_DISP(op) (((op) & 0x80) ? (((op) | 0xffffff80) << 1) : \
+ (((op) & 0x7f ) << 1))
+#define OPCODE_BFS(op) (((op) & 0xff00) == 0x8f00)
+#define OPCODE_BTS(op) (((op) & 0xff00) == 0x8d00)
+#define OPCODE_BRA(op) (((op) & 0xf000) == 0xa000)
+#define OPCODE_BRA_DISP(op) (((op) & 0x800) ? (((op) | 0xfffff800) << 1) : \
+ (((op) & 0x7ff) << 1))
+#define OPCODE_BRAF(op) (((op) & 0xf0ff) == 0x0023)
+#define OPCODE_BRAF_REG(op) (((op) & 0x0f00) >> 8)
+#define OPCODE_BSR(op) (((op) & 0xf000) == 0xb000)
+#define OPCODE_BSR_DISP(op) (((op) & 0x800) ? (((op) | 0xfffff800) << 1) : \
+ (((op) & 0x7ff) << 1))
+#define OPCODE_BSRF(op) (((op) & 0xf0ff) == 0x0003)
+#define OPCODE_BSRF_REG(op) (((op) >> 8) & 0xf)
+#define OPCODE_JMP(op) (((op) & 0xf0ff) == 0x402b)
+#define OPCODE_JMP_REG(op) (((op) >> 8) & 0xf)
+#define OPCODE_JSR(op) (((op) & 0xf0ff) == 0x400b)
+#define OPCODE_JSR_REG(op) (((op) >> 8) & 0xf)
+#define OPCODE_RTS(op) ((op) == 0xb)
+#define OPCODE_RTE(op) ((op) == 0x2b)
+
+#define SR_T_BIT_MASK 0x1
+#define STEP_OPCODE 0xc320
+#define BIOS_CALL_TRAP 0x3f
+
+/* Exception codes as per SH-4 core manual */
+#define ADDRESS_ERROR_LOAD_VEC 7
+#define ADDRESS_ERROR_STORE_VEC 8
+#define TRAP_VEC 11
+#define INVALID_INSN_VEC 12
+#define INVALID_SLOT_VEC 13
+#define NMI_VEC 14
+#define USER_BREAK_VEC 15
+#define SERIAL_BREAK_VEC 58
+
+/* Misc static */
+static int stepped_address;
+static short stepped_opcode;
+static const char hexchars[] = "0123456789abcdef";
+static char in_buffer[BUFMAX];
+static char out_buffer[OUTBUFMAX];
+
+static void kgdb_to_gdb(const char *s);
+
+#ifdef CONFIG_KGDB_THREAD
+static struct task_struct *trapped_thread;
+static struct task_struct *current_thread;
+typedef unsigned char threadref[8];
+#define BUF_THREAD_ID_SIZE 16
+#endif
+
+/* Return addr as a real volatile address */
+static inline unsigned int ctrl_inl(const unsigned long addr)
+{
+ return *(volatile unsigned long *) addr;
+}
+
+/* Correctly set *addr using volatile */
+static inline void ctrl_outl(const unsigned int b, unsigned long addr)
+{
+ *(volatile unsigned long *) addr = b;
+}
+
+/* Get high hex bits */
+static char highhex(const int x)
+{
+ return hexchars[(x >> 4) & 0xf];
+}
+
+/* Get low hex bits */
+static char lowhex(const int x)
+{
+ return hexchars[x & 0xf];
+}
+
+/* Convert ch to hex */
+static int hex(const char ch)
+{
+ if ((ch >= 'a') && (ch <= 'f'))
+ return (ch - 'a' + 10);
+ if ((ch >= '0') && (ch <= '9'))
+ return (ch - '0');
+ if ((ch >= 'A') && (ch <= 'F'))
+ return (ch - 'A' + 10);
+ return (-1);
+}
+
+/* Convert the memory pointed to by mem into hex, placing result in buf.
+ Returns a pointer to the last char put in buf (null) */
+static char *mem_to_hex(const char *mem, char *buf, const int count)
+{
+ int i;
+ int ch;
+ unsigned short s_val;
+ unsigned long l_val;
+
+ /* Check for 16 or 32 */
+ if (count == 2 && ((long) mem & 1) == 0) {
+ s_val = *(unsigned short *) mem;
+ mem = (char *) &s_val;
+ } else if (count == 4 && ((long) mem & 3) == 0) {
+ l_val = *(unsigned long *) mem;
+ mem = (char *) &l_val;
+ }
+ for (i = 0; i < count; i++) {
+ ch = *mem++;
+ *buf++ = highhex(ch);
+ *buf++ = lowhex(ch);
+ }
+ *buf = 0;
+ return (buf);
+}
+
+/* Convert the hex array pointed to by buf into binary, to be placed in mem.
+ Return a pointer to the character after the last byte written */
+static char *hex_to_mem(const char *buf, char *mem, const int count)
+{
+ int i;
+ unsigned char ch;
+
+ for (i = 0; i < count; i++) {
+ ch = hex(*buf++) << 4;
+ ch = ch + hex(*buf++);
+ *mem++ = ch;
+ }
+ return (mem);
+}
+
+/* While finding valid hex chars, convert to an integer, then return it */
+static int hex_to_int(char **ptr, int *int_value)
+{
+ int num_chars = 0;
+ int hex_value;
+
+ *int_value = 0;
+
+ while (**ptr) {
+ hex_value = hex(**ptr);
+ if (hex_value >= 0) {
+ *int_value = (*int_value << 4) | hex_value;
+ num_chars++;
+ } else
+ break;
+ (*ptr)++;
+ }
+ return num_chars;
+}
+
+/* Copy the binary array pointed to by buf into mem. Fix $, #,
+ and 0x7d escaped with 0x7d. Return a pointer to the character
+ after the last byte written. */
+static char *ebin_to_mem(const char *buf, char *mem, int count)
+{
+ for (; count > 0; count--, buf++) {
+ if (*buf == 0x7d)
+ *mem++ = *(++buf) ^ 0x20;
+ else
+ *mem++ = *buf;
+ }
+ return mem;
+}
+
+/* Pack a hex byte */
+static char *pack_hex_byte(char *pkt, int byte)
+{
+ *pkt++ = hexchars[(byte >> 4) & 0xf];
+ *pkt++ = hexchars[(byte & 0xf)];
+ return pkt;
+}
+
+#ifdef CONFIG_KGDB_THREAD
+
+/* Pack a thread ID */
+static char *pack_threadid(char *pkt, threadref * id)
+{
+ char *limit;
+ unsigned char *altid;
+
+ altid = (unsigned char *) id;
+
+ limit = pkt + BUF_THREAD_ID_SIZE;
+ while (pkt < limit)
+ pkt = pack_hex_byte(pkt, *altid++);
+ return pkt;
+}
+
+/* Convert an integer into our threadref */
+static void int_to_threadref(threadref * id, const int value)
+{
+ unsigned char *scan = (unsigned char *) id;
+ int i = 4;
+
+ while (i--)
+ *scan++ = 0;
+
+ *scan++ = (value >> 24) & 0xff;
+ *scan++ = (value >> 16) & 0xff;
+ *scan++ = (value >> 8) & 0xff;
+ *scan++ = (value & 0xff);
+}
+
+/* Return a task structure ptr for a particular pid */
+static struct task_struct *get_thread(int pid)
+{
+ struct task_struct *thread;
+
+ /* Use PID_MAX w/gdb for pid 0 */
+ if (pid == PID_MAX) pid = 0;
+
+ /* First check via PID */
+ thread = find_task_by_pid(pid);
+
+ if (thread)
+ return thread;
+
+ /* Start at the start */
+ thread = init_tasks[0];
+
+ /* Walk along the linked list of tasks */
+ do {
+ if (thread->pid == pid)
+ return thread;
+ thread = thread->next_task;
+ } while (thread != init_tasks[0]);
+
+ return NULL;
+}
+
+#endif /* CONFIG_KGDB_THREAD */
+
+/* Scan for the start char '$', read the packet and check the checksum */
+static void get_packet(char *buffer, int buflen)
+{
+ unsigned char checksum;
+ unsigned char xmitcsum;
+ int i;
+ int count;
+ char ch;
+
+ do {
+ /* Ignore everything until the start character */
+ while ((ch = get_debug_char()) != '$');
+
+ checksum = 0;
+ xmitcsum = -1;
+ count = 0;
+
+ /* Now, read until a # or end of buffer is found */
+ while (count < (buflen - 1)) {
+ ch = get_debug_char();
+
+ if (ch == '#')
+ break;
+
+ checksum = checksum + ch;
+ buffer[count] = ch;
+ count = count + 1;
+ }
+
+ buffer[count] = 0;
+
+ /* Continue to read checksum following # */
+ if (ch == '#') {
+ xmitcsum = hex(get_debug_char()) << 4;
+ xmitcsum += hex(get_debug_char());
+
+ /* Checksum */
+ if (checksum != xmitcsum)
+ put_debug_char('-'); /* Failed checksum */
+ else {
+ /* Ack successful transfer */
+ put_debug_char('+');
+
+ /* If a sequence char is present, reply
+ the sequence ID */
+ if (buffer[2] == ':') {
+ put_debug_char(buffer[0]);
+ put_debug_char(buffer[1]);
+
+ /* Remove sequence chars from buffer */
+ count = strlen(buffer);
+ for (i = 3; i <= count; i++)
+ buffer[i - 3] = buffer[i];
+ }
+ }
+ }
+ }
+ while (checksum != xmitcsum); /* Keep trying while we fail */
+}
+
+/* Send the packet in the buffer with run-length encoding */
+static void put_packet(char *buffer)
+{
+ int checksum;
+ char *src;
+ int runlen;
+ int encode;
+
+ do {
+ src = buffer;
+ put_debug_char('$');
+ checksum = 0;
+
+ /* Continue while we still have chars left */
+ while (*src) {
+ /* Check for runs up to 99 chars long */
+ for (runlen = 1; runlen < 99; runlen++) {
+ if (src[0] != src[runlen])
+ break;
+ }
+
+ if (runlen > 3) {
+ /* Got a useful amount, send encoding */
+ encode = runlen + ' ' - 4;
+ put_debug_char(*src); checksum += *src;
+ put_debug_char('*'); checksum += '*';
+ put_debug_char(encode); checksum += encode;
+ src += runlen;
+ } else {
+ /* Otherwise just send the current char */
+ put_debug_char(*src); checksum += *src;
+ src += 1;
+ }
+ }
+
+ /* '#' Separator, put high and low components of checksum */
+ put_debug_char('#');
+ put_debug_char(highhex(checksum));
+ put_debug_char(lowhex(checksum));
+ }
+ while ((get_debug_char()) != '+'); /* While no ack */
+}
+
+/* A bus error has occurred - perform a longjmp to return execution and
+ allow handling of the error */
+static void kgdb_handle_bus_error(void)
+{
+ longjmp(rem_com_env, 1);
+}
+
+/* Translate SH-3/4 exception numbers to unix-like signal values */
+static int compute_signal(const int excep_code)
+{
+ int sigval;
+
+ switch (excep_code) {
+
+ case INVALID_INSN_VEC:
+ case INVALID_SLOT_VEC:
+ sigval = SIGILL;
+ break;
+ case ADDRESS_ERROR_LOAD_VEC:
+ case ADDRESS_ERROR_STORE_VEC:
+ sigval = SIGSEGV;
+ break;
+
+ case SERIAL_BREAK_VEC:
+ case NMI_VEC:
+ sigval = SIGINT;
+ break;
+
+ case USER_BREAK_VEC:
+ case TRAP_VEC:
+ sigval = SIGTRAP;
+ break;
+
+ default:
+ sigval = SIGBUS; /* "software generated" */
+ break;
+ }
+
+ return (sigval);
+}
+
+/* Make a local copy of the registers passed into the handler (bletch) */
+static void kgdb_regs_to_gdb_regs(const struct kgdb_regs *regs,
+ int *gdb_regs)
+{
+ gdb_regs[R0] = regs->regs[R0];
+ gdb_regs[R1] = regs->regs[R1];
+ gdb_regs[R2] = regs->regs[R2];
+ gdb_regs[R3] = regs->regs[R3];
+ gdb_regs[R4] = regs->regs[R4];
+ gdb_regs[R5] = regs->regs[R5];
+ gdb_regs[R6] = regs->regs[R6];
+ gdb_regs[R7] = regs->regs[R7];
+ gdb_regs[R8] = regs->regs[R8];
+ gdb_regs[R9] = regs->regs[R9];
+ gdb_regs[R10] = regs->regs[R10];
+ gdb_regs[R11] = regs->regs[R11];
+ gdb_regs[R12] = regs->regs[R12];
+ gdb_regs[R13] = regs->regs[R13];
+ gdb_regs[R14] = regs->regs[R14];
+ gdb_regs[R15] = regs->regs[R15];
+ gdb_regs[PC] = regs->pc;
+ gdb_regs[PR] = regs->pr;
+ gdb_regs[GBR] = regs->gbr;
+ gdb_regs[MACH] = regs->mach;
+ gdb_regs[MACL] = regs->macl;
+ gdb_regs[SR] = regs->sr;
+ gdb_regs[VBR] = regs->vbr;
+}
+
+/* Copy local gdb registers back to kgdb regs, for later copy to kernel */
+static void gdb_regs_to_kgdb_regs(const int *gdb_regs,
+ struct kgdb_regs *regs)
+{
+ regs->regs[R0] = gdb_regs[R0];
+ regs->regs[R1] = gdb_regs[R1];
+ regs->regs[R2] = gdb_regs[R2];
+ regs->regs[R3] = gdb_regs[R3];
+ regs->regs[R4] = gdb_regs[R4];
+ regs->regs[R5] = gdb_regs[R5];
+ regs->regs[R6] = gdb_regs[R6];
+ regs->regs[R7] = gdb_regs[R7];
+ regs->regs[R8] = gdb_regs[R8];
+ regs->regs[R9] = gdb_regs[R9];
+ regs->regs[R10] = gdb_regs[R10];
+ regs->regs[R11] = gdb_regs[R11];
+ regs->regs[R12] = gdb_regs[R12];
+ regs->regs[R13] = gdb_regs[R13];
+ regs->regs[R14] = gdb_regs[R14];
+ regs->regs[R15] = gdb_regs[R15];
+ regs->pc = gdb_regs[PC];
+ regs->pr = gdb_regs[PR];
+ regs->gbr = gdb_regs[GBR];
+ regs->mach = gdb_regs[MACH];
+ regs->macl = gdb_regs[MACL];
+ regs->sr = gdb_regs[SR];
+ regs->vbr = gdb_regs[VBR];
+}
+
+#ifdef CONFIG_KGDB_THREAD
+/* Make a local copy of registers from the specified thread */
+asmlinkage void ret_from_fork(void);
+static void thread_regs_to_gdb_regs(const struct task_struct *thread,
+ int *gdb_regs)
+{
+ int regno;
+ int *tregs;
+
+ /* Initialize to zero */
+ for (regno = 0; regno < MAXREG; regno++)
+ gdb_regs[regno] = 0;
+
+ /* Just making sure... */
+ if (thread == NULL)
+ return;
+
+ /* A new fork has pt_regs on the stack from a fork() call */
+ if (thread->thread.pc == (unsigned long)ret_from_fork) {
+
+ int vbr_val;
+ struct pt_regs *kregs;
+ kregs = (struct pt_regs*)thread->thread.sp;
+
+ gdb_regs[R0] = kregs->regs[R0];
+ gdb_regs[R1] = kregs->regs[R1];
+ gdb_regs[R2] = kregs->regs[R2];
+ gdb_regs[R3] = kregs->regs[R3];
+ gdb_regs[R4] = kregs->regs[R4];
+ gdb_regs[R5] = kregs->regs[R5];
+ gdb_regs[R6] = kregs->regs[R6];
+ gdb_regs[R7] = kregs->regs[R7];
+ gdb_regs[R8] = kregs->regs[R8];
+ gdb_regs[R9] = kregs->regs[R9];
+ gdb_regs[R10] = kregs->regs[R10];
+ gdb_regs[R11] = kregs->regs[R11];
+ gdb_regs[R12] = kregs->regs[R12];
+ gdb_regs[R13] = kregs->regs[R13];
+ gdb_regs[R14] = kregs->regs[R14];
+ gdb_regs[R15] = kregs->regs[R15];
+ gdb_regs[PC] = kregs->pc;
+ gdb_regs[PR] = kregs->pr;
+ gdb_regs[GBR] = kregs->gbr;
+ gdb_regs[MACH] = kregs->mach;
+ gdb_regs[MACL] = kregs->macl;
+ gdb_regs[SR] = kregs->sr;
+
+ asm("stc vbr, %0":"=r"(vbr_val));
+ gdb_regs[VBR] = vbr_val;
+ return;
+ }
+
+ /* Otherwise, we have only some registers from switch_to() */
+ tregs = (int *)thread->thread.sp;
+ gdb_regs[R15] = (int)tregs;
+ gdb_regs[R14] = *tregs++;
+ gdb_regs[R13] = *tregs++;
+ gdb_regs[R12] = *tregs++;
+ gdb_regs[R11] = *tregs++;
+ gdb_regs[R10] = *tregs++;
+ gdb_regs[R9] = *tregs++;
+ gdb_regs[R8] = *tregs++;
+ gdb_regs[PR] = *tregs++;
+ gdb_regs[GBR] = *tregs++;
+ gdb_regs[PC] = thread->thread.pc;
+}
+#endif /* CONFIG_KGDB_THREAD */
+
+/* Calculate the new address for after a step */
+static short *get_step_address(void)
+{
+ short op = *(short *) trap_registers.pc;
+ long addr;
+
+ /* BT */
+ if (OPCODE_BT(op)) {
+ if (trap_registers.sr & SR_T_BIT_MASK)
+ addr = trap_registers.pc + 4 + OPCODE_BTF_DISP(op);
+ else
+ addr = trap_registers.pc + 2;
+ }
+
+ /* BTS */
+ else if (OPCODE_BTS(op)) {
+ if (trap_registers.sr & SR_T_BIT_MASK)
+ addr = trap_registers.pc + 4 + OPCODE_BTF_DISP(op);
+ else
+ addr = trap_registers.pc + 4; /* Not in delay slot */
+ }
+
+ /* BF */
+ else if (OPCODE_BF(op)) {
+ if (!(trap_registers.sr & SR_T_BIT_MASK))
+ addr = trap_registers.pc + 4 + OPCODE_BTF_DISP(op);
+ else
+ addr = trap_registers.pc + 2;
+ }
+
+ /* BFS */
+ else if (OPCODE_BFS(op)) {
+ if (!(trap_registers.sr & SR_T_BIT_MASK))
+ addr = trap_registers.pc + 4 + OPCODE_BTF_DISP(op);
+ else
+ addr = trap_registers.pc + 4; /* Not in delay slot */
+ }
+
+ /* BRA */
+ else if (OPCODE_BRA(op))
+ addr = trap_registers.pc + 4 + OPCODE_BRA_DISP(op);
+
+ /* BRAF */
+ else if (OPCODE_BRAF(op))
+ addr = trap_registers.pc + 4
+ + trap_registers.regs[OPCODE_BRAF_REG(op)];
+
+ /* BSR */
+ else if (OPCODE_BSR(op))
+ addr = trap_registers.pc + 4 + OPCODE_BSR_DISP(op);
+
+ /* BSRF */
+ else if (OPCODE_BSRF(op))
+ addr = trap_registers.pc + 4
+ + trap_registers.regs[OPCODE_BSRF_REG(op)];
+
+ /* JMP */
+ else if (OPCODE_JMP(op))
+ addr = trap_registers.regs[OPCODE_JMP_REG(op)];
+
+ /* JSR */
+ else if (OPCODE_JSR(op))
+ addr = trap_registers.regs[OPCODE_JSR_REG(op)];
+
+ /* RTS */
+ else if (OPCODE_RTS(op))
+ addr = trap_registers.pr;
+
+ /* RTE */
+ else if (OPCODE_RTE(op))
+ addr = trap_registers.regs[15];
+
+ /* Other */
+ else
+ addr = trap_registers.pc + 2;
+
+ kgdb_flush_icache_range(addr, addr + 2);
+ return (short *) addr;
+}
+
+/* Set up a single-step. Replace the instruction immediately after the
+ current instruction (i.e. next in the expected flow of control) with a
+ trap instruction, so that returning will cause only a single instruction
+ to be executed. Note that this model is slightly broken for instructions
+ with delay slots (e.g. B[TF]S, BSR, BRA etc), where both the branch
+ and the instruction in the delay slot will be executed. */
+static void do_single_step(void)
+{
+ unsigned short *addr = 0;
+
+ /* Determine where the target instruction will send us to */
+ addr = get_step_address();
+ stepped_address = (int)addr;
+
+ /* Replace it */
+ stepped_opcode = *(short *)addr;
+ *addr = STEP_OPCODE;
+
+ /* Flush and return */
+ kgdb_flush_icache_range((long) addr, (long) addr + 2);
+ return;
+}
+
+/* Undo a single step */
+static void undo_single_step(void)
+{
+ /* If we have stepped, put back the old instruction */
+ /* Use stepped_address in case we stopped elsewhere */
+ if (stepped_opcode != 0) {
+ *(short*)stepped_address = stepped_opcode;
+ kgdb_flush_icache_range(stepped_address, stepped_address + 2);
+ }
+ stepped_opcode = 0;
+}
+
+/* Send a signal message */
+static void send_signal_msg(const int signum)
+{
+#ifndef CONFIG_KGDB_THREAD
+ out_buffer[0] = 'S';
+ out_buffer[1] = highhex(signum);
+ out_buffer[2] = lowhex(signum);
+ out_buffer[3] = 0;
+ put_packet(out_buffer);
+#else /* CONFIG_KGDB_THREAD */
+ int threadid;
+ threadref thref;
+ char *out = out_buffer;
+ const char *tstring = "thread";
+
+ *out++ = 'T';
+ *out++ = highhex(signum);
+ *out++ = lowhex(signum);
+
+ while (*tstring) {
+ *out++ = *tstring++;
+ }
+ *out++ = ':';
+
+ threadid = trapped_thread->pid;
+ if (threadid == 0) threadid = PID_MAX;
+ int_to_threadref(&thref, threadid);
+ pack_threadid(out, &thref);
+ out += BUF_THREAD_ID_SIZE;
+ *out++ = ';';
+
+ *out = 0;
+ put_packet(out_buffer);
+#endif /* CONFIG_KGDB_THREAD */
+}
+
+/* Reply that all was well */
+static void send_ok_msg(void)
+{
+ strcpy(out_buffer, "OK");
+ put_packet(out_buffer);
+}
+
+/* Reply that an error occurred */
+static void send_err_msg(void)
+{
+ strcpy(out_buffer, "E01");
+ put_packet(out_buffer);
+}
+
+/* Empty message indicates unrecognised command */
+static void send_empty_msg(void)
+{
+ put_packet("");
+}
+
+/* Read memory due to 'm' message */
+static void read_mem_msg(void)
+{
+ char *ptr;
+ int addr;
+ int length;
+
+ /* Jmp, disable bus error handler */
+ if (setjmp(rem_com_env) == 0) {
+
+ kgdb_nofault = 1;
+
+ /* Walk through, have m<addr>,<length> */
+ ptr = &in_buffer[1];
+ if (hex_to_int(&ptr, &addr) && (*ptr++ == ','))
+ if (hex_to_int(&ptr, &length)) {
+ ptr = 0;
+ if (length * 2 > OUTBUFMAX)
+ length = OUTBUFMAX / 2;
+ mem_to_hex((char *) addr, out_buffer, length);
+ }
+ if (ptr)
+ send_err_msg();
+ else
+ put_packet(out_buffer);
+ } else
+ send_err_msg();
+
+ /* Restore bus error handler */
+ kgdb_nofault = 0;
+}
+
+/* Write memory due to 'M' or 'X' message */
+static void write_mem_msg(int binary)
+{
+ char *ptr;
+ int addr;
+ int length;
+
+ if (setjmp(rem_com_env) == 0) {
+
+ kgdb_nofault = 1;
+
+ /* Walk through, have M<addr>,<length>:<data> */
+ ptr = &in_buffer[1];
+ if (hex_to_int(&ptr, &addr) && (*ptr++ == ','))
+ if (hex_to_int(&ptr, &length) && (*ptr++ == ':')) {
+ if (binary)
+ ebin_to_mem(ptr, (char*)addr, length);
+ else
+ hex_to_mem(ptr, (char*)addr, length);
+ kgdb_flush_icache_range(addr, addr + length);
+ ptr = 0;
+ send_ok_msg();
+ }
+ if (ptr)
+ send_err_msg();
+ } else
+ send_err_msg();
+
+ /* Restore bus error handler */
+ kgdb_nofault = 0;
+}
+
+/* Continue message */
+static void continue_msg(void)
+{
+ /* Try to read optional parameter, PC unchanged if none */
+ char *ptr = &in_buffer[1];
+ int addr;
+
+ if (hex_to_int(&ptr, &addr))
+ trap_registers.pc = addr;
+}
+
+/* Continue message with signal */
+static void continue_with_sig_msg(void)
+{
+ int signal;
+ char *ptr = &in_buffer[1];
+ int addr;
+
+ /* Report limitation */
+ kgdb_to_gdb("Cannot force signal in kgdb, continuing anyway.\n");
+
+ /* Signal */
+ hex_to_int(&ptr, &signal);
+ if (*ptr == ';')
+ ptr++;
+
+ /* Optional address */
+ if (hex_to_int(&ptr, &addr))
+ trap_registers.pc = addr;
+}
+
+/* Step message */
+static void step_msg(void)
+{
+ continue_msg();
+ do_single_step();
+}
+
+/* Step message with signal */
+static void step_with_sig_msg(void)
+{
+ continue_with_sig_msg();
+ do_single_step();
+}
+
+/* Send register contents */
+static void send_regs_msg(void)
+{
+#ifdef CONFIG_KGDB_THREAD
+ if (!current_thread)
+ kgdb_regs_to_gdb_regs(&trap_registers, registers);
+ else
+ thread_regs_to_gdb_regs(current_thread, registers);
+#else
+ kgdb_regs_to_gdb_regs(&trap_registers, registers);
+#endif
+
+ mem_to_hex((char *) registers, out_buffer, NUMREGBYTES);
+ put_packet(out_buffer);
+}
+
+/* Set register contents - currently can't set other thread's registers */
+static void set_regs_msg(void)
+{
+#ifdef CONFIG_KGDB_THREAD
+ if (!current_thread) {
+#endif
+ kgdb_regs_to_gdb_regs(&trap_registers, registers);
+ hex_to_mem(&in_buffer[1], (char *) registers, NUMREGBYTES);
+ gdb_regs_to_kgdb_regs(registers, &trap_registers);
+ send_ok_msg();
+#ifdef CONFIG_KGDB_THREAD
+ } else
+ send_err_msg();
+#endif
+}
+
+
+#ifdef CONFIG_KGDB_THREAD
+
+/* Set the status for a thread */
+void set_thread_msg(void)
+{
+ int threadid;
+ struct task_struct *thread = NULL;
+ char *ptr;
+
+ switch (in_buffer[1]) {
+
+ /* To select which thread for gG etc messages, i.e. supported */
+ case 'g':
+
+ ptr = &in_buffer[2];
+ hex_to_int(&ptr, &threadid);
+ thread = get_thread(threadid);
+
+ /* If we haven't found it */
+ if (!thread) {
+ send_err_msg();
+ break;
+ }
+
+ /* Set current_thread (or not) */
+ if (thread == trapped_thread)
+ current_thread = NULL;
+ else
+ current_thread = thread;
+ send_ok_msg();
+ break;
+
+ /* To select which thread for cCsS messages, i.e. unsupported */
+ case 'c':
+ send_ok_msg();
+ break;
+
+ default:
+ send_empty_msg();
+ break;
+ }
+}
+
+/* Is a thread alive? */
+static void thread_status_msg(void)
+{
+ char *ptr;
+ int threadid;
+ struct task_struct *thread = NULL;
+
+ ptr = &in_buffer[1];
+ hex_to_int(&ptr, &threadid);
+ thread = get_thread(threadid);
+ if (thread)
+ send_ok_msg();
+ else
+ send_err_msg();
+}
+/* Send the current thread ID */
+static void thread_id_msg(void)
+{
+ int threadid;
+ threadref thref;
+
+ out_buffer[0] = 'Q';
+ out_buffer[1] = 'C';
+
+ if (current_thread)
+ threadid = current_thread->pid;
+ else if (trapped_thread)
+ threadid = trapped_thread->pid;
+ else /* Impossible, but just in case! */
+ {
+ send_err_msg();
+ return;
+ }
+
+ /* Translate pid 0 to PID_MAX for gdb */
+ if (threadid == 0) threadid = PID_MAX;
+
+ int_to_threadref(&thref, threadid);
+ pack_threadid(out_buffer + 2, &thref);
+ out_buffer[2 + BUF_THREAD_ID_SIZE] = '\0';
+ put_packet(out_buffer);
+}
+
+/* Send thread info */
+static void thread_info_msg(void)
+{
+ struct task_struct *thread = NULL;
+ int threadid;
+ char *pos;
+ threadref thref;
+
+ /* Start with 'm' */
+ out_buffer[0] = 'm';
+ pos = &out_buffer[1];
+
+ /* For all possible thread IDs - this will overrun if > 44 threads! */
+ /* Start at 1 and include PID_MAX (since GDB won't use pid 0...) */
+ for (threadid = 1; threadid <= PID_MAX; threadid++) {
+
+ read_lock(&tasklist_lock);
+ thread = get_thread(threadid);
+ read_unlock(&tasklist_lock);
+
+ /* If it's a valid thread */
+ if (thread) {
+ int_to_threadref(&thref, threadid);
+ pack_threadid(pos, &thref);
+ pos += BUF_THREAD_ID_SIZE;
+ *pos++ = ',';
+ }
+ }
+ *--pos = 0; /* Lose final comma */
+ put_packet(out_buffer);
+
+}
+
+/* Return printable info for gdb's 'info threads' command */
+static void thread_extra_info_msg(void)
+{
+ int threadid;
+ struct task_struct *thread = NULL;
+ char buffer[20], *ptr;
+ int i;
+
+ /* Extract thread ID */
+ ptr = &in_buffer[17];
+ hex_to_int(&ptr, &threadid);
+ thread = get_thread(threadid);
+
+ /* If we don't recognise it, say so */
+ if (thread == NULL)
+ strcpy(buffer, "(unknown)");
+ else
+ strcpy(buffer, thread->comm);
+
+ /* Construct packet */
+ for (i = 0, ptr = out_buffer; buffer[i]; i++)
+ ptr = pack_hex_byte(ptr, buffer[i]);
+
+ if (thread->thread.pc == (unsigned long)ret_from_fork) {
+ strcpy(buffer, "<new fork>");
+ for (i = 0; buffer[i]; i++)
+ ptr = pack_hex_byte(ptr, buffer[i]);
+ }
+
+ *ptr = '\0';
+ put_packet(out_buffer);
+}
+
+/* Handle all qFooBarBaz messages - have to use an if statement as
+ opposed to a switch because q messages can have > 1 char id. */
+static void query_msg(void)
+{
+ const char *q_start = &in_buffer[1];
+
+ /* qC = return current thread ID */
+ if (strncmp(q_start, "C", 1) == 0)
+ thread_id_msg();
+
+ /* qfThreadInfo = query all threads (first) */
+ else if (strncmp(q_start, "fThreadInfo", 11) == 0)
+ thread_info_msg();
+
+ /* qsThreadInfo = query all threads (subsequent). We know we have sent
+ them all after the qfThreadInfo message, so there are no to send */
+ else if (strncmp(q_start, "sThreadInfo", 11) == 0)
+ put_packet("l"); /* el = last */
+
+ /* qThreadExtraInfo = supply printable information per thread */
+ else if (strncmp(q_start, "ThreadExtraInfo", 15) == 0)
+ thread_extra_info_msg();
+
+ /* Unsupported - empty message as per spec */
+ else
+ send_empty_msg();
+}
+#endif /* CONFIG_KGDB_THREAD */
+
+/*
+ * Bring up the ports..
+ */
+static int kgdb_serial_setup(void)
+{
+ extern int kgdb_console_setup(struct console *co, char *options);
+ struct console dummy;
+
+ kgdb_console_setup(&dummy, 0);
+
+ return 0;
+}
+
+/* The command loop, read and act on requests */
+static void kgdb_command_loop(const int excep_code, const int trapa_value)
+{
+ int sigval;
+
+ if (excep_code == NMI_VEC) {
+#ifndef CONFIG_KGDB_NMI
+ KGDB_PRINTK("Ignoring unexpected NMI?\n");
+ return;
+#else /* CONFIG_KGDB_NMI */
+ if (!kgdb_enabled) {
+ kgdb_enabled = 1;
+ kgdb_init();
+ }
+#endif /* CONFIG_KGDB_NMI */
+ }
+
+ /* Ignore if we're disabled */
+ if (!kgdb_enabled)
+ return;
+
+#ifdef CONFIG_KGDB_THREAD
+ /* Until GDB specifies a thread */
+ current_thread = NULL;
+ trapped_thread = current;
+#endif
+
+ /* Enter GDB mode (e.g. after detach) */
+ if (!kgdb_in_gdb_mode) {
+ /* Do serial setup, notify user, issue preemptive ack */
+ kgdb_serial_setup();
+ KGDB_PRINTK("Waiting for GDB (on %s%d at %d baud)\n",
+ (kgdb_porttype ? kgdb_porttype->name : ""),
+ kgdb_portnum, kgdb_baud);
+ kgdb_in_gdb_mode = 1;
+ put_debug_char('+');
+ }
+
+ /* Reply to host that an exception has occurred */
+ sigval = compute_signal(excep_code);
+ send_signal_msg(sigval);
+
+ /* TRAP_VEC exception indicates a software trap inserted in place of
+ code by GDB so back up PC by one instruction, as this instruction
+ will later be replaced by its original one. Do NOT do this for
+ trap 0xff, since that indicates a compiled-in breakpoint which
+ will not be replaced (and we would retake the trap forever) */
+ if ((excep_code == TRAP_VEC) && (trapa_value != (0xff << 2))) {
+ trap_registers.pc -= 2;
+ }
+
+ /* Undo any stepping we may have done */
+ undo_single_step();
+
+ while (1) {
+
+ out_buffer[0] = 0;
+ get_packet(in_buffer, BUFMAX);
+
+ /* Examine first char of buffer to see what we need to do */
+ switch (in_buffer[0]) {
+
+ case '?': /* Send which signal we've received */
+ send_signal_msg(sigval);
+ break;
+
+ case 'g': /* Return the values of the CPU registers */
+ send_regs_msg();
+ break;
+
+ case 'G': /* Set the value of the CPU registers */
+ set_regs_msg();
+ break;
+
+ case 'm': /* Read LLLL bytes address AA..AA */
+ read_mem_msg();
+ break;
+
+ case 'M': /* Write LLLL bytes address AA..AA, ret OK */
+ write_mem_msg(0); /* 0 = data in hex */
+ break;
+
+ case 'X': /* Write LLLL bytes esc bin address AA..AA */
+ if (kgdb_bits == '8')
+ write_mem_msg(1); /* 1 = data in binary */
+ else
+ send_empty_msg();
+ break;
+
+ case 'C': /* Continue, signum included, we ignore it */
+ continue_with_sig_msg();
+ return;
+
+ case 'c': /* Continue at address AA..AA (optional) */
+ continue_msg();
+ return;
+
+ case 'S': /* Step, signum included, we ignore it */
+ step_with_sig_msg();
+ return;
+
+ case 's': /* Step one instruction from AA..AA */
+ step_msg();
+ return;
+
+#ifdef CONFIG_KGDB_THREAD
+
+ case 'H': /* Task related */
+ set_thread_msg();
+ break;
+
+ case 'T': /* Query thread status */
+ thread_status_msg();
+ break;
+
+ case 'q': /* Handle query - currently thread-related */
+ query_msg();
+ break;
+#endif
+
+ case 'k': /* 'Kill the program' with a kernel ? */
+ break;
+
+ case 'D': /* Detach from program, send reply OK */
+ kgdb_in_gdb_mode = 0;
+ send_ok_msg();
+ get_debug_char();
+ return;
+
+ default:
+ send_empty_msg();
+ break;
+ }
+ }
+}
+
+/* There has been an exception, most likely a breakpoint. */
+void kgdb_handle_exception(struct pt_regs *regs)
+{
+ int excep_code, vbr_val;
+ int count;
+ int trapa_value = ctrl_inl(TRA);
+
+ /* Copy kernel regs (from stack) */
+ for (count = 0; count < 16; count++)
+ trap_registers.regs[count] = regs->regs[count];
+ trap_registers.pc = regs->pc;
+ trap_registers.pr = regs->pr;
+ trap_registers.sr = regs->sr;
+ trap_registers.gbr = regs->gbr;
+ trap_registers.mach = regs->mach;
+ trap_registers.macl = regs->macl;
+
+ asm("stc vbr, %0":"=r"(vbr_val));
+ trap_registers.vbr = vbr_val;
+
+ /* Get excode for command loop call, user access */
+ asm("stc r2_bank, %0":"=r"(excep_code));
+ kgdb_excode = excep_code;
+
+ /* Other interesting environment items for reference */
+ asm("stc r6_bank, %0":"=r"(kgdb_g_imask));
+ kgdb_current = current;
+ kgdb_trapa_val = trapa_value;
+
+ /* Act on the exception */
+ kgdb_command_loop(excep_code >> 5, trapa_value);
+
+ kgdb_current = NULL;
+
+ /* Copy back the (maybe modified) registers */
+ for (count = 0; count < 16; count++)
+ regs->regs[count] = trap_registers.regs[count];
+ regs->pc = trap_registers.pc;
+ regs->pr = trap_registers.pr;
+ regs->sr = trap_registers.sr;
+ regs->gbr = trap_registers.gbr;
+ regs->mach = trap_registers.mach;
+ regs->macl = trap_registers.macl;
+
+ vbr_val = trap_registers.vbr;
+ asm("ldc %0, vbr": :"r"(vbr_val));
+
+ return;
+}
+
+/* Trigger a breakpoint by function */
+void breakpoint(void)
+{
+ if (!kgdb_enabled) {
+ kgdb_enabled = 1;
+ kgdb_init();
+ }
+ BREAKPOINT();
+}
+
+/* Initialise the KGDB data structures and serial configuration */
+int kgdb_init(void)
+{
+ if (!kgdb_enabled)
+ return 1;
+
+ in_nmi = 0;
+ kgdb_nofault = 0;
+ stepped_opcode = 0;
+ kgdb_in_gdb_mode = 0;
+
+ if (kgdb_serial_setup() != 0) {
+ KGDB_PRINTK("serial setup error\n");
+ return -1;
+ }
+
+ /* Init ptr to exception handler */
+ kgdb_debug_hook = kgdb_handle_exception;
+ kgdb_bus_err_hook = kgdb_handle_bus_error;
+
+ /* Enter kgdb now if requested, or just report init done */
+ if (kgdb_halt) {
+ kgdb_in_gdb_mode = 1;
+ put_debug_char('+');
+ breakpoint();
+ }
+ else
+ {
+ KGDB_PRINTK("stub is initialized.\n");
+ }
+
+ return 0;
+}
+
+/* Make function available for "user messages"; console will use it too. */
+
+char gdbmsgbuf[BUFMAX];
+#define MAXOUT ((BUFMAX-2)/2)
+
+static void kgdb_msg_write(const char *s, unsigned count)
+{
+ int i;
+ int wcount;
+ char *bufptr;
+
+ /* 'O'utput */
+ gdbmsgbuf[0] = 'O';
+
+ /* Fill and send buffers... */
+ while (count > 0) {
+ bufptr = gdbmsgbuf + 1;
+
+ /* Calculate how many this time */
+ wcount = (count > MAXOUT) ? MAXOUT : count;
+
+ /* Pack in hex chars */
+ for (i = 0; i < wcount; i++)
+ bufptr = pack_hex_byte(bufptr, s[i]);
+ *bufptr = '\0';
+
+ /* Move up */
+ s += wcount;
+ count -= wcount;
+
+ /* Write packet */
+ put_packet(gdbmsgbuf);
+ }
+}
+
+static void kgdb_to_gdb(const char *s)
+{
+ kgdb_msg_write(s, strlen(s));
+}
+
+#ifdef CONFIG_SH_KGDB_CONSOLE
+void kgdb_console_write(struct console *co, const char *s, unsigned count)
+{
+ /* Bail if we're not talking to GDB */
+ if (!kgdb_in_gdb_mode)
+ return;
+
+ kgdb_msg_write(s, count);
+}
+#endif
diff --git a/arch/sh/kernel/module.c b/arch/sh/kernel/module.c
new file mode 100644
index 00000000000..142a4e5b7eb
--- /dev/null
+++ b/arch/sh/kernel/module.c
@@ -0,0 +1,146 @@
+/* Kernel module help for SH.
+
+ 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.
+
+ This program 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 this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+#include <linux/moduleloader.h>
+#include <linux/elf.h>
+#include <linux/vmalloc.h>
+#include <linux/fs.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+
+#if 0
+#define DEBUGP printk
+#else
+#define DEBUGP(fmt...)
+#endif
+
+void *module_alloc(unsigned long size)
+{
+ if (size == 0)
+ return NULL;
+ return vmalloc(size);
+}
+
+
+/* Free memory returned from module_alloc */
+void module_free(struct module *mod, void *module_region)
+{
+ vfree(module_region);
+ /* FIXME: If module_region == mod->init_region, trim exception
+ table entries. */
+}
+
+/* We don't need anything special. */
+int module_frob_arch_sections(Elf_Ehdr *hdr,
+ Elf_Shdr *sechdrs,
+ char *secstrings,
+ struct module *mod)
+{
+ return 0;
+}
+
+#define COPY_UNALIGNED_WORD(sw, tw, align) \
+{ \
+ void *__s = &(sw), *__t = &(tw); \
+ unsigned short *__s2 = __s, *__t2 = __t; \
+ unsigned char *__s1 = __s, *__t1 = __t; \
+ switch ((align)) \
+ { \
+ case 0: \
+ *(unsigned long *) __t = *(unsigned long *) __s; \
+ break; \
+ case 2: \
+ *__t2++ = *__s2++; \
+ *__t2 = *__s2; \
+ break; \
+ default: \
+ *__t1++ = *__s1++; \
+ *__t1++ = *__s1++; \
+ *__t1++ = *__s1++; \
+ *__t1 = *__s1; \
+ break; \
+ } \
+}
+
+int apply_relocate_add(Elf32_Shdr *sechdrs,
+ const char *strtab,
+ unsigned int symindex,
+ unsigned int relsec,
+ struct module *me)
+{
+ unsigned int i;
+ Elf32_Rela *rel = (void *)sechdrs[relsec].sh_addr;
+ Elf32_Sym *sym;
+ Elf32_Addr relocation;
+ uint32_t *location;
+ uint32_t value;
+ int align;
+
+ DEBUGP("Applying relocate section %u to %u\n", relsec,
+ sechdrs[relsec].sh_info);
+ for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) {
+ /* This is where to make the change */
+ location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr
+ + rel[i].r_offset;
+ /* This is the symbol it is referring to. Note that all
+ undefined symbols have been resolved. */
+ sym = (Elf32_Sym *)sechdrs[symindex].sh_addr
+ + ELF32_R_SYM(rel[i].r_info);
+ relocation = sym->st_value + rel[i].r_addend;
+ align = (int)location & 3;
+
+ switch (ELF32_R_TYPE(rel[i].r_info)) {
+ case R_SH_DIR32:
+ COPY_UNALIGNED_WORD (*location, value, align);
+ value += relocation;
+ COPY_UNALIGNED_WORD (value, *location, align);
+ break;
+ case R_SH_REL32:
+ relocation = (relocation - (Elf32_Addr) location);
+ COPY_UNALIGNED_WORD (*location, value, align);
+ value += relocation;
+ COPY_UNALIGNED_WORD (value, *location, align);
+ break;
+ default:
+ printk(KERN_ERR "module %s: Unknown relocation: %u\n",
+ me->name, ELF32_R_TYPE(rel[i].r_info));
+ return -ENOEXEC;
+ }
+ }
+ return 0;
+}
+
+int apply_relocate(Elf32_Shdr *sechdrs,
+ const char *strtab,
+ unsigned int symindex,
+ unsigned int relsec,
+ struct module *me)
+{
+ printk(KERN_ERR "module %s: REL RELOCATION unsupported\n",
+ me->name);
+ return -ENOEXEC;
+}
+
+int module_finalize(const Elf_Ehdr *hdr,
+ const Elf_Shdr *sechdrs,
+ struct module *me)
+{
+ return 0;
+}
+
+void module_arch_cleanup(struct module *mod)
+{
+}
diff --git a/arch/sh/kernel/process.c b/arch/sh/kernel/process.c
new file mode 100644
index 00000000000..3d024590c24
--- /dev/null
+++ b/arch/sh/kernel/process.c
@@ -0,0 +1,531 @@
+/* $Id: process.c,v 1.28 2004/05/05 16:54:23 lethal Exp $
+ *
+ * linux/arch/sh/kernel/process.c
+ *
+ * Copyright (C) 1995 Linus Torvalds
+ *
+ * SuperH version: Copyright (C) 1999, 2000 Niibe Yutaka & Kaz Kojima
+ */
+
+/*
+ * This file handles the architecture-dependent parts of process handling..
+ */
+
+#include <linux/module.h>
+#include <linux/unistd.h>
+#include <linux/mm.h>
+#include <linux/elfcore.h>
+#include <linux/slab.h>
+#include <linux/a.out.h>
+#include <linux/ptrace.h>
+#include <linux/platform.h>
+#include <linux/kallsyms.h>
+
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/mmu_context.h>
+#include <asm/elf.h>
+#if defined(CONFIG_SH_HS7751RVOIP)
+#include <asm/hs7751rvoip/hs7751rvoip.h>
+#elif defined(CONFIG_SH_RTS7751R2D)
+#include <asm/rts7751r2d/rts7751r2d.h>
+#endif
+
+static int hlt_counter=0;
+
+int ubc_usercnt = 0;
+
+#define HARD_IDLE_TIMEOUT (HZ / 3)
+
+void disable_hlt(void)
+{
+ hlt_counter++;
+}
+
+EXPORT_SYMBOL(disable_hlt);
+
+void enable_hlt(void)
+{
+ hlt_counter--;
+}
+
+EXPORT_SYMBOL(enable_hlt);
+
+void default_idle(void)
+{
+ /* endless idle loop with no priority at all */
+ while (1) {
+ if (hlt_counter) {
+ while (1)
+ if (need_resched())
+ break;
+ } else {
+ while (!need_resched())
+ cpu_sleep();
+ }
+
+ schedule();
+ }
+}
+
+void cpu_idle(void)
+{
+ default_idle();
+}
+
+void machine_restart(char * __unused)
+{
+ /* SR.BL=1 and invoke address error to let CPU reset (manual reset) */
+ asm volatile("ldc %0, sr\n\t"
+ "mov.l @%1, %0" : : "r" (0x10000000), "r" (0x80000001));
+}
+
+EXPORT_SYMBOL(machine_restart);
+
+void machine_halt(void)
+{
+#if defined(CONFIG_SH_HS7751RVOIP)
+ unsigned short value;
+
+ value = ctrl_inw(PA_OUTPORTR);
+ ctrl_outw((value & 0xffdf), PA_OUTPORTR);
+#elif defined(CONFIG_SH_RTS7751R2D)
+ ctrl_outw(0x0001, PA_POWOFF);
+#endif
+ while (1)
+ cpu_sleep();
+}
+
+EXPORT_SYMBOL(machine_halt);
+
+void machine_power_off(void)
+{
+#if defined(CONFIG_SH_HS7751RVOIP)
+ unsigned short value;
+
+ value = ctrl_inw(PA_OUTPORTR);
+ ctrl_outw((value & 0xffdf), PA_OUTPORTR);
+#elif defined(CONFIG_SH_RTS7751R2D)
+ ctrl_outw(0x0001, PA_POWOFF);
+#endif
+}
+
+EXPORT_SYMBOL(machine_power_off);
+
+void show_regs(struct pt_regs * regs)
+{
+ printk("\n");
+ printk("Pid : %d, Comm: %20s\n", current->pid, current->comm);
+ print_symbol("PC is at %s\n", regs->pc);
+ printk("PC : %08lx SP : %08lx SR : %08lx ",
+ regs->pc, regs->regs[15], regs->sr);
+#ifdef CONFIG_MMU
+ printk("TEA : %08x ", ctrl_inl(MMU_TEA));
+#else
+ printk(" ");
+#endif
+ printk("%s\n", print_tainted());
+
+ printk("R0 : %08lx R1 : %08lx R2 : %08lx R3 : %08lx\n",
+ regs->regs[0],regs->regs[1],
+ regs->regs[2],regs->regs[3]);
+ printk("R4 : %08lx R5 : %08lx R6 : %08lx R7 : %08lx\n",
+ regs->regs[4],regs->regs[5],
+ regs->regs[6],regs->regs[7]);
+ printk("R8 : %08lx R9 : %08lx R10 : %08lx R11 : %08lx\n",
+ regs->regs[8],regs->regs[9],
+ regs->regs[10],regs->regs[11]);
+ printk("R12 : %08lx R13 : %08lx R14 : %08lx\n",
+ regs->regs[12],regs->regs[13],
+ regs->regs[14]);
+ printk("MACH: %08lx MACL: %08lx GBR : %08lx PR : %08lx\n",
+ regs->mach, regs->macl, regs->gbr, regs->pr);
+
+ /*
+ * If we're in kernel mode, dump the stack too..
+ */
+ if (!user_mode(regs)) {
+ extern void show_task(unsigned long *sp);
+ unsigned long sp = regs->regs[15];
+
+ show_task((unsigned long *)sp);
+ }
+}
+
+/*
+ * Create a kernel thread
+ */
+
+/*
+ * This is the mechanism for creating a new kernel thread.
+ *
+ */
+extern void kernel_thread_helper(void);
+__asm__(".align 5\n"
+ "kernel_thread_helper:\n\t"
+ "jsr @r5\n\t"
+ " nop\n\t"
+ "mov.l 1f, r1\n\t"
+ "jsr @r1\n\t"
+ " mov r0, r4\n\t"
+ ".align 2\n\t"
+ "1:.long do_exit");
+
+int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
+{ /* Don't use this in BL=1(cli). Or else, CPU resets! */
+ struct pt_regs regs;
+
+ memset(&regs, 0, sizeof(regs));
+ regs.regs[4] = (unsigned long) arg;
+ regs.regs[5] = (unsigned long) fn;
+
+ regs.pc = (unsigned long) kernel_thread_helper;
+ regs.sr = (1 << 30);
+
+ /* Ok, create the new process.. */
+ return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, &regs, 0, NULL, NULL);
+}
+
+/*
+ * Free current thread data structures etc..
+ */
+void exit_thread(void)
+{
+ if (current->thread.ubc_pc) {
+ current->thread.ubc_pc = 0;
+ ubc_usercnt -= 1;
+ }
+}
+
+void flush_thread(void)
+{
+#if defined(CONFIG_SH_FPU)
+ struct task_struct *tsk = current;
+ struct pt_regs *regs = (struct pt_regs *)
+ ((unsigned long)tsk->thread_info
+ + THREAD_SIZE - sizeof(struct pt_regs)
+ - sizeof(unsigned long));
+
+ /* Forget lazy FPU state */
+ clear_fpu(tsk, regs);
+ clear_used_math();
+#endif
+}
+
+void release_thread(struct task_struct *dead_task)
+{
+ /* do nothing */
+}
+
+/* Fill in the fpu structure for a core dump.. */
+int dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpu)
+{
+ int fpvalid = 0;
+
+#if defined(CONFIG_SH_FPU)
+ struct task_struct *tsk = current;
+
+ fpvalid = !!tsk_used_math(tsk);
+ if (fpvalid) {
+ unlazy_fpu(tsk, regs);
+ memcpy(fpu, &tsk->thread.fpu.hard, sizeof(*fpu));
+ }
+#endif
+
+ return fpvalid;
+}
+
+/*
+ * Capture the user space registers if the task is not running (in user space)
+ */
+int dump_task_regs(struct task_struct *tsk, elf_gregset_t *regs)
+{
+ struct pt_regs ptregs;
+
+ ptregs = *(struct pt_regs *)
+ ((unsigned long)tsk->thread_info + THREAD_SIZE
+ - sizeof(struct pt_regs)
+#ifdef CONFIG_SH_DSP
+ - sizeof(struct pt_dspregs)
+#endif
+ - sizeof(unsigned long));
+ elf_core_copy_regs(regs, &ptregs);
+
+ return 1;
+}
+
+int
+dump_task_fpu (struct task_struct *tsk, elf_fpregset_t *fpu)
+{
+ int fpvalid = 0;
+
+#if defined(CONFIG_SH_FPU)
+ fpvalid = !!tsk_used_math(tsk);
+ if (fpvalid) {
+ struct pt_regs *regs = (struct pt_regs *)
+ ((unsigned long)tsk->thread_info
+ + THREAD_SIZE - sizeof(struct pt_regs)
+ - sizeof(unsigned long));
+ unlazy_fpu(tsk, regs);
+ memcpy(fpu, &tsk->thread.fpu.hard, sizeof(*fpu));
+ }
+#endif
+
+ return fpvalid;
+}
+
+asmlinkage void ret_from_fork(void);
+
+int copy_thread(int nr, unsigned long clone_flags, unsigned long usp,
+ unsigned long unused,
+ struct task_struct *p, struct pt_regs *regs)
+{
+ struct pt_regs *childregs;
+#if defined(CONFIG_SH_FPU)
+ struct task_struct *tsk = current;
+
+ unlazy_fpu(tsk, regs);
+ p->thread.fpu = tsk->thread.fpu;
+ copy_to_stopped_child_used_math(p);
+#endif
+
+ childregs = ((struct pt_regs *)
+ (THREAD_SIZE + (unsigned long) p->thread_info)
+#ifdef CONFIG_SH_DSP
+ - sizeof(struct pt_dspregs)
+#endif
+ - sizeof(unsigned long)) - 1;
+ *childregs = *regs;
+
+ if (user_mode(regs)) {
+ childregs->regs[15] = usp;
+ } else {
+ childregs->regs[15] = (unsigned long)p->thread_info + THREAD_SIZE;
+ }
+ if (clone_flags & CLONE_SETTLS) {
+ childregs->gbr = childregs->regs[0];
+ }
+ childregs->regs[0] = 0; /* Set return value for child */
+
+ p->thread.sp = (unsigned long) childregs;
+ p->thread.pc = (unsigned long) ret_from_fork;
+
+ p->thread.ubc_pc = 0;
+
+ return 0;
+}
+
+/*
+ * fill in the user structure for a core dump..
+ */
+void dump_thread(struct pt_regs * regs, struct user * dump)
+{
+ dump->magic = CMAGIC;
+ dump->start_code = current->mm->start_code;
+ dump->start_data = current->mm->start_data;
+ dump->start_stack = regs->regs[15] & ~(PAGE_SIZE - 1);
+ dump->u_tsize = (current->mm->end_code - dump->start_code) >> PAGE_SHIFT;
+ dump->u_dsize = (current->mm->brk + (PAGE_SIZE-1) - dump->start_data) >> PAGE_SHIFT;
+ dump->u_ssize = (current->mm->start_stack - dump->start_stack +
+ PAGE_SIZE - 1) >> PAGE_SHIFT;
+ /* Debug registers will come here. */
+
+ dump->regs = *regs;
+
+ dump->u_fpvalid = dump_fpu(regs, &dump->fpu);
+}
+
+/* Tracing by user break controller. */
+static void
+ubc_set_tracing(int asid, unsigned long pc)
+{
+ ctrl_outl(pc, UBC_BARA);
+
+ /* We don't have any ASID settings for the SH-2! */
+ if (cpu_data->type != CPU_SH7604)
+ ctrl_outb(asid, UBC_BASRA);
+
+ ctrl_outl(0, UBC_BAMRA);
+
+ if (cpu_data->type == CPU_SH7729) {
+ ctrl_outw(BBR_INST | BBR_READ | BBR_CPU, UBC_BBRA);
+ ctrl_outl(BRCR_PCBA | BRCR_PCTE, UBC_BRCR);
+ } else {
+ ctrl_outw(BBR_INST | BBR_READ, UBC_BBRA);
+ ctrl_outw(BRCR_PCBA, UBC_BRCR);
+ }
+}
+
+/*
+ * switch_to(x,y) should switch tasks from x to y.
+ *
+ */
+struct task_struct *__switch_to(struct task_struct *prev, struct task_struct *next)
+{
+#if defined(CONFIG_SH_FPU)
+ struct pt_regs *regs = (struct pt_regs *)
+ ((unsigned long)prev->thread_info
+ + THREAD_SIZE - sizeof(struct pt_regs)
+ - sizeof(unsigned long));
+ unlazy_fpu(prev, regs);
+#endif
+
+#ifdef CONFIG_PREEMPT
+ {
+ unsigned long flags;
+ struct pt_regs *regs;
+
+ local_irq_save(flags);
+ regs = (struct pt_regs *)
+ ((unsigned long)prev->thread_info
+ + THREAD_SIZE - sizeof(struct pt_regs)
+#ifdef CONFIG_SH_DSP
+ - sizeof(struct pt_dspregs)
+#endif
+ - sizeof(unsigned long));
+ if (user_mode(regs) && regs->regs[15] >= 0xc0000000) {
+ int offset = (int)regs->regs[15];
+
+ /* Reset stack pointer: clear critical region mark */
+ regs->regs[15] = regs->regs[1];
+ if (regs->pc < regs->regs[0])
+ /* Go to rewind point */
+ regs->pc = regs->regs[0] + offset;
+ }
+ local_irq_restore(flags);
+ }
+#endif
+
+ /*
+ * Restore the kernel mode register
+ * k7 (r7_bank1)
+ */
+ asm volatile("ldc %0, r7_bank"
+ : /* no output */
+ : "r" (next->thread_info));
+
+#ifdef CONFIG_MMU
+ /* If no tasks are using the UBC, we're done */
+ if (ubc_usercnt == 0)
+ /* If no tasks are using the UBC, we're done */;
+ else if (next->thread.ubc_pc && next->mm) {
+ ubc_set_tracing(next->mm->context & MMU_CONTEXT_ASID_MASK,
+ next->thread.ubc_pc);
+ } else {
+ ctrl_outw(0, UBC_BBRA);
+ ctrl_outw(0, UBC_BBRB);
+ }
+#endif
+
+ return prev;
+}
+
+asmlinkage int sys_fork(unsigned long r4, unsigned long r5,
+ unsigned long r6, unsigned long r7,
+ struct pt_regs regs)
+{
+#ifdef CONFIG_MMU
+ return do_fork(SIGCHLD, regs.regs[15], &regs, 0, NULL, NULL);
+#else
+ /* fork almost works, enough to trick you into looking elsewhere :-( */
+ return -EINVAL;
+#endif
+}
+
+asmlinkage int sys_clone(unsigned long clone_flags, unsigned long newsp,
+ unsigned long parent_tidptr,
+ unsigned long child_tidptr,
+ struct pt_regs regs)
+{
+ if (!newsp)
+ newsp = regs.regs[15];
+ return do_fork(clone_flags, newsp, &regs, 0,
+ (int __user *)parent_tidptr, (int __user *)child_tidptr);
+}
+
+/*
+ * This is trivial, and on the face of it looks like it
+ * could equally well be done in user mode.
+ *
+ * Not so, for quite unobvious reasons - register pressure.
+ * In user mode vfork() cannot have a stack frame, and if
+ * done by calling the "clone()" system call directly, you
+ * do not have enough call-clobbered registers to hold all
+ * the information you need.
+ */
+asmlinkage int sys_vfork(unsigned long r4, unsigned long r5,
+ unsigned long r6, unsigned long r7,
+ struct pt_regs regs)
+{
+ return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs.regs[15], &regs,
+ 0, NULL, NULL);
+}
+
+/*
+ * sys_execve() executes a new program.
+ */
+asmlinkage int sys_execve(char *ufilename, char **uargv,
+ char **uenvp, unsigned long r7,
+ struct pt_regs regs)
+{
+ int error;
+ char *filename;
+
+ filename = getname((char __user *)ufilename);
+ error = PTR_ERR(filename);
+ if (IS_ERR(filename))
+ goto out;
+
+ error = do_execve(filename,
+ (char __user * __user *)uargv,
+ (char __user * __user *)uenvp,
+ &regs);
+ if (error == 0) {
+ task_lock(current);
+ current->ptrace &= ~PT_DTRACE;
+ task_unlock(current);
+ }
+ putname(filename);
+out:
+ return error;
+}
+
+unsigned long get_wchan(struct task_struct *p)
+{
+ unsigned long schedule_frame;
+ unsigned long pc;
+
+ if (!p || p == current || p->state == TASK_RUNNING)
+ return 0;
+
+ /*
+ * The same comment as on the Alpha applies here, too ...
+ */
+ pc = thread_saved_pc(p);
+ if (in_sched_functions(pc)) {
+ schedule_frame = ((unsigned long *)(long)p->thread.sp)[1];
+ return (unsigned long)((unsigned long *)schedule_frame)[1];
+ }
+ return pc;
+}
+
+asmlinkage void break_point_trap(unsigned long r4, unsigned long r5,
+ unsigned long r6, unsigned long r7,
+ struct pt_regs regs)
+{
+ /* Clear tracing. */
+ ctrl_outw(0, UBC_BBRA);
+ ctrl_outw(0, UBC_BBRB);
+ current->thread.ubc_pc = 0;
+ ubc_usercnt -= 1;
+
+ force_sig(SIGTRAP, current);
+}
+
+asmlinkage void break_point_trap_software(unsigned long r4, unsigned long r5,
+ unsigned long r6, unsigned long r7,
+ struct pt_regs regs)
+{
+ regs.pc -= 2;
+ force_sig(SIGTRAP, current);
+}
diff --git a/arch/sh/kernel/ptrace.c b/arch/sh/kernel/ptrace.c
new file mode 100644
index 00000000000..1b0dfb4d8ea
--- /dev/null
+++ b/arch/sh/kernel/ptrace.c
@@ -0,0 +1,320 @@
+/*
+ * linux/arch/sh/kernel/ptrace.c
+ *
+ * Original x86 implementation:
+ * By Ross Biro 1/23/92
+ * edited by Linus Torvalds
+ *
+ * SuperH version: Copyright (C) 1999, 2000 Kaz Kojima & Niibe Yutaka
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+#include <linux/errno.h>
+#include <linux/ptrace.h>
+#include <linux/user.h>
+#include <linux/slab.h>
+#include <linux/security.h>
+
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/pgtable.h>
+#include <asm/system.h>
+#include <asm/processor.h>
+#include <asm/mmu_context.h>
+
+/*
+ * does not yet catch signals sent when the child dies.
+ * in exit.c or in signal.c.
+ */
+
+/*
+ * This routine will get a word off of the process kernel stack.
+ */
+static inline int get_stack_long(struct task_struct *task, int offset)
+{
+ unsigned char *stack;
+
+ stack = (unsigned char *)
+ task->thread_info + THREAD_SIZE - sizeof(struct pt_regs)
+#ifdef CONFIG_SH_DSP
+ - sizeof(struct pt_dspregs)
+#endif
+ - sizeof(unsigned long);
+ stack += offset;
+ return (*((int *)stack));
+}
+
+/*
+ * This routine will put a word on the process kernel stack.
+ */
+static inline int put_stack_long(struct task_struct *task, int offset,
+ unsigned long data)
+{
+ unsigned char *stack;
+
+ stack = (unsigned char *)
+ task->thread_info + THREAD_SIZE - sizeof(struct pt_regs)
+#ifdef CONFIG_SH_DSP
+ - sizeof(struct pt_dspregs)
+#endif
+ - sizeof(unsigned long);
+ stack += offset;
+ *(unsigned long *) stack = data;
+ return 0;
+}
+
+/*
+ * Called by kernel/ptrace.c when detaching..
+ *
+ * Make sure single step bits etc are not set.
+ */
+void ptrace_disable(struct task_struct *child)
+{
+ /* nothing to do.. */
+}
+
+asmlinkage int sys_ptrace(long request, long pid, long addr, long data)
+{
+ struct task_struct *child;
+ struct user * dummy = NULL;
+ int ret;
+
+ lock_kernel();
+ ret = -EPERM;
+ if (request == PTRACE_TRACEME) {
+ /* are we already being traced? */
+ if (current->ptrace & PT_PTRACED)
+ goto out;
+ ret = security_ptrace(current->parent, current);
+ if (ret)
+ goto out;
+ /* set the ptrace bit in the process flags. */
+ current->ptrace |= PT_PTRACED;
+ ret = 0;
+ goto out;
+ }
+ ret = -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;
+
+ ret = -EPERM;
+ if (pid == 1) /* you may not mess with init */
+ goto out_tsk;
+
+ if (request == PTRACE_ATTACH) {
+ ret = ptrace_attach(child);
+ goto out_tsk;
+ }
+
+ ret = ptrace_check_attach(child, request == PTRACE_KILL);
+ if (ret < 0)
+ goto out_tsk;
+
+ switch (request) {
+ /* when I and D space are separate, these will need to be fixed. */
+ case PTRACE_PEEKTEXT: /* read word at location addr. */
+ case PTRACE_PEEKDATA: {
+ unsigned long tmp;
+ int copied;
+
+ copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
+ ret = -EIO;
+ if (copied != sizeof(tmp))
+ break;
+ ret = put_user(tmp,(unsigned long *) data);
+ break;
+ }
+
+ /* read the word at location addr in the USER area. */
+ case PTRACE_PEEKUSR: {
+ unsigned long tmp;
+
+ ret = -EIO;
+ if ((addr & 3) || addr < 0 ||
+ addr > sizeof(struct user) - 3)
+ break;
+
+ if (addr < sizeof(struct pt_regs))
+ tmp = get_stack_long(child, addr);
+ else if (addr >= (long) &dummy->fpu &&
+ addr < (long) &dummy->u_fpvalid) {
+ if (!tsk_used_math(child)) {
+ if (addr == (long)&dummy->fpu.fpscr)
+ tmp = FPSCR_INIT;
+ else
+ tmp = 0;
+ } else
+ tmp = ((long *)&child->thread.fpu)
+ [(addr - (long)&dummy->fpu) >> 2];
+ } else if (addr == (long) &dummy->u_fpvalid)
+ tmp = !!tsk_used_math(child);
+ else
+ tmp = 0;
+ ret = put_user(tmp, (unsigned long *)data);
+ break;
+ }
+
+ /* when I and D space are separate, this will have to be fixed. */
+ case PTRACE_POKETEXT: /* write the word at location addr. */
+ case PTRACE_POKEDATA:
+ ret = 0;
+ if (access_process_vm(child, addr, &data, sizeof(data), 1) == sizeof(data))
+ break;
+ ret = -EIO;
+ break;
+
+ case PTRACE_POKEUSR: /* write the word at location addr in the USER area */
+ ret = -EIO;
+ if ((addr & 3) || addr < 0 ||
+ addr > sizeof(struct user) - 3)
+ break;
+
+ if (addr < sizeof(struct pt_regs))
+ ret = put_stack_long(child, addr, data);
+ else if (addr >= (long) &dummy->fpu &&
+ addr < (long) &dummy->u_fpvalid) {
+ set_stopped_child_used_math(child);
+ ((long *)&child->thread.fpu)
+ [(addr - (long)&dummy->fpu) >> 2] = data;
+ ret = 0;
+ } else if (addr == (long) &dummy->u_fpvalid) {
+ conditional_stopped_child_used_math(data, child);
+ ret = 0;
+ }
+ break;
+
+ case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */
+ case PTRACE_CONT: { /* restart after signal. */
+ ret = -EIO;
+ if ((unsigned long) data > _NSIG)
+ 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);
+ ret = 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: {
+ ret = 0;
+ if (child->exit_state == EXIT_ZOMBIE) /* already dead */
+ break;
+ child->exit_code = SIGKILL;
+ wake_up_process(child);
+ break;
+ }
+
+ case PTRACE_SINGLESTEP: { /* set the trap flag. */
+ long pc;
+ struct pt_regs *dummy = NULL;
+
+ ret = -EIO;
+ if ((unsigned long) data > _NSIG)
+ break;
+ clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
+ if ((child->ptrace & PT_DTRACE) == 0) {
+ /* Spurious delayed TF traps may occur */
+ child->ptrace |= PT_DTRACE;
+ }
+
+ pc = get_stack_long(child, (long)&dummy->pc);
+
+ /* Next scheduling will set up UBC */
+ if (child->thread.ubc_pc == 0)
+ ubc_usercnt += 1;
+ child->thread.ubc_pc = pc;
+
+ child->exit_code = data;
+ /* give it a chance to run. */
+ wake_up_process(child);
+ ret = 0;
+ break;
+ }
+
+ case PTRACE_DETACH: /* detach a process that was attached. */
+ ret = ptrace_detach(child, data);
+ break;
+
+#ifdef CONFIG_SH_DSP
+ case PTRACE_GETDSPREGS: {
+ unsigned long dp;
+
+ ret = -EIO;
+ dp = ((unsigned long) child) + THREAD_SIZE -
+ sizeof(struct pt_dspregs);
+ if (*((int *) (dp - 4)) == SR_FD) {
+ copy_to_user(addr, (void *) dp,
+ sizeof(struct pt_dspregs));
+ ret = 0;
+ }
+ break;
+ }
+
+ case PTRACE_SETDSPREGS: {
+ unsigned long dp;
+ int i;
+
+ ret = -EIO;
+ dp = ((unsigned long) child) + THREAD_SIZE -
+ sizeof(struct pt_dspregs);
+ if (*((int *) (dp - 4)) == SR_FD) {
+ copy_from_user((void *) dp, addr,
+ sizeof(struct pt_dspregs));
+ ret = 0;
+ }
+ break;
+ }
+#endif
+ default:
+ ret = ptrace_request(child, request, addr, data);
+ break;
+ }
+out_tsk:
+ put_task_struct(child);
+out:
+ unlock_kernel();
+ return ret;
+}
+
+asmlinkage void do_syscall_trace(void)
+{
+ struct task_struct *tsk = current;
+
+ if (!test_thread_flag(TIF_SYSCALL_TRACE))
+ return;
+ if (!(tsk->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 (tsk->exit_code) {
+ send_sig(tsk->exit_code, tsk, 1);
+ tsk->exit_code = 0;
+ }
+}
diff --git a/arch/sh/kernel/semaphore.c b/arch/sh/kernel/semaphore.c
new file mode 100644
index 00000000000..a3c24dcbf01
--- /dev/null
+++ b/arch/sh/kernel/semaphore.c
@@ -0,0 +1,139 @@
+/*
+ * Just taken from alpha implementation.
+ * This can't work well, perhaps.
+ */
+/*
+ * Generic semaphore code. Buyer beware. Do your own
+ * specific changes in <asm/semaphore-helper.h>
+ */
+
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/init.h>
+#include <asm/semaphore.h>
+#include <asm/semaphore-helper.h>
+
+spinlock_t semaphore_wake_lock;
+
+/*
+ * Semaphores are implemented using a two-way counter:
+ * The "count" variable is decremented for each process
+ * that tries to sleep, while the "waking" variable is
+ * incremented when the "up()" code goes to wake up waiting
+ * processes.
+ *
+ * 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.
+ *
+ * waking_non_zero() (from asm/semaphore.h) must execute
+ * atomically.
+ *
+ * When __up() is called, the count was negative before
+ * incrementing it, and we need to wake up somebody.
+ *
+ * This routine adds one to the count of processes that need to
+ * wake up and exit. ALL waiting processes actually wake up but
+ * only the one that gets to the "waking" field first will gate
+ * through and acquire the semaphore. The others will go back
+ * to sleep.
+ *
+ * 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.
+ */
+void __up(struct semaphore *sem)
+{
+ wake_one_more(sem);
+ wake_up(&sem->wait);
+}
+
+/*
+ * Perform the "down" function. Return zero for semaphore acquired,
+ * return negative for signalled out of the function.
+ *
+ * If called from __down, the return is ignored and the wait loop is
+ * not interruptible. This means that a task waiting on a semaphore
+ * using "down()" cannot be killed until someone does an "up()" on
+ * the semaphore.
+ *
+ * If called from __down_interruptible, the return value gets checked
+ * upon return. If the return value is negative then the task continues
+ * with the negative value in the return register (it can be tested by
+ * the caller).
+ *
+ * Either form may be used in conjunction with "up()".
+ *
+ */
+
+#define DOWN_VAR \
+ struct task_struct *tsk = current; \
+ wait_queue_t wait; \
+ init_waitqueue_entry(&wait, tsk);
+
+#define DOWN_HEAD(task_state) \
+ \
+ \
+ tsk->state = (task_state); \
+ add_wait_queue(&sem->wait, &wait); \
+ \
+ /* \
+ * Ok, we're set up. sem->count is known to be less than zero \
+ * so we must wait. \
+ * \
+ * We can let go the lock for purposes of waiting. \
+ * We re-acquire it after awaking so as to protect \
+ * all semaphore operations. \
+ * \
+ * If "up()" is called before we call waking_non_zero() then \
+ * we will catch it right away. If it is called later then \
+ * we will have to go through a wakeup cycle to catch it. \
+ * \
+ * Multiple waiters contend for the semaphore lock to see \
+ * who gets to gate through and who has to wait some more. \
+ */ \
+ for (;;) {
+
+#define DOWN_TAIL(task_state) \
+ tsk->state = (task_state); \
+ } \
+ tsk->state = TASK_RUNNING; \
+ remove_wait_queue(&sem->wait, &wait);
+
+void __sched __down(struct semaphore * sem)
+{
+ DOWN_VAR
+ DOWN_HEAD(TASK_UNINTERRUPTIBLE)
+ if (waking_non_zero(sem))
+ break;
+ schedule();
+ DOWN_TAIL(TASK_UNINTERRUPTIBLE)
+}
+
+int __sched __down_interruptible(struct semaphore * sem)
+{
+ int ret = 0;
+ DOWN_VAR
+ DOWN_HEAD(TASK_INTERRUPTIBLE)
+
+ ret = waking_non_zero_interruptible(sem, tsk);
+ if (ret)
+ {
+ if (ret == 1)
+ /* ret != 0 only if we get interrupted -arca */
+ ret = 0;
+ break;
+ }
+ schedule();
+ DOWN_TAIL(TASK_INTERRUPTIBLE)
+ return ret;
+}
+
+int __down_trylock(struct semaphore * sem)
+{
+ return waking_non_zero_trylock(sem);
+}
diff --git a/arch/sh/kernel/setup.c b/arch/sh/kernel/setup.c
new file mode 100644
index 00000000000..25b9d9ebe85
--- /dev/null
+++ b/arch/sh/kernel/setup.c
@@ -0,0 +1,649 @@
+/* $Id: setup.c,v 1.30 2003/10/13 07:21:19 lethal Exp $
+ *
+ * linux/arch/sh/kernel/setup.c
+ *
+ * Copyright (C) 1999 Niibe Yutaka
+ * Copyright (C) 2002, 2003 Paul Mundt
+ */
+
+/*
+ * This file handles the architecture-dependent parts of initialization
+ */
+
+#include <linux/tty.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/initrd.h>
+#include <linux/bootmem.h>
+#include <linux/console.h>
+#include <linux/seq_file.h>
+#include <linux/root_dev.h>
+#include <linux/utsname.h>
+#include <linux/cpu.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/io_generic.h>
+#include <asm/sections.h>
+#include <asm/irq.h>
+#include <asm/setup.h>
+
+#ifdef CONFIG_SH_KGDB
+#include <asm/kgdb.h>
+static int kgdb_parse_options(char *options);
+#endif
+extern void * __rd_start, * __rd_end;
+/*
+ * Machine setup..
+ */
+
+/*
+ * Initialize loops_per_jiffy as 10000000 (1000MIPS).
+ * This value will be used at the very early stage of serial setup.
+ * The bigger value means no problem.
+ */
+struct sh_cpuinfo boot_cpu_data = { CPU_SH_NONE, 0, 10000000, };
+struct screen_info screen_info;
+
+#if defined(CONFIG_SH_UNKNOWN)
+struct sh_machine_vector sh_mv;
+#endif
+
+/* We need this to satisfy some external references. */
+struct screen_info screen_info = {
+ 0, 25, /* orig-x, orig-y */
+ 0, /* unused */
+ 0, /* orig-video-page */
+ 0, /* orig-video-mode */
+ 80, /* orig-video-cols */
+ 0,0,0, /* ega_ax, ega_bx, ega_cx */
+ 25, /* orig-video-lines */
+ 0, /* orig-video-isVGA */
+ 16 /* orig-video-points */
+};
+
+extern void platform_setup(void);
+extern char *get_system_type(void);
+extern int root_mountflags;
+
+#define MV_NAME_SIZE 32
+
+static struct sh_machine_vector* __init get_mv_byname(const char* name);
+
+/*
+ * This is set up by the setup-routine at boot-time
+ */
+#define PARAM ((unsigned char *)empty_zero_page)
+
+#define MOUNT_ROOT_RDONLY (*(unsigned long *) (PARAM+0x000))
+#define RAMDISK_FLAGS (*(unsigned long *) (PARAM+0x004))
+#define ORIG_ROOT_DEV (*(unsigned long *) (PARAM+0x008))
+#define LOADER_TYPE (*(unsigned long *) (PARAM+0x00c))
+#define INITRD_START (*(unsigned long *) (PARAM+0x010))
+#define INITRD_SIZE (*(unsigned long *) (PARAM+0x014))
+/* ... */
+#define COMMAND_LINE ((char *) (PARAM+0x100))
+
+#define RAMDISK_IMAGE_START_MASK 0x07FF
+#define RAMDISK_PROMPT_FLAG 0x8000
+#define RAMDISK_LOAD_FLAG 0x4000
+
+static char command_line[COMMAND_LINE_SIZE] = { 0, };
+
+struct resource standard_io_resources[] = {
+ { "dma1", 0x00, 0x1f },
+ { "pic1", 0x20, 0x3f },
+ { "timer", 0x40, 0x5f },
+ { "keyboard", 0x60, 0x6f },
+ { "dma page reg", 0x80, 0x8f },
+ { "pic2", 0xa0, 0xbf },
+ { "dma2", 0xc0, 0xdf },
+ { "fpu", 0xf0, 0xff }
+};
+
+#define STANDARD_IO_RESOURCES (sizeof(standard_io_resources)/sizeof(struct resource))
+
+/* System RAM - interrupted by the 640kB-1M hole */
+#define code_resource (ram_resources[3])
+#define data_resource (ram_resources[4])
+static struct resource ram_resources[] = {
+ { "System RAM", 0x000000, 0x09ffff, IORESOURCE_BUSY },
+ { "System RAM", 0x100000, 0x100000, IORESOURCE_BUSY },
+ { "Video RAM area", 0x0a0000, 0x0bffff },
+ { "Kernel code", 0x100000, 0 },
+ { "Kernel data", 0, 0 }
+};
+
+unsigned long memory_start, memory_end;
+
+static inline void parse_cmdline (char ** cmdline_p, char mv_name[MV_NAME_SIZE],
+ struct sh_machine_vector** mvp,
+ unsigned long *mv_io_base,
+ int *mv_mmio_enable)
+{
+ char c = ' ', *to = command_line, *from = COMMAND_LINE;
+ int len = 0;
+
+ /* Save unparsed command line copy for /proc/cmdline */
+ memcpy(saved_command_line, COMMAND_LINE, COMMAND_LINE_SIZE);
+ saved_command_line[COMMAND_LINE_SIZE-1] = '\0';
+
+ memory_start = (unsigned long)PAGE_OFFSET+__MEMORY_START;
+ memory_end = memory_start + __MEMORY_SIZE;
+
+ for (;;) {
+ /*
+ * "mem=XXX[kKmM]" defines a size of memory.
+ */
+ if (c == ' ' && !memcmp(from, "mem=", 4)) {
+ if (to != command_line)
+ to--;
+ {
+ unsigned long mem_size;
+
+ mem_size = memparse(from+4, &from);
+ memory_end = memory_start + mem_size;
+ }
+ }
+ if (c == ' ' && !memcmp(from, "sh_mv=", 6)) {
+ char* mv_end;
+ char* mv_comma;
+ int mv_len;
+ if (to != command_line)
+ to--;
+ from += 6;
+ mv_end = strchr(from, ' ');
+ if (mv_end == NULL)
+ mv_end = from + strlen(from);
+
+ mv_comma = strchr(from, ',');
+ if ((mv_comma != NULL) && (mv_comma < mv_end)) {
+ int ints[3];
+ get_options(mv_comma+1, ARRAY_SIZE(ints), ints);
+ *mv_io_base = ints[1];
+ *mv_mmio_enable = ints[2];
+ mv_len = mv_comma - from;
+ } else {
+ mv_len = mv_end - from;
+ }
+ if (mv_len > (MV_NAME_SIZE-1))
+ mv_len = MV_NAME_SIZE-1;
+ memcpy(mv_name, from, mv_len);
+ mv_name[mv_len] = '\0';
+ from = mv_end;
+
+ *mvp = get_mv_byname(mv_name);
+ }
+ c = *(from++);
+ if (!c)
+ break;
+ if (COMMAND_LINE_SIZE <= ++len)
+ break;
+ *(to++) = c;
+ }
+ *to = '\0';
+ *cmdline_p = command_line;
+}
+
+static int __init sh_mv_setup(char **cmdline_p)
+{
+#if defined(CONFIG_SH_UNKNOWN)
+ extern struct sh_machine_vector mv_unknown;
+#endif
+ struct sh_machine_vector *mv = NULL;
+ char mv_name[MV_NAME_SIZE] = "";
+ unsigned long mv_io_base = 0;
+ int mv_mmio_enable = 0;
+
+ parse_cmdline(cmdline_p, mv_name, &mv, &mv_io_base, &mv_mmio_enable);
+
+#ifdef CONFIG_SH_GENERIC
+ if (mv == NULL) {
+ mv = &mv_unknown;
+ if (*mv_name != '\0') {
+ printk("Warning: Unsupported machine %s, using unknown\n",
+ mv_name);
+ }
+ }
+ sh_mv = *mv;
+#endif
+#ifdef CONFIG_SH_UNKNOWN
+ sh_mv = mv_unknown;
+#endif
+
+ /*
+ * Manually walk the vec, fill in anything that the board hasn't yet
+ * by hand, wrapping to the generic implementation.
+ */
+#define mv_set(elem) do { \
+ if (!sh_mv.mv_##elem) \
+ sh_mv.mv_##elem = generic_##elem; \
+} while (0)
+
+ mv_set(inb); mv_set(inw); mv_set(inl);
+ mv_set(outb); mv_set(outw); mv_set(outl);
+
+ mv_set(inb_p); mv_set(inw_p); mv_set(inl_p);
+ mv_set(outb_p); mv_set(outw_p); mv_set(outl_p);
+
+ mv_set(insb); mv_set(insw); mv_set(insl);
+ mv_set(outsb); mv_set(outsw); mv_set(outsl);
+
+ mv_set(readb); mv_set(readw); mv_set(readl);
+ mv_set(writeb); mv_set(writew); mv_set(writel);
+
+ mv_set(ioremap);
+ mv_set(iounmap);
+
+ mv_set(isa_port2addr);
+ mv_set(irq_demux);
+
+#ifdef CONFIG_SH_UNKNOWN
+ __set_io_port_base(mv_io_base);
+#endif
+
+ return 0;
+}
+
+void __init setup_arch(char **cmdline_p)
+{
+ unsigned long bootmap_size;
+ unsigned long start_pfn, max_pfn, max_low_pfn;
+
+#ifdef CONFIG_EARLY_PRINTK
+ extern void enable_early_printk(void);
+
+ enable_early_printk();
+#endif
+#ifdef CONFIG_CMDLINE_BOOL
+ strcpy(COMMAND_LINE, CONFIG_CMDLINE);
+#endif
+
+ ROOT_DEV = old_decode_dev(ORIG_ROOT_DEV);
+
+#ifdef CONFIG_BLK_DEV_RAM
+ rd_image_start = RAMDISK_FLAGS & RAMDISK_IMAGE_START_MASK;
+ rd_prompt = ((RAMDISK_FLAGS & RAMDISK_PROMPT_FLAG) != 0);
+ rd_doload = ((RAMDISK_FLAGS & RAMDISK_LOAD_FLAG) != 0);
+#endif
+
+ if (!MOUNT_ROOT_RDONLY)
+ root_mountflags &= ~MS_RDONLY;
+ init_mm.start_code = (unsigned long) _text;
+ init_mm.end_code = (unsigned long) _etext;
+ init_mm.end_data = (unsigned long) _edata;
+ init_mm.brk = (unsigned long) _end;
+
+ code_resource.start = virt_to_bus(_text);
+ code_resource.end = virt_to_bus(_etext)-1;
+ data_resource.start = virt_to_bus(_etext);
+ data_resource.end = virt_to_bus(_edata)-1;
+
+ sh_mv_setup(cmdline_p);
+
+#define PFN_UP(x) (((x) + PAGE_SIZE-1) >> PAGE_SHIFT)
+#define PFN_DOWN(x) ((x) >> PAGE_SHIFT)
+#define PFN_PHYS(x) ((x) << PAGE_SHIFT)
+
+#ifdef CONFIG_DISCONTIGMEM
+ NODE_DATA(0)->bdata = &discontig_node_bdata[0];
+ NODE_DATA(1)->bdata = &discontig_node_bdata[1];
+
+ bootmap_size = init_bootmem_node(NODE_DATA(1),
+ PFN_UP(__MEMORY_START_2ND),
+ PFN_UP(__MEMORY_START_2ND),
+ PFN_DOWN(__MEMORY_START_2ND+__MEMORY_SIZE_2ND));
+ free_bootmem_node(NODE_DATA(1), __MEMORY_START_2ND, __MEMORY_SIZE_2ND);
+ reserve_bootmem_node(NODE_DATA(1), __MEMORY_START_2ND, bootmap_size);
+#endif
+
+ /*
+ * Find the highest page frame number we have available
+ */
+ max_pfn = PFN_DOWN(__pa(memory_end));
+
+ /*
+ * Determine low and high memory ranges:
+ */
+ max_low_pfn = max_pfn;
+
+ /*
+ * Partially used pages are not usable - thus
+ * we are rounding upwards:
+ */
+ start_pfn = PFN_UP(__pa(_end));
+
+ /*
+ * Find a proper area for the bootmem bitmap. After this
+ * bootstrap step all allocations (until the page allocator
+ * is intact) must be done via bootmem_alloc().
+ */
+ bootmap_size = init_bootmem_node(NODE_DATA(0), start_pfn,
+ __MEMORY_START>>PAGE_SHIFT,
+ max_low_pfn);
+ /*
+ * Register fully available low RAM pages with the bootmem allocator.
+ */
+ {
+ unsigned long curr_pfn, last_pfn, pages;
+
+ /*
+ * We are rounding up the start address of usable memory:
+ */
+ curr_pfn = PFN_UP(__MEMORY_START);
+ /*
+ * ... and at the end of the usable range downwards:
+ */
+ last_pfn = PFN_DOWN(__pa(memory_end));
+
+ if (last_pfn > max_low_pfn)
+ last_pfn = max_low_pfn;
+
+ pages = last_pfn - curr_pfn;
+ free_bootmem_node(NODE_DATA(0), PFN_PHYS(curr_pfn),
+ PFN_PHYS(pages));
+ }
+
+ /*
+ * Reserve the kernel text and
+ * Reserve the bootmem bitmap. We do this in two steps (first step
+ * was init_bootmem()), because this catches the (definitely buggy)
+ * case of us accidentally initializing the bootmem allocator with
+ * an invalid RAM area.
+ */
+ reserve_bootmem_node(NODE_DATA(0), __MEMORY_START+PAGE_SIZE,
+ (PFN_PHYS(start_pfn)+bootmap_size+PAGE_SIZE-1)-__MEMORY_START);
+
+ /*
+ * reserve physical page 0 - it's a special BIOS page on many boxes,
+ * enabling clean reboots, SMP operation, laptop functions.
+ */
+ reserve_bootmem_node(NODE_DATA(0), __MEMORY_START, PAGE_SIZE);
+
+#ifdef CONFIG_BLK_DEV_INITRD
+ ROOT_DEV = MKDEV(RAMDISK_MAJOR, 0);
+ if (&__rd_start != &__rd_end) {
+ LOADER_TYPE = 1;
+ INITRD_START = PHYSADDR((unsigned long)&__rd_start) - __MEMORY_START;
+ INITRD_SIZE = (unsigned long)&__rd_end - (unsigned long)&__rd_start;
+ }
+
+ if (LOADER_TYPE && INITRD_START) {
+ if (INITRD_START + INITRD_SIZE <= (max_low_pfn << PAGE_SHIFT)) {
+ reserve_bootmem_node(NODE_DATA(0), INITRD_START+__MEMORY_START, INITRD_SIZE);
+ initrd_start =
+ INITRD_START ? INITRD_START + PAGE_OFFSET + __MEMORY_START : 0;
+ initrd_end = initrd_start + INITRD_SIZE;
+ } else {
+ printk("initrd extends beyond end of memory "
+ "(0x%08lx > 0x%08lx)\ndisabling initrd\n",
+ INITRD_START + INITRD_SIZE,
+ max_low_pfn << PAGE_SHIFT);
+ initrd_start = 0;
+ }
+ }
+#endif
+
+#ifdef CONFIG_DUMMY_CONSOLE
+ conswitchp = &dummy_con;
+#endif
+
+ /* Perform the machine specific initialisation */
+ platform_setup();
+
+ paging_init();
+}
+
+struct sh_machine_vector* __init get_mv_byname(const char* name)
+{
+ extern int strcasecmp(const char *, const char *);
+ extern long __machvec_start, __machvec_end;
+ struct sh_machine_vector *all_vecs =
+ (struct sh_machine_vector *)&__machvec_start;
+
+ int i, n = ((unsigned long)&__machvec_end
+ - (unsigned long)&__machvec_start)/
+ sizeof(struct sh_machine_vector);
+
+ for (i = 0; i < n; ++i) {
+ struct sh_machine_vector *mv = &all_vecs[i];
+ if (mv == NULL)
+ continue;
+ if (strcasecmp(name, get_system_type()) == 0) {
+ return mv;
+ }
+ }
+ return NULL;
+}
+
+static struct cpu cpu[NR_CPUS];
+
+static int __init topology_init(void)
+{
+ int cpu_id;
+
+ for (cpu_id = 0; cpu_id < NR_CPUS; cpu_id++)
+ if (cpu_possible(cpu_id))
+ register_cpu(&cpu[cpu_id], cpu_id, NULL);
+
+ return 0;
+}
+
+subsys_initcall(topology_init);
+
+static const char *cpu_name[] = {
+ [CPU_SH7604] = "SH7604",
+ [CPU_SH7705] = "SH7705",
+ [CPU_SH7708] = "SH7708",
+ [CPU_SH7729] = "SH7729",
+ [CPU_SH7300] = "SH7300",
+ [CPU_SH7750] = "SH7750",
+ [CPU_SH7750S] = "SH7750S",
+ [CPU_SH7750R] = "SH7750R",
+ [CPU_SH7751] = "SH7751",
+ [CPU_SH7751R] = "SH7751R",
+ [CPU_SH7760] = "SH7760",
+ [CPU_SH73180] = "SH73180",
+ [CPU_ST40RA] = "ST40RA",
+ [CPU_ST40GX1] = "ST40GX1",
+ [CPU_SH4_202] = "SH4-202",
+ [CPU_SH4_501] = "SH4-501",
+ [CPU_SH_NONE] = "Unknown"
+};
+
+const char *get_cpu_subtype(void)
+{
+ return cpu_name[boot_cpu_data.type];
+}
+
+#ifdef CONFIG_PROC_FS
+static const char *cpu_flags[] = {
+ "none", "fpu", "p2flush", "mmuassoc", "dsp", "perfctr",
+};
+
+static void show_cpuflags(struct seq_file *m)
+{
+ unsigned long i;
+
+ seq_printf(m, "cpu flags\t:");
+
+ if (!cpu_data->flags) {
+ seq_printf(m, " %s\n", cpu_flags[0]);
+ return;
+ }
+
+ for (i = 0; i < cpu_data->flags; i++)
+ if ((cpu_data->flags & (1 << i)))
+ seq_printf(m, " %s", cpu_flags[i+1]);
+
+ seq_printf(m, "\n");
+}
+
+static void show_cacheinfo(struct seq_file *m, const char *type, struct cache_info info)
+{
+ unsigned int cache_size;
+
+ cache_size = info.ways * info.sets * info.linesz;
+
+ seq_printf(m, "%s size\t: %dKiB\n", type, cache_size >> 10);
+}
+
+/*
+ * Get CPU information for use by the procfs.
+ */
+static int show_cpuinfo(struct seq_file *m, void *v)
+{
+ unsigned int cpu = smp_processor_id();
+
+ if (!cpu && cpu_online(cpu))
+ seq_printf(m, "machine\t\t: %s\n", get_system_type());
+
+ seq_printf(m, "processor\t: %d\n", cpu);
+ seq_printf(m, "cpu family\t: %s\n", system_utsname.machine);
+ seq_printf(m, "cpu type\t: %s\n", get_cpu_subtype());
+
+ show_cpuflags(m);
+
+ seq_printf(m, "cache type\t: ");
+
+ /*
+ * Check for what type of cache we have, we support both the
+ * unified cache on the SH-2 and SH-3, as well as the harvard
+ * style cache on the SH-4.
+ */
+ if (test_bit(SH_CACHE_COMBINED, &(boot_cpu_data.icache.flags))) {
+ seq_printf(m, "unified\n");
+ show_cacheinfo(m, "cache", boot_cpu_data.icache);
+ } else {
+ seq_printf(m, "split (harvard)\n");
+ show_cacheinfo(m, "icache", boot_cpu_data.icache);
+ show_cacheinfo(m, "dcache", boot_cpu_data.dcache);
+ }
+
+ seq_printf(m, "bogomips\t: %lu.%02lu\n",
+ boot_cpu_data.loops_per_jiffy/(500000/HZ),
+ (boot_cpu_data.loops_per_jiffy/(5000/HZ)) % 100);
+
+#define PRINT_CLOCK(name, value) \
+ seq_printf(m, name " clock\t: %d.%02dMHz\n", \
+ ((value) / 1000000), ((value) % 1000000)/10000)
+
+ PRINT_CLOCK("cpu", boot_cpu_data.cpu_clock);
+ PRINT_CLOCK("bus", boot_cpu_data.bus_clock);
+#ifdef CONFIG_CPU_SUBTYPE_ST40STB1
+ PRINT_CLOCK("memory", boot_cpu_data.memory_clock);
+#endif
+ PRINT_CLOCK("module", boot_cpu_data.module_clock);
+
+ return 0;
+}
+
+
+static void *c_start(struct seq_file *m, loff_t *pos)
+{
+ return *pos < NR_CPUS ? cpu_data + *pos : NULL;
+}
+static void *c_next(struct seq_file *m, void *v, loff_t *pos)
+{
+ ++*pos;
+ return c_start(m, pos);
+}
+static void c_stop(struct seq_file *m, void *v)
+{
+}
+struct seq_operations cpuinfo_op = {
+ .start = c_start,
+ .next = c_next,
+ .stop = c_stop,
+ .show = show_cpuinfo,
+};
+#endif /* CONFIG_PROC_FS */
+
+#ifdef CONFIG_SH_KGDB
+/*
+ * Parse command-line kgdb options. By default KGDB is enabled,
+ * entered on error (or other action) using default serial info.
+ * The command-line option can include a serial port specification
+ * and an action to override default or configured behavior.
+ */
+struct kgdb_sermap kgdb_sci_sermap =
+{ "ttySC", 5, kgdb_sci_setup, NULL };
+
+struct kgdb_sermap *kgdb_serlist = &kgdb_sci_sermap;
+struct kgdb_sermap *kgdb_porttype = &kgdb_sci_sermap;
+
+void kgdb_register_sermap(struct kgdb_sermap *map)
+{
+ struct kgdb_sermap *last;
+
+ for (last = kgdb_serlist; last->next; last = last->next)
+ ;
+ last->next = map;
+ if (!map->namelen) {
+ map->namelen = strlen(map->name);
+ }
+}
+
+static int __init kgdb_parse_options(char *options)
+{
+ char c;
+ int baud;
+
+ /* Check for port spec (or use default) */
+
+ /* Determine port type and instance */
+ if (!memcmp(options, "tty", 3)) {
+ struct kgdb_sermap *map = kgdb_serlist;
+
+ while (map && memcmp(options, map->name, map->namelen))
+ map = map->next;
+
+ if (!map) {
+ KGDB_PRINTK("unknown port spec in %s\n", options);
+ return -1;
+ }
+
+ kgdb_porttype = map;
+ kgdb_serial_setup = map->setup_fn;
+ kgdb_portnum = options[map->namelen] - '0';
+ options += map->namelen + 1;
+
+ options = (*options == ',') ? options+1 : options;
+
+ /* Read optional parameters (baud/parity/bits) */
+ baud = simple_strtoul(options, &options, 10);
+ if (baud != 0) {
+ kgdb_baud = baud;
+
+ c = toupper(*options);
+ if (c == 'E' || c == 'O' || c == 'N') {
+ kgdb_parity = c;
+ options++;
+ }
+
+ c = *options;
+ if (c == '7' || c == '8') {
+ kgdb_bits = c;
+ options++;
+ }
+ options = (*options == ',') ? options+1 : options;
+ }
+ }
+
+ /* Check for action specification */
+ if (!memcmp(options, "halt", 4)) {
+ kgdb_halt = 1;
+ options += 4;
+ } else if (!memcmp(options, "disabled", 8)) {
+ kgdb_enabled = 0;
+ options += 8;
+ }
+
+ if (*options) {
+ KGDB_PRINTK("ignored unknown options: %s\n", options);
+ return 0;
+ }
+ return 1;
+}
+__setup("kgdb=", kgdb_parse_options);
+#endif /* CONFIG_SH_KGDB */
+
diff --git a/arch/sh/kernel/sh_bios.c b/arch/sh/kernel/sh_bios.c
new file mode 100644
index 00000000000..5b53e10bb9c
--- /dev/null
+++ b/arch/sh/kernel/sh_bios.c
@@ -0,0 +1,75 @@
+/*
+ * linux/arch/sh/kernel/sh_bios.c
+ * C interface for trapping into the standard LinuxSH BIOS.
+ *
+ * Copyright (C) 2000 Greg Banks, Mitch Davis
+ *
+ */
+
+#include <asm/sh_bios.h>
+
+#define BIOS_CALL_CONSOLE_WRITE 0
+#define BIOS_CALL_READ_BLOCK 1
+#define BIOS_CALL_ETH_NODE_ADDR 10
+#define BIOS_CALL_SHUTDOWN 11
+#define BIOS_CALL_CHAR_OUT 0x1f /* TODO: hack */
+#define BIOS_CALL_GDB_GET_MODE_PTR 0xfe
+#define BIOS_CALL_GDB_DETACH 0xff
+
+static __inline__ long sh_bios_call(long func, long arg0, long arg1, long arg2, long arg3)
+{
+ register long r0 __asm__("r0") = func;
+ register long r4 __asm__("r4") = arg0;
+ register long r5 __asm__("r5") = arg1;
+ register long r6 __asm__("r6") = arg2;
+ register long r7 __asm__("r7") = arg3;
+ __asm__ __volatile__("trapa #0x3f"
+ : "=z" (r0)
+ : "0" (r0), "r" (r4), "r" (r5), "r" (r6), "r" (r7)
+ : "memory");
+ return r0;
+}
+
+
+void sh_bios_console_write(const char *buf, unsigned int len)
+{
+ sh_bios_call(BIOS_CALL_CONSOLE_WRITE, (long)buf, (long)len, 0, 0);
+}
+
+
+void sh_bios_char_out(char ch)
+{
+ sh_bios_call(BIOS_CALL_CHAR_OUT, ch, 0, 0, 0);
+}
+
+
+int sh_bios_in_gdb_mode(void)
+{
+ static char queried = 0;
+ static char *gdb_mode_p = 0;
+
+ if (!queried)
+ {
+ /* Query the gdb stub for address of its gdb mode variable */
+ long r = sh_bios_call(BIOS_CALL_GDB_GET_MODE_PTR, 0, 0, 0, 0);
+ if (r != ~0) /* BIOS returns -1 for unknown function */
+ gdb_mode_p = (char *)r;
+ queried = 1;
+ }
+ return (gdb_mode_p != 0 ? *gdb_mode_p : 0);
+}
+
+void sh_bios_gdb_detach(void)
+{
+ sh_bios_call(BIOS_CALL_GDB_DETACH, 0, 0, 0, 0);
+}
+
+void sh_bios_get_node_addr (unsigned char *node_addr)
+{
+ sh_bios_call(BIOS_CALL_ETH_NODE_ADDR, 0, (long)node_addr, 0, 0);
+}
+
+void sh_bios_shutdown(unsigned int how)
+{
+ sh_bios_call(BIOS_CALL_SHUTDOWN, how, 0, 0, 0);
+}
diff --git a/arch/sh/kernel/sh_ksyms.c b/arch/sh/kernel/sh_ksyms.c
new file mode 100644
index 00000000000..6954fd62470
--- /dev/null
+++ b/arch/sh/kernel/sh_ksyms.c
@@ -0,0 +1,126 @@
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/smp.h>
+#include <linux/user.h>
+#include <linux/elfcore.h>
+#include <linux/sched.h>
+#include <linux/in6.h>
+#include <linux/interrupt.h>
+#include <linux/smp_lock.h>
+#include <linux/vmalloc.h>
+#include <linux/pci.h>
+#include <linux/irq.h>
+
+#include <asm/semaphore.h>
+#include <asm/processor.h>
+#include <asm/uaccess.h>
+#include <asm/checksum.h>
+#include <asm/io.h>
+#include <asm/delay.h>
+#include <asm/tlbflush.h>
+#include <asm/cacheflush.h>
+#include <asm/checksum.h>
+
+extern void dump_thread(struct pt_regs *, struct user *);
+extern int dump_fpu(struct pt_regs *, elf_fpregset_t *);
+extern struct hw_interrupt_type no_irq_type;
+
+EXPORT_SYMBOL(sh_mv);
+
+/* platform dependent support */
+EXPORT_SYMBOL(dump_thread);
+EXPORT_SYMBOL(dump_fpu);
+EXPORT_SYMBOL(iounmap);
+EXPORT_SYMBOL(enable_irq);
+EXPORT_SYMBOL(disable_irq);
+EXPORT_SYMBOL(probe_irq_mask);
+EXPORT_SYMBOL(kernel_thread);
+EXPORT_SYMBOL(disable_irq_nosync);
+EXPORT_SYMBOL(irq_desc);
+EXPORT_SYMBOL(no_irq_type);
+
+EXPORT_SYMBOL(strpbrk);
+EXPORT_SYMBOL(strstr);
+EXPORT_SYMBOL(strlen);
+EXPORT_SYMBOL(strnlen);
+EXPORT_SYMBOL(strchr);
+EXPORT_SYMBOL(strcat);
+EXPORT_SYMBOL(strncat);
+
+/* PCI exports */
+#ifdef CONFIG_PCI
+EXPORT_SYMBOL(pci_alloc_consistent);
+EXPORT_SYMBOL(pci_free_consistent);
+#endif
+
+/* mem exports */
+EXPORT_SYMBOL(memchr);
+EXPORT_SYMBOL(memcpy);
+EXPORT_SYMBOL(memcpy_fromio);
+EXPORT_SYMBOL(memcpy_toio);
+EXPORT_SYMBOL(memset);
+EXPORT_SYMBOL(memset_io);
+EXPORT_SYMBOL(memmove);
+EXPORT_SYMBOL(memcmp);
+EXPORT_SYMBOL(memscan);
+EXPORT_SYMBOL(__copy_user);
+EXPORT_SYMBOL(boot_cpu_data);
+
+#ifdef CONFIG_MMU
+EXPORT_SYMBOL(get_vm_area);
+#endif
+
+/* semaphore exports */
+EXPORT_SYMBOL(__up);
+EXPORT_SYMBOL(__down);
+EXPORT_SYMBOL(__down_interruptible);
+
+EXPORT_SYMBOL(__udelay);
+EXPORT_SYMBOL(__ndelay);
+EXPORT_SYMBOL(__const_udelay);
+
+EXPORT_SYMBOL(__div64_32);
+
+#define DECLARE_EXPORT(name) extern void name(void);EXPORT_SYMBOL(name)
+
+/* These symbols are generated by the compiler itself */
+DECLARE_EXPORT(__udivsi3);
+DECLARE_EXPORT(__udivdi3);
+DECLARE_EXPORT(__sdivsi3);
+DECLARE_EXPORT(__ashrdi3);
+DECLARE_EXPORT(__ashldi3);
+DECLARE_EXPORT(__lshrdi3);
+DECLARE_EXPORT(__movstr);
+
+EXPORT_SYMBOL(strcpy);
+
+#ifdef CONFIG_CPU_SH4
+DECLARE_EXPORT(__movstr_i4_even);
+DECLARE_EXPORT(__movstr_i4_odd);
+DECLARE_EXPORT(__movstrSI12_i4);
+
+/* needed by some modules */
+EXPORT_SYMBOL(flush_cache_all);
+EXPORT_SYMBOL(flush_cache_range);
+EXPORT_SYMBOL(flush_dcache_page);
+EXPORT_SYMBOL(__flush_purge_region);
+#endif
+
+#if defined(CONFIG_SH7705_CACHE_32KB)
+EXPORT_SYMBOL(flush_cache_all);
+EXPORT_SYMBOL(flush_cache_range);
+EXPORT_SYMBOL(flush_dcache_page);
+EXPORT_SYMBOL(__flush_purge_region);
+#endif
+
+EXPORT_SYMBOL(flush_tlb_page);
+EXPORT_SYMBOL(__down_trylock);
+
+#ifdef CONFIG_SMP
+EXPORT_SYMBOL(synchronize_irq);
+#endif
+
+EXPORT_SYMBOL(csum_partial);
+EXPORT_SYMBOL(csum_ipv6_magic);
+EXPORT_SYMBOL(consistent_sync);
+EXPORT_SYMBOL(clear_page);
diff --git a/arch/sh/kernel/signal.c b/arch/sh/kernel/signal.c
new file mode 100644
index 00000000000..06f1b47eded
--- /dev/null
+++ b/arch/sh/kernel/signal.c
@@ -0,0 +1,607 @@
+/*
+ * linux/arch/sh/kernel/signal.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * 1997-11-28 Modified for POSIX.1b signals by Richard Henderson
+ *
+ * SuperH version: Copyright (C) 1999, 2000 Niibe Yutaka & Kaz Kojima
+ *
+ */
+
+#include <linux/sched.h>
+#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/tty.h>
+#include <linux/personality.h>
+#include <linux/binfmts.h>
+
+#include <asm/ucontext.h>
+#include <asm/uaccess.h>
+#include <asm/pgtable.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,
+ unsigned long r5, unsigned long r6, unsigned long r7,
+ struct pt_regs regs)
+{
+ sigset_t saveset;
+
+ mask &= _BLOCKABLE;
+ spin_lock_irq(&current->sighand->siglock);
+ saveset = current->blocked;
+ siginitset(&current->blocked, mask);
+ recalc_sigpending();
+ spin_unlock_irq(&current->sighand->siglock);
+
+ regs.regs[0] = -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,
+ unsigned long r6, unsigned long r7,
+ struct pt_regs regs)
+{
+ sigset_t saveset, newset;
+
+ /* XXX: Don't preclude handling different sized sigset_t's. */
+ if (sigsetsize != sizeof(sigset_t))
+ return -EINVAL;
+
+ if (copy_from_user(&newset, unewset, sizeof(newset)))
+ return -EFAULT;
+ sigdelsetmask(&newset, ~_BLOCKABLE);
+ spin_lock_irq(&current->sighand->siglock);
+ saveset = current->blocked;
+ current->blocked = newset;
+ recalc_sigpending();
+ spin_unlock_irq(&current->sighand->siglock);
+
+ regs.regs[0] = -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 __user *act,
+ struct old_sigaction __user *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 __user *uss, stack_t __user *uoss,
+ unsigned long r6, unsigned long r7,
+ struct pt_regs regs)
+{
+ return do_sigaltstack(uss, uoss, regs.regs[15]);
+}
+
+
+/*
+ * Do a signal return; undo the signal stack.
+ */
+
+#define MOVW(n) (0x9300|((n)-2)) /* Move mem word at PC+n to R3 */
+#define TRAP16 0xc310 /* Syscall w/no args (NR in R3) */
+#define OR_R0_R0 0x200b /* or r0,r0 (insert to avoid hardware bug) */
+
+struct sigframe
+{
+ struct sigcontext sc;
+ unsigned long extramask[_NSIG_WORDS-1];
+ u16 retcode[8];
+};
+
+struct rt_sigframe
+{
+ struct siginfo info;
+ struct ucontext uc;
+ u16 retcode[8];
+};
+
+#ifdef CONFIG_SH_FPU
+static inline int restore_sigcontext_fpu(struct sigcontext __user *sc)
+{
+ struct task_struct *tsk = current;
+
+ if (!(cpu_data->flags & CPU_HAS_FPU))
+ return 0;
+
+ set_used_math();
+ return __copy_from_user(&tsk->thread.fpu.hard, &sc->sc_fpregs[0],
+ sizeof(long)*(16*2+2));
+}
+
+static inline int save_sigcontext_fpu(struct sigcontext __user *sc,
+ struct pt_regs *regs)
+{
+ struct task_struct *tsk = current;
+
+ if (!(cpu_data->flags & CPU_HAS_FPU))
+ return 0;
+
+ if (!used_math()) {
+ __put_user(0, &sc->sc_ownedfp);
+ return 0;
+ }
+
+ __put_user(1, &sc->sc_ownedfp);
+
+ /* This will cause a "finit" to be triggered by the next
+ attempted FPU operation by the 'current' process.
+ */
+ clear_used_math();
+
+ unlazy_fpu(tsk, regs);
+ return __copy_to_user(&sc->sc_fpregs[0], &tsk->thread.fpu.hard,
+ sizeof(long)*(16*2+2));
+}
+#endif /* CONFIG_SH_FPU */
+
+static int
+restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc, int *r0_p)
+{
+ unsigned int err = 0;
+
+#define COPY(x) err |= __get_user(regs->x, &sc->sc_##x)
+ COPY(regs[1]);
+ COPY(regs[2]); COPY(regs[3]);
+ COPY(regs[4]); COPY(regs[5]);
+ COPY(regs[6]); COPY(regs[7]);
+ COPY(regs[8]); COPY(regs[9]);
+ COPY(regs[10]); COPY(regs[11]);
+ COPY(regs[12]); COPY(regs[13]);
+ COPY(regs[14]); COPY(regs[15]);
+ COPY(gbr); COPY(mach);
+ COPY(macl); COPY(pr);
+ COPY(sr); COPY(pc);
+#undef COPY
+
+#ifdef CONFIG_SH_FPU
+ if (cpu_data->flags & CPU_HAS_FPU) {
+ int owned_fp;
+ struct task_struct *tsk = current;
+
+ regs->sr |= SR_FD; /* Release FPU */
+ clear_fpu(tsk, regs);
+ clear_used_math();
+ __get_user (owned_fp, &sc->sc_ownedfp);
+ if (owned_fp)
+ err |= restore_sigcontext_fpu(sc);
+ }
+#endif
+
+ regs->tra = -1; /* disable syscall checks */
+ err |= __get_user(*r0_p, &sc->sc_regs[0]);
+ return err;
+}
+
+asmlinkage int sys_sigreturn(unsigned long r4, unsigned long r5,
+ unsigned long r6, unsigned long r7,
+ struct pt_regs regs)
+{
+ struct sigframe __user *frame = (struct sigframe __user *)regs.regs[15];
+ sigset_t set;
+ int r0;
+
+ if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
+ goto badframe;
+
+ if (__get_user(set.sig[0], &frame->sc.oldmask)
+ || (_NSIG_WORDS > 1
+ && __copy_from_user(&set.sig[1], &frame->extramask,
+ sizeof(frame->extramask))))
+ goto badframe;
+
+ sigdelsetmask(&set, ~_BLOCKABLE);
+
+ spin_lock_irq(&current->sighand->siglock);
+ current->blocked = set;
+ recalc_sigpending();
+ spin_unlock_irq(&current->sighand->siglock);
+
+ if (restore_sigcontext(&regs, &frame->sc, &r0))
+ goto badframe;
+ return r0;
+
+badframe:
+ force_sig(SIGSEGV, current);
+ return 0;
+}
+
+asmlinkage int sys_rt_sigreturn(unsigned long r4, unsigned long r5,
+ unsigned long r6, unsigned long r7,
+ struct pt_regs regs)
+{
+ struct rt_sigframe __user *frame = (struct rt_sigframe __user *)regs.regs[15];
+ sigset_t set;
+ stack_t st;
+ int r0;
+
+ if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
+ goto badframe;
+
+ if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set)))
+ goto badframe;
+
+ sigdelsetmask(&set, ~_BLOCKABLE);
+ spin_lock_irq(&current->sighand->siglock);
+ current->blocked = set;
+ recalc_sigpending();
+ spin_unlock_irq(&current->sighand->siglock);
+
+ if (restore_sigcontext(&regs, &frame->uc.uc_mcontext, &r0))
+ 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.regs[15]);
+
+ return r0;
+
+badframe:
+ force_sig(SIGSEGV, current);
+ return 0;
+}
+
+/*
+ * Set up a signal frame.
+ */
+
+static int
+setup_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs,
+ unsigned long mask)
+{
+ int err = 0;
+
+#define COPY(x) err |= __put_user(regs->x, &sc->sc_##x)
+ COPY(regs[0]); COPY(regs[1]);
+ COPY(regs[2]); COPY(regs[3]);
+ COPY(regs[4]); COPY(regs[5]);
+ COPY(regs[6]); COPY(regs[7]);
+ COPY(regs[8]); COPY(regs[9]);
+ COPY(regs[10]); COPY(regs[11]);
+ COPY(regs[12]); COPY(regs[13]);
+ COPY(regs[14]); COPY(regs[15]);
+ COPY(gbr); COPY(mach);
+ COPY(macl); COPY(pr);
+ COPY(sr); COPY(pc);
+#undef COPY
+
+#ifdef CONFIG_SH_FPU
+ err |= save_sigcontext_fpu(sc, regs);
+#endif
+
+ /* non-iBCS2 extensions.. */
+ err |= __put_user(mask, &sc->oldmask);
+
+ return err;
+}
+
+/*
+ * Determine which stack to use..
+ */
+static inline void __user *
+get_sigframe(struct k_sigaction *ka, unsigned long sp, size_t frame_size)
+{
+ if (ka->sa.sa_flags & SA_ONSTACK) {
+ if (sas_ss_flags(sp) == 0)
+ sp = current->sas_ss_sp + current->sas_ss_size;
+ }
+
+ return (void __user *)((sp - frame_size) & -8ul);
+}
+
+static void setup_frame(int sig, struct k_sigaction *ka,
+ sigset_t *set, struct pt_regs *regs)
+{
+ struct sigframe __user *frame;
+ int err = 0;
+ int signal;
+
+ frame = get_sigframe(ka, regs->regs[15], 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->pr = (unsigned long) ka->sa.sa_restorer;
+ } else {
+ /* Generate return code (system call to sigreturn) */
+ err |= __put_user(MOVW(7), &frame->retcode[0]);
+ err |= __put_user(TRAP16, &frame->retcode[1]);
+ err |= __put_user(OR_R0_R0, &frame->retcode[2]);
+ err |= __put_user(OR_R0_R0, &frame->retcode[3]);
+ err |= __put_user(OR_R0_R0, &frame->retcode[4]);
+ err |= __put_user(OR_R0_R0, &frame->retcode[5]);
+ err |= __put_user(OR_R0_R0, &frame->retcode[6]);
+ err |= __put_user((__NR_sigreturn), &frame->retcode[7]);
+ regs->pr = (unsigned long) frame->retcode;
+ }
+
+ if (err)
+ goto give_sigsegv;
+
+ /* Set up registers for signal handler */
+ regs->regs[15] = (unsigned long) frame;
+ regs->regs[4] = signal; /* Arg for signal handler */
+ regs->regs[5] = 0;
+ regs->regs[6] = (unsigned long) &frame->sc;
+ regs->pc = (unsigned long) ka->sa.sa_handler;
+
+ 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
+
+ flush_cache_sigtramp(regs->pr);
+ if ((-regs->pr & (L1_CACHE_BYTES-1)) < sizeof(frame->retcode))
+ flush_cache_sigtramp(regs->pr + L1_CACHE_BYTES);
+ 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 __user *frame;
+ int err = 0;
+ int signal;
+
+ frame = get_sigframe(ka, regs->regs[15], 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->regs[15]),
+ &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->pr = (unsigned long) ka->sa.sa_restorer;
+ } else {
+ /* Generate return code (system call to rt_sigreturn) */
+ err |= __put_user(MOVW(7), &frame->retcode[0]);
+ err |= __put_user(TRAP16, &frame->retcode[1]);
+ err |= __put_user(OR_R0_R0, &frame->retcode[2]);
+ err |= __put_user(OR_R0_R0, &frame->retcode[3]);
+ err |= __put_user(OR_R0_R0, &frame->retcode[4]);
+ err |= __put_user(OR_R0_R0, &frame->retcode[5]);
+ err |= __put_user(OR_R0_R0, &frame->retcode[6]);
+ err |= __put_user((__NR_rt_sigreturn), &frame->retcode[7]);
+ regs->pr = (unsigned long) frame->retcode;
+ }
+
+ if (err)
+ goto give_sigsegv;
+
+ /* Set up registers for signal handler */
+ regs->regs[15] = (unsigned long) frame;
+ regs->regs[4] = signal; /* Arg for signal handler */
+ regs->regs[5] = (unsigned long) &frame->info;
+ regs->regs[6] = (unsigned long) &frame->uc;
+ regs->pc = (unsigned long) ka->sa.sa_handler;
+
+ 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
+
+ flush_cache_sigtramp(regs->pr);
+ if ((-regs->pr & (L1_CACHE_BYTES-1)) < sizeof(frame->retcode))
+ flush_cache_sigtramp(regs->pr + L1_CACHE_BYTES);
+ return;
+
+give_sigsegv:
+ force_sigsegv(sig, current);
+}
+
+/*
+ * OK, we're invoking a handler
+ */
+
+static void
+handle_signal(unsigned long sig, struct k_sigaction *ka, siginfo_t *info,
+ sigset_t *oldset, struct pt_regs *regs)
+{
+ /* Are we from a system call? */
+ if (regs->tra >= 0) {
+ /* If so, check system call restarting.. */
+ switch (regs->regs[0]) {
+ case -ERESTARTNOHAND:
+ regs->regs[0] = -EINTR;
+ break;
+
+ case -ERESTARTSYS:
+ if (!(ka->sa.sa_flags & SA_RESTART)) {
+ regs->regs[0] = -EINTR;
+ break;
+ }
+ /* fallthrough */
+ case -ERESTARTNOINTR:
+ regs->pc -= 2;
+ }
+ } else {
+ /* gUSA handling */
+#ifdef CONFIG_PREEMPT
+ unsigned long flags;
+
+ local_irq_save(flags);
+#endif
+ if (regs->regs[15] >= 0xc0000000) {
+ int offset = (int)regs->regs[15];
+
+ /* Reset stack pointer: clear critical region mark */
+ regs->regs[15] = regs->regs[1];
+ if (regs->pc < regs->regs[0])
+ /* Go to rewind point #1 */
+ regs->pc = regs->regs[0] + offset - 2;
+ }
+#ifdef CONFIG_PREEMPT
+ local_irq_restore(flags);
+#endif
+ }
+
+ /* 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_ONESHOT)
+ ka->sa.sa_handler = SIG_DFL;
+
+ if (!(ka->sa.sa_flags & SA_NODEFER)) {
+ spin_lock_irq(&current->sighand->siglock);
+ sigorsets(&current->blocked,&current->blocked,&ka->sa.sa_mask);
+ sigaddset(&current->blocked,sig);
+ recalc_sigpending();
+ spin_unlock_irq(&current->sighand->siglock);
+ }
+}
+
+/*
+ * Note that 'init' is a special process: it doesn't get signals it doesn't
+ * want to handle. Thus you cannot kill init even with a SIGKILL even by
+ * mistake.
+ *
+ * Note that we go through the signals twice: once to check the signals that
+ * the kernel can handle, and then we build all the user-level signal handling
+ * stack-frames in one go after that.
+ */
+int do_signal(struct pt_regs *regs, sigset_t *oldset)
+{
+ siginfo_t info;
+ int signr;
+ struct k_sigaction ka;
+
+ /*
+ * We want the common case to go fast, which
+ * is why we may in certain cases get here from
+ * kernel mode. Just return without doing anything
+ * if so.
+ */
+ if (!user_mode(regs))
+ return 1;
+
+ if (try_to_freeze(0))
+ goto no_signal;
+
+ if (!oldset)
+ oldset = &current->blocked;
+
+ signr = get_signal_to_deliver(&info, &ka, regs, NULL);
+ if (signr > 0) {
+ /* Whee! Actually deliver the signal. */
+ handle_signal(signr, &ka, &info, oldset, regs);
+ return 1;
+ }
+
+ no_signal:
+ /* Did we come from a system call? */
+ if (regs->tra >= 0) {
+ /* Restart the system call - no handlers present */
+ if (regs->regs[0] == -ERESTARTNOHAND ||
+ regs->regs[0] == -ERESTARTSYS ||
+ regs->regs[0] == -ERESTARTNOINTR ||
+ regs->regs[0] == -ERESTART_RESTARTBLOCK) {
+ regs->pc -= 2;
+ }
+ }
+ return 0;
+}
diff --git a/arch/sh/kernel/smp.c b/arch/sh/kernel/smp.c
new file mode 100644
index 00000000000..56a39d69e08
--- /dev/null
+++ b/arch/sh/kernel/smp.c
@@ -0,0 +1,199 @@
+/*
+ * arch/sh/kernel/smp.c
+ *
+ * SMP support for the SuperH processors.
+ *
+ * Copyright (C) 2002, 2003 Paul Mundt
+ *
+ * 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.
+ */
+#include <linux/config.h>
+#include <linux/cache.h>
+#include <linux/cpumask.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/threads.h>
+#include <linux/module.h>
+#include <linux/time.h>
+#include <linux/timex.h>
+#include <linux/sched.h>
+
+#include <asm/atomic.h>
+#include <asm/processor.h>
+#include <asm/system.h>
+#include <asm/mmu_context.h>
+#include <asm/smp.h>
+
+/*
+ * This was written with the Sega Saturn (SMP SH-2 7604) in mind,
+ * but is designed to be usable regardless if there's an MMU
+ * present or not.
+ */
+struct sh_cpuinfo cpu_data[NR_CPUS];
+
+extern void per_cpu_trap_init(void);
+
+cpumask_t cpu_possible_map;
+cpumask_t cpu_online_map;
+static atomic_t cpus_booted = ATOMIC_INIT(0);
+
+/* These are defined by the board-specific code. */
+
+/*
+ * Cause the function described by call_data to be executed on the passed
+ * cpu. When the function has finished, increment the finished field of
+ * call_data.
+ */
+void __smp_send_ipi(unsigned int cpu, unsigned int action);
+
+/*
+ * Find the number of available processors
+ */
+unsigned int __smp_probe_cpus(void);
+
+/*
+ * Start a particular processor
+ */
+void __smp_slave_init(unsigned int cpu);
+
+/*
+ * Run specified function on a particular processor.
+ */
+void __smp_call_function(unsigned int cpu);
+
+static inline void __init smp_store_cpu_info(unsigned int cpu)
+{
+ cpu_data[cpu].loops_per_jiffy = loops_per_jiffy;
+}
+
+void __init smp_prepare_cpus(unsigned int max_cpus)
+{
+ unsigned int cpu = smp_processor_id();
+ int i;
+
+ atomic_set(&cpus_booted, 1);
+ smp_store_cpu_info(cpu);
+
+ for (i = 0; i < __smp_probe_cpus(); i++)
+ cpu_set(i, cpu_possible_map);
+}
+
+void __devinit smp_prepare_boot_cpu(void)
+{
+ unsigned int cpu = smp_processor_id();
+
+ cpu_set(cpu, cpu_online_map);
+ cpu_set(cpu, cpu_possible_map);
+}
+
+int __cpu_up(unsigned int cpu)
+{
+ struct task_struct *tsk;
+
+ tsk = fork_idle(cpu);
+
+ if (IS_ERR(tsk))
+ panic("Failed forking idle task for cpu %d\n", cpu);
+
+ tsk->thread_info->cpu = cpu;
+
+ cpu_set(cpu, cpu_online_map);
+
+ return 0;
+}
+
+int start_secondary(void *unused)
+{
+ unsigned int cpu = smp_processor_id();
+
+ atomic_inc(&init_mm.mm_count);
+ current->active_mm = &init_mm;
+
+ smp_store_cpu_info(cpu);
+
+ __smp_slave_init(cpu);
+ per_cpu_trap_init();
+
+ atomic_inc(&cpus_booted);
+
+ cpu_idle();
+ return 0;
+}
+
+void __init smp_cpus_done(unsigned int max_cpus)
+{
+ smp_mb();
+}
+
+void smp_send_reschedule(int cpu)
+{
+ __smp_send_ipi(cpu, SMP_MSG_RESCHEDULE);
+}
+
+static void stop_this_cpu(void *unused)
+{
+ cpu_clear(smp_processor_id(), cpu_online_map);
+ local_irq_disable();
+
+ for (;;)
+ cpu_relax();
+}
+
+void smp_send_stop(void)
+{
+ smp_call_function(stop_this_cpu, 0, 1, 0);
+}
+
+
+struct smp_fn_call_struct smp_fn_call = {
+ .lock = SPIN_LOCK_UNLOCKED,
+ .finished = ATOMIC_INIT(0),
+};
+
+/*
+ * The caller of this wants the passed function to run on every cpu. If wait
+ * is set, wait until all cpus have finished the function before returning.
+ * The lock is here to protect the call structure.
+ * You must not call this function with disabled interrupts or from a
+ * hardware interrupt handler or from a bottom half handler.
+ */
+int smp_call_function(void (*func)(void *info), void *info, int retry, int wait)
+{
+ unsigned int nr_cpus = atomic_read(&cpus_booted);
+ int i;
+
+ if (nr_cpus < 2)
+ return 0;
+
+ /* Can deadlock when called with interrupts disabled */
+ WARN_ON(irqs_disabled());
+
+ spin_lock(&smp_fn_call.lock);
+
+ atomic_set(&smp_fn_call.finished, 0);
+ smp_fn_call.fn = func;
+ smp_fn_call.data = info;
+
+ for (i = 0; i < nr_cpus; i++)
+ if (i != smp_processor_id())
+ __smp_call_function(i);
+
+ if (wait)
+ while (atomic_read(&smp_fn_call.finished) != (nr_cpus - 1));
+
+ spin_unlock(&smp_fn_call.lock);
+
+ return 0;
+}
+
+/* Not really SMP stuff ... */
+int setup_profiling_timer(unsigned int multiplier)
+{
+ return 0;
+}
+
diff --git a/arch/sh/kernel/sys_sh.c b/arch/sh/kernel/sys_sh.c
new file mode 100644
index 00000000000..df5ac294c37
--- /dev/null
+++ b/arch/sh/kernel/sys_sh.c
@@ -0,0 +1,289 @@
+/*
+ * linux/arch/sh/kernel/sys_sh.c
+ *
+ * This file contains various random system calls that
+ * have a non-standard calling sequence on the Linux/SuperH
+ * platform.
+ *
+ * Taken from i386 version.
+ */
+
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+#include <linux/sem.h>
+#include <linux/msg.h>
+#include <linux/shm.h>
+#include <linux/stat.h>
+#include <linux/syscalls.h>
+#include <linux/mman.h>
+#include <linux/file.h>
+#include <linux/utsname.h>
+
+#include <asm/uaccess.h>
+#include <asm/ipc.h>
+
+/*
+ * sys_pipe() is the normal C calling standard for creating
+ * a pipe. It's not the way Unix traditionally does this, though.
+ */
+asmlinkage int sys_pipe(unsigned long r4, unsigned long r5,
+ unsigned long r6, unsigned long r7,
+ struct pt_regs regs)
+{
+ int fd[2];
+ int error;
+
+ error = do_pipe(fd);
+ if (!error) {
+ regs.regs[1] = fd[1];
+ return fd[0];
+ }
+ return error;
+}
+
+#if defined(HAVE_ARCH_UNMAPPED_AREA)
+/*
+ * To avoid cache alias, we map the shard page with same color.
+ */
+#define COLOUR_ALIGN(addr) (((addr)+SHMLBA-1)&~(SHMLBA-1))
+
+unsigned long arch_get_unmapped_area(struct file *filp, unsigned long addr,
+ unsigned long len, unsigned long pgoff, unsigned long flags)
+{
+ struct mm_struct *mm = current->mm;
+ struct vm_area_struct *vma;
+ unsigned long start_addr;
+
+ if (flags & MAP_FIXED) {
+ /* We do not accept a shared mapping if it would violate
+ * cache aliasing constraints.
+ */
+ if ((flags & MAP_SHARED) && (addr & (SHMLBA - 1)))
+ return -EINVAL;
+ return addr;
+ }
+
+ if (len > TASK_SIZE)
+ return -ENOMEM;
+
+ if (addr) {
+ if (flags & MAP_PRIVATE)
+ addr = PAGE_ALIGN(addr);
+ else
+ addr = COLOUR_ALIGN(addr);
+ vma = find_vma(mm, addr);
+ if (TASK_SIZE - len >= addr &&
+ (!vma || addr + len <= vma->vm_start))
+ return addr;
+ }
+ if (flags & MAP_PRIVATE)
+ addr = PAGE_ALIGN(mm->free_area_cache);
+ else
+ addr = COLOUR_ALIGN(mm->free_area_cache);
+ start_addr = addr;
+
+full_search:
+ for (vma = find_vma(mm, addr); ; vma = vma->vm_next) {
+ /* At this point: (!vma || addr < vma->vm_end). */
+ if (TASK_SIZE - len < addr) {
+ /*
+ * Start a new search - just in case we missed
+ * some holes.
+ */
+ if (start_addr != TASK_UNMAPPED_BASE) {
+ start_addr = addr = TASK_UNMAPPED_BASE;
+ goto full_search;
+ }
+ return -ENOMEM;
+ }
+ if (!vma || addr + len <= vma->vm_start) {
+ /*
+ * Remember the place where we stopped the search:
+ */
+ mm->free_area_cache = addr + len;
+ return addr;
+ }
+ addr = vma->vm_end;
+ if (!(flags & MAP_PRIVATE))
+ addr = COLOUR_ALIGN(addr);
+ }
+}
+#endif
+
+static inline long
+do_mmap2(unsigned long addr, unsigned long len, unsigned long prot,
+ unsigned long flags, int fd, unsigned long pgoff)
+{
+ int error = -EBADF;
+ struct file *file = NULL;
+
+ flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
+ if (!(flags & MAP_ANONYMOUS)) {
+ file = fget(fd);
+ if (!file)
+ goto out;
+ }
+
+ down_write(&current->mm->mmap_sem);
+ error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
+ up_write(&current->mm->mmap_sem);
+
+ if (file)
+ fput(file);
+out:
+ return error;
+}
+
+asmlinkage int old_mmap(unsigned long addr, unsigned long len,
+ unsigned long prot, unsigned long flags,
+ int fd, unsigned long off)
+{
+ if (off & ~PAGE_MASK)
+ return -EINVAL;
+ return do_mmap2(addr, len, prot, flags, fd, off>>PAGE_SHIFT);
+}
+
+asmlinkage long sys_mmap2(unsigned long addr, unsigned long len,
+ unsigned long prot, unsigned long flags,
+ unsigned long fd, unsigned long pgoff)
+{
+ return do_mmap2(addr, len, prot, flags, fd, pgoff);
+}
+
+/*
+ * sys_ipc() is the de-multiplexer for the SysV IPC calls..
+ *
+ * This is really horribly ugly.
+ */
+asmlinkage int sys_ipc(uint call, int first, int second,
+ int third, void __user *ptr, long fifth)
+{
+ int version, ret;
+
+ version = call >> 16; /* hack for backward compatibility */
+ call &= 0xffff;
+
+ if (call <= SEMCTL)
+ switch (call) {
+ case SEMOP:
+ return sys_semtimedop(first, (struct sembuf __user *)ptr,
+ second, NULL);
+ case SEMTIMEDOP:
+ return sys_semtimedop(first, (struct sembuf __user *)ptr,
+ second,
+ (const struct timespec __user *)fifth);
+ case SEMGET:
+ return sys_semget (first, second, third);
+ case SEMCTL: {
+ union semun fourth;
+ if (!ptr)
+ return -EINVAL;
+ if (get_user(fourth.__pad, (void * __user *) ptr))
+ return -EFAULT;
+ return sys_semctl (first, second, third, fourth);
+ }
+ default:
+ return -EINVAL;
+ }
+
+ if (call <= MSGCTL)
+ switch (call) {
+ case MSGSND:
+ return sys_msgsnd (first, (struct msgbuf __user *) ptr,
+ second, third);
+ case MSGRCV:
+ switch (version) {
+ case 0: {
+ struct ipc_kludge tmp;
+ if (!ptr)
+ return -EINVAL;
+
+ if (copy_from_user(&tmp,
+ (struct ipc_kludge __user *) ptr,
+ sizeof (tmp)))
+ return -EFAULT;
+ return sys_msgrcv (first, tmp.msgp, second,
+ tmp.msgtyp, third);
+ }
+ default:
+ return sys_msgrcv (first,
+ (struct msgbuf __user *) ptr,
+ second, fifth, third);
+ }
+ case MSGGET:
+ return sys_msgget ((key_t) first, second);
+ case MSGCTL:
+ return sys_msgctl (first, second,
+ (struct msqid_ds __user *) ptr);
+ default:
+ return -EINVAL;
+ }
+ if (call <= SHMCTL)
+ switch (call) {
+ case SHMAT:
+ switch (version) {
+ default: {
+ ulong raddr;
+ ret = do_shmat (first, (char __user *) ptr,
+ second, &raddr);
+ if (ret)
+ return ret;
+ return put_user (raddr, (ulong __user *) third);
+ }
+ case 1: /* iBCS2 emulator entry point */
+ if (!segment_eq(get_fs(), get_ds()))
+ return -EINVAL;
+ return do_shmat (first, (char __user *) ptr,
+ second, (ulong *) third);
+ }
+ case SHMDT:
+ return sys_shmdt ((char __user *)ptr);
+ case SHMGET:
+ return sys_shmget (first, second, third);
+ case SHMCTL:
+ return sys_shmctl (first, second,
+ (struct shmid_ds __user *) ptr);
+ default:
+ return -EINVAL;
+ }
+
+ return -EINVAL;
+}
+
+asmlinkage int sys_uname(struct old_utsname * name)
+{
+ int err;
+ if (!name)
+ return -EFAULT;
+ down_read(&uts_sem);
+ err=copy_to_user(name, &system_utsname, sizeof (*name));
+ up_read(&uts_sem);
+ return err?-EFAULT:0;
+}
+
+asmlinkage ssize_t sys_pread_wrapper(unsigned int fd, char * buf,
+ size_t count, long dummy, loff_t pos)
+{
+ return sys_pread64(fd, buf, count, pos);
+}
+
+asmlinkage ssize_t sys_pwrite_wrapper(unsigned int fd, const char * buf,
+ size_t count, long dummy, loff_t pos)
+{
+ return sys_pwrite64(fd, buf, count, pos);
+}
+
+asmlinkage int sys_fadvise64_64_wrapper(int fd, u32 offset0, u32 offset1,
+ u32 len0, u32 len1, int advice)
+{
+#ifdef __LITTLE_ENDIAN__
+ return sys_fadvise64_64(fd, (u64)offset1 << 32 | offset0,
+ (u64)len1 << 32 | len0, advice);
+#else
+ return sys_fadvise64_64(fd, (u64)offset0 << 32 | offset1,
+ (u64)len0 << 32 | len1, advice);
+#endif
+}
diff --git a/arch/sh/kernel/time.c b/arch/sh/kernel/time.c
new file mode 100644
index 00000000000..df7a9b9d4cb
--- /dev/null
+++ b/arch/sh/kernel/time.c
@@ -0,0 +1,657 @@
+/*
+ * arch/sh/kernel/time.c
+ *
+ * Copyright (C) 1999 Tetsuya Okada & Niibe Yutaka
+ * Copyright (C) 2000 Philipp Rumpf <prumpf@tux.org>
+ * Copyright (C) 2002, 2003, 2004 Paul Mundt
+ * Copyright (C) 2002 M. R. Brown <mrbrown@linux-sh.org>
+ *
+ * Some code taken from i386 version.
+ * Copyright (C) 1991, 1992, 1995 Linus Torvalds
+ */
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/param.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/time.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/smp.h>
+#include <linux/profile.h>
+
+#include <asm/processor.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/delay.h>
+#include <asm/machvec.h>
+#include <asm/rtc.h>
+#include <asm/freq.h>
+#include <asm/cpu/timer.h>
+#ifdef CONFIG_SH_KGDB
+#include <asm/kgdb.h>
+#endif
+
+#include <linux/timex.h>
+#include <linux/irq.h>
+
+#define TMU_TOCR_INIT 0x00
+#define TMU0_TCR_INIT 0x0020
+#define TMU_TSTR_INIT 1
+
+#define TMU0_TCR_CALIB 0x0000
+
+#ifdef CONFIG_CPU_SUBTYPE_ST40STB1
+#define CLOCKGEN_MEMCLKCR 0xbb040038
+#define MEMCLKCR_RATIO_MASK 0x7
+#endif /* CONFIG_CPU_SUBTYPE_ST40STB1 */
+
+extern unsigned long wall_jiffies;
+#define TICK_SIZE (tick_nsec / 1000)
+DEFINE_SPINLOCK(tmu0_lock);
+
+u64 jiffies_64 = INITIAL_JIFFIES;
+
+EXPORT_SYMBOL(jiffies_64);
+
+/* XXX: Can we initialize this in a routine somewhere? Dreamcast doesn't want
+ * these routines anywhere... */
+#ifdef CONFIG_SH_RTC
+void (*rtc_get_time)(struct timespec *) = sh_rtc_gettimeofday;
+int (*rtc_set_time)(const time_t) = sh_rtc_settimeofday;
+#else
+void (*rtc_get_time)(struct timespec *);
+int (*rtc_set_time)(const time_t);
+#endif
+
+#if defined(CONFIG_CPU_SUBTYPE_SH7300)
+static int md_table[] = { 1, 2, 3, 4, 6, 8, 12 };
+#endif
+#if defined(CONFIG_CPU_SH3)
+static int stc_multipliers[] = { 1, 2, 3, 4, 6, 1, 1, 1 };
+static int stc_values[] = { 0, 1, 4, 2, 5, 0, 0, 0 };
+#define bfc_divisors stc_multipliers
+#define bfc_values stc_values
+static int ifc_divisors[] = { 1, 2, 3, 4, 1, 1, 1, 1 };
+static int ifc_values[] = { 0, 1, 4, 2, 0, 0, 0, 0 };
+static int pfc_divisors[] = { 1, 2, 3, 4, 6, 1, 1, 1 };
+static int pfc_values[] = { 0, 1, 4, 2, 5, 0, 0, 0 };
+#elif defined(CONFIG_CPU_SH4)
+#if defined(CONFIG_CPU_SUBTYPE_SH73180)
+static int ifc_divisors[] = { 1, 2, 3, 4, 6, 8, 12, 16 };
+static int ifc_values[] = { 0, 1, 2, 3, 4, 5, 6, 7 };
+#define bfc_divisors ifc_divisors /* Same */
+#define bfc_values ifc_values
+#define pfc_divisors ifc_divisors /* Same */
+#define pfc_values ifc_values
+#else
+static int ifc_divisors[] = { 1, 2, 3, 4, 6, 8, 1, 1 };
+static int ifc_values[] = { 0, 1, 2, 3, 0, 4, 0, 5 };
+#define bfc_divisors ifc_divisors /* Same */
+#define bfc_values ifc_values
+static int pfc_divisors[] = { 2, 3, 4, 6, 8, 2, 2, 2 };
+static int pfc_values[] = { 0, 0, 1, 2, 0, 3, 0, 4 };
+#endif
+#else
+#error "Unknown ifc/bfc/pfc/stc values for this processor"
+#endif
+
+/*
+ * Scheduler clock - returns current time in nanosec units.
+ */
+unsigned long long sched_clock(void)
+{
+ return (unsigned long long)jiffies * (1000000000 / HZ);
+}
+
+static unsigned long do_gettimeoffset(void)
+{
+ int count;
+ unsigned long flags;
+
+ static int count_p = 0x7fffffff; /* for the first call after boot */
+ static unsigned long jiffies_p = 0;
+
+ /*
+ * cache volatile jiffies temporarily; we have IRQs turned off.
+ */
+ unsigned long jiffies_t;
+
+ spin_lock_irqsave(&tmu0_lock, flags);
+ /* timer count may underflow right here */
+ count = ctrl_inl(TMU0_TCNT); /* read the latched count */
+
+ jiffies_t = jiffies;
+
+ /*
+ * avoiding timer inconsistencies (they are rare, but they happen)...
+ * there is one kind of problem that must be avoided here:
+ * 1. the timer counter underflows
+ */
+
+ if( jiffies_t == jiffies_p ) {
+ if( count > count_p ) {
+ /* the nutcase */
+
+ if(ctrl_inw(TMU0_TCR) & 0x100) { /* Check UNF bit */
+ /*
+ * We cannot detect lost timer interrupts ...
+ * well, that's why we call them lost, don't we? :)
+ * [hmm, on the Pentium and Alpha we can ... sort of]
+ */
+ count -= LATCH;
+ } else {
+ printk("do_slow_gettimeoffset(): hardware timer problem?\n");
+ }
+ }
+ } else
+ jiffies_p = jiffies_t;
+
+ count_p = count;
+ spin_unlock_irqrestore(&tmu0_lock, flags);
+
+ count = ((LATCH-1) - count) * TICK_SIZE;
+ count = (count + LATCH/2) / LATCH;
+
+ return count;
+}
+
+void do_gettimeofday(struct timeval *tv)
+{
+ unsigned long seq;
+ unsigned long usec, sec;
+ unsigned long lost;
+
+ do {
+ seq = read_seqbegin(&xtime_lock);
+ usec = do_gettimeoffset();
+
+ lost = jiffies - wall_jiffies;
+ if (lost)
+ usec += lost * (1000000 / HZ);
+
+ sec = xtime.tv_sec;
+ usec += xtime.tv_nsec / 1000;
+ } while (read_seqretry(&xtime_lock, seq));
+
+ while (usec >= 1000000) {
+ usec -= 1000000;
+ sec++;
+ }
+
+ tv->tv_sec = sec;
+ tv->tv_usec = usec;
+}
+
+EXPORT_SYMBOL(do_gettimeofday);
+
+int do_settimeofday(struct timespec *tv)
+{
+ time_t wtm_sec, sec = tv->tv_sec;
+ long wtm_nsec, nsec = tv->tv_nsec;
+
+ if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC)
+ return -EINVAL;
+
+ write_seqlock_irq(&xtime_lock);
+ /*
+ * This is revolting. We need to set "xtime" correctly. However, the
+ * value in this location is the value at the most recent update of
+ * wall time. Discover what correction gettimeofday() would have
+ * made, and then undo it!
+ */
+ nsec -= 1000 * (do_gettimeoffset() +
+ (jiffies - wall_jiffies) * (1000000 / HZ));
+
+ wtm_sec = wall_to_monotonic.tv_sec + (xtime.tv_sec - sec);
+ wtm_nsec = wall_to_monotonic.tv_nsec + (xtime.tv_nsec - nsec);
+
+ set_normalized_timespec(&xtime, sec, nsec);
+ set_normalized_timespec(&wall_to_monotonic, wtm_sec, wtm_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);
+
+/* last time the RTC clock got updated */
+static long last_rtc_update;
+
+/*
+ * timer_interrupt() needs to keep up the real-time clock,
+ * as well as call the "do_timer()" routine every clocktick
+ */
+static inline void do_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ do_timer(regs);
+#ifndef CONFIG_SMP
+ update_process_times(user_mode(regs));
+#endif
+ profile_tick(CPU_PROFILING, regs);
+
+#ifdef CONFIG_HEARTBEAT
+ if (sh_mv.mv_heartbeat != NULL)
+ sh_mv.mv_heartbeat();
+#endif
+
+ /*
+ * If we have an externally synchronized Linux clock, then update
+ * RTC 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 (rtc_set_time(xtime.tv_sec) == 0)
+ last_rtc_update = xtime.tv_sec;
+ else
+ last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */
+ }
+}
+
+/*
+ * This is the same as the above, except we _also_ save the current
+ * Time Stamp Counter value at the time of the timer interrupt, so that
+ * we later on can estimate the time of day more exactly.
+ */
+static irqreturn_t timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ unsigned long timer_status;
+
+ /* Clear UNF bit */
+ timer_status = ctrl_inw(TMU0_TCR);
+ timer_status &= ~0x100;
+ ctrl_outw(timer_status, TMU0_TCR);
+
+ /*
+ * Here we are in the timer irq handler. We just have irqs locally
+ * disabled but we don't know if the timer_bh is running on the other
+ * CPU. We need to avoid to SMP race with it. NOTE: we don' t need
+ * the irq version of write_lock because as just said we have irq
+ * locally disabled. -arca
+ */
+ write_seqlock(&xtime_lock);
+ do_timer_interrupt(irq, NULL, regs);
+ write_sequnlock(&xtime_lock);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * Hah! We'll see if this works (switching from usecs to nsecs).
+ */
+static unsigned int __init get_timer_frequency(void)
+{
+ u32 freq;
+ struct timespec ts1, ts2;
+ unsigned long diff_nsec;
+ unsigned long factor;
+
+ /* Setup the timer: We don't want to generate interrupts, just
+ * have it count down at its natural rate.
+ */
+ ctrl_outb(0, TMU_TSTR);
+#if !defined(CONFIG_CPU_SUBTYPE_SH7300)
+ ctrl_outb(TMU_TOCR_INIT, TMU_TOCR);
+#endif
+ ctrl_outw(TMU0_TCR_CALIB, TMU0_TCR);
+ ctrl_outl(0xffffffff, TMU0_TCOR);
+ ctrl_outl(0xffffffff, TMU0_TCNT);
+
+ rtc_get_time(&ts2);
+
+ do {
+ rtc_get_time(&ts1);
+ } while (ts1.tv_nsec == ts2.tv_nsec && ts1.tv_sec == ts2.tv_sec);
+
+ /* actually start the timer */
+ ctrl_outb(TMU_TSTR_INIT, TMU_TSTR);
+
+ do {
+ rtc_get_time(&ts2);
+ } while (ts1.tv_nsec == ts2.tv_nsec && ts1.tv_sec == ts2.tv_sec);
+
+ freq = 0xffffffff - ctrl_inl(TMU0_TCNT);
+ if (ts2.tv_nsec < ts1.tv_nsec) {
+ ts2.tv_nsec += 1000000000;
+ ts2.tv_sec--;
+ }
+
+ diff_nsec = (ts2.tv_sec - ts1.tv_sec) * 1000000000 + (ts2.tv_nsec - ts1.tv_nsec);
+
+ /* this should work well if the RTC has a precision of n Hz, where
+ * n is an integer. I don't think we have to worry about the other
+ * cases. */
+ factor = (1000000000 + diff_nsec/2) / diff_nsec;
+
+ if (factor * diff_nsec > 1100000000 ||
+ factor * diff_nsec < 900000000)
+ panic("weird RTC (diff_nsec %ld)", diff_nsec);
+
+ return freq * factor;
+}
+
+void (*board_time_init)(void);
+void (*board_timer_setup)(struct irqaction *irq);
+
+static unsigned int sh_pclk_freq __initdata = CONFIG_SH_PCLK_FREQ;
+
+static int __init sh_pclk_setup(char *str)
+{
+ unsigned int freq;
+
+ if (get_option(&str, &freq))
+ sh_pclk_freq = freq;
+
+ return 1;
+}
+__setup("sh_pclk=", sh_pclk_setup);
+
+static struct irqaction irq0 = { timer_interrupt, SA_INTERRUPT, CPU_MASK_NONE, "timer", NULL, NULL};
+
+void get_current_frequency_divisors(unsigned int *ifc, unsigned int *bfc, unsigned int *pfc)
+{
+ unsigned int frqcr = ctrl_inw(FRQCR);
+
+#if defined(CONFIG_CPU_SH3)
+#if defined(CONFIG_CPU_SUBTYPE_SH7300)
+ *ifc = md_table[((frqcr & 0x0070) >> 4)];
+ *bfc = md_table[((frqcr & 0x0700) >> 8)];
+ *pfc = md_table[frqcr & 0x0007];
+#elif defined(CONFIG_CPU_SUBTYPE_SH7705)
+ *bfc = stc_multipliers[(frqcr & 0x0300) >> 8];
+ *ifc = ifc_divisors[(frqcr & 0x0030) >> 4];
+ *pfc = pfc_divisors[frqcr & 0x0003];
+#else
+ unsigned int tmp;
+
+ tmp = (frqcr & 0x8000) >> 13;
+ tmp |= (frqcr & 0x0030) >> 4;
+ *bfc = stc_multipliers[tmp];
+ tmp = (frqcr & 0x4000) >> 12;
+ tmp |= (frqcr & 0x000c) >> 2;
+ *ifc = ifc_divisors[tmp];
+ tmp = (frqcr & 0x2000) >> 11;
+ tmp |= frqcr & 0x0003;
+ *pfc = pfc_divisors[tmp];
+#endif
+#elif defined(CONFIG_CPU_SH4)
+#if defined(CONFIG_CPU_SUBTYPE_SH73180)
+ *ifc = ifc_divisors[(frqcr>> 20) & 0x0007];
+ *bfc = bfc_divisors[(frqcr>> 12) & 0x0007];
+ *pfc = pfc_divisors[frqcr & 0x0007];
+#else
+ *ifc = ifc_divisors[(frqcr >> 6) & 0x0007];
+ *bfc = bfc_divisors[(frqcr >> 3) & 0x0007];
+ *pfc = pfc_divisors[frqcr & 0x0007];
+#endif
+#endif
+}
+
+/*
+ * This bit of ugliness builds up accessor routines to get at both
+ * the divisors and the physical values.
+ */
+#define _FREQ_TABLE(x) \
+ unsigned int get_##x##_divisor(unsigned int value) \
+ { return x##_divisors[value]; } \
+ \
+ unsigned int get_##x##_value(unsigned int divisor) \
+ { return x##_values[(divisor - 1)]; }
+
+_FREQ_TABLE(ifc);
+_FREQ_TABLE(bfc);
+_FREQ_TABLE(pfc);
+
+#ifdef CONFIG_CPU_SUBTYPE_ST40STB1
+
+/*
+ * The ST40 divisors are totally different so we set the cpu data
+ * clocks using a different algorithm
+ *
+ * I've just plugged this from the 2.4 code
+ * - Alex Bennee <kernel-hacker@bennee.com>
+ */
+#define CCN_PVR_CHIP_SHIFT 24
+#define CCN_PVR_CHIP_MASK 0xff
+#define CCN_PVR_CHIP_ST40STB1 0x4
+
+
+struct frqcr_data {
+ unsigned short frqcr;
+
+ struct {
+ unsigned char multiplier;
+ unsigned char divisor;
+ } factor[3];
+};
+
+static struct frqcr_data st40_frqcr_table[] = {
+ { 0x000, {{1,1}, {1,1}, {1,2}}},
+ { 0x002, {{1,1}, {1,1}, {1,4}}},
+ { 0x004, {{1,1}, {1,1}, {1,8}}},
+ { 0x008, {{1,1}, {1,2}, {1,2}}},
+ { 0x00A, {{1,1}, {1,2}, {1,4}}},
+ { 0x00C, {{1,1}, {1,2}, {1,8}}},
+ { 0x011, {{1,1}, {2,3}, {1,6}}},
+ { 0x013, {{1,1}, {2,3}, {1,3}}},
+ { 0x01A, {{1,1}, {1,2}, {1,4}}},
+ { 0x01C, {{1,1}, {1,2}, {1,8}}},
+ { 0x023, {{1,1}, {2,3}, {1,3}}},
+ { 0x02C, {{1,1}, {1,2}, {1,8}}},
+ { 0x048, {{1,2}, {1,2}, {1,4}}},
+ { 0x04A, {{1,2}, {1,2}, {1,6}}},
+ { 0x04C, {{1,2}, {1,2}, {1,8}}},
+ { 0x05A, {{1,2}, {1,3}, {1,6}}},
+ { 0x05C, {{1,2}, {1,3}, {1,6}}},
+ { 0x063, {{1,2}, {1,4}, {1,4}}},
+ { 0x06C, {{1,2}, {1,4}, {1,8}}},
+ { 0x091, {{1,3}, {1,3}, {1,6}}},
+ { 0x093, {{1,3}, {1,3}, {1,6}}},
+ { 0x0A3, {{1,3}, {1,6}, {1,6}}},
+ { 0x0DA, {{1,4}, {1,4}, {1,8}}},
+ { 0x0DC, {{1,4}, {1,4}, {1,8}}},
+ { 0x0EC, {{1,4}, {1,8}, {1,8}}},
+ { 0x123, {{1,4}, {1,4}, {1,8}}},
+ { 0x16C, {{1,4}, {1,8}, {1,8}}},
+};
+
+struct memclk_data {
+ unsigned char multiplier;
+ unsigned char divisor;
+};
+
+static struct memclk_data st40_memclk_table[8] = {
+ {1,1}, // 000
+ {1,2}, // 001
+ {1,3}, // 010
+ {2,3}, // 011
+ {1,4}, // 100
+ {1,6}, // 101
+ {1,8}, // 110
+ {1,8} // 111
+};
+
+static void st40_specific_time_init(unsigned int module_clock, unsigned short frqcr)
+{
+ unsigned int cpu_clock, master_clock, bus_clock, memory_clock;
+ struct frqcr_data *d;
+ int a;
+ unsigned long memclkcr;
+ struct memclk_data *e;
+
+ for (a = 0; a < ARRAY_SIZE(st40_frqcr_table); a++) {
+ d = &st40_frqcr_table[a];
+
+ if (d->frqcr == (frqcr & 0x1ff))
+ break;
+ }
+
+ if (a == ARRAY_SIZE(st40_frqcr_table)) {
+ d = st40_frqcr_table;
+
+ printk("ERROR: Unrecognised FRQCR value (0x%x), "
+ "using default multipliers\n", frqcr);
+ }
+
+ memclkcr = ctrl_inl(CLOCKGEN_MEMCLKCR);
+ e = &st40_memclk_table[memclkcr & MEMCLKCR_RATIO_MASK];
+
+ printk(KERN_INFO "Clock multipliers: CPU: %d/%d Bus: %d/%d "
+ "Mem: %d/%d Periph: %d/%d\n",
+ d->factor[0].multiplier, d->factor[0].divisor,
+ d->factor[1].multiplier, d->factor[1].divisor,
+ e->multiplier, e->divisor,
+ d->factor[2].multiplier, d->factor[2].divisor);
+
+ master_clock = module_clock * d->factor[2].divisor
+ / d->factor[2].multiplier;
+ bus_clock = master_clock * d->factor[1].multiplier
+ / d->factor[1].divisor;
+ memory_clock = master_clock * e->multiplier
+ / e->divisor;
+ cpu_clock = master_clock * d->factor[0].multiplier
+ / d->factor[0].divisor;
+
+ current_cpu_data.cpu_clock = cpu_clock;
+ current_cpu_data.master_clock = master_clock;
+ current_cpu_data.bus_clock = bus_clock;
+ current_cpu_data.memory_clock = memory_clock;
+ current_cpu_data.module_clock = module_clock;
+}
+#endif
+
+void __init time_init(void)
+{
+ unsigned int timer_freq = 0;
+ unsigned int ifc, pfc, bfc;
+ unsigned long interval;
+#ifdef CONFIG_CPU_SUBTYPE_ST40STB1
+ unsigned long pvr;
+ unsigned short frqcr;
+#endif
+
+ if (board_time_init)
+ board_time_init();
+
+ /*
+ * If we don't have an RTC (such as with the SH7300), don't attempt to
+ * probe the timer frequency. Rely on an either hardcoded peripheral
+ * clock value, or on the sh_pclk command line option. Note that we
+ * still need to have CONFIG_SH_PCLK_FREQ set in order for things like
+ * CLOCK_TICK_RATE to be sane.
+ */
+ current_cpu_data.module_clock = sh_pclk_freq;
+
+#ifdef CONFIG_SH_PCLK_CALC
+ /* XXX: Switch this over to a more generic test. */
+ {
+ unsigned int freq;
+
+ /*
+ * If we've specified a peripheral clock frequency, and we have
+ * an RTC, compare it against the autodetected value. Complain
+ * if there's a mismatch.
+ */
+ timer_freq = get_timer_frequency();
+ freq = timer_freq * 4;
+
+ if (sh_pclk_freq && (sh_pclk_freq/100*99 > freq || sh_pclk_freq/100*101 < freq)) {
+ printk(KERN_NOTICE "Calculated peripheral clock value "
+ "%d differs from sh_pclk value %d, fixing..\n",
+ freq, sh_pclk_freq);
+ current_cpu_data.module_clock = freq;
+ }
+ }
+#endif
+
+#ifdef CONFIG_CPU_SUBTYPE_ST40STB1
+ /* XXX: Update ST40 code to use board_time_init() */
+ pvr = ctrl_inl(CCN_PVR);
+ frqcr = ctrl_inw(FRQCR);
+ printk("time.c ST40 Probe: PVR %08lx, FRQCR %04hx\n", pvr, frqcr);
+
+ if (((pvr >> CCN_PVR_CHIP_SHIFT) & CCN_PVR_CHIP_MASK) == CCN_PVR_CHIP_ST40STB1)
+ st40_specific_time_init(current_cpu_data.module_clock, frqcr);
+ else
+#endif
+ get_current_frequency_divisors(&ifc, &bfc, &pfc);
+
+ if (rtc_get_time) {
+ rtc_get_time(&xtime);
+ } else {
+ xtime.tv_sec = mktime(2000, 1, 1, 0, 0, 0);
+ xtime.tv_nsec = 0;
+ }
+
+ set_normalized_timespec(&wall_to_monotonic,
+ -xtime.tv_sec, -xtime.tv_nsec);
+
+ if (board_timer_setup) {
+ board_timer_setup(&irq0);
+ } else {
+ setup_irq(TIMER_IRQ, &irq0);
+ }
+
+ /*
+ * for ST40 chips the current_cpu_data should already be set
+ * so not having valid pfc/bfc/ifc shouldn't be a problem
+ */
+ if (!current_cpu_data.master_clock)
+ current_cpu_data.master_clock = current_cpu_data.module_clock * pfc;
+ if (!current_cpu_data.bus_clock)
+ current_cpu_data.bus_clock = current_cpu_data.master_clock / bfc;
+ if (!current_cpu_data.cpu_clock)
+ current_cpu_data.cpu_clock = current_cpu_data.master_clock / ifc;
+
+ printk("CPU clock: %d.%02dMHz\n",
+ (current_cpu_data.cpu_clock / 1000000),
+ (current_cpu_data.cpu_clock % 1000000)/10000);
+ printk("Bus clock: %d.%02dMHz\n",
+ (current_cpu_data.bus_clock / 1000000),
+ (current_cpu_data.bus_clock % 1000000)/10000);
+#ifdef CONFIG_CPU_SUBTYPE_ST40STB1
+ printk("Memory clock: %d.%02dMHz\n",
+ (current_cpu_data.memory_clock / 1000000),
+ (current_cpu_data.memory_clock % 1000000)/10000);
+#endif
+ printk("Module clock: %d.%02dMHz\n",
+ (current_cpu_data.module_clock / 1000000),
+ (current_cpu_data.module_clock % 1000000)/10000);
+
+ interval = (current_cpu_data.module_clock/4 + HZ/2) / HZ;
+
+ printk("Interval = %ld\n", interval);
+
+ /* Start TMU0 */
+ ctrl_outb(0, TMU_TSTR);
+#if !defined(CONFIG_CPU_SUBTYPE_SH7300)
+ ctrl_outb(TMU_TOCR_INIT, TMU_TOCR);
+#endif
+ ctrl_outw(TMU0_TCR_INIT, TMU0_TCR);
+ ctrl_outl(interval, TMU0_TCOR);
+ ctrl_outl(interval, TMU0_TCNT);
+ ctrl_outb(TMU_TSTR_INIT, TMU_TSTR);
+
+#if defined(CONFIG_SH_KGDB)
+ /*
+ * Set up kgdb as requested. We do it here because the serial
+ * init uses the timer vars we just set up for figuring baud.
+ */
+ kgdb_init();
+#endif
+}
diff --git a/arch/sh/kernel/traps.c b/arch/sh/kernel/traps.c
new file mode 100644
index 00000000000..7eb06719d84
--- /dev/null
+++ b/arch/sh/kernel/traps.c
@@ -0,0 +1,712 @@
+/* $Id: traps.c,v 1.17 2004/05/02 01:46:30 sugioka Exp $
+ *
+ * linux/arch/sh/traps.c
+ *
+ * SuperH version: Copyright (C) 1999 Niibe Yutaka
+ * Copyright (C) 2000 Philipp Rumpf
+ * Copyright (C) 2000 David Howells
+ * Copyright (C) 2002, 2003 Paul Mundt
+ */
+
+/*
+ * 'Traps.c' handles hardware traps and faults after we have saved some
+ * state in 'entry.S'.
+ */
+#include <linux/config.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/ptrace.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+#include <linux/module.h>
+#include <linux/kallsyms.h>
+
+#include <asm/system.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/atomic.h>
+#include <asm/processor.h>
+#include <asm/sections.h>
+
+#ifdef CONFIG_SH_KGDB
+#include <asm/kgdb.h>
+#define CHK_REMOTE_DEBUG(regs) \
+{ \
+ if ((kgdb_debug_hook != (kgdb_debug_hook_t *) NULL) && (!user_mode(regs))) \
+ { \
+ (*kgdb_debug_hook)(regs); \
+ } \
+}
+#else
+#define CHK_REMOTE_DEBUG(regs)
+#endif
+
+#define DO_ERROR(trapnr, signr, str, name, tsk) \
+asmlinkage void do_##name(unsigned long r4, unsigned long r5, \
+ unsigned long r6, unsigned long r7, \
+ struct pt_regs regs) \
+{ \
+ unsigned long error_code; \
+ \
+ /* Check if it's a DSP instruction */ \
+ if (is_dsp_inst(&regs)) { \
+ /* Enable DSP mode, and restart instruction. */ \
+ regs.sr |= SR_DSP; \
+ return; \
+ } \
+ \
+ asm volatile("stc r2_bank, %0": "=r" (error_code)); \
+ local_irq_enable(); \
+ tsk->thread.error_code = error_code; \
+ tsk->thread.trap_no = trapnr; \
+ CHK_REMOTE_DEBUG(&regs); \
+ force_sig(signr, tsk); \
+ die_if_no_fixup(str,&regs,error_code); \
+}
+
+#ifdef CONFIG_CPU_SH2
+#define TRAP_RESERVED_INST 4
+#define TRAP_ILLEGAL_SLOT_INST 6
+#else
+#define TRAP_RESERVED_INST 12
+#define TRAP_ILLEGAL_SLOT_INST 13
+#endif
+
+/*
+ * These constants are for searching for possible module text
+ * segments. VMALLOC_OFFSET comes from mm/vmalloc.c; MODULE_RANGE is
+ * a guess of how much space is likely to be vmalloced.
+ */
+#define VMALLOC_OFFSET (8*1024*1024)
+#define MODULE_RANGE (8*1024*1024)
+
+spinlock_t die_lock;
+
+void die(const char * str, struct pt_regs * regs, long err)
+{
+ static int die_counter;
+
+ console_verbose();
+ spin_lock_irq(&die_lock);
+ printk("%s: %04lx [#%d]\n", str, err & 0xffff, ++die_counter);
+ CHK_REMOTE_DEBUG(regs);
+ show_regs(regs);
+ spin_unlock_irq(&die_lock);
+ do_exit(SIGSEGV);
+}
+
+static inline void die_if_kernel(const char * str, struct pt_regs * regs, long err)
+{
+ if (!user_mode(regs))
+ die(str, regs, err);
+}
+
+static int handle_unaligned_notify_count = 10;
+
+/*
+ * try and fix up kernelspace address errors
+ * - userspace errors just cause EFAULT to be returned, resulting in SEGV
+ * - kernel/userspace interfaces cause a jump to an appropriate handler
+ * - other kernel errors are bad
+ * - return 0 if fixed-up, -EFAULT if non-fatal (to the kernel) fault
+ */
+static int die_if_no_fixup(const char * str, struct pt_regs * regs, long err)
+{
+ if (!user_mode(regs))
+ {
+ const struct exception_table_entry *fixup;
+ fixup = search_exception_tables(regs->pc);
+ if (fixup) {
+ regs->pc = fixup->fixup;
+ return 0;
+ }
+ die(str, regs, err);
+ }
+ return -EFAULT;
+}
+
+/*
+ * handle an instruction that does an unaligned memory access by emulating the
+ * desired behaviour
+ * - note that PC _may not_ point to the faulting instruction
+ * (if that instruction is in a branch delay slot)
+ * - return 0 if emulation okay, -EFAULT on existential error
+ */
+static int handle_unaligned_ins(u16 instruction, struct pt_regs *regs)
+{
+ int ret, index, count;
+ unsigned long *rm, *rn;
+ unsigned char *src, *dst;
+
+ index = (instruction>>8)&15; /* 0x0F00 */
+ rn = &regs->regs[index];
+
+ index = (instruction>>4)&15; /* 0x00F0 */
+ rm = &regs->regs[index];
+
+ count = 1<<(instruction&3);
+
+ ret = -EFAULT;
+ switch (instruction>>12) {
+ case 0: /* mov.[bwl] to/from memory via r0+rn */
+ if (instruction & 8) {
+ /* from memory */
+ src = (unsigned char*) *rm;
+ src += regs->regs[0];
+ dst = (unsigned char*) rn;
+ *(unsigned long*)dst = 0;
+
+#ifdef __LITTLE_ENDIAN__
+ if (copy_from_user(dst, src, count))
+ goto fetch_fault;
+
+ if ((count == 2) && dst[1] & 0x80) {
+ dst[2] = 0xff;
+ dst[3] = 0xff;
+ }
+#else
+ dst += 4-count;
+
+ if (__copy_user(dst, src, count))
+ goto fetch_fault;
+
+ if ((count == 2) && dst[2] & 0x80) {
+ dst[0] = 0xff;
+ dst[1] = 0xff;
+ }
+#endif
+ } else {
+ /* to memory */
+ src = (unsigned char*) rm;
+#if !defined(__LITTLE_ENDIAN__)
+ src += 4-count;
+#endif
+ dst = (unsigned char*) *rn;
+ dst += regs->regs[0];
+
+ if (copy_to_user(dst, src, count))
+ goto fetch_fault;
+ }
+ ret = 0;
+ break;
+
+ case 1: /* mov.l Rm,@(disp,Rn) */
+ src = (unsigned char*) rm;
+ dst = (unsigned char*) *rn;
+ dst += (instruction&0x000F)<<2;
+
+ if (copy_to_user(dst,src,4))
+ goto fetch_fault;
+ ret = 0;
+ break;
+
+ case 2: /* mov.[bwl] to memory, possibly with pre-decrement */
+ if (instruction & 4)
+ *rn -= count;
+ src = (unsigned char*) rm;
+ dst = (unsigned char*) *rn;
+#if !defined(__LITTLE_ENDIAN__)
+ src += 4-count;
+#endif
+ if (copy_to_user(dst, src, count))
+ goto fetch_fault;
+ ret = 0;
+ break;
+
+ case 5: /* mov.l @(disp,Rm),Rn */
+ src = (unsigned char*) *rm;
+ src += (instruction&0x000F)<<2;
+ dst = (unsigned char*) rn;
+ *(unsigned long*)dst = 0;
+
+ if (copy_from_user(dst,src,4))
+ goto fetch_fault;
+ ret = 0;
+ break;
+
+ case 6: /* mov.[bwl] from memory, possibly with post-increment */
+ src = (unsigned char*) *rm;
+ if (instruction & 4)
+ *rm += count;
+ dst = (unsigned char*) rn;
+ *(unsigned long*)dst = 0;
+
+#ifdef __LITTLE_ENDIAN__
+ if (copy_from_user(dst, src, count))
+ goto fetch_fault;
+
+ if ((count == 2) && dst[1] & 0x80) {
+ dst[2] = 0xff;
+ dst[3] = 0xff;
+ }
+#else
+ dst += 4-count;
+
+ if (copy_from_user(dst, src, count))
+ goto fetch_fault;
+
+ if ((count == 2) && dst[2] & 0x80) {
+ dst[0] = 0xff;
+ dst[1] = 0xff;
+ }
+#endif
+ ret = 0;
+ break;
+
+ case 8:
+ switch ((instruction&0xFF00)>>8) {
+ case 0x81: /* mov.w R0,@(disp,Rn) */
+ src = (unsigned char*) &regs->regs[0];
+#if !defined(__LITTLE_ENDIAN__)
+ src += 2;
+#endif
+ dst = (unsigned char*) *rm; /* called Rn in the spec */
+ dst += (instruction&0x000F)<<1;
+
+ if (copy_to_user(dst, src, 2))
+ goto fetch_fault;
+ ret = 0;
+ break;
+
+ case 0x85: /* mov.w @(disp,Rm),R0 */
+ src = (unsigned char*) *rm;
+ src += (instruction&0x000F)<<1;
+ dst = (unsigned char*) &regs->regs[0];
+ *(unsigned long*)dst = 0;
+
+#if !defined(__LITTLE_ENDIAN__)
+ dst += 2;
+#endif
+
+ if (copy_from_user(dst, src, 2))
+ goto fetch_fault;
+
+#ifdef __LITTLE_ENDIAN__
+ if (dst[1] & 0x80) {
+ dst[2] = 0xff;
+ dst[3] = 0xff;
+ }
+#else
+ if (dst[2] & 0x80) {
+ dst[0] = 0xff;
+ dst[1] = 0xff;
+ }
+#endif
+ ret = 0;
+ break;
+ }
+ break;
+ }
+ return ret;
+
+ fetch_fault:
+ /* Argh. Address not only misaligned but also non-existent.
+ * Raise an EFAULT and see if it's trapped
+ */
+ return die_if_no_fixup("Fault in unaligned fixup", regs, 0);
+}
+
+/*
+ * emulate the instruction in the delay slot
+ * - fetches the instruction from PC+2
+ */
+static inline int handle_unaligned_delayslot(struct pt_regs *regs)
+{
+ u16 instruction;
+
+ if (copy_from_user(&instruction, (u16 *)(regs->pc+2), 2)) {
+ /* the instruction-fetch faulted */
+ if (user_mode(regs))
+ return -EFAULT;
+
+ /* kernel */
+ die("delay-slot-insn faulting in handle_unaligned_delayslot", regs, 0);
+ }
+
+ return handle_unaligned_ins(instruction,regs);
+}
+
+/*
+ * handle an instruction that does an unaligned memory access
+ * - have to be careful of branch delay-slot instructions that fault
+ * SH3:
+ * - if the branch would be taken PC points to the branch
+ * - if the branch would not be taken, PC points to delay-slot
+ * SH4:
+ * - PC always points to delayed branch
+ * - return 0 if handled, -EFAULT if failed (may not return if in kernel)
+ */
+
+/* Macros to determine offset from current PC for branch instructions */
+/* Explicit type coercion is used to force sign extension where needed */
+#define SH_PC_8BIT_OFFSET(instr) ((((signed char)(instr))*2) + 4)
+#define SH_PC_12BIT_OFFSET(instr) ((((signed short)(instr<<4))>>3) + 4)
+
+static int handle_unaligned_access(u16 instruction, struct pt_regs *regs)
+{
+ u_int rm;
+ int ret, index;
+
+ index = (instruction>>8)&15; /* 0x0F00 */
+ rm = regs->regs[index];
+
+ /* shout about the first ten userspace fixups */
+ if (user_mode(regs) && handle_unaligned_notify_count>0) {
+ handle_unaligned_notify_count--;
+
+ printk("Fixing up unaligned userspace access in \"%s\" pid=%d pc=0x%p ins=0x%04hx\n",
+ current->comm,current->pid,(u16*)regs->pc,instruction);
+ }
+
+ ret = -EFAULT;
+ switch (instruction&0xF000) {
+ case 0x0000:
+ if (instruction==0x000B) {
+ /* rts */
+ ret = handle_unaligned_delayslot(regs);
+ if (ret==0)
+ regs->pc = regs->pr;
+ }
+ else if ((instruction&0x00FF)==0x0023) {
+ /* braf @Rm */
+ ret = handle_unaligned_delayslot(regs);
+ if (ret==0)
+ regs->pc += rm + 4;
+ }
+ else if ((instruction&0x00FF)==0x0003) {
+ /* bsrf @Rm */
+ ret = handle_unaligned_delayslot(regs);
+ if (ret==0) {
+ regs->pr = regs->pc + 4;
+ regs->pc += rm + 4;
+ }
+ }
+ else {
+ /* mov.[bwl] to/from memory via r0+rn */
+ goto simple;
+ }
+ break;
+
+ case 0x1000: /* mov.l Rm,@(disp,Rn) */
+ goto simple;
+
+ case 0x2000: /* mov.[bwl] to memory, possibly with pre-decrement */
+ goto simple;
+
+ case 0x4000:
+ if ((instruction&0x00FF)==0x002B) {
+ /* jmp @Rm */
+ ret = handle_unaligned_delayslot(regs);
+ if (ret==0)
+ regs->pc = rm;
+ }
+ else if ((instruction&0x00FF)==0x000B) {
+ /* jsr @Rm */
+ ret = handle_unaligned_delayslot(regs);
+ if (ret==0) {
+ regs->pr = regs->pc + 4;
+ regs->pc = rm;
+ }
+ }
+ else {
+ /* mov.[bwl] to/from memory via r0+rn */
+ goto simple;
+ }
+ break;
+
+ case 0x5000: /* mov.l @(disp,Rm),Rn */
+ goto simple;
+
+ case 0x6000: /* mov.[bwl] from memory, possibly with post-increment */
+ goto simple;
+
+ case 0x8000: /* bf lab, bf/s lab, bt lab, bt/s lab */
+ switch (instruction&0x0F00) {
+ case 0x0100: /* mov.w R0,@(disp,Rm) */
+ goto simple;
+ case 0x0500: /* mov.w @(disp,Rm),R0 */
+ goto simple;
+ case 0x0B00: /* bf lab - no delayslot*/
+ break;
+ case 0x0F00: /* bf/s lab */
+ ret = handle_unaligned_delayslot(regs);
+ if (ret==0) {
+#if defined(CONFIG_CPU_SH4) || defined(CONFIG_SH7705_CACHE_32KB)
+ if ((regs->sr & 0x00000001) != 0)
+ regs->pc += 4; /* next after slot */
+ else
+#endif
+ regs->pc += SH_PC_8BIT_OFFSET(instruction);
+ }
+ break;
+ case 0x0900: /* bt lab - no delayslot */
+ break;
+ case 0x0D00: /* bt/s lab */
+ ret = handle_unaligned_delayslot(regs);
+ if (ret==0) {
+#if defined(CONFIG_CPU_SH4) || defined(CONFIG_SH7705_CACHE_32KB)
+ if ((regs->sr & 0x00000001) == 0)
+ regs->pc += 4; /* next after slot */
+ else
+#endif
+ regs->pc += SH_PC_8BIT_OFFSET(instruction);
+ }
+ break;
+ }
+ break;
+
+ case 0xA000: /* bra label */
+ ret = handle_unaligned_delayslot(regs);
+ if (ret==0)
+ regs->pc += SH_PC_12BIT_OFFSET(instruction);
+ break;
+
+ case 0xB000: /* bsr label */
+ ret = handle_unaligned_delayslot(regs);
+ if (ret==0) {
+ regs->pr = regs->pc + 4;
+ regs->pc += SH_PC_12BIT_OFFSET(instruction);
+ }
+ break;
+ }
+ return ret;
+
+ /* handle non-delay-slot instruction */
+ simple:
+ ret = handle_unaligned_ins(instruction,regs);
+ if (ret==0)
+ regs->pc += 2;
+ return ret;
+}
+
+/*
+ * Handle various address error exceptions
+ */
+asmlinkage void do_address_error(struct pt_regs *regs,
+ unsigned long writeaccess,
+ unsigned long address)
+{
+ unsigned long error_code;
+ mm_segment_t oldfs;
+ u16 instruction;
+ int tmp;
+
+ asm volatile("stc r2_bank,%0": "=r" (error_code));
+
+ oldfs = get_fs();
+
+ if (user_mode(regs)) {
+ local_irq_enable();
+ current->thread.error_code = error_code;
+ current->thread.trap_no = (writeaccess) ? 8 : 7;
+
+ /* bad PC is not something we can fix */
+ if (regs->pc & 1)
+ goto uspace_segv;
+
+ set_fs(USER_DS);
+ if (copy_from_user(&instruction, (u16 *)(regs->pc), 2)) {
+ /* Argh. Fault on the instruction itself.
+ This should never happen non-SMP
+ */
+ set_fs(oldfs);
+ goto uspace_segv;
+ }
+
+ tmp = handle_unaligned_access(instruction, regs);
+ set_fs(oldfs);
+
+ if (tmp==0)
+ return; /* sorted */
+
+ uspace_segv:
+ printk(KERN_NOTICE "Killing process \"%s\" due to unaligned access\n", current->comm);
+ force_sig(SIGSEGV, current);
+ } else {
+ if (regs->pc & 1)
+ die("unaligned program counter", regs, error_code);
+
+ set_fs(KERNEL_DS);
+ if (copy_from_user(&instruction, (u16 *)(regs->pc), 2)) {
+ /* Argh. Fault on the instruction itself.
+ This should never happen non-SMP
+ */
+ set_fs(oldfs);
+ die("insn faulting in do_address_error", regs, 0);
+ }
+
+ handle_unaligned_access(instruction, regs);
+ set_fs(oldfs);
+ }
+}
+
+#ifdef CONFIG_SH_DSP
+/*
+ * SH-DSP support gerg@snapgear.com.
+ */
+int is_dsp_inst(struct pt_regs *regs)
+{
+ unsigned short inst;
+
+ /*
+ * Safe guard if DSP mode is already enabled or we're lacking
+ * the DSP altogether.
+ */
+ if (!(cpu_data->flags & CPU_HAS_DSP) || (regs->sr & SR_DSP))
+ return 0;
+
+ get_user(inst, ((unsigned short *) regs->pc));
+
+ inst &= 0xf000;
+
+ /* Check for any type of DSP or support instruction */
+ if ((inst == 0xf000) || (inst == 0x4000))
+ return 1;
+
+ return 0;
+}
+#else
+#define is_dsp_inst(regs) (0)
+#endif /* CONFIG_SH_DSP */
+
+DO_ERROR(TRAP_RESERVED_INST, SIGILL, "reserved instruction", reserved_inst, current)
+DO_ERROR(TRAP_ILLEGAL_SLOT_INST, SIGILL, "illegal slot instruction", illegal_slot_inst, current)
+
+asmlinkage void do_exception_error(unsigned long r4, unsigned long r5,
+ unsigned long r6, unsigned long r7,
+ struct pt_regs regs)
+{
+ long ex;
+ asm volatile("stc r2_bank, %0" : "=r" (ex));
+ die_if_kernel("exception", &regs, ex);
+}
+
+#if defined(CONFIG_SH_STANDARD_BIOS)
+void *gdb_vbr_vector;
+
+static inline void __init gdb_vbr_init(void)
+{
+ register unsigned long vbr;
+
+ /*
+ * Read the old value of the VBR register to initialise
+ * the vector through which debug and BIOS traps are
+ * delegated by the Linux trap handler.
+ */
+ asm volatile("stc vbr, %0" : "=r" (vbr));
+
+ gdb_vbr_vector = (void *)(vbr + 0x100);
+ printk("Setting GDB trap vector to 0x%08lx\n",
+ (unsigned long)gdb_vbr_vector);
+}
+#endif
+
+void __init per_cpu_trap_init(void)
+{
+ extern void *vbr_base;
+
+#ifdef CONFIG_SH_STANDARD_BIOS
+ gdb_vbr_init();
+#endif
+
+ /* NOTE: The VBR value should be at P1
+ (or P2, virtural "fixed" address space).
+ It's definitely should not in physical address. */
+
+ asm volatile("ldc %0, vbr"
+ : /* no output */
+ : "r" (&vbr_base)
+ : "memory");
+}
+
+void __init trap_init(void)
+{
+ extern void *exception_handling_table[];
+
+ exception_handling_table[TRAP_RESERVED_INST]
+ = (void *)do_reserved_inst;
+ exception_handling_table[TRAP_ILLEGAL_SLOT_INST]
+ = (void *)do_illegal_slot_inst;
+
+#ifdef CONFIG_CPU_SH4
+ if (!(cpu_data->flags & CPU_HAS_FPU)) {
+ /* For SH-4 lacking an FPU, treat floating point instructions
+ as reserved. */
+ /* entry 64 corresponds to EXPEVT=0x800 */
+ exception_handling_table[64] = (void *)do_reserved_inst;
+ exception_handling_table[65] = (void *)do_illegal_slot_inst;
+ }
+#endif
+
+ /* Setup VBR for boot cpu */
+ per_cpu_trap_init();
+}
+
+void show_stack(struct task_struct *tsk, unsigned long *sp)
+{
+ unsigned long *stack, addr;
+ unsigned long module_start = VMALLOC_START;
+ unsigned long module_end = VMALLOC_END;
+ int i = 1;
+
+ if (tsk && !sp) {
+ sp = (unsigned long *)tsk->thread.sp;
+ }
+
+ if (!sp) {
+ __asm__ __volatile__ (
+ "mov r15, %0\n\t"
+ "stc r7_bank, %1\n\t"
+ : "=r" (module_start),
+ "=r" (module_end)
+ );
+
+ sp = (unsigned long *)module_start;
+ }
+
+ stack = sp;
+
+ printk("\nCall trace: ");
+#ifdef CONFIG_KALLSYMS
+ printk("\n");
+#endif
+
+ while (!kstack_end(stack)) {
+ addr = *stack++;
+ if (((addr >= (unsigned long)_text) &&
+ (addr <= (unsigned long)_etext)) ||
+ ((addr >= module_start) && (addr <= module_end))) {
+ /*
+ * For 80-columns display, 6 entry is maximum.
+ * NOTE: '[<8c00abcd>] ' consumes 13 columns .
+ */
+#ifndef CONFIG_KALLSYMS
+ if (i && ((i % 6) == 0))
+ printk("\n ");
+#endif
+ printk("[<%08lx>] ", addr);
+ print_symbol("%s\n", addr);
+ i++;
+ }
+ }
+
+ printk("\n");
+}
+
+void show_task(unsigned long *sp)
+{
+ show_stack(NULL, sp);
+}
+
+void dump_stack(void)
+{
+ show_stack(NULL, NULL);
+}
+EXPORT_SYMBOL(dump_stack);
diff --git a/arch/sh/kernel/vmlinux.lds.S b/arch/sh/kernel/vmlinux.lds.S
new file mode 100644
index 00000000000..51bdc1cf783
--- /dev/null
+++ b/arch/sh/kernel/vmlinux.lds.S
@@ -0,0 +1,155 @@
+/* $Id: vmlinux.lds.S,v 1.8 2003/05/16 17:18:14 lethal Exp $
+ * ld script to make SuperH Linux kernel
+ * Written by Niibe Yutaka
+ */
+#include <linux/config.h>
+#include <asm-generic/vmlinux.lds.h>
+
+#ifdef CONFIG_CPU_LITTLE_ENDIAN
+OUTPUT_FORMAT("elf32-sh-linux", "elf32-sh-linux", "elf32-sh-linux")
+#else
+OUTPUT_FORMAT("elf32-shbig-linux", "elf32-shbig-linux", "elf32-shbig-linux")
+#endif
+OUTPUT_ARCH(sh)
+ENTRY(_start)
+SECTIONS
+{
+ . = 0x80000000 + CONFIG_MEMORY_START + CONFIG_ZERO_PAGE_OFFSET;
+ _text = .; /* Text and read-only data */
+ text = .; /* Text and read-only data */
+ .empty_zero_page : {
+ *(.empty_zero_page)
+ } = 0
+ .text : {
+ *(.text)
+ SCHED_TEXT
+ LOCK_TEXT
+ *(.fixup)
+ *(.gnu.warning)
+ } = 0x0009
+
+ . = ALIGN(16); /* Exception table */
+ __start___ex_table = .;
+ __ex_table : { *(__ex_table) }
+ __stop___ex_table = .;
+
+ RODATA
+
+ _etext = .; /* End of text section */
+
+ .data : { /* Data */
+ *(.data)
+
+ /* Align the initial ramdisk image (INITRD) on page boundaries. */
+ . = ALIGN(4096);
+ __rd_start = .;
+ *(.initrd)
+ . = ALIGN(4096);
+ __rd_end = .;
+
+ CONSTRUCTORS
+ }
+
+ . = ALIGN(4096);
+ .data.page_aligned : { *(.data.idt) }
+
+ . = ALIGN(32);
+ __per_cpu_start = .;
+ .data.percpu : { *(.data.percpu) }
+ __per_cpu_end = .;
+ .data.cacheline_aligned : { *(.data.cacheline_aligned) }
+
+ _edata = .; /* End of data section */
+
+ . = ALIGN(8192); /* init_task */
+ .data.init_task : { *(.data.init_task) }
+ /* stack */
+ .stack : { stack = .; _stack = .; }
+
+ . = ALIGN(4096); /* Init code and data */
+ __init_begin = .;
+ _sinittext = .;
+ .init.text : { *(.init.text) }
+ _einittext = .;
+ .init.data : { *(.init.data) }
+ . = ALIGN(16);
+ __setup_start = .;
+ .init.setup : { *(.init.setup) }
+ __setup_end = .;
+ __initcall_start = .;
+ .initcall.init : {
+ *(.initcall1.init)
+ *(.initcall2.init)
+ *(.initcall3.init)
+ *(.initcall4.init)
+ *(.initcall5.init)
+ *(.initcall6.init)
+ *(.initcall7.init)
+ }
+ __initcall_end = .;
+ __con_initcall_start = .;
+ .con_initcall.init : { *(.con_initcall.init) }
+ __con_initcall_end = .;
+ SECURITY_INIT
+ __initramfs_start = .;
+ .init.ramfs : { *(.init.ramfs) }
+ __initramfs_end = .;
+ __machvec_start = .;
+ .init.machvec : { *(.init.machvec) }
+ __machvec_end = .;
+ . = ALIGN(4096);
+ __init_end = .;
+
+ . = ALIGN(4);
+ __bss_start = .; /* BSS */
+ .bss : { *(.bss) }
+
+ . = ALIGN(4);
+ _end = . ;
+
+ /* When something in the kernel is NOT compiled as a module, the
+ * module cleanup code and data are put into these segments. Both
+ * can then be thrown away, as cleanup code is never called unless
+ * it's a module.
+ */
+ /DISCARD/ : {
+ *(.exit.text)
+ *(.exit.data)
+ *(.exitcall.exit)
+ }
+
+ /* Stabs debugging sections. */
+ .stab 0 : { *(.stab) }
+ .stabstr 0 : { *(.stabstr) }
+ .stab.excl 0 : { *(.stab.excl) }
+ .stab.exclstr 0 : { *(.stab.exclstr) }
+ .stab.index 0 : { *(.stab.index) }
+ .stab.indexstr 0 : { *(.stab.indexstr) }
+ .comment 0 : { *(.comment) }
+ /* DWARF debug sections.
+ Symbols in the DWARF debugging section are relative to the beginning
+ of the section so we begin .debug at 0. */
+ /* DWARF 1 */
+ .debug 0 : { *(.debug) }
+ .line 0 : { *(.line) }
+ /* GNU DWARF 1 extensions */
+ .debug_srcinfo 0 : { *(.debug_srcinfo) }
+ .debug_sfnames 0 : { *(.debug_sfnames) }
+ /* DWARF 1.1 and DWARF 2 */
+ .debug_aranges 0 : { *(.debug_aranges) }
+ .debug_pubnames 0 : { *(.debug_pubnames) }
+ /* DWARF 2 */
+ .debug_info 0 : { *(.debug_info) }
+ .debug_abbrev 0 : { *(.debug_abbrev) }
+ .debug_line 0 : { *(.debug_line) }
+ .debug_frame 0 : { *(.debug_frame) }
+ .debug_str 0 : { *(.debug_str) }
+ .debug_loc 0 : { *(.debug_loc) }
+ .debug_macinfo 0 : { *(.debug_macinfo) }
+ /* SGI/MIPS DWARF 2 extensions */
+ .debug_weaknames 0 : { *(.debug_weaknames) }
+ .debug_funcnames 0 : { *(.debug_funcnames) }
+ .debug_typenames 0 : { *(.debug_typenames) }
+ .debug_varnames 0 : { *(.debug_varnames) }
+ /* These must appear regardless of . */
+}