summaryrefslogtreecommitdiff
path: root/arch/mn10300/kernel
diff options
context:
space:
mode:
authorAndré Fabian Silva Delgado <emulatorman@parabola.nu>2015-08-05 17:04:01 -0300
committerAndré Fabian Silva Delgado <emulatorman@parabola.nu>2015-08-05 17:04:01 -0300
commit57f0f512b273f60d52568b8c6b77e17f5636edc0 (patch)
tree5e910f0e82173f4ef4f51111366a3f1299037a7b /arch/mn10300/kernel
Initial import
Diffstat (limited to 'arch/mn10300/kernel')
-rw-r--r--arch/mn10300/kernel/Makefile28
-rw-r--r--arch/mn10300/kernel/asm-offsets.c107
-rw-r--r--arch/mn10300/kernel/cevt-mn10300.c142
-rw-r--r--arch/mn10300/kernel/csrc-mn10300.c34
-rw-r--r--arch/mn10300/kernel/entry.S772
-rw-r--r--arch/mn10300/kernel/fpu-low.S258
-rw-r--r--arch/mn10300/kernel/fpu-nofpu-low.S39
-rw-r--r--arch/mn10300/kernel/fpu-nofpu.c30
-rw-r--r--arch/mn10300/kernel/fpu.c175
-rw-r--r--arch/mn10300/kernel/gdb-io-serial-low.S91
-rw-r--r--arch/mn10300/kernel/gdb-io-serial.c174
-rw-r--r--arch/mn10300/kernel/gdb-io-ttysm-low.S93
-rw-r--r--arch/mn10300/kernel/gdb-io-ttysm.c303
-rw-r--r--arch/mn10300/kernel/gdb-low.S115
-rw-r--r--arch/mn10300/kernel/gdb-stub.c1923
-rw-r--r--arch/mn10300/kernel/head.S450
-rw-r--r--arch/mn10300/kernel/internal.h40
-rw-r--r--arch/mn10300/kernel/io.c30
-rw-r--r--arch/mn10300/kernel/irq.c355
-rw-r--r--arch/mn10300/kernel/kgdb.c501
-rw-r--r--arch/mn10300/kernel/kprobes.c656
-rw-r--r--arch/mn10300/kernel/mn10300-debug.c58
-rw-r--r--arch/mn10300/kernel/mn10300-serial-low.S194
-rw-r--r--arch/mn10300/kernel/mn10300-serial.c1787
-rw-r--r--arch/mn10300/kernel/mn10300-serial.h130
-rw-r--r--arch/mn10300/kernel/mn10300-watchdog-low.S66
-rw-r--r--arch/mn10300/kernel/mn10300-watchdog.c205
-rw-r--r--arch/mn10300/kernel/mn10300_ksyms.c42
-rw-r--r--arch/mn10300/kernel/module.c156
-rw-r--r--arch/mn10300/kernel/process.c192
-rw-r--r--arch/mn10300/kernel/profile-low.S72
-rw-r--r--arch/mn10300/kernel/profile.c51
-rw-r--r--arch/mn10300/kernel/ptrace.c385
-rw-r--r--arch/mn10300/kernel/rtc.c132
-rw-r--r--arch/mn10300/kernel/setup.c283
-rw-r--r--arch/mn10300/kernel/sigframe.h33
-rw-r--r--arch/mn10300/kernel/signal.c431
-rw-r--r--arch/mn10300/kernel/smp-low.S97
-rw-r--r--arch/mn10300/kernel/smp.c1185
-rw-r--r--arch/mn10300/kernel/switch_to.S179
-rw-r--r--arch/mn10300/kernel/sys_mn10300.c33
-rw-r--r--arch/mn10300/kernel/time.c124
-rw-r--r--arch/mn10300/kernel/traps.c616
-rw-r--r--arch/mn10300/kernel/vmlinux.lds.S93
44 files changed, 12860 insertions, 0 deletions
diff --git a/arch/mn10300/kernel/Makefile b/arch/mn10300/kernel/Makefile
new file mode 100644
index 000000000..561029f7f
--- /dev/null
+++ b/arch/mn10300/kernel/Makefile
@@ -0,0 +1,28 @@
+#
+# Makefile for the MN10300-specific core kernel code
+#
+extra-y := head.o vmlinux.lds
+
+fpu-obj-y := fpu-nofpu.o fpu-nofpu-low.o
+fpu-obj-$(CONFIG_FPU) := fpu.o fpu-low.o
+
+obj-y := process.o signal.o entry.o traps.o irq.o \
+ ptrace.o setup.o time.o sys_mn10300.o io.o \
+ switch_to.o mn10300_ksyms.o $(fpu-obj-y) \
+ csrc-mn10300.o cevt-mn10300.o
+
+obj-$(CONFIG_SMP) += smp.o smp-low.o
+
+obj-$(CONFIG_MN10300_WD_TIMER) += mn10300-watchdog.o mn10300-watchdog-low.o
+
+obj-$(CONFIG_MN10300_TTYSM) += mn10300-serial.o mn10300-serial-low.o \
+ mn10300-debug.o
+obj-$(CONFIG_GDBSTUB) += gdb-stub.o gdb-low.o
+obj-$(CONFIG_GDBSTUB_ON_TTYSx) += gdb-io-serial.o gdb-io-serial-low.o
+obj-$(CONFIG_GDBSTUB_ON_TTYSMx) += gdb-io-ttysm.o gdb-io-ttysm-low.o
+
+obj-$(CONFIG_MN10300_RTC) += rtc.o
+obj-$(CONFIG_PROFILE) += profile.o profile-low.o
+obj-$(CONFIG_MODULES) += module.o
+obj-$(CONFIG_KPROBES) += kprobes.o
+obj-$(CONFIG_KGDB) += kgdb.o
diff --git a/arch/mn10300/kernel/asm-offsets.c b/arch/mn10300/kernel/asm-offsets.c
new file mode 100644
index 000000000..e5a61c659
--- /dev/null
+++ b/arch/mn10300/kernel/asm-offsets.c
@@ -0,0 +1,107 @@
+/*
+ * Generate definitions needed by assembly language modules.
+ * This code generates raw asm output which is post-processed
+ * to extract and format the required data.
+ */
+
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/personality.h>
+#include <linux/kbuild.h>
+#include <asm/ucontext.h>
+#include <asm/processor.h>
+#include <asm/thread_info.h>
+#include <asm/ptrace.h>
+#include "sigframe.h"
+#include "mn10300-serial.h"
+
+void foo(void)
+{
+ OFFSET(SIGCONTEXT_d0, sigcontext, d0);
+ OFFSET(SIGCONTEXT_d1, sigcontext, d1);
+ BLANK();
+
+ OFFSET(TI_task, thread_info, task);
+ OFFSET(TI_frame, thread_info, frame);
+ OFFSET(TI_flags, thread_info, flags);
+ OFFSET(TI_cpu, thread_info, cpu);
+ OFFSET(TI_preempt_count, thread_info, preempt_count);
+ OFFSET(TI_addr_limit, thread_info, addr_limit);
+ BLANK();
+
+ OFFSET(REG_D0, pt_regs, d0);
+ OFFSET(REG_D1, pt_regs, d1);
+ OFFSET(REG_D2, pt_regs, d2);
+ OFFSET(REG_D3, pt_regs, d3);
+ OFFSET(REG_A0, pt_regs, a0);
+ OFFSET(REG_A1, pt_regs, a1);
+ OFFSET(REG_A2, pt_regs, a2);
+ OFFSET(REG_A3, pt_regs, a3);
+ OFFSET(REG_E0, pt_regs, e0);
+ OFFSET(REG_E1, pt_regs, e1);
+ OFFSET(REG_E2, pt_regs, e2);
+ OFFSET(REG_E3, pt_regs, e3);
+ OFFSET(REG_E4, pt_regs, e4);
+ OFFSET(REG_E5, pt_regs, e5);
+ OFFSET(REG_E6, pt_regs, e6);
+ OFFSET(REG_E7, pt_regs, e7);
+ OFFSET(REG_SP, pt_regs, sp);
+ OFFSET(REG_EPSW, pt_regs, epsw);
+ OFFSET(REG_PC, pt_regs, pc);
+ OFFSET(REG_LAR, pt_regs, lar);
+ OFFSET(REG_LIR, pt_regs, lir);
+ OFFSET(REG_MDR, pt_regs, mdr);
+ OFFSET(REG_MCVF, pt_regs, mcvf);
+ OFFSET(REG_MCRL, pt_regs, mcrl);
+ OFFSET(REG_MCRH, pt_regs, mcrh);
+ OFFSET(REG_MDRQ, pt_regs, mdrq);
+ OFFSET(REG_ORIG_D0, pt_regs, orig_d0);
+ OFFSET(REG_NEXT, pt_regs, next);
+ DEFINE(REG__END, sizeof(struct pt_regs));
+ BLANK();
+
+ OFFSET(THREAD_UREGS, thread_struct, uregs);
+ OFFSET(THREAD_PC, thread_struct, pc);
+ OFFSET(THREAD_SP, thread_struct, sp);
+ OFFSET(THREAD_A3, thread_struct, a3);
+ OFFSET(THREAD_USP, thread_struct, usp);
+#ifdef CONFIG_FPU
+ OFFSET(THREAD_FPU_FLAGS, thread_struct, fpu_flags);
+ OFFSET(THREAD_FPU_STATE, thread_struct, fpu_state);
+ DEFINE(__THREAD_USING_FPU, THREAD_USING_FPU);
+ DEFINE(__THREAD_HAS_FPU, THREAD_HAS_FPU);
+#endif /* CONFIG_FPU */
+ BLANK();
+
+ OFFSET(TASK_THREAD, task_struct, thread);
+ BLANK();
+
+ DEFINE(CLONE_VM_asm, CLONE_VM);
+ DEFINE(CLONE_FS_asm, CLONE_FS);
+ DEFINE(CLONE_FILES_asm, CLONE_FILES);
+ DEFINE(CLONE_SIGHAND_asm, CLONE_SIGHAND);
+ DEFINE(CLONE_UNTRACED_asm, CLONE_UNTRACED);
+ DEFINE(SIGCHLD_asm, SIGCHLD);
+ BLANK();
+
+ OFFSET(RT_SIGFRAME_sigcontext, rt_sigframe, uc.uc_mcontext);
+
+ DEFINE(PAGE_SIZE_asm, PAGE_SIZE);
+
+ OFFSET(__rx_buffer, mn10300_serial_port, rx_buffer);
+ OFFSET(__rx_inp, mn10300_serial_port, rx_inp);
+ OFFSET(__rx_outp, mn10300_serial_port, rx_outp);
+ OFFSET(__uart_state, mn10300_serial_port, uart.state);
+ OFFSET(__tx_xchar, mn10300_serial_port, tx_xchar);
+ OFFSET(__tx_flags, mn10300_serial_port, tx_flags);
+ OFFSET(__intr_flags, mn10300_serial_port, intr_flags);
+ OFFSET(__rx_icr, mn10300_serial_port, rx_icr);
+ OFFSET(__tx_icr, mn10300_serial_port, tx_icr);
+ OFFSET(__tm_icr, mn10300_serial_port, _tmicr);
+ OFFSET(__iobase, mn10300_serial_port, _iobase);
+
+ DEFINE(__UART_XMIT_SIZE, UART_XMIT_SIZE);
+ OFFSET(__xmit_buffer, uart_state, xmit.buf);
+ OFFSET(__xmit_head, uart_state, xmit.head);
+ OFFSET(__xmit_tail, uart_state, xmit.tail);
+}
diff --git a/arch/mn10300/kernel/cevt-mn10300.c b/arch/mn10300/kernel/cevt-mn10300.c
new file mode 100644
index 000000000..60f64ca17
--- /dev/null
+++ b/arch/mn10300/kernel/cevt-mn10300.c
@@ -0,0 +1,142 @@
+/* MN10300 clockevents
+ *
+ * Copyright (C) 2010 Red Hat, Inc. All Rights Reserved.
+ * Written by Mark Salter (msalter@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+#include <linux/clockchips.h>
+#include <linux/interrupt.h>
+#include <linux/percpu.h>
+#include <linux/smp.h>
+#include <asm/timex.h>
+#include "internal.h"
+
+#ifdef CONFIG_SMP
+#if (CONFIG_NR_CPUS > 2) && !defined(CONFIG_GEENERIC_CLOCKEVENTS_BROADCAST)
+#error "This doesn't scale well! Need per-core local timers."
+#endif
+#else /* CONFIG_SMP */
+#define stop_jiffies_counter1()
+#define reload_jiffies_counter1(x)
+#define TMJC1IRQ TMJCIRQ
+#endif
+
+
+static int next_event(unsigned long delta,
+ struct clock_event_device *evt)
+{
+ unsigned int cpu = smp_processor_id();
+
+ if (cpu == 0) {
+ stop_jiffies_counter();
+ reload_jiffies_counter(delta - 1);
+ } else {
+ stop_jiffies_counter1();
+ reload_jiffies_counter1(delta - 1);
+ }
+ return 0;
+}
+
+static void set_clock_mode(enum clock_event_mode mode,
+ struct clock_event_device *evt)
+{
+ /* Nothing to do ... */
+}
+
+static DEFINE_PER_CPU(struct clock_event_device, mn10300_clockevent_device);
+static DEFINE_PER_CPU(struct irqaction, timer_irq);
+
+static irqreturn_t timer_interrupt(int irq, void *dev_id)
+{
+ struct clock_event_device *cd;
+ unsigned int cpu = smp_processor_id();
+
+ if (cpu == 0)
+ stop_jiffies_counter();
+ else
+ stop_jiffies_counter1();
+
+ cd = &per_cpu(mn10300_clockevent_device, cpu);
+ cd->event_handler(cd);
+
+ return IRQ_HANDLED;
+}
+
+static void event_handler(struct clock_event_device *dev)
+{
+}
+
+static inline void setup_jiffies_interrupt(int irq,
+ struct irqaction *action)
+{
+ u16 tmp;
+ setup_irq(irq, action);
+ set_intr_level(irq, NUM2GxICR_LEVEL(CONFIG_TIMER_IRQ_LEVEL));
+ GxICR(irq) |= GxICR_ENABLE | GxICR_DETECT | GxICR_REQUEST;
+ tmp = GxICR(irq);
+}
+
+int __init init_clockevents(void)
+{
+ struct clock_event_device *cd;
+ struct irqaction *iact;
+ unsigned int cpu = smp_processor_id();
+
+ cd = &per_cpu(mn10300_clockevent_device, cpu);
+
+ if (cpu == 0) {
+ stop_jiffies_counter();
+ cd->irq = TMJCIRQ;
+ } else {
+ stop_jiffies_counter1();
+ cd->irq = TMJC1IRQ;
+ }
+
+ cd->name = "Timestamp";
+ cd->features = CLOCK_EVT_FEAT_ONESHOT;
+
+ /* Calculate shift/mult. We want to spawn at least 1 second */
+ clockevents_calc_mult_shift(cd, MN10300_JCCLK, 1);
+
+ /* Calculate the min / max delta */
+ cd->max_delta_ns = clockevent_delta2ns(TMJCBR_MAX, cd);
+ cd->min_delta_ns = clockevent_delta2ns(100, cd);
+
+ cd->rating = 200;
+ cd->cpumask = cpumask_of(smp_processor_id());
+ cd->set_mode = set_clock_mode;
+ cd->event_handler = event_handler;
+ cd->set_next_event = next_event;
+
+ iact = &per_cpu(timer_irq, cpu);
+ iact->flags = IRQF_SHARED | IRQF_TIMER;
+ iact->handler = timer_interrupt;
+
+ clockevents_register_device(cd);
+
+#if defined(CONFIG_SMP) && !defined(CONFIG_GENERIC_CLOCKEVENTS_BROADCAST)
+ /* setup timer irq affinity so it only runs on this cpu */
+ {
+ struct irq_data *data;
+ data = irq_get_irq_data(cd->irq);
+ cpumask_copy(data->affinity, cpumask_of(cpu));
+ iact->flags |= IRQF_NOBALANCING;
+ }
+#endif
+
+ if (cpu == 0) {
+ reload_jiffies_counter(MN10300_JC_PER_HZ - 1);
+ iact->name = "CPU0 Timer";
+ } else {
+ reload_jiffies_counter1(MN10300_JC_PER_HZ - 1);
+ iact->name = "CPU1 Timer";
+ }
+
+ setup_jiffies_interrupt(cd->irq, iact);
+
+ return 0;
+}
diff --git a/arch/mn10300/kernel/csrc-mn10300.c b/arch/mn10300/kernel/csrc-mn10300.c
new file mode 100644
index 000000000..45644cf18
--- /dev/null
+++ b/arch/mn10300/kernel/csrc-mn10300.c
@@ -0,0 +1,34 @@
+/* MN10300 clocksource
+ *
+ * Copyright (C) 2010 Red Hat, Inc. All Rights Reserved.
+ * Written by Mark Salter (msalter@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+#include <linux/clocksource.h>
+#include <linux/init.h>
+#include <asm/timex.h>
+#include "internal.h"
+
+static cycle_t mn10300_read(struct clocksource *cs)
+{
+ return read_timestamp_counter();
+}
+
+static struct clocksource clocksource_mn10300 = {
+ .name = "TSC",
+ .rating = 200,
+ .read = mn10300_read,
+ .mask = CLOCKSOURCE_MASK(32),
+ .flags = CLOCK_SOURCE_IS_CONTINUOUS,
+};
+
+int __init init_clocksource(void)
+{
+ startup_timestamp_counter();
+ clocksource_register_hz(&clocksource_mn10300, MN10300_TSCCLK);
+ return 0;
+}
diff --git a/arch/mn10300/kernel/entry.S b/arch/mn10300/kernel/entry.S
new file mode 100644
index 000000000..177d61de5
--- /dev/null
+++ b/arch/mn10300/kernel/entry.S
@@ -0,0 +1,772 @@
+###############################################################################
+#
+# MN10300 Exception and interrupt entry points
+#
+# Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd.
+# Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
+# Modified by David Howells (dhowells@redhat.com)
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public Licence
+# as published by the Free Software Foundation; either version
+# 2 of the Licence, or (at your option) any later version.
+#
+###############################################################################
+#include <linux/sys.h>
+#include <linux/linkage.h>
+#include <asm/smp.h>
+#include <asm/irqflags.h>
+#include <asm/thread_info.h>
+#include <asm/intctl-regs.h>
+#include <asm/busctl-regs.h>
+#include <asm/timer-regs.h>
+#include <unit/leds.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/errno.h>
+#include <asm/asm-offsets.h>
+#include <asm/frame.inc>
+
+#if defined(CONFIG_SMP) && defined(CONFIG_GDBSTUB)
+#include <asm/gdb-stub.h>
+#endif /* CONFIG_SMP && CONFIG_GDBSTUB */
+
+#ifdef CONFIG_PREEMPT
+#define preempt_stop LOCAL_IRQ_DISABLE
+#else
+#define preempt_stop
+#define resume_kernel restore_all
+#endif
+
+ .am33_2
+
+###############################################################################
+#
+# the return path for a forked child
+# - on entry, D0 holds the address of the previous task to run
+#
+###############################################################################
+ENTRY(ret_from_fork)
+ call schedule_tail[],0
+ GET_THREAD_INFO a2
+
+ # return 0 to indicate child process
+ clr d0
+ mov d0,(REG_D0,fp)
+ jmp syscall_exit
+
+ENTRY(ret_from_kernel_thread)
+ call schedule_tail[],0
+ mov (REG_D0,fp),d0
+ mov (REG_A0,fp),a0
+ calls (a0)
+ GET_THREAD_INFO a2 # A2 must be set on return from sys_exit()
+ clr d0
+ mov d0,(REG_D0,fp)
+ jmp syscall_exit
+
+###############################################################################
+#
+# system call handler
+#
+###############################################################################
+ENTRY(system_call)
+ add -4,sp
+ SAVE_ALL
+ mov d0,(REG_ORIG_D0,fp)
+ GET_THREAD_INFO a2
+ cmp nr_syscalls,d0
+ bcc syscall_badsys
+ btst _TIF_SYSCALL_TRACE,(TI_flags,a2)
+ bne syscall_entry_trace
+syscall_call:
+ add d0,d0,a1
+ add a1,a1
+ mov (REG_A0,fp),d0
+ mov (sys_call_table,a1),a0
+ calls (a0)
+ mov d0,(REG_D0,fp)
+syscall_exit:
+ # make sure we don't miss an interrupt setting need_resched or
+ # sigpending between sampling and the rti
+ LOCAL_IRQ_DISABLE
+ mov (TI_flags,a2),d2
+ btst _TIF_ALLWORK_MASK,d2
+ bne syscall_exit_work
+restore_all:
+ RESTORE_ALL
+
+###############################################################################
+#
+# perform work that needs to be done immediately before resumption and syscall
+# tracing
+#
+###############################################################################
+ ALIGN
+syscall_exit_work:
+ mov (REG_EPSW,fp),d0
+ and EPSW_nSL,d0
+ beq resume_kernel # returning to supervisor mode
+
+ LOCAL_IRQ_ENABLE # could let syscall_trace_exit() call
+ # schedule() instead
+ btst _TIF_SYSCALL_TRACE,d2
+ beq work_pending
+ mov fp,d0
+ call syscall_trace_exit[],0 # do_syscall_trace(regs)
+ jmp resume_userspace
+
+ ALIGN
+work_pending:
+ btst _TIF_NEED_RESCHED,d2
+ beq work_notifysig
+
+work_resched:
+ call schedule[],0
+
+resume_userspace:
+ # make sure we don't miss an interrupt setting need_resched or
+ # sigpending between sampling and the rti
+ LOCAL_IRQ_DISABLE
+
+ # is there any work to be done other than syscall tracing?
+ mov (TI_flags,a2),d2
+ btst _TIF_WORK_MASK,d2
+ beq restore_all
+
+ LOCAL_IRQ_ENABLE
+ btst _TIF_NEED_RESCHED,d2
+ bne work_resched
+
+ # deal with pending signals and notify-resume requests
+work_notifysig:
+ mov fp,d0
+ mov d2,d1
+ call do_notify_resume[],0
+ jmp resume_userspace
+
+ # perform syscall entry tracing
+syscall_entry_trace:
+ mov -ENOSYS,d0
+ mov d0,(REG_D0,fp)
+ mov fp,d0
+ call syscall_trace_entry[],0 # returns the syscall number to actually use
+ mov (REG_D1,fp),d1
+ cmp nr_syscalls,d0
+ bcs syscall_call
+ jmp syscall_exit
+
+syscall_badsys:
+ mov -ENOSYS,d0
+ mov d0,(REG_D0,fp)
+ jmp resume_userspace
+
+ # userspace resumption stub bypassing syscall exit tracing
+ .globl ret_from_exception, ret_from_intr
+ ALIGN
+ret_from_exception:
+ preempt_stop
+ret_from_intr:
+ GET_THREAD_INFO a2
+ mov (REG_EPSW,fp),d0 # need to deliver signals before
+ # returning to userspace
+ and EPSW_nSL,d0
+ bne resume_userspace # returning to userspace
+
+#ifdef CONFIG_PREEMPT
+resume_kernel:
+ LOCAL_IRQ_DISABLE
+ mov (TI_preempt_count,a2),d0 # non-zero preempt_count ?
+ cmp 0,d0
+ bne restore_all
+
+need_resched:
+ btst _TIF_NEED_RESCHED,(TI_flags,a2)
+ beq restore_all
+ mov (REG_EPSW,fp),d0
+ and EPSW_IM,d0
+ cmp EPSW_IM_7,d0 # interrupts off (exception path) ?
+ bne restore_all
+ call preempt_schedule_irq[],0
+ jmp need_resched
+#else
+ jmp resume_kernel
+#endif
+
+
+###############################################################################
+#
+# IRQ handler entry point
+# - intended to be entered at multiple priorities
+#
+###############################################################################
+ENTRY(irq_handler)
+ add -4,sp
+ SAVE_ALL
+
+ # it's not a syscall
+ mov 0xffffffff,d0
+ mov d0,(REG_ORIG_D0,fp)
+
+ mov fp,d0
+ call do_IRQ[],0 # do_IRQ(regs)
+
+ jmp ret_from_intr
+
+###############################################################################
+#
+# Double Fault handler entry point
+# - note that there will not be a stack, D0/A0 will hold EPSW/PC as were
+#
+###############################################################################
+ .section .bss
+ .balign THREAD_SIZE
+ .space THREAD_SIZE
+__df_stack:
+ .previous
+
+ENTRY(double_fault)
+ mov a0,(__df_stack-4) # PC as was
+ mov d0,(__df_stack-8) # EPSW as was
+ mn10300_set_dbfleds # display 'db-f' on the LEDs
+ mov 0xaa55aa55,d0
+ mov d0,(__df_stack-12) # no ORIG_D0
+ mov sp,a0 # save corrupted SP
+ mov __df_stack-12,sp # emergency supervisor stack
+ SAVE_ALL
+ mov a0,(REG_A0,fp) # save corrupted SP as A0 (which got
+ # clobbered by the CPU)
+ mov fp,d0
+ calls do_double_fault
+double_fault_loop:
+ bra double_fault_loop
+
+###############################################################################
+#
+# Bus Error handler entry point
+# - handle external (async) bus errors separately
+#
+###############################################################################
+ENTRY(raw_bus_error)
+ add -4,sp
+ mov d0,(sp)
+#if defined(CONFIG_ERRATUM_NEED_TO_RELOAD_MMUCTR)
+ mov (MMUCTR),d0
+ mov d0,(MMUCTR)
+#endif
+ mov (BCBERR),d0 # what
+ btst BCBERR_BEMR_DMA,d0 # see if it was an external bus error
+ beq __common_exception_aux # it wasn't
+
+ SAVE_ALL
+ mov (BCBEAR),d1 # destination of erroneous access
+
+ mov (REG_ORIG_D0,fp),d2
+ mov d2,(REG_D0,fp)
+ mov -1,d2
+ mov d2,(REG_ORIG_D0,fp)
+
+ add -4,sp
+ mov fp,(12,sp) # frame pointer
+ call io_bus_error[],0
+ jmp restore_all
+
+###############################################################################
+#
+# NMI exception entry points
+#
+# This is used by ordinary interrupt channels that have the GxICR_NMI bit set
+# in addition to the main NMI and Watchdog channels. SMP NMI IPIs use this
+# facility.
+#
+###############################################################################
+ENTRY(nmi_handler)
+ add -4,sp
+ mov d0,(sp)
+ mov (TBR),d0
+
+#ifdef CONFIG_SMP
+ add -4,sp
+ mov d0,(sp) # save d0(TBR)
+ movhu (NMIAGR),d0
+ and NMIAGR_GN,d0
+ lsr 0x2,d0
+ cmp CALL_FUNCTION_NMI_IPI,d0
+ bne nmi_not_smp_callfunc # if not call function, jump
+
+ # function call nmi ipi
+ add 4,sp # no need to store TBR
+ mov GxICR_DETECT,d0 # clear NMI request
+ movbu d0,(GxICR(CALL_FUNCTION_NMI_IPI))
+ movhu (GxICR(CALL_FUNCTION_NMI_IPI)),d0
+ and ~EPSW_NMID,epsw # enable NMI
+
+ mov (sp),d0 # restore d0
+ SAVE_ALL
+ call smp_nmi_call_function_interrupt[],0
+ RESTORE_ALL
+
+nmi_not_smp_callfunc:
+#ifdef CONFIG_KERNEL_DEBUGGER
+ cmp DEBUGGER_NMI_IPI,d0
+ bne nmi_not_debugger # if not kernel debugger NMI IPI, jump
+
+ # kernel debugger NMI IPI
+ add 4,sp # no need to store TBR
+ mov GxICR_DETECT,d0 # clear NMI
+ movbu d0,(GxICR(DEBUGGER_NMI_IPI))
+ movhu (GxICR(DEBUGGER_NMI_IPI)),d0
+ and ~EPSW_NMID,epsw # enable NMI
+
+ mov (sp),d0
+ SAVE_ALL
+ mov fp,d0 # arg 0: stacked register file
+ mov a2,d1 # arg 1: exception number
+ call debugger_nmi_interrupt[],0
+ RESTORE_ALL
+
+nmi_not_debugger:
+#endif /* CONFIG_KERNEL_DEBUGGER */
+ mov (sp),d0 # restore TBR to d0
+ add 4,sp
+#endif /* CONFIG_SMP */
+
+ bra __common_exception_nonmi
+
+###############################################################################
+#
+# General exception entry point
+#
+###############################################################################
+ENTRY(__common_exception)
+ add -4,sp
+ mov d0,(sp)
+#if defined(CONFIG_ERRATUM_NEED_TO_RELOAD_MMUCTR)
+ mov (MMUCTR),d0
+ mov d0,(MMUCTR)
+#endif
+
+__common_exception_aux:
+ mov (TBR),d0
+ and ~EPSW_NMID,epsw # turn NMIs back on if not NMI
+ or EPSW_IE,epsw
+
+__common_exception_nonmi:
+ and 0x0000FFFF,d0 # turn the exception code into a vector
+ # table index
+
+ btst 0x00000007,d0
+ bne 1f
+ cmp 0x00000400,d0
+ bge 1f
+
+ SAVE_ALL # build the stack frame
+
+ mov (REG_D0,fp),a2 # get the exception number
+ mov (REG_ORIG_D0,fp),d0
+ mov d0,(REG_D0,fp)
+ mov -1,d0
+ mov d0,(REG_ORIG_D0,fp)
+
+#ifdef CONFIG_GDBSTUB
+#ifdef CONFIG_SMP
+ call gdbstub_busy_check[],0
+ and d0,d0 # check return value
+ beq 2f
+#else /* CONFIG_SMP */
+ btst 0x01,(gdbstub_busy)
+ beq 2f
+#endif /* CONFIG_SMP */
+ and ~EPSW_IE,epsw
+ mov fp,d0
+ mov a2,d1
+ call gdbstub_exception[],0 # gdbstub itself caused an exception
+ bra restore_all
+2:
+#endif /* CONFIG_GDBSTUB */
+
+ mov fp,d0 # arg 0: stacked register file
+ mov a2,d1 # arg 1: exception number
+ lsr 1,a2
+
+ mov (exception_table,a2),a2
+ calls (a2)
+ jmp ret_from_exception
+
+1: pi # BUG() equivalent
+
+###############################################################################
+#
+# Exception handler functions table
+#
+###############################################################################
+ .data
+ENTRY(exception_table)
+ .rept 0x400>>1
+ .long uninitialised_exception
+ .endr
+ .previous
+
+###############################################################################
+#
+# Change an entry in the exception table
+# - D0 exception code, D1 handler
+#
+###############################################################################
+ENTRY(set_excp_vector)
+ lsr 1,d0
+ add exception_table,d0
+ mov d1,(d0)
+ mov 4,d1
+ ret [],0
+
+###############################################################################
+#
+# System call table
+#
+###############################################################################
+ .data
+ENTRY(sys_call_table)
+ .long sys_restart_syscall /* 0 */
+ .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 /* old 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_old_select
+ .long sys_symlink
+ .long sys_lstat
+ .long sys_readlink /* 85 */
+ .long sys_uselib
+ .long sys_swapon
+ .long sys_reboot
+ .long sys_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_ni_syscall /* old sys_uname */
+ .long sys_ni_syscall /* 110 - iopl */
+ .long sys_vhangup
+ .long sys_ni_syscall /* old "idle" system call */
+ .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 /* 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 /* reserved 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 sys_query_module */
+ .long sys_poll
+ .long sys_ni_syscall /* was 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_pread64 /* 180 */
+ .long sys_pwrite64
+ .long sys_chown16
+ .long sys_getcwd
+ .long sys_capget
+ .long sys_capset /* 185 */
+ .long sys_sigaltstack
+ .long sys_sendfile
+ .long sys_ni_syscall /* reserved for streams1 */
+ .long sys_ni_syscall /* reserved for streams2 */
+ .long sys_vfork /* 190 */
+ .long sys_getrlimit
+ .long sys_mmap_pgoff
+ .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
+ .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 /* sys_set_thread_area */
+ .long sys_ni_syscall /* sys_get_thread_area */
+ .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
+ .long sys_ni_syscall /* sys_vserver */
+ .long sys_mbind
+ .long sys_get_mempolicy /* 275 */
+ .long sys_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_kexec_load
+ .long sys_waitid
+ .long sys_ni_syscall /* 285 */ /* available */
+ .long sys_add_key
+ .long sys_request_key
+ .long sys_keyctl
+ .long sys_cacheflush
+ .long sys_ioprio_set /* 290 */
+ .long sys_ioprio_get
+ .long sys_inotify_init
+ .long sys_inotify_add_watch
+ .long sys_inotify_rm_watch
+ .long sys_migrate_pages /* 295 */
+ .long sys_openat
+ .long sys_mkdirat
+ .long sys_mknodat
+ .long sys_fchownat
+ .long sys_futimesat /* 300 */
+ .long sys_fstatat64
+ .long sys_unlinkat
+ .long sys_renameat
+ .long sys_linkat
+ .long sys_symlinkat /* 305 */
+ .long sys_readlinkat
+ .long sys_fchmodat
+ .long sys_faccessat
+ .long sys_pselect6
+ .long sys_ppoll /* 310 */
+ .long sys_unshare
+ .long sys_set_robust_list
+ .long sys_get_robust_list
+ .long sys_splice
+ .long sys_sync_file_range /* 315 */
+ .long sys_tee
+ .long sys_vmsplice
+ .long sys_move_pages
+ .long sys_getcpu
+ .long sys_epoll_pwait /* 320 */
+ .long sys_utimensat
+ .long sys_signalfd
+ .long sys_timerfd_create
+ .long sys_eventfd
+ .long sys_fallocate /* 325 */
+ .long sys_timerfd_settime
+ .long sys_timerfd_gettime
+ .long sys_signalfd4
+ .long sys_eventfd2
+ .long sys_epoll_create1 /* 330 */
+ .long sys_dup3
+ .long sys_pipe2
+ .long sys_inotify_init1
+ .long sys_preadv
+ .long sys_pwritev /* 335 */
+ .long sys_rt_tgsigqueueinfo
+ .long sys_perf_event_open
+ .long sys_recvmmsg
+ .long sys_setns
+
+
+nr_syscalls=(.-sys_call_table)/4
diff --git a/arch/mn10300/kernel/fpu-low.S b/arch/mn10300/kernel/fpu-low.S
new file mode 100644
index 000000000..78df25cfa
--- /dev/null
+++ b/arch/mn10300/kernel/fpu-low.S
@@ -0,0 +1,258 @@
+/* MN10300 Low level FPU management operations
+ *
+ * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+#include <linux/linkage.h>
+#include <asm/cpu-regs.h>
+#include <asm/smp.h>
+#include <asm/thread_info.h>
+#include <asm/asm-offsets.h>
+#include <asm/frame.inc>
+
+.macro FPU_INIT_STATE_ALL
+ fmov 0,fs0
+ fmov fs0,fs1
+ fmov fs0,fs2
+ fmov fs0,fs3
+ fmov fs0,fs4
+ fmov fs0,fs5
+ fmov fs0,fs6
+ fmov fs0,fs7
+ fmov fs0,fs8
+ fmov fs0,fs9
+ fmov fs0,fs10
+ fmov fs0,fs11
+ fmov fs0,fs12
+ fmov fs0,fs13
+ fmov fs0,fs14
+ fmov fs0,fs15
+ fmov fs0,fs16
+ fmov fs0,fs17
+ fmov fs0,fs18
+ fmov fs0,fs19
+ fmov fs0,fs20
+ fmov fs0,fs21
+ fmov fs0,fs22
+ fmov fs0,fs23
+ fmov fs0,fs24
+ fmov fs0,fs25
+ fmov fs0,fs26
+ fmov fs0,fs27
+ fmov fs0,fs28
+ fmov fs0,fs29
+ fmov fs0,fs30
+ fmov fs0,fs31
+ fmov FPCR_INIT,fpcr
+.endm
+
+.macro FPU_SAVE_ALL areg,dreg
+ fmov fs0,(\areg+)
+ fmov fs1,(\areg+)
+ fmov fs2,(\areg+)
+ fmov fs3,(\areg+)
+ fmov fs4,(\areg+)
+ fmov fs5,(\areg+)
+ fmov fs6,(\areg+)
+ fmov fs7,(\areg+)
+ fmov fs8,(\areg+)
+ fmov fs9,(\areg+)
+ fmov fs10,(\areg+)
+ fmov fs11,(\areg+)
+ fmov fs12,(\areg+)
+ fmov fs13,(\areg+)
+ fmov fs14,(\areg+)
+ fmov fs15,(\areg+)
+ fmov fs16,(\areg+)
+ fmov fs17,(\areg+)
+ fmov fs18,(\areg+)
+ fmov fs19,(\areg+)
+ fmov fs20,(\areg+)
+ fmov fs21,(\areg+)
+ fmov fs22,(\areg+)
+ fmov fs23,(\areg+)
+ fmov fs24,(\areg+)
+ fmov fs25,(\areg+)
+ fmov fs26,(\areg+)
+ fmov fs27,(\areg+)
+ fmov fs28,(\areg+)
+ fmov fs29,(\areg+)
+ fmov fs30,(\areg+)
+ fmov fs31,(\areg+)
+ fmov fpcr,\dreg
+ mov \dreg,(\areg)
+.endm
+
+.macro FPU_RESTORE_ALL areg,dreg
+ fmov (\areg+),fs0
+ fmov (\areg+),fs1
+ fmov (\areg+),fs2
+ fmov (\areg+),fs3
+ fmov (\areg+),fs4
+ fmov (\areg+),fs5
+ fmov (\areg+),fs6
+ fmov (\areg+),fs7
+ fmov (\areg+),fs8
+ fmov (\areg+),fs9
+ fmov (\areg+),fs10
+ fmov (\areg+),fs11
+ fmov (\areg+),fs12
+ fmov (\areg+),fs13
+ fmov (\areg+),fs14
+ fmov (\areg+),fs15
+ fmov (\areg+),fs16
+ fmov (\areg+),fs17
+ fmov (\areg+),fs18
+ fmov (\areg+),fs19
+ fmov (\areg+),fs20
+ fmov (\areg+),fs21
+ fmov (\areg+),fs22
+ fmov (\areg+),fs23
+ fmov (\areg+),fs24
+ fmov (\areg+),fs25
+ fmov (\areg+),fs26
+ fmov (\areg+),fs27
+ fmov (\areg+),fs28
+ fmov (\areg+),fs29
+ fmov (\areg+),fs30
+ fmov (\areg+),fs31
+ mov (\areg),\dreg
+ fmov \dreg,fpcr
+.endm
+
+###############################################################################
+#
+# void fpu_init_state(void)
+# - initialise the FPU
+#
+###############################################################################
+ .globl fpu_init_state
+ .type fpu_init_state,@function
+fpu_init_state:
+ mov epsw,d0
+ or EPSW_FE,epsw
+
+#ifdef CONFIG_MN10300_PROC_MN103E010
+ nop
+ nop
+ nop
+#endif
+ FPU_INIT_STATE_ALL
+#ifdef CONFIG_MN10300_PROC_MN103E010
+ nop
+ nop
+ nop
+#endif
+ mov d0,epsw
+ ret [],0
+
+ .size fpu_init_state,.-fpu_init_state
+
+###############################################################################
+#
+# void fpu_save(struct fpu_state_struct *)
+# - save the fpu state
+# - note that an FPU Operational exception might occur during this process
+#
+###############################################################################
+ .globl fpu_save
+ .type fpu_save,@function
+fpu_save:
+ mov epsw,d1
+ or EPSW_FE,epsw /* enable the FPU so we can access it */
+
+#ifdef CONFIG_MN10300_PROC_MN103E010
+ nop
+ nop
+#endif
+ mov d0,a0
+ FPU_SAVE_ALL a0,d0
+#ifdef CONFIG_MN10300_PROC_MN103E010
+ nop
+ nop
+#endif
+
+ mov d1,epsw
+ ret [],0
+
+ .size fpu_save,.-fpu_save
+
+###############################################################################
+#
+# void fpu_disabled(void)
+# - handle an exception due to the FPU being disabled
+# when CONFIG_FPU is enabled
+#
+###############################################################################
+ .type fpu_disabled,@function
+ .globl fpu_disabled
+fpu_disabled:
+ or EPSW_nAR|EPSW_FE,epsw
+ nop
+ nop
+ nop
+
+ mov sp,a1
+ mov (a1),d1 /* get epsw of user context */
+ and ~(THREAD_SIZE-1),a1 /* a1: (thread_info *ti) */
+ mov (TI_task,a1),a2 /* a2: (task_struct *tsk) */
+ btst EPSW_nSL,d1
+ beq fpu_used_in_kernel
+
+ or EPSW_FE,d1
+ mov d1,(sp)
+ mov (TASK_THREAD+THREAD_FPU_FLAGS,a2),d1
+#ifndef CONFIG_LAZY_SAVE_FPU
+ or __THREAD_HAS_FPU,d1
+ mov d1,(TASK_THREAD+THREAD_FPU_FLAGS,a2)
+#else /* !CONFIG_LAZY_SAVE_FPU */
+ mov (fpu_state_owner),a0
+ cmp 0,a0
+ beq fpu_regs_save_end
+
+ mov (TASK_THREAD+THREAD_UREGS,a0),a1
+ add TASK_THREAD+THREAD_FPU_STATE,a0
+ FPU_SAVE_ALL a0,d0
+
+ mov (REG_EPSW,a1),d0
+ and ~EPSW_FE,d0
+ mov d0,(REG_EPSW,a1)
+
+fpu_regs_save_end:
+ mov a2,(fpu_state_owner)
+#endif /* !CONFIG_LAZY_SAVE_FPU */
+
+ btst __THREAD_USING_FPU,d1
+ beq fpu_regs_init
+ add TASK_THREAD+THREAD_FPU_STATE,a2
+ FPU_RESTORE_ALL a2,d0
+ rti
+
+fpu_regs_init:
+ FPU_INIT_STATE_ALL
+ add TASK_THREAD+THREAD_FPU_FLAGS,a2
+ bset __THREAD_USING_FPU,(0,a2)
+ rti
+
+fpu_used_in_kernel:
+ and ~(EPSW_nAR|EPSW_FE),epsw
+ nop
+ nop
+
+ add -4,sp
+ SAVE_ALL
+ mov -1,d0
+ mov d0,(REG_ORIG_D0,fp)
+
+ and ~EPSW_NMID,epsw
+
+ mov fp,d0
+ call fpu_disabled_in_kernel[],0
+ jmp ret_from_exception
+
+ .size fpu_disabled,.-fpu_disabled
diff --git a/arch/mn10300/kernel/fpu-nofpu-low.S b/arch/mn10300/kernel/fpu-nofpu-low.S
new file mode 100644
index 000000000..7ea087a54
--- /dev/null
+++ b/arch/mn10300/kernel/fpu-nofpu-low.S
@@ -0,0 +1,39 @@
+/* MN10300 Low level FPU management operations
+ *
+ * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+#include <linux/linkage.h>
+#include <asm/cpu-regs.h>
+#include <asm/smp.h>
+#include <asm/thread_info.h>
+#include <asm/asm-offsets.h>
+#include <asm/frame.inc>
+
+###############################################################################
+#
+# void fpu_disabled(void)
+# - handle an exception due to the FPU being disabled
+# when CONFIG_FPU is disabled
+#
+###############################################################################
+ .type fpu_disabled,@function
+ .globl fpu_disabled
+fpu_disabled:
+ add -4,sp
+ SAVE_ALL
+ mov -1,d0
+ mov d0,(REG_ORIG_D0,fp)
+
+ and ~EPSW_NMID,epsw
+
+ mov fp,d0
+ call unexpected_fpu_exception[],0
+ jmp ret_from_exception
+
+ .size fpu_disabled,.-fpu_disabled
diff --git a/arch/mn10300/kernel/fpu-nofpu.c b/arch/mn10300/kernel/fpu-nofpu.c
new file mode 100644
index 000000000..31c765b92
--- /dev/null
+++ b/arch/mn10300/kernel/fpu-nofpu.c
@@ -0,0 +1,30 @@
+/* MN10300 FPU management
+ *
+ * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+#include <asm/fpu.h>
+
+/*
+ * handle an FPU operational exception
+ * - there's a possibility that if the FPU is asynchronous, the signal might
+ * be meant for a process other than the current one
+ */
+asmlinkage
+void unexpected_fpu_exception(struct pt_regs *regs, enum exception_code code)
+{
+ panic("An FPU exception was received, but there's no FPU enabled.");
+}
+
+/*
+ * fill in the FPU structure for a core dump
+ */
+int dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpreg)
+{
+ return 0; /* not valid */
+}
diff --git a/arch/mn10300/kernel/fpu.c b/arch/mn10300/kernel/fpu.c
new file mode 100644
index 000000000..064fa194d
--- /dev/null
+++ b/arch/mn10300/kernel/fpu.c
@@ -0,0 +1,175 @@
+/* MN10300 FPU management
+ *
+ * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+#include <asm/uaccess.h>
+#include <asm/fpu.h>
+#include <asm/elf.h>
+#include <asm/exceptions.h>
+
+#ifdef CONFIG_LAZY_SAVE_FPU
+struct task_struct *fpu_state_owner;
+#endif
+
+/*
+ * error functions in FPU disabled exception
+ */
+asmlinkage void fpu_disabled_in_kernel(struct pt_regs *regs)
+{
+ die_if_no_fixup("An FPU Disabled exception happened in kernel space\n",
+ regs, EXCEP_FPU_DISABLED);
+}
+
+/*
+ * handle an FPU operational exception
+ * - there's a possibility that if the FPU is asynchronous, the signal might
+ * be meant for a process other than the current one
+ */
+asmlinkage void fpu_exception(struct pt_regs *regs, enum exception_code code)
+{
+ struct task_struct *tsk = current;
+ siginfo_t info;
+ u32 fpcr;
+
+ if (!user_mode(regs))
+ die_if_no_fixup("An FPU Operation exception happened in"
+ " kernel space\n",
+ regs, code);
+
+ if (!is_using_fpu(tsk))
+ die_if_no_fixup("An FPU Operation exception happened,"
+ " but the FPU is not in use",
+ regs, code);
+
+ info.si_signo = SIGFPE;
+ info.si_errno = 0;
+ info.si_addr = (void *) tsk->thread.uregs->pc;
+ info.si_code = FPE_FLTINV;
+
+ unlazy_fpu(tsk);
+
+ fpcr = tsk->thread.fpu_state.fpcr;
+
+ if (fpcr & FPCR_EC_Z)
+ info.si_code = FPE_FLTDIV;
+ else if (fpcr & FPCR_EC_O)
+ info.si_code = FPE_FLTOVF;
+ else if (fpcr & FPCR_EC_U)
+ info.si_code = FPE_FLTUND;
+ else if (fpcr & FPCR_EC_I)
+ info.si_code = FPE_FLTRES;
+
+ force_sig_info(SIGFPE, &info, tsk);
+}
+
+/*
+ * save the FPU state to a signal context
+ */
+int fpu_setup_sigcontext(struct fpucontext *fpucontext)
+{
+ struct task_struct *tsk = current;
+
+ if (!is_using_fpu(tsk))
+ return 0;
+
+ /* transfer the current FPU state to memory and cause fpu_init() to be
+ * triggered by the next attempted FPU operation by the current
+ * process.
+ */
+ preempt_disable();
+
+#ifndef CONFIG_LAZY_SAVE_FPU
+ if (tsk->thread.fpu_flags & THREAD_HAS_FPU) {
+ fpu_save(&tsk->thread.fpu_state);
+ tsk->thread.uregs->epsw &= ~EPSW_FE;
+ tsk->thread.fpu_flags &= ~THREAD_HAS_FPU;
+ }
+#else /* !CONFIG_LAZY_SAVE_FPU */
+ if (fpu_state_owner == tsk) {
+ fpu_save(&tsk->thread.fpu_state);
+ fpu_state_owner->thread.uregs->epsw &= ~EPSW_FE;
+ fpu_state_owner = NULL;
+ }
+#endif /* !CONFIG_LAZY_SAVE_FPU */
+
+ preempt_enable();
+
+ /* we no longer have a valid current FPU state */
+ clear_using_fpu(tsk);
+
+ /* transfer the saved FPU state onto the userspace stack */
+ if (copy_to_user(fpucontext,
+ &tsk->thread.fpu_state,
+ min(sizeof(struct fpu_state_struct),
+ sizeof(struct fpucontext))))
+ return -1;
+
+ return 1;
+}
+
+/*
+ * kill a process's FPU state during restoration after signal handling
+ */
+void fpu_kill_state(struct task_struct *tsk)
+{
+ /* disown anything left in the FPU */
+ preempt_disable();
+
+#ifndef CONFIG_LAZY_SAVE_FPU
+ if (tsk->thread.fpu_flags & THREAD_HAS_FPU) {
+ tsk->thread.uregs->epsw &= ~EPSW_FE;
+ tsk->thread.fpu_flags &= ~THREAD_HAS_FPU;
+ }
+#else /* !CONFIG_LAZY_SAVE_FPU */
+ if (fpu_state_owner == tsk) {
+ fpu_state_owner->thread.uregs->epsw &= ~EPSW_FE;
+ fpu_state_owner = NULL;
+ }
+#endif /* !CONFIG_LAZY_SAVE_FPU */
+
+ preempt_enable();
+
+ /* we no longer have a valid current FPU state */
+ clear_using_fpu(tsk);
+}
+
+/*
+ * restore the FPU state from a signal context
+ */
+int fpu_restore_sigcontext(struct fpucontext *fpucontext)
+{
+ struct task_struct *tsk = current;
+ int ret;
+
+ /* load up the old FPU state */
+ ret = copy_from_user(&tsk->thread.fpu_state, fpucontext,
+ min(sizeof(struct fpu_state_struct),
+ sizeof(struct fpucontext)));
+ if (!ret)
+ set_using_fpu(tsk);
+
+ return ret;
+}
+
+/*
+ * fill in the FPU structure for a core dump
+ */
+int dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpreg)
+{
+ struct task_struct *tsk = current;
+ int fpvalid;
+
+ fpvalid = is_using_fpu(tsk);
+ if (fpvalid) {
+ unlazy_fpu(tsk);
+ memcpy(fpreg, &tsk->thread.fpu_state, sizeof(*fpreg));
+ }
+
+ return fpvalid;
+}
diff --git a/arch/mn10300/kernel/gdb-io-serial-low.S b/arch/mn10300/kernel/gdb-io-serial-low.S
new file mode 100644
index 000000000..b1d0152e9
--- /dev/null
+++ b/arch/mn10300/kernel/gdb-io-serial-low.S
@@ -0,0 +1,91 @@
+###############################################################################
+#
+# 16550 serial Rx interrupt handler for gdbstub I/O
+#
+# Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
+# Written by David Howells (dhowells@redhat.com)
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public Licence
+# as published by the Free Software Foundation; either version
+# 2 of the Licence, or (at your option) any later version.
+#
+###############################################################################
+#include <linux/sys.h>
+#include <linux/linkage.h>
+#include <asm/smp.h>
+#include <asm/cpu-regs.h>
+#include <asm/thread_info.h>
+#include <asm/frame.inc>
+#include <asm/intctl-regs.h>
+#include <asm/irqflags.h>
+#include <unit/serial.h>
+
+ .text
+
+###############################################################################
+#
+# GDB stub serial receive interrupt entry point
+# - intended to run at interrupt priority 0
+#
+###############################################################################
+ .globl gdbstub_io_rx_handler
+ .type gdbstub_io_rx_handler,@function
+gdbstub_io_rx_handler:
+ movm [d2,d3,a2,a3],(sp)
+
+#if 1
+ movbu (GDBPORT_SERIAL_IIR),d2
+#endif
+
+ mov (gdbstub_rx_inp),a3
+gdbstub_io_rx_more:
+ mov a3,a2
+ add 2,a3
+ and 0x00000fff,a3
+ mov (gdbstub_rx_outp),d3
+ cmp a3,d3
+ beq gdbstub_io_rx_overflow
+
+ movbu (GDBPORT_SERIAL_LSR),d3
+ btst UART_LSR_DR,d3
+ beq gdbstub_io_rx_done
+ movbu (GDBPORT_SERIAL_RX),d2
+ movbu d3,(gdbstub_rx_buffer+1,a2)
+ movbu d2,(gdbstub_rx_buffer,a2)
+ mov a3,(gdbstub_rx_inp)
+ bra gdbstub_io_rx_more
+
+gdbstub_io_rx_done:
+ mov GxICR_DETECT,d2
+ movbu d2,(XIRQxICR(GDBPORT_SERIAL_IRQ)) # ACK the interrupt
+ movhu (XIRQxICR(GDBPORT_SERIAL_IRQ)),d2 # flush
+ movm (sp),[d2,d3,a2,a3]
+ bset 0x01,(gdbstub_busy)
+ beq gdbstub_io_rx_enter
+ rti
+
+gdbstub_io_rx_overflow:
+ bset 0x01,(gdbstub_rx_overflow)
+ bra gdbstub_io_rx_done
+
+gdbstub_io_rx_enter:
+ LOCAL_CHANGE_INTR_MASK_LEVEL(NUM2EPSW_IM(CONFIG_GDBSTUB_IRQ_LEVEL+1))
+ add -4,sp
+ SAVE_ALL
+
+ mov 0xffffffff,d0
+ mov d0,(REG_ORIG_D0,fp)
+ mov 0x280,d1
+
+ mov fp,d0
+ call gdbstub_rx_irq[],0 # gdbstub_rx_irq(regs,excep)
+
+ LOCAL_CLI
+ bclr 0x01,(gdbstub_busy)
+
+ .globl gdbstub_return
+gdbstub_return:
+ RESTORE_ALL
+
+ .size gdbstub_io_rx_handler,.-gdbstub_io_rx_handler
diff --git a/arch/mn10300/kernel/gdb-io-serial.c b/arch/mn10300/kernel/gdb-io-serial.c
new file mode 100644
index 000000000..df5124274
--- /dev/null
+++ b/arch/mn10300/kernel/gdb-io-serial.c
@@ -0,0 +1,174 @@
+/* 16550 serial driver for gdbstub I/O
+ *
+ * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/console.h>
+#include <linux/init.h>
+#include <linux/nmi.h>
+
+#include <asm/pgtable.h>
+#include <asm/gdb-stub.h>
+#include <asm/exceptions.h>
+#include <asm/serial-regs.h>
+#include <unit/serial.h>
+#include <asm/smp.h>
+
+/*
+ * initialise the GDB stub
+ */
+void gdbstub_io_init(void)
+{
+ u16 tmp;
+
+ /* set up the serial port */
+ GDBPORT_SERIAL_LCR = UART_LCR_WLEN8; /* 1N8 */
+ GDBPORT_SERIAL_FCR = (UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR |
+ UART_FCR_CLEAR_XMIT);
+
+ FLOWCTL_CLEAR(DTR);
+ FLOWCTL_SET(RTS);
+
+ gdbstub_io_set_baud(115200);
+
+ /* we want to get serial receive interrupts */
+ XIRQxICR(GDBPORT_SERIAL_IRQ) = 0;
+ tmp = XIRQxICR(GDBPORT_SERIAL_IRQ);
+
+#if CONFIG_GDBSTUB_IRQ_LEVEL == 0
+ IVAR0 = EXCEP_IRQ_LEVEL0;
+#elif CONFIG_GDBSTUB_IRQ_LEVEL == 1
+ IVAR1 = EXCEP_IRQ_LEVEL1;
+#elif CONFIG_GDBSTUB_IRQ_LEVEL == 2
+ IVAR2 = EXCEP_IRQ_LEVEL2;
+#elif CONFIG_GDBSTUB_IRQ_LEVEL == 3
+ IVAR3 = EXCEP_IRQ_LEVEL3;
+#elif CONFIG_GDBSTUB_IRQ_LEVEL == 4
+ IVAR4 = EXCEP_IRQ_LEVEL4;
+#elif CONFIG_GDBSTUB_IRQ_LEVEL == 5
+ IVAR5 = EXCEP_IRQ_LEVEL5;
+#else
+#error "Unknown irq level for gdbstub."
+#endif
+
+ set_intr_stub(NUM2EXCEP_IRQ_LEVEL(CONFIG_GDBSTUB_IRQ_LEVEL),
+ gdbstub_io_rx_handler);
+
+ XIRQxICR(GDBPORT_SERIAL_IRQ) &= ~GxICR_REQUEST;
+ XIRQxICR(GDBPORT_SERIAL_IRQ) =
+ GxICR_ENABLE | NUM2GxICR_LEVEL(CONFIG_GDBSTUB_IRQ_LEVEL);
+ tmp = XIRQxICR(GDBPORT_SERIAL_IRQ);
+
+ GDBPORT_SERIAL_IER = UART_IER_RDI | UART_IER_RLSI;
+
+ /* permit level 0 IRQs to take place */
+ arch_local_change_intr_mask_level(
+ NUM2EPSW_IM(CONFIG_GDBSTUB_IRQ_LEVEL + 1));
+}
+
+/*
+ * set up the GDB stub serial port baud rate timers
+ */
+void gdbstub_io_set_baud(unsigned baud)
+{
+ unsigned value;
+ u8 lcr;
+
+ value = 18432000 / 16 / baud;
+
+ lcr = GDBPORT_SERIAL_LCR;
+ GDBPORT_SERIAL_LCR |= UART_LCR_DLAB;
+ GDBPORT_SERIAL_DLL = value & 0xff;
+ GDBPORT_SERIAL_DLM = (value >> 8) & 0xff;
+ GDBPORT_SERIAL_LCR = lcr;
+}
+
+/*
+ * wait for a character to come from the debugger
+ */
+int gdbstub_io_rx_char(unsigned char *_ch, int nonblock)
+{
+ unsigned ix;
+ u8 ch, st;
+#if defined(CONFIG_MN10300_WD_TIMER)
+ int cpu;
+#endif
+
+ *_ch = 0xff;
+
+ if (gdbstub_rx_unget) {
+ *_ch = gdbstub_rx_unget;
+ gdbstub_rx_unget = 0;
+ return 0;
+ }
+
+ try_again:
+ /* pull chars out of the buffer */
+ ix = gdbstub_rx_outp;
+ barrier();
+ if (ix == gdbstub_rx_inp) {
+ if (nonblock)
+ return -EAGAIN;
+#ifdef CONFIG_MN10300_WD_TIMER
+ for (cpu = 0; cpu < NR_CPUS; cpu++)
+ watchdog_alert_counter[cpu] = 0;
+#endif
+ goto try_again;
+ }
+
+ ch = gdbstub_rx_buffer[ix++];
+ st = gdbstub_rx_buffer[ix++];
+ barrier();
+ gdbstub_rx_outp = ix & 0x00000fff;
+
+ if (st & UART_LSR_BI) {
+ gdbstub_proto("### GDB Rx Break Detected ###\n");
+ return -EINTR;
+ } else if (st & (UART_LSR_FE | UART_LSR_OE | UART_LSR_PE)) {
+ gdbstub_proto("### GDB Rx Error (st=%02x) ###\n", st);
+ return -EIO;
+ } else {
+ gdbstub_proto("### GDB Rx %02x (st=%02x) ###\n", ch, st);
+ *_ch = ch & 0x7f;
+ return 0;
+ }
+}
+
+/*
+ * send a character to the debugger
+ */
+void gdbstub_io_tx_char(unsigned char ch)
+{
+ FLOWCTL_SET(DTR);
+ LSR_WAIT_FOR(THRE);
+ /* FLOWCTL_WAIT_FOR(CTS); */
+
+ if (ch == 0x0a) {
+ GDBPORT_SERIAL_TX = 0x0d;
+ LSR_WAIT_FOR(THRE);
+ /* FLOWCTL_WAIT_FOR(CTS); */
+ }
+ GDBPORT_SERIAL_TX = ch;
+
+ FLOWCTL_CLEAR(DTR);
+}
+
+/*
+ * send a character to the debugger
+ */
+void gdbstub_io_tx_flush(void)
+{
+ LSR_WAIT_FOR(TEMT);
+ LSR_WAIT_FOR(THRE);
+ FLOWCTL_CLEAR(DTR);
+}
diff --git a/arch/mn10300/kernel/gdb-io-ttysm-low.S b/arch/mn10300/kernel/gdb-io-ttysm-low.S
new file mode 100644
index 000000000..060b7cca7
--- /dev/null
+++ b/arch/mn10300/kernel/gdb-io-ttysm-low.S
@@ -0,0 +1,93 @@
+###############################################################################
+#
+# MN10300 On-chip serial Rx interrupt handler for GDB stub I/O
+#
+# Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
+# Written by David Howells (dhowells@redhat.com)
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public Licence
+# as published by the Free Software Foundation; either version
+# 2 of the Licence, or (at your option) any later version.
+#
+###############################################################################
+#include <linux/sys.h>
+#include <linux/linkage.h>
+#include <asm/smp.h>
+#include <asm/thread_info.h>
+#include <asm/cpu-regs.h>
+#include <asm/frame.inc>
+#include <asm/intctl-regs.h>
+#include <unit/serial.h>
+#include "mn10300-serial.h"
+
+ .text
+
+###############################################################################
+#
+# GDB stub serial receive interrupt entry point
+# - intended to run at interrupt priority 0
+#
+###############################################################################
+ .globl gdbstub_io_rx_handler
+ .type gdbstub_io_rx_handler,@function
+gdbstub_io_rx_handler:
+ movm [d2,d3,a2,a3],(sp)
+
+ mov (gdbstub_rx_inp),a3
+gdbstub_io_rx_more:
+ mov a3,a2
+ add 2,a3
+ and PAGE_SIZE_asm-1,a3
+ mov (gdbstub_rx_outp),d3
+ cmp a3,d3
+ beq gdbstub_io_rx_overflow
+
+ movbu (SCgSTR),d3
+ btst SC01STR_RBF,d3
+ beq gdbstub_io_rx_done
+ movbu (SCgRXB),d2
+ movbu d3,(gdbstub_rx_buffer+1,a2)
+ movbu d2,(gdbstub_rx_buffer,a2)
+ mov a3,(gdbstub_rx_inp)
+ bra gdbstub_io_rx_more
+
+gdbstub_io_rx_done:
+ mov GxICR_DETECT,d2
+ movbu d2,(GxICR(SCgRXIRQ)) # ACK the interrupt
+ movhu (GxICR(SCgRXIRQ)),d2 # flush
+
+ movm (sp),[d2,d3,a2,a3]
+ bset 0x01,(gdbstub_busy)
+ beq gdbstub_io_rx_enter
+ rti
+
+gdbstub_io_rx_overflow:
+ bset 0x01,(gdbstub_rx_overflow)
+ bra gdbstub_io_rx_done
+
+###############################################################################
+#
+# debugging interrupt - enter the GDB stub proper
+#
+###############################################################################
+gdbstub_io_rx_enter:
+ or EPSW_IE|EPSW_IM_1,epsw
+ add -4,sp
+ SAVE_ALL
+
+ mov 0xffffffff,d0
+ mov d0,(REG_ORIG_D0,fp)
+ mov 0x280,d1
+
+ mov fp,d0
+ call gdbstub_rx_irq[],0 # gdbstub_io_rx_irq(regs,excep)
+
+ and ~EPSW_IE,epsw
+ bclr 0x01,(gdbstub_busy)
+
+ .globl gdbstub_return
+gdbstub_return:
+ RESTORE_ALL
+
+ .size gdbstub_io_rx_handler,.-gdbstub_io_rx_handler
diff --git a/arch/mn10300/kernel/gdb-io-ttysm.c b/arch/mn10300/kernel/gdb-io-ttysm.c
new file mode 100644
index 000000000..caae8cac9
--- /dev/null
+++ b/arch/mn10300/kernel/gdb-io-ttysm.c
@@ -0,0 +1,303 @@
+/* MN10300 On-chip serial driver for gdbstub I/O
+ *
+ * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/console.h>
+#include <linux/init.h>
+#include <linux/tty.h>
+#include <asm/pgtable.h>
+#include <asm/gdb-stub.h>
+#include <asm/exceptions.h>
+#include <unit/clock.h>
+#include "mn10300-serial.h"
+
+#if defined(CONFIG_GDBSTUB_ON_TTYSM0)
+struct mn10300_serial_port *const gdbstub_port = &mn10300_serial_port_sif0;
+#elif defined(CONFIG_GDBSTUB_ON_TTYSM1)
+struct mn10300_serial_port *const gdbstub_port = &mn10300_serial_port_sif1;
+#else
+struct mn10300_serial_port *const gdbstub_port = &mn10300_serial_port_sif2;
+#endif
+
+
+/*
+ * initialise the GDB stub I/O routines
+ */
+void __init gdbstub_io_init(void)
+{
+ uint16_t scxctr;
+ int tmp;
+
+ switch (gdbstub_port->clock_src) {
+ case MNSCx_CLOCK_SRC_IOCLK:
+ gdbstub_port->ioclk = MN10300_IOCLK;
+ break;
+
+#ifdef MN10300_IOBCLK
+ case MNSCx_CLOCK_SRC_IOBCLK:
+ gdbstub_port->ioclk = MN10300_IOBCLK;
+ break;
+#endif
+ default:
+ BUG();
+ }
+
+ /* set up the serial port */
+ gdbstub_io_set_baud(115200);
+
+ /* we want to get serial receive interrupts */
+ set_intr_level(gdbstub_port->rx_irq,
+ NUM2GxICR_LEVEL(CONFIG_DEBUGGER_IRQ_LEVEL));
+ set_intr_level(gdbstub_port->tx_irq,
+ NUM2GxICR_LEVEL(CONFIG_DEBUGGER_IRQ_LEVEL));
+ set_intr_stub(NUM2EXCEP_IRQ_LEVEL(CONFIG_DEBUGGER_IRQ_LEVEL),
+ gdbstub_io_rx_handler);
+
+ *gdbstub_port->rx_icr |= GxICR_ENABLE;
+ tmp = *gdbstub_port->rx_icr;
+
+ /* enable the device */
+ scxctr = SC01CTR_CLN_8BIT; /* 1N8 */
+ switch (gdbstub_port->div_timer) {
+ case MNSCx_DIV_TIMER_16BIT:
+ scxctr |= SC0CTR_CK_TM8UFLOW_8; /* == SC1CTR_CK_TM9UFLOW_8
+ == SC2CTR_CK_TM10UFLOW_8 */
+ break;
+
+ case MNSCx_DIV_TIMER_8BIT:
+ scxctr |= SC0CTR_CK_TM2UFLOW_8;
+ break;
+ }
+
+ scxctr |= SC01CTR_TXE | SC01CTR_RXE;
+
+ *gdbstub_port->_control = scxctr;
+ tmp = *gdbstub_port->_control;
+
+ /* permit level 0 IRQs only */
+ arch_local_change_intr_mask_level(
+ NUM2EPSW_IM(CONFIG_DEBUGGER_IRQ_LEVEL + 1));
+}
+
+/*
+ * set up the GDB stub serial port baud rate timers
+ */
+void gdbstub_io_set_baud(unsigned baud)
+{
+ const unsigned bits = 10; /* 1 [start] + 8 [data] + 0 [parity] +
+ * 1 [stop] */
+ unsigned long ioclk = gdbstub_port->ioclk;
+ unsigned xdiv, tmp;
+ uint16_t tmxbr;
+ uint8_t tmxmd;
+
+ if (!baud) {
+ baud = 9600;
+ } else if (baud == 134) {
+ baud = 269; /* 134 is really 134.5 */
+ xdiv = 2;
+ }
+
+try_alternative:
+ xdiv = 1;
+
+ switch (gdbstub_port->div_timer) {
+ case MNSCx_DIV_TIMER_16BIT:
+ tmxmd = TM8MD_SRC_IOCLK;
+ tmxbr = tmp = (ioclk / (baud * xdiv) + 4) / 8 - 1;
+ if (tmp > 0 && tmp <= 65535)
+ goto timer_okay;
+
+ tmxmd = TM8MD_SRC_IOCLK_8;
+ tmxbr = tmp = (ioclk / (baud * 8 * xdiv) + 4) / 8 - 1;
+ if (tmp > 0 && tmp <= 65535)
+ goto timer_okay;
+
+ tmxmd = TM8MD_SRC_IOCLK_32;
+ tmxbr = tmp = (ioclk / (baud * 32 * xdiv) + 4) / 8 - 1;
+ if (tmp > 0 && tmp <= 65535)
+ goto timer_okay;
+
+ break;
+
+ case MNSCx_DIV_TIMER_8BIT:
+ tmxmd = TM2MD_SRC_IOCLK;
+ tmxbr = tmp = (ioclk / (baud * xdiv) + 4) / 8 - 1;
+ if (tmp > 0 && tmp <= 255)
+ goto timer_okay;
+
+ tmxmd = TM2MD_SRC_IOCLK_8;
+ tmxbr = tmp = (ioclk / (baud * 8 * xdiv) + 4) / 8 - 1;
+ if (tmp > 0 && tmp <= 255)
+ goto timer_okay;
+
+ tmxmd = TM2MD_SRC_IOCLK_32;
+ tmxbr = tmp = (ioclk / (baud * 32 * xdiv) + 4) / 8 - 1;
+ if (tmp > 0 && tmp <= 255)
+ goto timer_okay;
+ break;
+ }
+
+ /* as a last resort, if the quotient is zero, default to 9600 bps */
+ baud = 9600;
+ goto try_alternative;
+
+timer_okay:
+ gdbstub_port->uart.timeout = (2 * bits * HZ) / baud;
+ gdbstub_port->uart.timeout += HZ / 50;
+
+ /* set the timer to produce the required baud rate */
+ switch (gdbstub_port->div_timer) {
+ case MNSCx_DIV_TIMER_16BIT:
+ *gdbstub_port->_tmxmd = 0;
+ *gdbstub_port->_tmxbr = tmxbr;
+ *gdbstub_port->_tmxmd = TM8MD_INIT_COUNTER;
+ *gdbstub_port->_tmxmd = tmxmd | TM8MD_COUNT_ENABLE;
+ break;
+
+ case MNSCx_DIV_TIMER_8BIT:
+ *gdbstub_port->_tmxmd = 0;
+ *(volatile u8 *) gdbstub_port->_tmxbr = (u8)tmxbr;
+ *gdbstub_port->_tmxmd = TM2MD_INIT_COUNTER;
+ *gdbstub_port->_tmxmd = tmxmd | TM2MD_COUNT_ENABLE;
+ break;
+ }
+}
+
+/*
+ * wait for a character to come from the debugger
+ */
+int gdbstub_io_rx_char(unsigned char *_ch, int nonblock)
+{
+ unsigned ix;
+ u8 ch, st;
+#if defined(CONFIG_MN10300_WD_TIMER)
+ int cpu;
+#endif
+
+ *_ch = 0xff;
+
+ if (gdbstub_rx_unget) {
+ *_ch = gdbstub_rx_unget;
+ gdbstub_rx_unget = 0;
+ return 0;
+ }
+
+try_again:
+ /* pull chars out of the buffer */
+ ix = gdbstub_rx_outp;
+ barrier();
+ if (ix == gdbstub_rx_inp) {
+ if (nonblock)
+ return -EAGAIN;
+#ifdef CONFIG_MN10300_WD_TIMER
+ for (cpu = 0; cpu < NR_CPUS; cpu++)
+ watchdog_alert_counter[cpu] = 0;
+#endif
+ goto try_again;
+ }
+
+ ch = gdbstub_rx_buffer[ix++];
+ st = gdbstub_rx_buffer[ix++];
+ barrier();
+ gdbstub_rx_outp = ix & (PAGE_SIZE - 1);
+
+ st &= SC01STR_RXF | SC01STR_RBF | SC01STR_FEF | SC01STR_PEF |
+ SC01STR_OEF;
+
+ /* deal with what we've got
+ * - note that the UART doesn't do BREAK-detection for us
+ */
+ if (st & SC01STR_FEF && ch == 0) {
+ switch (gdbstub_port->rx_brk) {
+ case 0: gdbstub_port->rx_brk = 1; goto try_again;
+ case 1: gdbstub_port->rx_brk = 2; goto try_again;
+ case 2:
+ gdbstub_port->rx_brk = 3;
+ gdbstub_proto("### GDB MNSERIAL Rx Break Detected"
+ " ###\n");
+ return -EINTR;
+ default:
+ goto try_again;
+ }
+ } else if (st & SC01STR_FEF) {
+ if (gdbstub_port->rx_brk)
+ goto try_again;
+
+ gdbstub_proto("### GDB MNSERIAL Framing Error ###\n");
+ return -EIO;
+ } else if (st & SC01STR_OEF) {
+ if (gdbstub_port->rx_brk)
+ goto try_again;
+
+ gdbstub_proto("### GDB MNSERIAL Overrun Error ###\n");
+ return -EIO;
+ } else if (st & SC01STR_PEF) {
+ if (gdbstub_port->rx_brk)
+ goto try_again;
+
+ gdbstub_proto("### GDB MNSERIAL Parity Error ###\n");
+ return -EIO;
+ } else {
+ /* look for the tail-end char on a break run */
+ if (gdbstub_port->rx_brk == 3) {
+ switch (ch) {
+ case 0xFF:
+ case 0xFE:
+ case 0xFC:
+ case 0xF8:
+ case 0xF0:
+ case 0xE0:
+ case 0xC0:
+ case 0x80:
+ case 0x00:
+ gdbstub_port->rx_brk = 0;
+ goto try_again;
+ default:
+ break;
+ }
+ }
+
+ gdbstub_port->rx_brk = 0;
+ gdbstub_io("### GDB Rx %02x (st=%02x) ###\n", ch, st);
+ *_ch = ch & 0x7f;
+ return 0;
+ }
+}
+
+/*
+ * send a character to the debugger
+ */
+void gdbstub_io_tx_char(unsigned char ch)
+{
+ while (*gdbstub_port->_status & SC01STR_TBF)
+ continue;
+
+ if (ch == 0x0a) {
+ *(u8 *) gdbstub_port->_txb = 0x0d;
+ while (*gdbstub_port->_status & SC01STR_TBF)
+ continue;
+ }
+
+ *(u8 *) gdbstub_port->_txb = ch;
+}
+
+/*
+ * flush the transmission buffers
+ */
+void gdbstub_io_tx_flush(void)
+{
+ while (*gdbstub_port->_status & (SC01STR_TBF | SC01STR_TXF))
+ continue;
+}
diff --git a/arch/mn10300/kernel/gdb-low.S b/arch/mn10300/kernel/gdb-low.S
new file mode 100644
index 000000000..e2725552c
--- /dev/null
+++ b/arch/mn10300/kernel/gdb-low.S
@@ -0,0 +1,115 @@
+###############################################################################
+#
+# MN10300 Low-level gdbstub routines
+#
+# Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
+# Written by David Howells (dhowells@redhat.com)
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public Licence
+# as published by the Free Software Foundation; either version
+# 2 of the Licence, or (at your option) any later version.
+#
+###############################################################################
+#include <linux/sys.h>
+#include <linux/linkage.h>
+#include <asm/smp.h>
+#include <asm/cache.h>
+#include <asm/cpu-regs.h>
+#include <asm/exceptions.h>
+#include <asm/frame.inc>
+#include <asm/serial-regs.h>
+
+ .text
+
+###############################################################################
+#
+# GDB stub read memory with guard
+# - D0 holds the memory address to read
+# - D1 holds the address to store the byte into
+#
+###############################################################################
+ .globl gdbstub_read_byte_guard
+ .globl gdbstub_read_byte_cont
+ENTRY(gdbstub_read_byte)
+ mov d0,a0
+ mov d1,a1
+ clr d0
+gdbstub_read_byte_guard:
+ movbu (a0),d1
+gdbstub_read_byte_cont:
+ movbu d1,(a1)
+ ret [],0
+
+ .globl gdbstub_read_word_guard
+ .globl gdbstub_read_word_cont
+ENTRY(gdbstub_read_word)
+ mov d0,a0
+ mov d1,a1
+ clr d0
+gdbstub_read_word_guard:
+ movhu (a0),d1
+gdbstub_read_word_cont:
+ movhu d1,(a1)
+ ret [],0
+
+ .globl gdbstub_read_dword_guard
+ .globl gdbstub_read_dword_cont
+ENTRY(gdbstub_read_dword)
+ mov d0,a0
+ mov d1,a1
+ clr d0
+gdbstub_read_dword_guard:
+ mov (a0),d1
+gdbstub_read_dword_cont:
+ mov d1,(a1)
+ ret [],0
+
+###############################################################################
+#
+# GDB stub write memory with guard
+# - D0 holds the byte to store
+# - D1 holds the memory address to write
+#
+###############################################################################
+ .globl gdbstub_write_byte_guard
+ .globl gdbstub_write_byte_cont
+ENTRY(gdbstub_write_byte)
+ mov d0,a0
+ mov d1,a1
+ clr d0
+gdbstub_write_byte_guard:
+ movbu a0,(a1)
+gdbstub_write_byte_cont:
+ ret [],0
+
+ .globl gdbstub_write_word_guard
+ .globl gdbstub_write_word_cont
+ENTRY(gdbstub_write_word)
+ mov d0,a0
+ mov d1,a1
+ clr d0
+gdbstub_write_word_guard:
+ movhu a0,(a1)
+gdbstub_write_word_cont:
+ ret [],0
+
+ .globl gdbstub_write_dword_guard
+ .globl gdbstub_write_dword_cont
+ENTRY(gdbstub_write_dword)
+ mov d0,a0
+ mov d1,a1
+ clr d0
+gdbstub_write_dword_guard:
+ mov a0,(a1)
+gdbstub_write_dword_cont:
+ ret [],0
+
+###############################################################################
+#
+# GDB stub BUG() trap
+#
+###############################################################################
+ENTRY(__gdbstub_bug_trap)
+ .byte 0xF7,0xF7 # don't use 0xFF as the JTAG unit preempts that
+ ret [],0
diff --git a/arch/mn10300/kernel/gdb-stub.c b/arch/mn10300/kernel/gdb-stub.c
new file mode 100644
index 000000000..a128c57b5
--- /dev/null
+++ b/arch/mn10300/kernel/gdb-stub.c
@@ -0,0 +1,1923 @@
+/* MN10300 GDB stub
+ *
+ * Originally written by Glenn Engel, Lake Stevens Instrument Division
+ *
+ * Contributed by HP Systems
+ *
+ * Modified for SPARC by Stu Grossman, Cygnus Support.
+ *
+ * Modified for Linux/MIPS (and MIPS in general) by Andreas Busse
+ * Send complaints, suggestions etc. to <andy@waldorf-gmbh.de>
+ *
+ * Copyright (C) 1995 Andreas Busse
+ *
+ * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
+ * Modified for Linux/mn10300 by David Howells <dhowells@redhat.com>
+ */
+
+/*
+ * To enable debugger support, two things need to happen. One, 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.
+ * Two, a breakpoint needs to be generated to begin communication. This
+ * is most easily accomplished by a call to breakpoint(). Breakpoint()
+ * simulates a breakpoint by executing a BREAK instruction.
+ *
+ *
+ * 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
+ *
+ * c Resume at current address SNN ( signal NN)
+ * cAA..AA Continue at address AA..AA SNN
+ *
+ * s Step one instruction SNN
+ * sAA..AA Step one instruction from AA..AA SNN
+ *
+ * k kill
+ *
+ * ? What was the last sigval ? SNN (signal NN)
+ *
+ * bBB..BB Set baud rate to BB..BB OK or BNN, then sets
+ * baud rate
+ *
+ * All commands and responses are sent with a packet which includes a
+ * checksum. A packet consists of
+ *
+ * $<packet info>#<checksum>.
+ *
+ * where
+ * <packet info> :: <characters representing the command or response>
+ * <checksum> :: < two hex digits computed as modulo 256 sum of <packetinfo>>
+ *
+ * When a packet is received, it is first acknowledged with either '+' or '-'.
+ * '+' indicates a successful transfer. '-' indicates a failed transfer.
+ *
+ * Example:
+ *
+ * Host: Reply:
+ * $m0,10#2a +$00010203040506070809101112131415#42
+ *
+ *
+ * ==============
+ * MORE EXAMPLES:
+ * ==============
+ *
+ * For reference -- the following are the steps that one
+ * company took (RidgeRun Inc) to get remote gdb debugging
+ * going. In this scenario the host machine was a PC and the
+ * target platform was a Galileo EVB64120A MIPS evaluation
+ * board.
+ *
+ * Step 1:
+ * First download gdb-5.0.tar.gz from the internet.
+ * and then build/install the package.
+ *
+ * Example:
+ * $ tar zxf gdb-5.0.tar.gz
+ * $ cd gdb-5.0
+ * $ ./configure --target=am33_2.0-linux-gnu
+ * $ make
+ * $ install
+ * am33_2.0-linux-gnu-gdb
+ *
+ * Step 2:
+ * Configure linux for remote debugging and build it.
+ *
+ * Example:
+ * $ cd ~/linux
+ * $ make menuconfig <go to "Kernel Hacking" and turn on remote debugging>
+ * $ make dep; make vmlinux
+ *
+ * Step 3:
+ * Download the kernel to the remote target and start
+ * the kernel running. It will promptly halt and wait
+ * for the host gdb session to connect. It does this
+ * since the "Kernel Hacking" option has defined
+ * CONFIG_REMOTE_DEBUG which in turn enables your calls
+ * to:
+ * set_debug_traps();
+ * breakpoint();
+ *
+ * Step 4:
+ * Start the gdb session on the host.
+ *
+ * Example:
+ * $ am33_2.0-linux-gnu-gdb vmlinux
+ * (gdb) set remotebaud 115200
+ * (gdb) target remote /dev/ttyS1
+ * ...at this point you are connected to
+ * the remote target and can use gdb
+ * in the normal fasion. Setting
+ * breakpoints, single stepping,
+ * printing variables, etc.
+ *
+ */
+
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/console.h>
+#include <linux/init.h>
+#include <linux/bug.h>
+
+#include <asm/pgtable.h>
+#include <asm/gdb-stub.h>
+#include <asm/exceptions.h>
+#include <asm/debugger.h>
+#include <asm/serial-regs.h>
+#include <asm/busctl-regs.h>
+#include <unit/leds.h>
+#include <unit/serial.h>
+
+/* define to use F7F7 rather than FF which is subverted by JTAG debugger */
+#undef GDBSTUB_USE_F7F7_AS_BREAKPOINT
+
+/*
+ * BUFMAX defines the maximum number of characters in inbound/outbound buffers
+ * at least NUMREGBYTES*2 are needed for register packets
+ */
+#define BUFMAX 2048
+
+static const char gdbstub_banner[] =
+ "Linux/MN10300 GDB Stub (c) RedHat 2007\n";
+
+u8 gdbstub_rx_buffer[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE)));
+u32 gdbstub_rx_inp;
+u32 gdbstub_rx_outp;
+u8 gdbstub_busy;
+u8 gdbstub_rx_overflow;
+u8 gdbstub_rx_unget;
+
+static u8 gdbstub_flush_caches;
+static char input_buffer[BUFMAX];
+static char output_buffer[BUFMAX];
+static char trans_buffer[BUFMAX];
+
+struct gdbstub_bkpt {
+ u8 *addr; /* address of breakpoint */
+ u8 len; /* size of breakpoint */
+ u8 origbytes[7]; /* original bytes */
+};
+
+static struct gdbstub_bkpt gdbstub_bkpts[256];
+
+/*
+ * local prototypes
+ */
+static void getpacket(char *buffer);
+static int putpacket(char *buffer);
+static int computeSignal(enum exception_code excep);
+static int hex(unsigned char ch);
+static int hexToInt(char **ptr, int *intValue);
+static unsigned char *mem2hex(const void *mem, char *buf, int count,
+ int may_fault);
+static const char *hex2mem(const char *buf, void *_mem, int count,
+ int may_fault);
+
+/*
+ * Convert ch from a hex digit to an int
+ */
+static int hex(unsigned 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;
+}
+
+#ifdef CONFIG_GDBSTUB_DEBUGGING
+
+void debug_to_serial(const char *p, int n)
+{
+ __debug_to_serial(p, n);
+ /* gdbstub_console_write(NULL, p, n); */
+}
+
+void gdbstub_printk(const char *fmt, ...)
+{
+ va_list args;
+ int len;
+
+ /* Emit the output into the temporary buffer */
+ va_start(args, fmt);
+ len = vsnprintf(trans_buffer, sizeof(trans_buffer), fmt, args);
+ va_end(args);
+ debug_to_serial(trans_buffer, len);
+}
+
+#endif
+
+static inline char *gdbstub_strcpy(char *dst, const char *src)
+{
+ int loop = 0;
+ while ((dst[loop] = src[loop]))
+ loop++;
+ return dst;
+}
+
+/*
+ * scan for the sequence $<data>#<checksum>
+ */
+static void getpacket(char *buffer)
+{
+ unsigned char checksum;
+ unsigned char xmitcsum;
+ unsigned char ch;
+ int count, i, ret, error;
+
+ for (;;) {
+ /*
+ * wait around for the start character,
+ * ignore all other characters
+ */
+ do {
+ gdbstub_io_rx_char(&ch, 0);
+ } while (ch != '$');
+
+ checksum = 0;
+ xmitcsum = -1;
+ count = 0;
+ error = 0;
+
+ /*
+ * now, read until a # or end of buffer is found
+ */
+ while (count < BUFMAX) {
+ ret = gdbstub_io_rx_char(&ch, 0);
+ if (ret < 0)
+ error = ret;
+
+ if (ch == '#')
+ break;
+ checksum += ch;
+ buffer[count] = ch;
+ count++;
+ }
+
+ if (error == -EIO) {
+ gdbstub_proto("### GDB Rx Error - Skipping packet"
+ " ###\n");
+ gdbstub_proto("### GDB Tx NAK\n");
+ gdbstub_io_tx_char('-');
+ continue;
+ }
+
+ if (count >= BUFMAX || error)
+ continue;
+
+ buffer[count] = 0;
+
+ /* read the checksum */
+ ret = gdbstub_io_rx_char(&ch, 0);
+ if (ret < 0)
+ error = ret;
+ xmitcsum = hex(ch) << 4;
+
+ ret = gdbstub_io_rx_char(&ch, 0);
+ if (ret < 0)
+ error = ret;
+ xmitcsum |= hex(ch);
+
+ if (error) {
+ if (error == -EIO)
+ gdbstub_io("### GDB Rx Error -"
+ " Skipping packet\n");
+ gdbstub_io("### GDB Tx NAK\n");
+ gdbstub_io_tx_char('-');
+ continue;
+ }
+
+ /* check the checksum */
+ if (checksum != xmitcsum) {
+ gdbstub_io("### GDB Tx NAK\n");
+ gdbstub_io_tx_char('-'); /* failed checksum */
+ continue;
+ }
+
+ gdbstub_proto("### GDB Rx '$%s#%02x' ###\n", buffer, checksum);
+ gdbstub_io("### GDB Tx ACK\n");
+ gdbstub_io_tx_char('+'); /* successful transfer */
+
+ /*
+ * if a sequence char is present,
+ * reply the sequence ID
+ */
+ if (buffer[2] == ':') {
+ gdbstub_io_tx_char(buffer[0]);
+ gdbstub_io_tx_char(buffer[1]);
+
+ /*
+ * remove sequence chars from buffer
+ */
+ count = 0;
+ while (buffer[count])
+ count++;
+ for (i = 3; i <= count; i++)
+ buffer[i - 3] = buffer[i];
+ }
+
+ break;
+ }
+}
+
+/*
+ * send the packet in buffer.
+ * - return 0 if successfully ACK'd
+ * - return 1 if abandoned due to new incoming packet
+ */
+static int putpacket(char *buffer)
+{
+ unsigned char checksum;
+ unsigned char ch;
+ int count;
+
+ /*
+ * $<packet info>#<checksum>.
+ */
+ gdbstub_proto("### GDB Tx $'%s'#?? ###\n", buffer);
+
+ do {
+ gdbstub_io_tx_char('$');
+ checksum = 0;
+ count = 0;
+
+ while ((ch = buffer[count]) != 0) {
+ gdbstub_io_tx_char(ch);
+ checksum += ch;
+ count += 1;
+ }
+
+ gdbstub_io_tx_char('#');
+ gdbstub_io_tx_char(hex_asc_hi(checksum));
+ gdbstub_io_tx_char(hex_asc_lo(checksum));
+
+ } while (gdbstub_io_rx_char(&ch, 0),
+ ch == '-' && (gdbstub_io("### GDB Rx NAK\n"), 0),
+ ch != '-' && ch != '+' &&
+ (gdbstub_io("### GDB Rx ??? %02x\n", ch), 0),
+ ch != '+' && ch != '$');
+
+ if (ch == '+') {
+ gdbstub_io("### GDB Rx ACK\n");
+ return 0;
+ }
+
+ gdbstub_io("### GDB Tx Abandoned\n");
+ gdbstub_rx_unget = ch;
+ return 1;
+}
+
+/*
+ * While we find nice hex chars, build an int.
+ * Return number of chars processed.
+ */
+static int hexToInt(char **ptr, int *intValue)
+{
+ int numChars = 0;
+ int hexValue;
+
+ *intValue = 0;
+
+ while (**ptr) {
+ hexValue = hex(**ptr);
+ if (hexValue < 0)
+ break;
+
+ *intValue = (*intValue << 4) | hexValue;
+ numChars++;
+
+ (*ptr)++;
+ }
+
+ return (numChars);
+}
+
+#ifdef CONFIG_GDBSTUB_ALLOW_SINGLE_STEP
+/*
+ * We single-step by setting breakpoints. When an exception
+ * is handled, we need to restore the instructions hoisted
+ * when the breakpoints were set.
+ *
+ * This is where we save the original instructions.
+ */
+static struct gdb_bp_save {
+ u8 *addr;
+ u8 opcode[2];
+} step_bp[2];
+
+static const unsigned char gdbstub_insn_sizes[256] =
+{
+ /* 1 2 3 4 5 6 7 8 9 a b c d e f */
+ 1, 3, 3, 3, 1, 3, 3, 3, 1, 3, 3, 3, 1, 3, 3, 3, /* 0 */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 1 */
+ 2, 2, 2, 2, 3, 3, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3, /* 2 */
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 1, 1, 1, /* 3 */
+ 1, 1, 2, 2, 1, 1, 2, 2, 1, 1, 2, 2, 1, 1, 2, 2, /* 4 */
+ 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, /* 5 */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 6 */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 7 */
+ 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, /* 8 */
+ 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, /* 9 */
+ 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, /* a */
+ 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, /* b */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 2, /* c */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* d */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* e */
+ 0, 2, 2, 2, 2, 2, 2, 4, 0, 3, 0, 4, 0, 6, 7, 1 /* f */
+};
+
+static int __gdbstub_mark_bp(u8 *addr, int ix)
+{
+ /* vmalloc area */
+ if (((u8 *) VMALLOC_START <= addr) && (addr < (u8 *) VMALLOC_END))
+ goto okay;
+ /* SRAM, SDRAM */
+ if (((u8 *) 0x80000000UL <= addr) && (addr < (u8 *) 0xa0000000UL))
+ goto okay;
+ return 0;
+
+okay:
+ if (gdbstub_read_byte(addr + 0, &step_bp[ix].opcode[0]) < 0 ||
+ gdbstub_read_byte(addr + 1, &step_bp[ix].opcode[1]) < 0)
+ return 0;
+
+ step_bp[ix].addr = addr;
+ return 1;
+}
+
+static inline void __gdbstub_restore_bp(void)
+{
+#ifdef GDBSTUB_USE_F7F7_AS_BREAKPOINT
+ if (step_bp[0].addr) {
+ gdbstub_write_byte(step_bp[0].opcode[0], step_bp[0].addr + 0);
+ gdbstub_write_byte(step_bp[0].opcode[1], step_bp[0].addr + 1);
+ }
+ if (step_bp[1].addr) {
+ gdbstub_write_byte(step_bp[1].opcode[0], step_bp[1].addr + 0);
+ gdbstub_write_byte(step_bp[1].opcode[1], step_bp[1].addr + 1);
+ }
+#else
+ if (step_bp[0].addr)
+ gdbstub_write_byte(step_bp[0].opcode[0], step_bp[0].addr + 0);
+ if (step_bp[1].addr)
+ gdbstub_write_byte(step_bp[1].opcode[0], step_bp[1].addr + 0);
+#endif
+
+ gdbstub_flush_caches = 1;
+
+ step_bp[0].addr = NULL;
+ step_bp[0].opcode[0] = 0;
+ step_bp[0].opcode[1] = 0;
+ step_bp[1].addr = NULL;
+ step_bp[1].opcode[0] = 0;
+ step_bp[1].opcode[1] = 0;
+}
+
+/*
+ * emulate single stepping by means of breakpoint instructions
+ */
+static int gdbstub_single_step(struct pt_regs *regs)
+{
+ unsigned size;
+ uint32_t x;
+ uint8_t cur, *pc, *sp;
+
+ step_bp[0].addr = NULL;
+ step_bp[0].opcode[0] = 0;
+ step_bp[0].opcode[1] = 0;
+ step_bp[1].addr = NULL;
+ step_bp[1].opcode[0] = 0;
+ step_bp[1].opcode[1] = 0;
+ x = 0;
+
+ pc = (u8 *) regs->pc;
+ sp = (u8 *) (regs + 1);
+ if (gdbstub_read_byte(pc, &cur) < 0)
+ return -EFAULT;
+
+ gdbstub_bkpt("Single Step from %p { %02x }\n", pc, cur);
+
+ gdbstub_flush_caches = 1;
+
+ size = gdbstub_insn_sizes[cur];
+ if (size > 0) {
+ if (!__gdbstub_mark_bp(pc + size, 0))
+ goto fault;
+ } else {
+ switch (cur) {
+ /* Bxx (d8,PC) */
+ case 0xc0 ... 0xca:
+ if (gdbstub_read_byte(pc + 1, (u8 *) &x) < 0)
+ goto fault;
+ if (!__gdbstub_mark_bp(pc + 2, 0))
+ goto fault;
+ if ((x < 0 || x > 2) &&
+ !__gdbstub_mark_bp(pc + (s8) x, 1))
+ goto fault;
+ break;
+
+ /* LXX (d8,PC) */
+ case 0xd0 ... 0xda:
+ if (!__gdbstub_mark_bp(pc + 1, 0))
+ goto fault;
+ if (regs->pc != regs->lar &&
+ !__gdbstub_mark_bp((u8 *) regs->lar, 1))
+ goto fault;
+ break;
+
+ /* SETLB - loads the next for bytes into the LIR
+ * register */
+ case 0xdb:
+ if (!__gdbstub_mark_bp(pc + 1, 0))
+ goto fault;
+ break;
+
+ /* JMP (d16,PC) or CALL (d16,PC) */
+ case 0xcc:
+ case 0xcd:
+ if (gdbstub_read_byte(pc + 1, ((u8 *) &x) + 0) < 0 ||
+ gdbstub_read_byte(pc + 2, ((u8 *) &x) + 1) < 0)
+ goto fault;
+ if (!__gdbstub_mark_bp(pc + (s16) x, 0))
+ goto fault;
+ break;
+
+ /* JMP (d32,PC) or CALL (d32,PC) */
+ case 0xdc:
+ case 0xdd:
+ if (gdbstub_read_byte(pc + 1, ((u8 *) &x) + 0) < 0 ||
+ gdbstub_read_byte(pc + 2, ((u8 *) &x) + 1) < 0 ||
+ gdbstub_read_byte(pc + 3, ((u8 *) &x) + 2) < 0 ||
+ gdbstub_read_byte(pc + 4, ((u8 *) &x) + 3) < 0)
+ goto fault;
+ if (!__gdbstub_mark_bp(pc + (s32) x, 0))
+ goto fault;
+ break;
+
+ /* RETF */
+ case 0xde:
+ if (!__gdbstub_mark_bp((u8 *) regs->mdr, 0))
+ goto fault;
+ break;
+
+ /* RET */
+ case 0xdf:
+ if (gdbstub_read_byte(pc + 2, (u8 *) &x) < 0)
+ goto fault;
+ sp += (s8)x;
+ if (gdbstub_read_byte(sp + 0, ((u8 *) &x) + 0) < 0 ||
+ gdbstub_read_byte(sp + 1, ((u8 *) &x) + 1) < 0 ||
+ gdbstub_read_byte(sp + 2, ((u8 *) &x) + 2) < 0 ||
+ gdbstub_read_byte(sp + 3, ((u8 *) &x) + 3) < 0)
+ goto fault;
+ if (!__gdbstub_mark_bp((u8 *) x, 0))
+ goto fault;
+ break;
+
+ case 0xf0:
+ if (gdbstub_read_byte(pc + 1, &cur) < 0)
+ goto fault;
+
+ if (cur >= 0xf0 && cur <= 0xf7) {
+ /* JMP (An) / CALLS (An) */
+ switch (cur & 3) {
+ case 0: x = regs->a0; break;
+ case 1: x = regs->a1; break;
+ case 2: x = regs->a2; break;
+ case 3: x = regs->a3; break;
+ }
+ if (!__gdbstub_mark_bp((u8 *) x, 0))
+ goto fault;
+ } else if (cur == 0xfc) {
+ /* RETS */
+ if (gdbstub_read_byte(
+ sp + 0, ((u8 *) &x) + 0) < 0 ||
+ gdbstub_read_byte(
+ sp + 1, ((u8 *) &x) + 1) < 0 ||
+ gdbstub_read_byte(
+ sp + 2, ((u8 *) &x) + 2) < 0 ||
+ gdbstub_read_byte(
+ sp + 3, ((u8 *) &x) + 3) < 0)
+ goto fault;
+ if (!__gdbstub_mark_bp((u8 *) x, 0))
+ goto fault;
+ } else if (cur == 0xfd) {
+ /* RTI */
+ if (gdbstub_read_byte(
+ sp + 4, ((u8 *) &x) + 0) < 0 ||
+ gdbstub_read_byte(
+ sp + 5, ((u8 *) &x) + 1) < 0 ||
+ gdbstub_read_byte(
+ sp + 6, ((u8 *) &x) + 2) < 0 ||
+ gdbstub_read_byte(
+ sp + 7, ((u8 *) &x) + 3) < 0)
+ goto fault;
+ if (!__gdbstub_mark_bp((u8 *) x, 0))
+ goto fault;
+ } else {
+ if (!__gdbstub_mark_bp(pc + 2, 0))
+ goto fault;
+ }
+
+ break;
+
+ /* potential 3-byte conditional branches */
+ case 0xf8:
+ if (gdbstub_read_byte(pc + 1, &cur) < 0)
+ goto fault;
+ if (!__gdbstub_mark_bp(pc + 3, 0))
+ goto fault;
+
+ if (cur >= 0xe8 && cur <= 0xeb) {
+ if (gdbstub_read_byte(
+ pc + 2, ((u8 *) &x) + 0) < 0)
+ goto fault;
+ if ((x < 0 || x > 3) &&
+ !__gdbstub_mark_bp(pc + (s8) x, 1))
+ goto fault;
+ }
+ break;
+
+ case 0xfa:
+ if (gdbstub_read_byte(pc + 1, &cur) < 0)
+ goto fault;
+
+ if (cur == 0xff) {
+ /* CALLS (d16,PC) */
+ if (gdbstub_read_byte(
+ pc + 2, ((u8 *) &x) + 0) < 0 ||
+ gdbstub_read_byte(
+ pc + 3, ((u8 *) &x) + 1) < 0)
+ goto fault;
+ if (!__gdbstub_mark_bp(pc + (s16) x, 0))
+ goto fault;
+ } else {
+ if (!__gdbstub_mark_bp(pc + 4, 0))
+ goto fault;
+ }
+ break;
+
+ case 0xfc:
+ if (gdbstub_read_byte(pc + 1, &cur) < 0)
+ goto fault;
+ if (cur == 0xff) {
+ /* CALLS (d32,PC) */
+ if (gdbstub_read_byte(
+ pc + 2, ((u8 *) &x) + 0) < 0 ||
+ gdbstub_read_byte(
+ pc + 3, ((u8 *) &x) + 1) < 0 ||
+ gdbstub_read_byte(
+ pc + 4, ((u8 *) &x) + 2) < 0 ||
+ gdbstub_read_byte(
+ pc + 5, ((u8 *) &x) + 3) < 0)
+ goto fault;
+ if (!__gdbstub_mark_bp(
+ pc + (s32) x, 0))
+ goto fault;
+ } else {
+ if (!__gdbstub_mark_bp(
+ pc + 6, 0))
+ goto fault;
+ }
+ break;
+
+ }
+ }
+
+ gdbstub_bkpt("Step: %02x at %p; %02x at %p\n",
+ step_bp[0].opcode[0], step_bp[0].addr,
+ step_bp[1].opcode[0], step_bp[1].addr);
+
+ if (step_bp[0].addr) {
+#ifdef GDBSTUB_USE_F7F7_AS_BREAKPOINT
+ if (gdbstub_write_byte(0xF7, step_bp[0].addr + 0) < 0 ||
+ gdbstub_write_byte(0xF7, step_bp[0].addr + 1) < 0)
+ goto fault;
+#else
+ if (gdbstub_write_byte(0xFF, step_bp[0].addr + 0) < 0)
+ goto fault;
+#endif
+ }
+
+ if (step_bp[1].addr) {
+#ifdef GDBSTUB_USE_F7F7_AS_BREAKPOINT
+ if (gdbstub_write_byte(0xF7, step_bp[1].addr + 0) < 0 ||
+ gdbstub_write_byte(0xF7, step_bp[1].addr + 1) < 0)
+ goto fault;
+#else
+ if (gdbstub_write_byte(0xFF, step_bp[1].addr + 0) < 0)
+ goto fault;
+#endif
+ }
+
+ return 0;
+
+ fault:
+ /* uh-oh - silly address alert, try and restore things */
+ __gdbstub_restore_bp();
+ return -EFAULT;
+}
+#endif /* CONFIG_GDBSTUB_ALLOW_SINGLE_STEP */
+
+#ifdef CONFIG_GDBSTUB_CONSOLE
+
+void gdbstub_console_write(struct console *con, const char *p, unsigned n)
+{
+ static const char gdbstub_cr[] = { 0x0d };
+ char outbuf[26];
+ int qty;
+ u8 busy;
+
+ busy = gdbstub_busy;
+ gdbstub_busy = 1;
+
+ outbuf[0] = 'O';
+
+ while (n > 0) {
+ qty = 1;
+
+ while (n > 0 && qty < 20) {
+ mem2hex(p, outbuf + qty, 2, 0);
+ qty += 2;
+ if (*p == 0x0a) {
+ mem2hex(gdbstub_cr, outbuf + qty, 2, 0);
+ qty += 2;
+ }
+ p++;
+ n--;
+ }
+
+ outbuf[qty] = 0;
+ putpacket(outbuf);
+ }
+
+ gdbstub_busy = busy;
+}
+
+static kdev_t gdbstub_console_dev(struct console *con)
+{
+ return MKDEV(1, 3); /* /dev/null */
+}
+
+static struct console gdbstub_console = {
+ .name = "gdb",
+ .write = gdbstub_console_write,
+ .device = gdbstub_console_dev,
+ .flags = CON_PRINTBUFFER,
+ .index = -1,
+};
+
+#endif
+
+/*
+ * Convert the memory pointed to by mem into hex, placing result in buf.
+ * - if successful, return a pointer to the last char put in buf (NUL)
+ * - in case of mem fault, return NULL
+ * may_fault is non-zero if we are reading from arbitrary memory, but is
+ * currently not used.
+ */
+static
+unsigned char *mem2hex(const void *_mem, char *buf, int count, int may_fault)
+{
+ const u8 *mem = _mem;
+ u8 ch[4];
+
+ if ((u32) mem & 1 && count >= 1) {
+ if (gdbstub_read_byte(mem, ch) != 0)
+ return 0;
+ buf = hex_byte_pack(buf, ch[0]);
+ mem++;
+ count--;
+ }
+
+ if ((u32) mem & 3 && count >= 2) {
+ if (gdbstub_read_word(mem, ch) != 0)
+ return 0;
+ buf = hex_byte_pack(buf, ch[0]);
+ buf = hex_byte_pack(buf, ch[1]);
+ mem += 2;
+ count -= 2;
+ }
+
+ while (count >= 4) {
+ if (gdbstub_read_dword(mem, ch) != 0)
+ return 0;
+ buf = hex_byte_pack(buf, ch[0]);
+ buf = hex_byte_pack(buf, ch[1]);
+ buf = hex_byte_pack(buf, ch[2]);
+ buf = hex_byte_pack(buf, ch[3]);
+ mem += 4;
+ count -= 4;
+ }
+
+ if (count >= 2) {
+ if (gdbstub_read_word(mem, ch) != 0)
+ return 0;
+ buf = hex_byte_pack(buf, ch[0]);
+ buf = hex_byte_pack(buf, ch[1]);
+ mem += 2;
+ count -= 2;
+ }
+
+ if (count >= 1) {
+ if (gdbstub_read_byte(mem, ch) != 0)
+ return 0;
+ buf = hex_byte_pack(buf, ch[0]);
+ }
+
+ *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
+ * may_fault is non-zero if we are reading from arbitrary memory, but is
+ * currently not used.
+ */
+static
+const char *hex2mem(const char *buf, void *_mem, int count, int may_fault)
+{
+ u8 *mem = _mem;
+ union {
+ u32 val;
+ u8 b[4];
+ } ch;
+
+ if ((u32) mem & 1 && count >= 1) {
+ ch.b[0] = hex(*buf++) << 4;
+ ch.b[0] |= hex(*buf++);
+ if (gdbstub_write_byte(ch.val, mem) != 0)
+ return 0;
+ mem++;
+ count--;
+ }
+
+ if ((u32) mem & 3 && count >= 2) {
+ ch.b[0] = hex(*buf++) << 4;
+ ch.b[0] |= hex(*buf++);
+ ch.b[1] = hex(*buf++) << 4;
+ ch.b[1] |= hex(*buf++);
+ if (gdbstub_write_word(ch.val, mem) != 0)
+ return 0;
+ mem += 2;
+ count -= 2;
+ }
+
+ while (count >= 4) {
+ ch.b[0] = hex(*buf++) << 4;
+ ch.b[0] |= hex(*buf++);
+ ch.b[1] = hex(*buf++) << 4;
+ ch.b[1] |= hex(*buf++);
+ ch.b[2] = hex(*buf++) << 4;
+ ch.b[2] |= hex(*buf++);
+ ch.b[3] = hex(*buf++) << 4;
+ ch.b[3] |= hex(*buf++);
+ if (gdbstub_write_dword(ch.val, mem) != 0)
+ return 0;
+ mem += 4;
+ count -= 4;
+ }
+
+ if (count >= 2) {
+ ch.b[0] = hex(*buf++) << 4;
+ ch.b[0] |= hex(*buf++);
+ ch.b[1] = hex(*buf++) << 4;
+ ch.b[1] |= hex(*buf++);
+ if (gdbstub_write_word(ch.val, mem) != 0)
+ return 0;
+ mem += 2;
+ count -= 2;
+ }
+
+ if (count >= 1) {
+ ch.b[0] = hex(*buf++) << 4;
+ ch.b[0] |= hex(*buf++);
+ if (gdbstub_write_byte(ch.val, mem) != 0)
+ return 0;
+ }
+
+ return buf;
+}
+
+/*
+ * This table contains the mapping between MN10300 exception codes, and
+ * signals, which are primarily what GDB understands. It also indicates
+ * which hardware traps we need to commandeer when initializing the stub.
+ */
+static const struct excep_to_sig_map {
+ enum exception_code excep; /* MN10300 exception code */
+ unsigned char signo; /* Signal that we map this into */
+} excep_to_sig_map[] = {
+ { EXCEP_ITLBMISS, SIGSEGV },
+ { EXCEP_DTLBMISS, SIGSEGV },
+ { EXCEP_TRAP, SIGTRAP },
+ { EXCEP_ISTEP, SIGTRAP },
+ { EXCEP_IBREAK, SIGTRAP },
+ { EXCEP_OBREAK, SIGTRAP },
+ { EXCEP_UNIMPINS, SIGILL },
+ { EXCEP_UNIMPEXINS, SIGILL },
+ { EXCEP_MEMERR, SIGSEGV },
+ { EXCEP_MISALIGN, SIGSEGV },
+ { EXCEP_BUSERROR, SIGBUS },
+ { EXCEP_ILLINSACC, SIGSEGV },
+ { EXCEP_ILLDATACC, SIGSEGV },
+ { EXCEP_IOINSACC, SIGSEGV },
+ { EXCEP_PRIVINSACC, SIGSEGV },
+ { EXCEP_PRIVDATACC, SIGSEGV },
+ { EXCEP_FPU_DISABLED, SIGFPE },
+ { EXCEP_FPU_UNIMPINS, SIGFPE },
+ { EXCEP_FPU_OPERATION, SIGFPE },
+ { EXCEP_WDT, SIGALRM },
+ { EXCEP_NMI, SIGQUIT },
+ { EXCEP_IRQ_LEVEL0, SIGINT },
+ { EXCEP_IRQ_LEVEL1, SIGINT },
+ { EXCEP_IRQ_LEVEL2, SIGINT },
+ { EXCEP_IRQ_LEVEL3, SIGINT },
+ { EXCEP_IRQ_LEVEL4, SIGINT },
+ { EXCEP_IRQ_LEVEL5, SIGINT },
+ { EXCEP_IRQ_LEVEL6, SIGINT },
+ { 0, 0}
+};
+
+/*
+ * convert the MN10300 exception code into a UNIX signal number
+ */
+static int computeSignal(enum exception_code excep)
+{
+ const struct excep_to_sig_map *map;
+
+ for (map = excep_to_sig_map; map->signo; map++)
+ if (map->excep == excep)
+ return map->signo;
+
+ return SIGHUP; /* default for things we don't know about */
+}
+
+static u32 gdbstub_fpcr, gdbstub_fpufs_array[32];
+
+/*
+ *
+ */
+static void gdbstub_store_fpu(void)
+{
+#ifdef CONFIG_FPU
+
+ asm volatile(
+ "or %2,epsw\n"
+#ifdef CONFIG_MN10300_PROC_MN103E010
+ "nop\n"
+ "nop\n"
+#endif
+ "mov %1, a1\n"
+ "fmov fs0, (a1+)\n"
+ "fmov fs1, (a1+)\n"
+ "fmov fs2, (a1+)\n"
+ "fmov fs3, (a1+)\n"
+ "fmov fs4, (a1+)\n"
+ "fmov fs5, (a1+)\n"
+ "fmov fs6, (a1+)\n"
+ "fmov fs7, (a1+)\n"
+ "fmov fs8, (a1+)\n"
+ "fmov fs9, (a1+)\n"
+ "fmov fs10, (a1+)\n"
+ "fmov fs11, (a1+)\n"
+ "fmov fs12, (a1+)\n"
+ "fmov fs13, (a1+)\n"
+ "fmov fs14, (a1+)\n"
+ "fmov fs15, (a1+)\n"
+ "fmov fs16, (a1+)\n"
+ "fmov fs17, (a1+)\n"
+ "fmov fs18, (a1+)\n"
+ "fmov fs19, (a1+)\n"
+ "fmov fs20, (a1+)\n"
+ "fmov fs21, (a1+)\n"
+ "fmov fs22, (a1+)\n"
+ "fmov fs23, (a1+)\n"
+ "fmov fs24, (a1+)\n"
+ "fmov fs25, (a1+)\n"
+ "fmov fs26, (a1+)\n"
+ "fmov fs27, (a1+)\n"
+ "fmov fs28, (a1+)\n"
+ "fmov fs29, (a1+)\n"
+ "fmov fs30, (a1+)\n"
+ "fmov fs31, (a1+)\n"
+ "fmov fpcr, %0\n"
+ : "=d"(gdbstub_fpcr)
+ : "g" (&gdbstub_fpufs_array), "i"(EPSW_FE)
+ : "a1"
+ );
+#endif
+}
+
+/*
+ *
+ */
+static void gdbstub_load_fpu(void)
+{
+#ifdef CONFIG_FPU
+
+ asm volatile(
+ "or %1,epsw\n"
+#ifdef CONFIG_MN10300_PROC_MN103E010
+ "nop\n"
+ "nop\n"
+#endif
+ "mov %0, a1\n"
+ "fmov (a1+), fs0\n"
+ "fmov (a1+), fs1\n"
+ "fmov (a1+), fs2\n"
+ "fmov (a1+), fs3\n"
+ "fmov (a1+), fs4\n"
+ "fmov (a1+), fs5\n"
+ "fmov (a1+), fs6\n"
+ "fmov (a1+), fs7\n"
+ "fmov (a1+), fs8\n"
+ "fmov (a1+), fs9\n"
+ "fmov (a1+), fs10\n"
+ "fmov (a1+), fs11\n"
+ "fmov (a1+), fs12\n"
+ "fmov (a1+), fs13\n"
+ "fmov (a1+), fs14\n"
+ "fmov (a1+), fs15\n"
+ "fmov (a1+), fs16\n"
+ "fmov (a1+), fs17\n"
+ "fmov (a1+), fs18\n"
+ "fmov (a1+), fs19\n"
+ "fmov (a1+), fs20\n"
+ "fmov (a1+), fs21\n"
+ "fmov (a1+), fs22\n"
+ "fmov (a1+), fs23\n"
+ "fmov (a1+), fs24\n"
+ "fmov (a1+), fs25\n"
+ "fmov (a1+), fs26\n"
+ "fmov (a1+), fs27\n"
+ "fmov (a1+), fs28\n"
+ "fmov (a1+), fs29\n"
+ "fmov (a1+), fs30\n"
+ "fmov (a1+), fs31\n"
+ "fmov %2, fpcr\n"
+ :
+ : "g" (&gdbstub_fpufs_array), "i"(EPSW_FE), "d"(gdbstub_fpcr)
+ : "a1"
+ );
+#endif
+}
+
+/*
+ * set a software breakpoint
+ */
+int gdbstub_set_breakpoint(u8 *addr, int len)
+{
+ int bkpt, loop, xloop;
+
+#ifdef GDBSTUB_USE_F7F7_AS_BREAKPOINT
+ len = (len + 1) & ~1;
+#endif
+
+ gdbstub_bkpt("setbkpt(%p,%d)\n", addr, len);
+
+ for (bkpt = 255; bkpt >= 0; bkpt--)
+ if (!gdbstub_bkpts[bkpt].addr)
+ break;
+ if (bkpt < 0)
+ return -ENOSPC;
+
+ for (loop = 0; loop < len; loop++)
+ if (gdbstub_read_byte(&addr[loop],
+ &gdbstub_bkpts[bkpt].origbytes[loop]
+ ) < 0)
+ return -EFAULT;
+
+ gdbstub_flush_caches = 1;
+
+#ifdef GDBSTUB_USE_F7F7_AS_BREAKPOINT
+ for (loop = 0; loop < len; loop++)
+ if (gdbstub_write_byte(0xF7, &addr[loop]) < 0)
+ goto restore;
+#else
+ for (loop = 0; loop < len; loop++)
+ if (gdbstub_write_byte(0xFF, &addr[loop]) < 0)
+ goto restore;
+#endif
+
+ gdbstub_bkpts[bkpt].addr = addr;
+ gdbstub_bkpts[bkpt].len = len;
+
+ gdbstub_bkpt("Set BKPT[%02x]: %p-%p {%02x%02x%02x%02x%02x%02x%02x}\n",
+ bkpt,
+ gdbstub_bkpts[bkpt].addr,
+ gdbstub_bkpts[bkpt].addr + gdbstub_bkpts[bkpt].len - 1,
+ gdbstub_bkpts[bkpt].origbytes[0],
+ gdbstub_bkpts[bkpt].origbytes[1],
+ gdbstub_bkpts[bkpt].origbytes[2],
+ gdbstub_bkpts[bkpt].origbytes[3],
+ gdbstub_bkpts[bkpt].origbytes[4],
+ gdbstub_bkpts[bkpt].origbytes[5],
+ gdbstub_bkpts[bkpt].origbytes[6]
+ );
+
+ return 0;
+
+restore:
+ for (xloop = 0; xloop < loop; xloop++)
+ gdbstub_write_byte(gdbstub_bkpts[bkpt].origbytes[xloop],
+ addr + xloop);
+ return -EFAULT;
+}
+
+/*
+ * clear a software breakpoint
+ */
+int gdbstub_clear_breakpoint(u8 *addr, int len)
+{
+ int bkpt, loop;
+
+#ifdef GDBSTUB_USE_F7F7_AS_BREAKPOINT
+ len = (len + 1) & ~1;
+#endif
+
+ gdbstub_bkpt("clearbkpt(%p,%d)\n", addr, len);
+
+ for (bkpt = 255; bkpt >= 0; bkpt--)
+ if (gdbstub_bkpts[bkpt].addr == addr &&
+ gdbstub_bkpts[bkpt].len == len)
+ break;
+ if (bkpt < 0)
+ return -ENOENT;
+
+ gdbstub_bkpts[bkpt].addr = NULL;
+
+ gdbstub_flush_caches = 1;
+
+ for (loop = 0; loop < len; loop++)
+ if (gdbstub_write_byte(gdbstub_bkpts[bkpt].origbytes[loop],
+ addr + loop) < 0)
+ return -EFAULT;
+
+ return 0;
+}
+
+/*
+ * This function does all command processing for interfacing to gdb
+ * - returns 0 if the exception should be skipped, -ERROR otherwise.
+ */
+static int gdbstub(struct pt_regs *regs, enum exception_code excep)
+{
+ unsigned long *stack;
+ unsigned long epsw, mdr;
+ uint32_t zero, ssp;
+ uint8_t broke;
+ char *ptr;
+ int sigval;
+ int addr;
+ int length;
+ int loop;
+
+ if (excep == EXCEP_FPU_DISABLED)
+ return -ENOTSUPP;
+
+ gdbstub_flush_caches = 0;
+
+ mn10300_set_gdbleds(1);
+
+ asm volatile("mov mdr,%0" : "=d"(mdr));
+ local_save_flags(epsw);
+ arch_local_change_intr_mask_level(
+ NUM2EPSW_IM(CONFIG_DEBUGGER_IRQ_LEVEL + 1));
+
+ gdbstub_store_fpu();
+
+#ifdef CONFIG_GDBSTUB_IMMEDIATE
+ /* skip the initial pause loop */
+ if (regs->pc == (unsigned long) __gdbstub_pause)
+ regs->pc = (unsigned long) start_kernel;
+#endif
+
+ /* if we were single stepping, restore the opcodes hoisted for the
+ * breakpoint[s] */
+ broke = 0;
+#ifdef CONFIG_GDBSTUB_ALLOW_SINGLE_STEP
+ if ((step_bp[0].addr && step_bp[0].addr == (u8 *) regs->pc) ||
+ (step_bp[1].addr && step_bp[1].addr == (u8 *) regs->pc))
+ broke = 1;
+
+ __gdbstub_restore_bp();
+#endif
+
+ if (gdbstub_rx_unget) {
+ sigval = SIGINT;
+ if (gdbstub_rx_unget != 3)
+ goto packet_waiting;
+ gdbstub_rx_unget = 0;
+ }
+
+ stack = (unsigned long *) regs->sp;
+ sigval = broke ? SIGTRAP : computeSignal(excep);
+
+ /* send information about a BUG() */
+ if (!user_mode(regs) && excep == EXCEP_SYSCALL15) {
+ const struct bug_entry *bug;
+
+ bug = find_bug(regs->pc);
+ if (bug)
+ goto found_bug;
+ length = snprintf(trans_buffer, sizeof(trans_buffer),
+ "BUG() at address %lx\n", regs->pc);
+ goto send_bug_pkt;
+
+ found_bug:
+ length = snprintf(trans_buffer, sizeof(trans_buffer),
+ "BUG() at address %lx (%s:%d)\n",
+ regs->pc, bug->file, bug->line);
+
+ send_bug_pkt:
+ ptr = output_buffer;
+ *ptr++ = 'O';
+ ptr = mem2hex(trans_buffer, ptr, length, 0);
+ *ptr = 0;
+ putpacket(output_buffer);
+
+ regs->pc -= 2;
+ sigval = SIGABRT;
+ } else if (regs->pc == (unsigned long) __gdbstub_bug_trap) {
+ regs->pc = regs->mdr;
+ sigval = SIGABRT;
+ }
+
+ /*
+ * send a message to the debugger's user saying what happened if it may
+ * not be clear cut (we can't map exceptions onto signals properly)
+ */
+ if (sigval != SIGINT && sigval != SIGTRAP && sigval != SIGILL) {
+ static const char title[] = "Excep ", tbcberr[] = "BCBERR ";
+ static const char crlf[] = "\r\n";
+ char hx;
+ u32 bcberr = BCBERR;
+
+ ptr = output_buffer;
+ *ptr++ = 'O';
+ ptr = mem2hex(title, ptr, sizeof(title) - 1, 0);
+
+ hx = hex_asc_hi(excep >> 8);
+ ptr = hex_byte_pack(ptr, hx);
+ hx = hex_asc_lo(excep >> 8);
+ ptr = hex_byte_pack(ptr, hx);
+ hx = hex_asc_hi(excep);
+ ptr = hex_byte_pack(ptr, hx);
+ hx = hex_asc_lo(excep);
+ ptr = hex_byte_pack(ptr, hx);
+
+ ptr = mem2hex(crlf, ptr, sizeof(crlf) - 1, 0);
+ *ptr = 0;
+ putpacket(output_buffer); /* send it off... */
+
+ /* BCBERR */
+ ptr = output_buffer;
+ *ptr++ = 'O';
+ ptr = mem2hex(tbcberr, ptr, sizeof(tbcberr) - 1, 0);
+
+ hx = hex_asc_hi(bcberr >> 24);
+ ptr = hex_byte_pack(ptr, hx);
+ hx = hex_asc_lo(bcberr >> 24);
+ ptr = hex_byte_pack(ptr, hx);
+ hx = hex_asc_hi(bcberr >> 16);
+ ptr = hex_byte_pack(ptr, hx);
+ hx = hex_asc_lo(bcberr >> 16);
+ ptr = hex_byte_pack(ptr, hx);
+ hx = hex_asc_hi(bcberr >> 8);
+ ptr = hex_byte_pack(ptr, hx);
+ hx = hex_asc_lo(bcberr >> 8);
+ ptr = hex_byte_pack(ptr, hx);
+ hx = hex_asc_hi(bcberr);
+ ptr = hex_byte_pack(ptr, hx);
+ hx = hex_asc_lo(bcberr);
+ ptr = hex_byte_pack(ptr, hx);
+
+ ptr = mem2hex(crlf, ptr, sizeof(crlf) - 1, 0);
+ *ptr = 0;
+ putpacket(output_buffer); /* send it off... */
+ }
+
+ /*
+ * tell the debugger that an exception has occurred
+ */
+ ptr = output_buffer;
+
+ /*
+ * Send trap type (converted to signal)
+ */
+ *ptr++ = 'T';
+ ptr = hex_byte_pack(ptr, sigval);
+
+ /*
+ * Send Error PC
+ */
+ ptr = hex_byte_pack(ptr, GDB_REGID_PC);
+ *ptr++ = ':';
+ ptr = mem2hex(&regs->pc, ptr, 4, 0);
+ *ptr++ = ';';
+
+ /*
+ * Send frame pointer
+ */
+ ptr = hex_byte_pack(ptr, GDB_REGID_FP);
+ *ptr++ = ':';
+ ptr = mem2hex(&regs->a3, ptr, 4, 0);
+ *ptr++ = ';';
+
+ /*
+ * Send stack pointer
+ */
+ ssp = (unsigned long) (regs + 1);
+ ptr = hex_byte_pack(ptr, GDB_REGID_SP);
+ *ptr++ = ':';
+ ptr = mem2hex(&ssp, ptr, 4, 0);
+ *ptr++ = ';';
+
+ *ptr++ = 0;
+ putpacket(output_buffer); /* send it off... */
+
+packet_waiting:
+ /*
+ * Wait for input from remote GDB
+ */
+ while (1) {
+ output_buffer[0] = 0;
+ getpacket(input_buffer);
+
+ switch (input_buffer[0]) {
+ /* request repeat of last signal number */
+ case '?':
+ output_buffer[0] = 'S';
+ output_buffer[1] = hex_asc_hi(sigval);
+ output_buffer[2] = hex_asc_lo(sigval);
+ output_buffer[3] = 0;
+ break;
+
+ case 'd':
+ /* toggle debug flag */
+ break;
+
+ /*
+ * Return the value of the CPU registers
+ */
+ case 'g':
+ zero = 0;
+ ssp = (u32) (regs + 1);
+ ptr = output_buffer;
+ ptr = mem2hex(&regs->d0, ptr, 4, 0);
+ ptr = mem2hex(&regs->d1, ptr, 4, 0);
+ ptr = mem2hex(&regs->d2, ptr, 4, 0);
+ ptr = mem2hex(&regs->d3, ptr, 4, 0);
+ ptr = mem2hex(&regs->a0, ptr, 4, 0);
+ ptr = mem2hex(&regs->a1, ptr, 4, 0);
+ ptr = mem2hex(&regs->a2, ptr, 4, 0);
+ ptr = mem2hex(&regs->a3, ptr, 4, 0);
+
+ ptr = mem2hex(&ssp, ptr, 4, 0); /* 8 */
+ ptr = mem2hex(&regs->pc, ptr, 4, 0);
+ ptr = mem2hex(&regs->mdr, ptr, 4, 0);
+ ptr = mem2hex(&regs->epsw, ptr, 4, 0);
+ ptr = mem2hex(&regs->lir, ptr, 4, 0);
+ ptr = mem2hex(&regs->lar, ptr, 4, 0);
+ ptr = mem2hex(&regs->mdrq, ptr, 4, 0);
+
+ ptr = mem2hex(&regs->e0, ptr, 4, 0); /* 15 */
+ ptr = mem2hex(&regs->e1, ptr, 4, 0);
+ ptr = mem2hex(&regs->e2, ptr, 4, 0);
+ ptr = mem2hex(&regs->e3, ptr, 4, 0);
+ ptr = mem2hex(&regs->e4, ptr, 4, 0);
+ ptr = mem2hex(&regs->e5, ptr, 4, 0);
+ ptr = mem2hex(&regs->e6, ptr, 4, 0);
+ ptr = mem2hex(&regs->e7, ptr, 4, 0);
+
+ ptr = mem2hex(&ssp, ptr, 4, 0);
+ ptr = mem2hex(&regs, ptr, 4, 0);
+ ptr = mem2hex(&regs->sp, ptr, 4, 0);
+ ptr = mem2hex(&regs->mcrh, ptr, 4, 0); /* 26 */
+ ptr = mem2hex(&regs->mcrl, ptr, 4, 0);
+ ptr = mem2hex(&regs->mcvf, ptr, 4, 0);
+
+ ptr = mem2hex(&gdbstub_fpcr, ptr, 4, 0); /* 29 - FPCR */
+ ptr = mem2hex(&zero, ptr, 4, 0);
+ ptr = mem2hex(&zero, ptr, 4, 0);
+ for (loop = 0; loop < 32; loop++)
+ ptr = mem2hex(&gdbstub_fpufs_array[loop],
+ ptr, 4, 0); /* 32 - FS0-31 */
+
+ break;
+
+ /*
+ * set the value of the CPU registers - return OK
+ */
+ case 'G':
+ {
+ const char *ptr;
+
+ ptr = &input_buffer[1];
+ ptr = hex2mem(ptr, &regs->d0, 4, 0);
+ ptr = hex2mem(ptr, &regs->d1, 4, 0);
+ ptr = hex2mem(ptr, &regs->d2, 4, 0);
+ ptr = hex2mem(ptr, &regs->d3, 4, 0);
+ ptr = hex2mem(ptr, &regs->a0, 4, 0);
+ ptr = hex2mem(ptr, &regs->a1, 4, 0);
+ ptr = hex2mem(ptr, &regs->a2, 4, 0);
+ ptr = hex2mem(ptr, &regs->a3, 4, 0);
+
+ ptr = hex2mem(ptr, &ssp, 4, 0); /* 8 */
+ ptr = hex2mem(ptr, &regs->pc, 4, 0);
+ ptr = hex2mem(ptr, &regs->mdr, 4, 0);
+ ptr = hex2mem(ptr, &regs->epsw, 4, 0);
+ ptr = hex2mem(ptr, &regs->lir, 4, 0);
+ ptr = hex2mem(ptr, &regs->lar, 4, 0);
+ ptr = hex2mem(ptr, &regs->mdrq, 4, 0);
+
+ ptr = hex2mem(ptr, &regs->e0, 4, 0); /* 15 */
+ ptr = hex2mem(ptr, &regs->e1, 4, 0);
+ ptr = hex2mem(ptr, &regs->e2, 4, 0);
+ ptr = hex2mem(ptr, &regs->e3, 4, 0);
+ ptr = hex2mem(ptr, &regs->e4, 4, 0);
+ ptr = hex2mem(ptr, &regs->e5, 4, 0);
+ ptr = hex2mem(ptr, &regs->e6, 4, 0);
+ ptr = hex2mem(ptr, &regs->e7, 4, 0);
+
+ ptr = hex2mem(ptr, &ssp, 4, 0);
+ ptr = hex2mem(ptr, &zero, 4, 0);
+ ptr = hex2mem(ptr, &regs->sp, 4, 0);
+ ptr = hex2mem(ptr, &regs->mcrh, 4, 0); /* 26 */
+ ptr = hex2mem(ptr, &regs->mcrl, 4, 0);
+ ptr = hex2mem(ptr, &regs->mcvf, 4, 0);
+
+ ptr = hex2mem(ptr, &zero, 4, 0); /* 29 - FPCR */
+ ptr = hex2mem(ptr, &zero, 4, 0);
+ ptr = hex2mem(ptr, &zero, 4, 0);
+ for (loop = 0; loop < 32; loop++) /* 32 - FS0-31 */
+ ptr = hex2mem(ptr, &zero, 4, 0);
+
+#if 0
+ /*
+ * See if the stack pointer has moved. If so, then copy
+ * the saved locals and ins to the new location.
+ */
+ unsigned long *newsp = (unsigned long *) registers[SP];
+ if (sp != newsp)
+ sp = memcpy(newsp, sp, 16 * 4);
+#endif
+
+ gdbstub_strcpy(output_buffer, "OK");
+ }
+ break;
+
+ /*
+ * mAA..AA,LLLL Read LLLL bytes at address AA..AA
+ */
+ case 'm':
+ ptr = &input_buffer[1];
+
+ if (hexToInt(&ptr, &addr) &&
+ *ptr++ == ',' &&
+ hexToInt(&ptr, &length)
+ ) {
+ if (mem2hex((char *) addr, output_buffer,
+ length, 1))
+ break;
+ gdbstub_strcpy(output_buffer, "E03");
+ } else {
+ gdbstub_strcpy(output_buffer, "E01");
+ }
+ break;
+
+ /*
+ * MAA..AA,LLLL: Write LLLL bytes at address AA.AA
+ * return OK
+ */
+ case 'M':
+ ptr = &input_buffer[1];
+
+ if (hexToInt(&ptr, &addr) &&
+ *ptr++ == ',' &&
+ hexToInt(&ptr, &length) &&
+ *ptr++ == ':'
+ ) {
+ if (hex2mem(ptr, (char *) addr, length, 1))
+ gdbstub_strcpy(output_buffer, "OK");
+ else
+ gdbstub_strcpy(output_buffer, "E03");
+
+ gdbstub_flush_caches = 1;
+ } else {
+ gdbstub_strcpy(output_buffer, "E02");
+ }
+ break;
+
+ /*
+ * cAA..AA Continue at address AA..AA(optional)
+ */
+ case 'c':
+ /* try to read optional parameter, pc unchanged if no
+ * parm */
+
+ ptr = &input_buffer[1];
+ if (hexToInt(&ptr, &addr))
+ regs->pc = addr;
+ goto done;
+
+ /*
+ * kill the program
+ */
+ case 'k' :
+ goto done; /* just continue */
+
+ /*
+ * Reset the whole machine (FIXME: system dependent)
+ */
+ case 'r':
+ break;
+
+ /*
+ * Step to next instruction
+ */
+ case 's':
+ /* Using the T flag doesn't seem to perform single
+ * stepping (it seems to wind up being caught by the
+ * JTAG unit), so we have to use breakpoints and
+ * continue instead.
+ */
+#ifdef CONFIG_GDBSTUB_ALLOW_SINGLE_STEP
+ if (gdbstub_single_step(regs) < 0)
+ /* ignore any fault error for now */
+ gdbstub_printk("unable to set single-step"
+ " bp\n");
+ goto done;
+#else
+ gdbstub_strcpy(output_buffer, "E01");
+ break;
+#endif
+
+ /*
+ * Set baud rate (bBB)
+ */
+ case 'b':
+ do {
+ int baudrate;
+
+ ptr = &input_buffer[1];
+ if (!hexToInt(&ptr, &baudrate)) {
+ gdbstub_strcpy(output_buffer, "B01");
+ break;
+ }
+
+ if (baudrate) {
+ /* ACK before changing speed */
+ putpacket("OK");
+ gdbstub_io_set_baud(baudrate);
+ }
+ } while (0);
+ break;
+
+ /*
+ * Set breakpoint
+ */
+ case 'Z':
+ ptr = &input_buffer[1];
+
+ if (!hexToInt(&ptr, &loop) || *ptr++ != ',' ||
+ !hexToInt(&ptr, &addr) || *ptr++ != ',' ||
+ !hexToInt(&ptr, &length)
+ ) {
+ gdbstub_strcpy(output_buffer, "E01");
+ break;
+ }
+
+ /* only support software breakpoints */
+ gdbstub_strcpy(output_buffer, "E03");
+ if (loop != 0 ||
+ length < 1 ||
+ length > 7 ||
+ (unsigned long) addr < 4096)
+ break;
+
+ if (gdbstub_set_breakpoint((u8 *) addr, length) < 0)
+ break;
+
+ gdbstub_strcpy(output_buffer, "OK");
+ break;
+
+ /*
+ * Clear breakpoint
+ */
+ case 'z':
+ ptr = &input_buffer[1];
+
+ if (!hexToInt(&ptr, &loop) || *ptr++ != ',' ||
+ !hexToInt(&ptr, &addr) || *ptr++ != ',' ||
+ !hexToInt(&ptr, &length)
+ ) {
+ gdbstub_strcpy(output_buffer, "E01");
+ break;
+ }
+
+ /* only support software breakpoints */
+ gdbstub_strcpy(output_buffer, "E03");
+ if (loop != 0 ||
+ length < 1 ||
+ length > 7 ||
+ (unsigned long) addr < 4096)
+ break;
+
+ if (gdbstub_clear_breakpoint((u8 *) addr, length) < 0)
+ break;
+
+ gdbstub_strcpy(output_buffer, "OK");
+ break;
+
+ default:
+ gdbstub_proto("### GDB Unsupported Cmd '%s'\n",
+ input_buffer);
+ break;
+ }
+
+ /* reply to the request */
+ putpacket(output_buffer);
+ }
+
+done:
+ /*
+ * Need to flush the instruction cache here, as we may
+ * have deposited a breakpoint, and the icache probably
+ * has no way of knowing that a data ref to some location
+ * may have changed something that is in the instruction
+ * cache.
+ * NB: We flush both caches, just to be sure...
+ */
+ if (gdbstub_flush_caches)
+ debugger_local_cache_flushinv();
+
+ gdbstub_load_fpu();
+ mn10300_set_gdbleds(0);
+ if (excep == EXCEP_NMI)
+ NMICR = NMICR_NMIF;
+
+ touch_softlockup_watchdog();
+
+ local_irq_restore(epsw);
+ return 0;
+}
+
+/*
+ * Determine if we hit a debugger special breakpoint that needs skipping over
+ * automatically.
+ */
+int at_debugger_breakpoint(struct pt_regs *regs)
+{
+ return 0;
+}
+
+/*
+ * handle event interception
+ */
+asmlinkage int debugger_intercept(enum exception_code excep,
+ int signo, int si_code, struct pt_regs *regs)
+{
+ static u8 notfirst = 1;
+ int ret;
+
+ if (gdbstub_busy)
+ gdbstub_printk("--> gdbstub reentered itself\n");
+ gdbstub_busy = 1;
+
+ if (notfirst) {
+ unsigned long mdr;
+ asm("mov mdr,%0" : "=d"(mdr));
+
+ gdbstub_entry(
+ "--> debugger_intercept(%p,%04x) [MDR=%lx PC=%lx]\n",
+ regs, excep, mdr, regs->pc);
+
+ gdbstub_entry(
+ "PC: %08lx EPSW: %08lx SSP: %08lx mode: %s\n",
+ regs->pc, regs->epsw, (unsigned long) &ret,
+ user_mode(regs) ? "User" : "Super");
+ gdbstub_entry(
+ "d0: %08lx d1: %08lx d2: %08lx d3: %08lx\n",
+ regs->d0, regs->d1, regs->d2, regs->d3);
+ gdbstub_entry(
+ "a0: %08lx a1: %08lx a2: %08lx a3: %08lx\n",
+ regs->a0, regs->a1, regs->a2, regs->a3);
+ gdbstub_entry(
+ "e0: %08lx e1: %08lx e2: %08lx e3: %08lx\n",
+ regs->e0, regs->e1, regs->e2, regs->e3);
+ gdbstub_entry(
+ "e4: %08lx e5: %08lx e6: %08lx e7: %08lx\n",
+ regs->e4, regs->e5, regs->e6, regs->e7);
+ gdbstub_entry(
+ "lar: %08lx lir: %08lx mdr: %08lx usp: %08lx\n",
+ regs->lar, regs->lir, regs->mdr, regs->sp);
+ gdbstub_entry(
+ "cvf: %08lx crl: %08lx crh: %08lx drq: %08lx\n",
+ regs->mcvf, regs->mcrl, regs->mcrh, regs->mdrq);
+ gdbstub_entry(
+ "threadinfo=%p task=%p)\n",
+ current_thread_info(), current);
+ } else {
+ notfirst = 1;
+ }
+
+ ret = gdbstub(regs, excep);
+
+ gdbstub_entry("<-- debugger_intercept()\n");
+ gdbstub_busy = 0;
+ return ret;
+}
+
+/*
+ * handle the GDB stub itself causing an exception
+ */
+asmlinkage void gdbstub_exception(struct pt_regs *regs,
+ enum exception_code excep)
+{
+ unsigned long mdr;
+
+ asm("mov mdr,%0" : "=d"(mdr));
+ gdbstub_entry("--> gdbstub exception({%p},%04x) [MDR=%lx]\n",
+ regs, excep, mdr);
+
+ while ((unsigned long) regs == 0xffffffff) {}
+
+ /* handle guarded memory accesses where we know it might fault */
+ if (regs->pc == (unsigned) gdbstub_read_byte_guard) {
+ regs->pc = (unsigned) gdbstub_read_byte_cont;
+ goto fault;
+ }
+
+ if (regs->pc == (unsigned) gdbstub_read_word_guard) {
+ regs->pc = (unsigned) gdbstub_read_word_cont;
+ goto fault;
+ }
+
+ if (regs->pc == (unsigned) gdbstub_read_dword_guard) {
+ regs->pc = (unsigned) gdbstub_read_dword_cont;
+ goto fault;
+ }
+
+ if (regs->pc == (unsigned) gdbstub_write_byte_guard) {
+ regs->pc = (unsigned) gdbstub_write_byte_cont;
+ goto fault;
+ }
+
+ if (regs->pc == (unsigned) gdbstub_write_word_guard) {
+ regs->pc = (unsigned) gdbstub_write_word_cont;
+ goto fault;
+ }
+
+ if (regs->pc == (unsigned) gdbstub_write_dword_guard) {
+ regs->pc = (unsigned) gdbstub_write_dword_cont;
+ goto fault;
+ }
+
+ gdbstub_printk("\n### GDB stub caused an exception ###\n");
+
+ /* something went horribly wrong */
+ console_verbose();
+ show_registers(regs);
+
+ panic("GDB Stub caused an unexpected exception - can't continue\n");
+
+ /* we caught an attempt by the stub to access silly memory */
+fault:
+ gdbstub_entry("<-- gdbstub exception() = EFAULT\n");
+ regs->d0 = -EFAULT;
+ return;
+}
+
+/*
+ * send an exit message to GDB
+ */
+void gdbstub_exit(int status)
+{
+ unsigned char checksum;
+ unsigned char ch;
+ int count;
+
+ gdbstub_busy = 1;
+ output_buffer[0] = 'W';
+ output_buffer[1] = hex_asc_hi(status);
+ output_buffer[2] = hex_asc_lo(status);
+ output_buffer[3] = 0;
+
+ gdbstub_io_tx_char('$');
+ checksum = 0;
+ count = 0;
+
+ while ((ch = output_buffer[count]) != 0) {
+ gdbstub_io_tx_char(ch);
+ checksum += ch;
+ count += 1;
+ }
+
+ gdbstub_io_tx_char('#');
+ gdbstub_io_tx_char(hex_asc_hi(checksum));
+ gdbstub_io_tx_char(hex_asc_lo(checksum));
+
+ /* make sure the output is flushed, or else RedBoot might clobber it */
+ gdbstub_io_tx_flush();
+
+ gdbstub_busy = 0;
+}
+
+/*
+ * initialise the GDB stub
+ */
+asmlinkage void __init gdbstub_init(void)
+{
+#ifdef CONFIG_GDBSTUB_IMMEDIATE
+ unsigned char ch;
+ int ret;
+#endif
+
+ gdbstub_busy = 1;
+
+ printk(KERN_INFO "%s", gdbstub_banner);
+
+ gdbstub_io_init();
+
+ gdbstub_entry("--> gdbstub_init\n");
+
+ /* try to talk to GDB (or anyone insane enough to want to type GDB
+ * protocol by hand) */
+ gdbstub_io("### GDB Tx ACK\n");
+ gdbstub_io_tx_char('+'); /* 'hello world' */
+
+#ifdef CONFIG_GDBSTUB_IMMEDIATE
+ gdbstub_printk("GDB Stub waiting for packet\n");
+
+ /* in case GDB is started before us, ACK any packets that are already
+ * sitting there (presumably "$?#xx")
+ */
+ do { gdbstub_io_rx_char(&ch, 0); } while (ch != '$');
+ do { gdbstub_io_rx_char(&ch, 0); } while (ch != '#');
+ /* eat first csum byte */
+ do { ret = gdbstub_io_rx_char(&ch, 0); } while (ret != 0);
+ /* eat second csum byte */
+ do { ret = gdbstub_io_rx_char(&ch, 0); } while (ret != 0);
+
+ gdbstub_io("### GDB Tx NAK\n");
+ gdbstub_io_tx_char('-'); /* NAK it */
+
+#else
+ printk("GDB Stub ready\n");
+#endif
+
+ gdbstub_busy = 0;
+ gdbstub_entry("<-- gdbstub_init\n");
+}
+
+/*
+ * register the console at a more appropriate time
+ */
+#ifdef CONFIG_GDBSTUB_CONSOLE
+static int __init gdbstub_postinit(void)
+{
+ printk(KERN_NOTICE "registering console\n");
+ register_console(&gdbstub_console);
+ return 0;
+}
+
+__initcall(gdbstub_postinit);
+#endif
+
+/*
+ * handle character reception on GDB serial port
+ * - jump into the GDB stub if BREAK is detected on the serial line
+ */
+asmlinkage void gdbstub_rx_irq(struct pt_regs *regs, enum exception_code excep)
+{
+ char ch;
+ int ret;
+
+ gdbstub_entry("--> gdbstub_rx_irq\n");
+
+ do {
+ ret = gdbstub_io_rx_char(&ch, 1);
+ if (ret != -EIO && ret != -EAGAIN) {
+ if (ret != -EINTR)
+ gdbstub_rx_unget = ch;
+ gdbstub(regs, excep);
+ }
+ } while (ret != -EAGAIN);
+
+ gdbstub_entry("<-- gdbstub_rx_irq\n");
+}
diff --git a/arch/mn10300/kernel/head.S b/arch/mn10300/kernel/head.S
new file mode 100644
index 000000000..73e00fc78
--- /dev/null
+++ b/arch/mn10300/kernel/head.S
@@ -0,0 +1,450 @@
+/* Boot entry point for MN10300 kernel
+ *
+ * Copyright (C) 2005 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#include <linux/init.h>
+#include <linux/threads.h>
+#include <linux/linkage.h>
+#include <linux/serial_reg.h>
+#include <asm/thread_info.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/frame.inc>
+#include <asm/param.h>
+#include <unit/serial.h>
+#ifdef CONFIG_SMP
+#include <asm/smp.h>
+#include <asm/intctl-regs.h>
+#include <asm/cpu-regs.h>
+#include <proc/smp-regs.h>
+#endif /* CONFIG_SMP */
+
+ __HEAD
+
+###############################################################################
+#
+# bootloader entry point
+#
+###############################################################################
+ .globl _start
+ .type _start,@function
+_start:
+#ifdef CONFIG_SMP
+ #
+ # If this is a secondary CPU (AP), then deal with that elsewhere
+ #
+ mov (CPUID),d3
+ and CPUID_MASK,d3
+ bne startup_secondary
+
+ #
+ # We're dealing with the primary CPU (BP) here, then.
+ # Keep BP's D0,D1,D2 register for boot check.
+ #
+
+ # Set up the Boot IPI for each secondary CPU
+ mov 0x1,a0
+loop_set_secondary_icr:
+ mov a0,a1
+ asl CROSS_ICR_CPU_SHIFT,a1
+ add CROSS_GxICR(SMP_BOOT_IRQ,0),a1
+ movhu (a1),d3
+ or GxICR_ENABLE|GxICR_LEVEL_0,d3
+ movhu d3,(a1)
+ movhu (a1),d3 # flush
+ inc a0
+ cmp NR_CPUS,a0
+ bne loop_set_secondary_icr
+#endif /* CONFIG_SMP */
+
+ # save commandline pointer
+ mov d0,a3
+
+ # preload the PGD pointer register
+ mov swapper_pg_dir,d0
+ mov d0,(PTBR)
+ clr d0
+ movbu d0,(PIDR)
+
+ # turn on the TLBs
+ mov MMUCTR_IIV|MMUCTR_DIV,d0
+ mov d0,(MMUCTR)
+#ifdef CONFIG_AM34_2
+ mov MMUCTR_ITE|MMUCTR_DTE|MMUCTR_CE|MMUCTR_WTE,d0
+#else
+ mov MMUCTR_ITE|MMUCTR_DTE|MMUCTR_CE,d0
+#endif
+ mov d0,(MMUCTR)
+
+ # turn on AM33v2 exception handling mode and set the trap table base
+ movhu (CPUP),d0
+ or CPUP_EXM_AM33V2,d0
+ movhu d0,(CPUP)
+ mov CONFIG_INTERRUPT_VECTOR_BASE,d0
+ mov d0,(TBR)
+
+ # invalidate and enable both of the caches
+#ifdef CONFIG_SMP
+ mov ECHCTR,a0
+ clr d0
+ mov d0,(a0)
+#endif
+ mov CHCTR,a0
+ clr d0
+ movhu d0,(a0) # turn off first
+ mov CHCTR_ICINV|CHCTR_DCINV,d0
+ movhu d0,(a0)
+ setlb
+ mov (a0),d0
+ btst CHCTR_ICBUSY|CHCTR_DCBUSY,d0 # wait till not busy
+ lne
+
+#ifdef CONFIG_MN10300_CACHE_ENABLED
+#ifdef CONFIG_MN10300_CACHE_WBACK
+#ifndef CONFIG_MN10300_CACHE_WBACK_NOWRALLOC
+ mov CHCTR_ICEN|CHCTR_DCEN|CHCTR_DCWTMD_WRBACK,d0
+#else
+ mov CHCTR_ICEN|CHCTR_DCEN|CHCTR_DCWTMD_WRBACK|CHCTR_DCALMD,d0
+#endif /* NOWRALLOC */
+#else
+ mov CHCTR_ICEN|CHCTR_DCEN|CHCTR_DCWTMD_WRTHROUGH,d0
+#endif /* WBACK */
+ movhu d0,(a0) # enable
+#endif /* ENABLED */
+
+ # turn on RTS on the debug serial port if applicable
+#ifdef CONFIG_MN10300_UNIT_ASB2305
+ bset UART_MCR_RTS,(ASB2305_DEBUG_MCR)
+#endif
+
+ # clear the BSS area
+ mov __bss_start,a0
+ mov __bss_stop,a1
+ clr d0
+bssclear:
+ cmp a1,a0
+ bge bssclear_end
+ mov d0,(a0)
+ inc4 a0
+ bra bssclear
+bssclear_end:
+
+ # retrieve the parameters (including command line) before we overwrite
+ # them
+ cmp 0xabadcafe,d1
+ bne __no_parameters
+
+__copy_parameters:
+ mov redboot_command_line,a0
+ mov a0,a1
+ add COMMAND_LINE_SIZE,a1
+1:
+ movbu (a3),d0
+ inc a3
+ movbu d0,(a0)
+ inc a0
+ cmp a1,a0
+ blt 1b
+
+ mov redboot_platform_name,a0
+ mov a0,a1
+ add COMMAND_LINE_SIZE,a1
+ mov d2,a3
+1:
+ movbu (a3),d0
+ inc a3
+ movbu d0,(a0)
+ inc a0
+ cmp a1,a0
+ blt 1b
+
+__no_parameters:
+
+ # set up the registers with recognisable rubbish in them
+ mov init_thread_union+THREAD_SIZE-12,sp
+
+ mov 0xea01eaea,d0
+ mov d0,(4,sp) # EPSW save area
+ mov 0xea02eaea,d0
+ mov d0,(8,sp) # PC save area
+
+ mov 0xeb0060ed,d0
+ mov d0,mdr
+ mov 0xeb0061ed,d0
+ mov d0,mdrq
+ mov 0xeb0062ed,d0
+ mov d0,mcrh
+ mov 0xeb0063ed,d0
+ mov d0,mcrl
+ mov 0xeb0064ed,d0
+ mov d0,mcvf
+ mov 0xed0065ed,a3
+ mov a3,usp
+
+ mov 0xed00e0ed,e0
+ mov 0xed00e1ed,e1
+ mov 0xed00e2ed,e2
+ mov 0xed00e3ed,e3
+ mov 0xed00e4ed,e4
+ mov 0xed00e5ed,e5
+ mov 0xed00e6ed,e6
+ mov 0xed00e7ed,e7
+
+ mov 0xed00d0ed,d0
+ mov 0xed00d1ed,d1
+ mov 0xed00d2ed,d2
+ mov 0xed00d3ed,d3
+ mov 0xed00a0ed,a0
+ mov 0xed00a1ed,a1
+ mov 0xed00a2ed,a2
+ mov 0,a3
+
+ # set up the initial kernel stack
+ SAVE_ALL
+ mov 0xffffffff,d0
+ mov d0,(REG_ORIG_D0,fp)
+
+ # put different recognisable rubbish in the regs
+ mov 0xfb0060ed,d0
+ mov d0,mdr
+ mov 0xfb0061ed,d0
+ mov d0,mdrq
+ mov 0xfb0062ed,d0
+ mov d0,mcrh
+ mov 0xfb0063ed,d0
+ mov d0,mcrl
+ mov 0xfb0064ed,d0
+ mov d0,mcvf
+ mov 0xfd0065ed,a0
+ mov a0,usp
+
+ mov 0xfd00e0ed,e0
+ mov 0xfd00e1ed,e1
+ mov 0xfd00e2ed,e2
+ mov 0xfd00e3ed,e3
+ mov 0xfd00e4ed,e4
+ mov 0xfd00e5ed,e5
+ mov 0xfd00e6ed,e6
+ mov 0xfd00e7ed,e7
+
+ mov 0xfd00d0ed,d0
+ mov 0xfd00d1ed,d1
+ mov 0xfd00d2ed,d2
+ mov 0xfd00d3ed,d3
+ mov 0xfd00a0ed,a0
+ mov 0xfd00a1ed,a1
+ mov 0xfd00a2ed,a2
+
+ # we may be holding current in E2
+#ifdef CONFIG_MN10300_CURRENT_IN_E2
+ mov init_task,e2
+#endif
+
+ # initialise the processor and the unit
+ call processor_init[],0
+ call unit_init[],0
+
+#ifdef CONFIG_SMP
+ # mark the primary CPU in cpu_boot_map
+ mov cpu_boot_map,a0
+ mov 0x1,d0
+ mov d0,(a0)
+
+ # signal each secondary CPU to begin booting
+ mov 0x1,d2 # CPU ID
+
+loop_request_boot_secondary:
+ mov d2,a0
+ # send SMP_BOOT_IPI to secondary CPU
+ asl CROSS_ICR_CPU_SHIFT,a0
+ add CROSS_GxICR(SMP_BOOT_IRQ,0),a0
+ movhu (a0),d0
+ or GxICR_REQUEST|GxICR_DETECT,d0
+ movhu d0,(a0)
+ movhu (a0),d0 # flush
+
+ # wait up to 100ms for AP's IPI to be received
+ clr d3
+wait_on_secondary_boot:
+ mov DELAY_TIME_BOOT_IPI,d0
+ call __delay[],0
+ inc d3
+ mov cpu_boot_map,a0
+ mov (a0),d0
+ lsr d2,d0
+ btst 0x1,d0
+ bne 1f
+ cmp TIME_OUT_COUNT_BOOT_IPI,d3
+ bne wait_on_secondary_boot
+1:
+ inc d2
+ cmp NR_CPUS,d2
+ bne loop_request_boot_secondary
+#endif /* CONFIG_SMP */
+
+#ifdef CONFIG_GDBSTUB
+ call gdbstub_init[],0
+
+#ifdef CONFIG_GDBSTUB_IMMEDIATE
+ .globl __gdbstub_pause
+__gdbstub_pause:
+ bra __gdbstub_pause
+#endif
+#endif
+
+ jmp start_kernel
+ .size _start,.-_start
+
+###############################################################################
+#
+# Secondary CPU boot point
+#
+###############################################################################
+#ifdef CONFIG_SMP
+startup_secondary:
+ # preload the PGD pointer register
+ mov swapper_pg_dir,d0
+ mov d0,(PTBR)
+ clr d0
+ movbu d0,(PIDR)
+
+ # turn on the TLBs
+ mov MMUCTR_IIV|MMUCTR_DIV,d0
+ mov d0,(MMUCTR)
+#ifdef CONFIG_AM34_2
+ mov MMUCTR_ITE|MMUCTR_DTE|MMUCTR_CE|MMUCTR_WTE,d0
+#else
+ mov MMUCTR_ITE|MMUCTR_DTE|MMUCTR_CE,d0
+#endif
+ mov d0,(MMUCTR)
+
+ # turn on AM33v2 exception handling mode and set the trap table base
+ movhu (CPUP),d0
+ or CPUP_EXM_AM33V2,d0
+ movhu d0,(CPUP)
+
+ # set the interrupt vector table
+ mov CONFIG_INTERRUPT_VECTOR_BASE,d0
+ mov d0,(TBR)
+
+ # invalidate and enable both of the caches
+ mov ECHCTR,a0
+ clr d0
+ mov d0,(a0)
+ mov CHCTR,a0
+ clr d0
+ movhu d0,(a0) # turn off first
+ mov CHCTR_ICINV|CHCTR_DCINV,d0
+ movhu d0,(a0)
+ setlb
+ mov (a0),d0
+ btst CHCTR_ICBUSY|CHCTR_DCBUSY,d0 # wait till not busy (use CPU loop buffer)
+ lne
+
+#ifdef CONFIG_MN10300_CACHE_ENABLED
+#ifdef CONFIG_MN10300_CACHE_WBACK
+#ifndef CONFIG_MN10300_CACHE_WBACK_NOWRALLOC
+ mov CHCTR_ICEN|CHCTR_DCEN|CHCTR_DCWTMD_WRBACK,d0
+#else
+ mov CHCTR_ICEN|CHCTR_DCEN|CHCTR_DCWTMD_WRBACK|CHCTR_DCALMD,d0
+#endif /* !NOWRALLOC */
+#else
+ mov CHCTR_ICEN|CHCTR_DCEN|CHCTR_DCWTMD_WRTHROUGH,d0
+#endif /* WBACK */
+ movhu d0,(a0) # enable
+#endif /* ENABLED */
+
+ # Clear the boot IPI interrupt for this CPU
+ movhu (GxICR(SMP_BOOT_IRQ)),d0
+ and ~GxICR_REQUEST,d0
+ movhu d0,(GxICR(SMP_BOOT_IRQ))
+ movhu (GxICR(SMP_BOOT_IRQ)),d0 # flush
+
+ /* get stack */
+ mov CONFIG_INTERRUPT_VECTOR_BASE + CONFIG_BOOT_STACK_OFFSET,a0
+ mov (CPUID),d0
+ and CPUID_MASK,d0
+ mulu CONFIG_BOOT_STACK_SIZE,d0
+ sub d0,a0
+ mov a0,sp
+
+ # init interrupt for AP
+ call smp_prepare_cpu_init[],0
+
+ # mark this secondary CPU in cpu_boot_map
+ mov (CPUID),d0
+ mov 0x1,d1
+ asl d0,d1
+ mov cpu_boot_map,a0
+ bset d1,(a0)
+
+ or EPSW_IE|EPSW_IM_1,epsw # permit level 0 interrupts
+ nop
+ nop
+#ifdef CONFIG_MN10300_CACHE_WBACK
+ # flush the local cache if it's in writeback mode
+ call mn10300_local_dcache_flush_inv[],0
+ setlb
+ mov (CHCTR),d0
+ btst CHCTR_DCBUSY,d0 # wait till not busy (use CPU loop buffer)
+ lne
+#endif
+
+ # now sleep waiting for further instructions
+secondary_sleep:
+ mov CPUM_SLEEP,d0
+ movhu d0,(CPUM)
+ nop
+ nop
+ bra secondary_sleep
+ .size startup_secondary,.-startup_secondary
+#endif /* CONFIG_SMP */
+
+###############################################################################
+#
+#
+#
+###############################################################################
+ENTRY(__head_end)
+
+/*
+ * This is initialized to disallow all access to the low 2G region
+ * - the high 2G region is managed directly by the MMU
+ * - range 0x70000000-0x7C000000 are initialised for use by VMALLOC
+ */
+ .section .bss
+ .balign PAGE_SIZE
+ENTRY(swapper_pg_dir)
+ .space PTRS_PER_PGD*4
+
+/*
+ * The page tables are initialized to only 8MB here - the final page
+ * tables are set up later depending on memory size.
+ */
+
+ .balign PAGE_SIZE
+ENTRY(empty_zero_page)
+ .space PAGE_SIZE
+
+ .balign PAGE_SIZE
+ENTRY(empty_bad_page)
+ .space PAGE_SIZE
+
+ .balign PAGE_SIZE
+ENTRY(empty_bad_pte_table)
+ .space PAGE_SIZE
+
+ .balign PAGE_SIZE
+ENTRY(large_page_table)
+ .space PAGE_SIZE
+
+ .balign PAGE_SIZE
+ENTRY(kernel_vmalloc_ptes)
+ .space ((VMALLOC_END-VMALLOC_START)/PAGE_SIZE)*4
diff --git a/arch/mn10300/kernel/internal.h b/arch/mn10300/kernel/internal.h
new file mode 100644
index 000000000..561785581
--- /dev/null
+++ b/arch/mn10300/kernel/internal.h
@@ -0,0 +1,40 @@
+/* Internal definitions for the arch part of the core kernel
+ *
+ * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#include <linux/irqreturn.h>
+
+struct clocksource;
+struct clock_event_device;
+
+/*
+ * entry.S
+ */
+extern void ret_from_fork(struct task_struct *) __attribute__((noreturn));
+extern void ret_from_kernel_thread(struct task_struct *) __attribute__((noreturn));
+
+/*
+ * smp-low.S
+ */
+#ifdef CONFIG_SMP
+extern void mn10300_low_ipi_handler(void);
+#endif
+
+/*
+ * smp.c
+ */
+#ifdef CONFIG_SMP
+extern void smp_jump_to_debugger(void);
+#endif
+
+/*
+ * time.c
+ */
+extern irqreturn_t local_timer_interrupt(void);
diff --git a/arch/mn10300/kernel/io.c b/arch/mn10300/kernel/io.c
new file mode 100644
index 000000000..e96fdf6bb
--- /dev/null
+++ b/arch/mn10300/kernel/io.c
@@ -0,0 +1,30 @@
+/* MN10300 Misaligned multibyte-word I/O
+ *
+ * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <asm/io.h>
+
+/*
+ * output data from a potentially misaligned buffer
+ */
+void __outsl(unsigned long addr, const void *buffer, int count)
+{
+ const unsigned char *buf = buffer;
+ unsigned long val;
+
+ while (count--) {
+ memcpy(&val, buf, 4);
+ outl(val, addr);
+ buf += 4;
+ }
+}
+EXPORT_SYMBOL(__outsl);
diff --git a/arch/mn10300/kernel/irq.c b/arch/mn10300/kernel/irq.c
new file mode 100644
index 000000000..6ab3b73ef
--- /dev/null
+++ b/arch/mn10300/kernel/irq.c
@@ -0,0 +1,355 @@
+/* MN10300 Arch-specific interrupt handling
+ *
+ * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/kernel_stat.h>
+#include <linux/seq_file.h>
+#include <linux/cpumask.h>
+#include <asm/setup.h>
+#include <asm/serial-regs.h>
+
+unsigned long __mn10300_irq_enabled_epsw[NR_CPUS] __cacheline_aligned_in_smp = {
+ [0 ... NR_CPUS - 1] = EPSW_IE | EPSW_IM_7
+};
+EXPORT_SYMBOL(__mn10300_irq_enabled_epsw);
+
+#ifdef CONFIG_SMP
+static char irq_affinity_online[NR_IRQS] = {
+ [0 ... NR_IRQS - 1] = 0
+};
+
+#define NR_IRQ_WORDS ((NR_IRQS + 31) / 32)
+static unsigned long irq_affinity_request[NR_IRQ_WORDS] = {
+ [0 ... NR_IRQ_WORDS - 1] = 0
+};
+#endif /* CONFIG_SMP */
+
+atomic_t irq_err_count;
+
+/*
+ * MN10300 interrupt controller operations
+ */
+static void mn10300_cpupic_ack(struct irq_data *d)
+{
+ unsigned int irq = d->irq;
+ unsigned long flags;
+ u16 tmp;
+
+ flags = arch_local_cli_save();
+ GxICR_u8(irq) = GxICR_DETECT;
+ tmp = GxICR(irq);
+ arch_local_irq_restore(flags);
+}
+
+static void __mask_and_set_icr(unsigned int irq,
+ unsigned int mask, unsigned int set)
+{
+ unsigned long flags;
+ u16 tmp;
+
+ flags = arch_local_cli_save();
+ tmp = GxICR(irq);
+ GxICR(irq) = (tmp & mask) | set;
+ tmp = GxICR(irq);
+ arch_local_irq_restore(flags);
+}
+
+static void mn10300_cpupic_mask(struct irq_data *d)
+{
+ __mask_and_set_icr(d->irq, GxICR_LEVEL, 0);
+}
+
+static void mn10300_cpupic_mask_ack(struct irq_data *d)
+{
+ unsigned int irq = d->irq;
+#ifdef CONFIG_SMP
+ unsigned long flags;
+ u16 tmp;
+
+ flags = arch_local_cli_save();
+
+ if (!test_and_clear_bit(irq, irq_affinity_request)) {
+ tmp = GxICR(irq);
+ GxICR(irq) = (tmp & GxICR_LEVEL) | GxICR_DETECT;
+ tmp = GxICR(irq);
+ } else {
+ u16 tmp2;
+ tmp = GxICR(irq);
+ GxICR(irq) = (tmp & GxICR_LEVEL);
+ tmp2 = GxICR(irq);
+
+ irq_affinity_online[irq] =
+ cpumask_any_and(d->affinity, cpu_online_mask);
+ CROSS_GxICR(irq, irq_affinity_online[irq]) =
+ (tmp & (GxICR_LEVEL | GxICR_ENABLE)) | GxICR_DETECT;
+ tmp = CROSS_GxICR(irq, irq_affinity_online[irq]);
+ }
+
+ arch_local_irq_restore(flags);
+#else /* CONFIG_SMP */
+ __mask_and_set_icr(irq, GxICR_LEVEL, GxICR_DETECT);
+#endif /* CONFIG_SMP */
+}
+
+static void mn10300_cpupic_unmask(struct irq_data *d)
+{
+ __mask_and_set_icr(d->irq, GxICR_LEVEL, GxICR_ENABLE);
+}
+
+static void mn10300_cpupic_unmask_clear(struct irq_data *d)
+{
+ unsigned int irq = d->irq;
+ /* the MN10300 PIC latches its interrupt request bit, even after the
+ * device has ceased to assert its interrupt line and the interrupt
+ * channel has been disabled in the PIC, so for level-triggered
+ * interrupts we need to clear the request bit when we re-enable */
+#ifdef CONFIG_SMP
+ unsigned long flags;
+ u16 tmp;
+
+ flags = arch_local_cli_save();
+
+ if (!test_and_clear_bit(irq, irq_affinity_request)) {
+ tmp = GxICR(irq);
+ GxICR(irq) = (tmp & GxICR_LEVEL) | GxICR_ENABLE | GxICR_DETECT;
+ tmp = GxICR(irq);
+ } else {
+ tmp = GxICR(irq);
+
+ irq_affinity_online[irq] = cpumask_any_and(d->affinity,
+ cpu_online_mask);
+ CROSS_GxICR(irq, irq_affinity_online[irq]) = (tmp & GxICR_LEVEL) | GxICR_ENABLE | GxICR_DETECT;
+ tmp = CROSS_GxICR(irq, irq_affinity_online[irq]);
+ }
+
+ arch_local_irq_restore(flags);
+#else /* CONFIG_SMP */
+ __mask_and_set_icr(irq, GxICR_LEVEL, GxICR_ENABLE | GxICR_DETECT);
+#endif /* CONFIG_SMP */
+}
+
+#ifdef CONFIG_SMP
+static int
+mn10300_cpupic_setaffinity(struct irq_data *d, const struct cpumask *mask,
+ bool force)
+{
+ unsigned long flags;
+
+ flags = arch_local_cli_save();
+ set_bit(d->irq, irq_affinity_request);
+ arch_local_irq_restore(flags);
+ return 0;
+}
+#endif /* CONFIG_SMP */
+
+/*
+ * MN10300 PIC level-triggered IRQ handling.
+ *
+ * The PIC has no 'ACK' function per se. It is possible to clear individual
+ * channel latches, but each latch relatches whether or not the channel is
+ * masked, so we need to clear the latch when we unmask the channel.
+ *
+ * Also for this reason, we don't supply an ack() op (it's unused anyway if
+ * mask_ack() is provided), and mask_ack() just masks.
+ */
+static struct irq_chip mn10300_cpu_pic_level = {
+ .name = "cpu_l",
+ .irq_disable = mn10300_cpupic_mask,
+ .irq_enable = mn10300_cpupic_unmask_clear,
+ .irq_ack = NULL,
+ .irq_mask = mn10300_cpupic_mask,
+ .irq_mask_ack = mn10300_cpupic_mask,
+ .irq_unmask = mn10300_cpupic_unmask_clear,
+#ifdef CONFIG_SMP
+ .irq_set_affinity = mn10300_cpupic_setaffinity,
+#endif
+};
+
+/*
+ * MN10300 PIC edge-triggered IRQ handling.
+ *
+ * We use the latch clearing function of the PIC as the 'ACK' function.
+ */
+static struct irq_chip mn10300_cpu_pic_edge = {
+ .name = "cpu_e",
+ .irq_disable = mn10300_cpupic_mask,
+ .irq_enable = mn10300_cpupic_unmask,
+ .irq_ack = mn10300_cpupic_ack,
+ .irq_mask = mn10300_cpupic_mask,
+ .irq_mask_ack = mn10300_cpupic_mask_ack,
+ .irq_unmask = mn10300_cpupic_unmask,
+#ifdef CONFIG_SMP
+ .irq_set_affinity = mn10300_cpupic_setaffinity,
+#endif
+};
+
+/*
+ * 'what should we do if we get a hw irq event on an illegal vector'.
+ * each architecture has to answer this themselves.
+ */
+void ack_bad_irq(int irq)
+{
+ printk(KERN_WARNING "unexpected IRQ trap at vector %02x\n", irq);
+}
+
+/*
+ * change the level at which an IRQ executes
+ * - must not be called whilst interrupts are being processed!
+ */
+void set_intr_level(int irq, u16 level)
+{
+ BUG_ON(in_interrupt());
+
+ __mask_and_set_icr(irq, GxICR_ENABLE, level);
+}
+
+/*
+ * mark an interrupt to be ACK'd after interrupt handlers have been run rather
+ * than before
+ */
+void mn10300_set_lateack_irq_type(int irq)
+{
+ irq_set_chip_and_handler(irq, &mn10300_cpu_pic_level,
+ handle_level_irq);
+}
+
+/*
+ * initialise the interrupt system
+ */
+void __init init_IRQ(void)
+{
+ int irq;
+
+ for (irq = 0; irq < NR_IRQS; irq++)
+ if (irq_get_chip(irq) == &no_irq_chip)
+ /* due to the PIC latching interrupt requests, even
+ * when the IRQ is disabled, IRQ_PENDING is superfluous
+ * and we can use handle_level_irq() for edge-triggered
+ * interrupts */
+ irq_set_chip_and_handler(irq, &mn10300_cpu_pic_edge,
+ handle_level_irq);
+
+ unit_init_IRQ();
+}
+
+/*
+ * handle normal device IRQs
+ */
+asmlinkage void do_IRQ(void)
+{
+ unsigned long sp, epsw, irq_disabled_epsw, old_irq_enabled_epsw;
+ unsigned int cpu_id = smp_processor_id();
+ int irq;
+
+ sp = current_stack_pointer();
+ BUG_ON(sp - (sp & ~(THREAD_SIZE - 1)) < STACK_WARN);
+
+ /* make sure local_irq_enable() doesn't muck up the interrupt priority
+ * setting in EPSW */
+ old_irq_enabled_epsw = __mn10300_irq_enabled_epsw[cpu_id];
+ local_save_flags(epsw);
+ __mn10300_irq_enabled_epsw[cpu_id] = EPSW_IE | (EPSW_IM & epsw);
+ irq_disabled_epsw = EPSW_IE | MN10300_CLI_LEVEL;
+
+#ifdef CONFIG_MN10300_WD_TIMER
+ __IRQ_STAT(cpu_id, __irq_count)++;
+#endif
+
+ irq_enter();
+
+ for (;;) {
+ /* ask the interrupt controller for the next IRQ to process
+ * - the result we get depends on EPSW.IM
+ */
+ irq = IAGR & IAGR_GN;
+ if (!irq)
+ break;
+
+ local_irq_restore(irq_disabled_epsw);
+
+ generic_handle_irq(irq >> 2);
+
+ /* restore IRQ controls for IAGR access */
+ local_irq_restore(epsw);
+ }
+
+ __mn10300_irq_enabled_epsw[cpu_id] = old_irq_enabled_epsw;
+
+ irq_exit();
+}
+
+/*
+ * Display interrupt management information through /proc/interrupts
+ */
+int arch_show_interrupts(struct seq_file *p, int prec)
+{
+#ifdef CONFIG_MN10300_WD_TIMER
+ int j;
+
+ seq_printf(p, "%*s: ", prec, "NMI");
+ for (j = 0; j < NR_CPUS; j++)
+ if (cpu_online(j))
+ seq_printf(p, "%10u ", nmi_count(j));
+ seq_putc(p, '\n');
+#endif
+
+ seq_printf(p, "%*s: ", prec, "ERR");
+ seq_printf(p, "%10u\n", atomic_read(&irq_err_count));
+ return 0;
+}
+
+#ifdef CONFIG_HOTPLUG_CPU
+void migrate_irqs(void)
+{
+ int irq;
+ unsigned int self, new;
+ unsigned long flags;
+
+ self = smp_processor_id();
+ for (irq = 0; irq < NR_IRQS; irq++) {
+ struct irq_data *data = irq_get_irq_data(irq);
+
+ if (irqd_is_per_cpu(data))
+ continue;
+
+ if (cpumask_test_cpu(self, &data->affinity) &&
+ !cpumask_intersects(&irq_affinity[irq], cpu_online_mask)) {
+ int cpu_id;
+ cpu_id = cpumask_first(cpu_online_mask);
+ cpumask_set_cpu(cpu_id, &data->affinity);
+ }
+ /* We need to operate irq_affinity_online atomically. */
+ arch_local_cli_save(flags);
+ if (irq_affinity_online[irq] == self) {
+ u16 x, tmp;
+
+ x = GxICR(irq);
+ GxICR(irq) = x & GxICR_LEVEL;
+ tmp = GxICR(irq);
+
+ new = cpumask_any_and(&data->affinity,
+ cpu_online_mask);
+ irq_affinity_online[irq] = new;
+
+ CROSS_GxICR(irq, new) =
+ (x & GxICR_LEVEL) | GxICR_DETECT;
+ tmp = CROSS_GxICR(irq, new);
+
+ x &= GxICR_LEVEL | GxICR_ENABLE;
+ if (GxICR(irq) & GxICR_REQUEST)
+ x |= GxICR_REQUEST | GxICR_DETECT;
+ CROSS_GxICR(irq, new) = x;
+ tmp = CROSS_GxICR(irq, new);
+ }
+ arch_local_irq_restore(flags);
+ }
+}
+#endif /* CONFIG_HOTPLUG_CPU */
diff --git a/arch/mn10300/kernel/kgdb.c b/arch/mn10300/kernel/kgdb.c
new file mode 100644
index 000000000..997708234
--- /dev/null
+++ b/arch/mn10300/kernel/kgdb.c
@@ -0,0 +1,501 @@
+/* kgdb support for MN10300
+ *
+ * Copyright (C) 2010 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#include <linux/slab.h>
+#include <linux/ptrace.h>
+#include <linux/kgdb.h>
+#include <linux/uaccess.h>
+#include <unit/leds.h>
+#include <unit/serial.h>
+#include <asm/debugger.h>
+#include <asm/serial-regs.h>
+#include "internal.h"
+
+/*
+ * Software single-stepping breakpoint save (used by __switch_to())
+ */
+static struct thread_info *kgdb_sstep_thread;
+u8 *kgdb_sstep_bp_addr[2];
+u8 kgdb_sstep_bp[2];
+
+/*
+ * Copy kernel exception frame registers to the GDB register file
+ */
+void pt_regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *regs)
+{
+ unsigned long ssp = (unsigned long) (regs + 1);
+
+ gdb_regs[GDB_FR_D0] = regs->d0;
+ gdb_regs[GDB_FR_D1] = regs->d1;
+ gdb_regs[GDB_FR_D2] = regs->d2;
+ gdb_regs[GDB_FR_D3] = regs->d3;
+ gdb_regs[GDB_FR_A0] = regs->a0;
+ gdb_regs[GDB_FR_A1] = regs->a1;
+ gdb_regs[GDB_FR_A2] = regs->a2;
+ gdb_regs[GDB_FR_A3] = regs->a3;
+ gdb_regs[GDB_FR_SP] = (regs->epsw & EPSW_nSL) ? regs->sp : ssp;
+ gdb_regs[GDB_FR_PC] = regs->pc;
+ gdb_regs[GDB_FR_MDR] = regs->mdr;
+ gdb_regs[GDB_FR_EPSW] = regs->epsw;
+ gdb_regs[GDB_FR_LIR] = regs->lir;
+ gdb_regs[GDB_FR_LAR] = regs->lar;
+ gdb_regs[GDB_FR_MDRQ] = regs->mdrq;
+ gdb_regs[GDB_FR_E0] = regs->e0;
+ gdb_regs[GDB_FR_E1] = regs->e1;
+ gdb_regs[GDB_FR_E2] = regs->e2;
+ gdb_regs[GDB_FR_E3] = regs->e3;
+ gdb_regs[GDB_FR_E4] = regs->e4;
+ gdb_regs[GDB_FR_E5] = regs->e5;
+ gdb_regs[GDB_FR_E6] = regs->e6;
+ gdb_regs[GDB_FR_E7] = regs->e7;
+ gdb_regs[GDB_FR_SSP] = ssp;
+ gdb_regs[GDB_FR_MSP] = 0;
+ gdb_regs[GDB_FR_USP] = regs->sp;
+ gdb_regs[GDB_FR_MCRH] = regs->mcrh;
+ gdb_regs[GDB_FR_MCRL] = regs->mcrl;
+ gdb_regs[GDB_FR_MCVF] = regs->mcvf;
+ gdb_regs[GDB_FR_DUMMY0] = 0;
+ gdb_regs[GDB_FR_DUMMY1] = 0;
+ gdb_regs[GDB_FR_FS0] = 0;
+}
+
+/*
+ * Extracts kernel SP/PC values understandable by gdb from the values
+ * saved by switch_to().
+ */
+void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *p)
+{
+ gdb_regs[GDB_FR_SSP] = p->thread.sp;
+ gdb_regs[GDB_FR_PC] = p->thread.pc;
+ gdb_regs[GDB_FR_A3] = p->thread.a3;
+ gdb_regs[GDB_FR_USP] = p->thread.usp;
+ gdb_regs[GDB_FR_FPCR] = p->thread.fpu_state.fpcr;
+}
+
+/*
+ * Fill kernel exception frame registers from the GDB register file
+ */
+void gdb_regs_to_pt_regs(unsigned long *gdb_regs, struct pt_regs *regs)
+{
+ regs->d0 = gdb_regs[GDB_FR_D0];
+ regs->d1 = gdb_regs[GDB_FR_D1];
+ regs->d2 = gdb_regs[GDB_FR_D2];
+ regs->d3 = gdb_regs[GDB_FR_D3];
+ regs->a0 = gdb_regs[GDB_FR_A0];
+ regs->a1 = gdb_regs[GDB_FR_A1];
+ regs->a2 = gdb_regs[GDB_FR_A2];
+ regs->a3 = gdb_regs[GDB_FR_A3];
+ regs->sp = gdb_regs[GDB_FR_SP];
+ regs->pc = gdb_regs[GDB_FR_PC];
+ regs->mdr = gdb_regs[GDB_FR_MDR];
+ regs->epsw = gdb_regs[GDB_FR_EPSW];
+ regs->lir = gdb_regs[GDB_FR_LIR];
+ regs->lar = gdb_regs[GDB_FR_LAR];
+ regs->mdrq = gdb_regs[GDB_FR_MDRQ];
+ regs->e0 = gdb_regs[GDB_FR_E0];
+ regs->e1 = gdb_regs[GDB_FR_E1];
+ regs->e2 = gdb_regs[GDB_FR_E2];
+ regs->e3 = gdb_regs[GDB_FR_E3];
+ regs->e4 = gdb_regs[GDB_FR_E4];
+ regs->e5 = gdb_regs[GDB_FR_E5];
+ regs->e6 = gdb_regs[GDB_FR_E6];
+ regs->e7 = gdb_regs[GDB_FR_E7];
+ regs->sp = gdb_regs[GDB_FR_SSP];
+ /* gdb_regs[GDB_FR_MSP]; */
+ // regs->usp = gdb_regs[GDB_FR_USP];
+ regs->mcrh = gdb_regs[GDB_FR_MCRH];
+ regs->mcrl = gdb_regs[GDB_FR_MCRL];
+ regs->mcvf = gdb_regs[GDB_FR_MCVF];
+ /* gdb_regs[GDB_FR_DUMMY0]; */
+ /* gdb_regs[GDB_FR_DUMMY1]; */
+
+ // regs->fpcr = gdb_regs[GDB_FR_FPCR];
+ // regs->fs0 = gdb_regs[GDB_FR_FS0];
+}
+
+struct kgdb_arch arch_kgdb_ops = {
+ .gdb_bpt_instr = { 0xff },
+ .flags = KGDB_HW_BREAKPOINT,
+};
+
+static const unsigned char mn10300_kgdb_insn_sizes[256] =
+{
+ /* 1 2 3 4 5 6 7 8 9 a b c d e f */
+ 1, 3, 3, 3, 1, 3, 3, 3, 1, 3, 3, 3, 1, 3, 3, 3, /* 0 */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 1 */
+ 2, 2, 2, 2, 3, 3, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3, /* 2 */
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 1, 1, 1, /* 3 */
+ 1, 1, 2, 2, 1, 1, 2, 2, 1, 1, 2, 2, 1, 1, 2, 2, /* 4 */
+ 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, /* 5 */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 6 */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 7 */
+ 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, /* 8 */
+ 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, /* 9 */
+ 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, /* a */
+ 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, /* b */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 2, /* c */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* d */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* e */
+ 0, 2, 2, 2, 2, 2, 2, 4, 0, 3, 0, 4, 0, 6, 7, 1 /* f */
+};
+
+/*
+ * Attempt to emulate single stepping by means of breakpoint instructions.
+ * Although there is a single-step trace flag in EPSW, its use is not
+ * sufficiently documented and is only intended for use with the JTAG debugger.
+ */
+static int kgdb_arch_do_singlestep(struct pt_regs *regs)
+{
+ unsigned long arg;
+ unsigned size;
+ u8 *pc = (u8 *)regs->pc, *sp = (u8 *)(regs + 1), cur;
+ u8 *x = NULL, *y = NULL;
+ int ret;
+
+ ret = probe_kernel_read(&cur, pc, 1);
+ if (ret < 0)
+ return ret;
+
+ size = mn10300_kgdb_insn_sizes[cur];
+ if (size > 0) {
+ x = pc + size;
+ goto set_x;
+ }
+
+ switch (cur) {
+ /* Bxx (d8,PC) */
+ case 0xc0 ... 0xca:
+ ret = probe_kernel_read(&arg, pc + 1, 1);
+ if (ret < 0)
+ return ret;
+ x = pc + 2;
+ if (arg >= 0 && arg <= 2)
+ goto set_x;
+ y = pc + (s8)arg;
+ goto set_x_and_y;
+
+ /* LXX (d8,PC) */
+ case 0xd0 ... 0xda:
+ x = pc + 1;
+ if (regs->pc == regs->lar)
+ goto set_x;
+ y = (u8 *)regs->lar;
+ goto set_x_and_y;
+
+ /* SETLB - loads the next four bytes into the LIR register
+ * (which mustn't include a breakpoint instruction) */
+ case 0xdb:
+ x = pc + 5;
+ goto set_x;
+
+ /* JMP (d16,PC) or CALL (d16,PC) */
+ case 0xcc:
+ case 0xcd:
+ ret = probe_kernel_read(&arg, pc + 1, 2);
+ if (ret < 0)
+ return ret;
+ x = pc + (s16)arg;
+ goto set_x;
+
+ /* JMP (d32,PC) or CALL (d32,PC) */
+ case 0xdc:
+ case 0xdd:
+ ret = probe_kernel_read(&arg, pc + 1, 4);
+ if (ret < 0)
+ return ret;
+ x = pc + (s32)arg;
+ goto set_x;
+
+ /* RETF */
+ case 0xde:
+ x = (u8 *)regs->mdr;
+ goto set_x;
+
+ /* RET */
+ case 0xdf:
+ ret = probe_kernel_read(&arg, pc + 2, 1);
+ if (ret < 0)
+ return ret;
+ ret = probe_kernel_read(&x, sp + (s8)arg, 4);
+ if (ret < 0)
+ return ret;
+ goto set_x;
+
+ case 0xf0:
+ ret = probe_kernel_read(&cur, pc + 1, 1);
+ if (ret < 0)
+ return ret;
+
+ if (cur >= 0xf0 && cur <= 0xf7) {
+ /* JMP (An) / CALLS (An) */
+ switch (cur & 3) {
+ case 0: x = (u8 *)regs->a0; break;
+ case 1: x = (u8 *)regs->a1; break;
+ case 2: x = (u8 *)regs->a2; break;
+ case 3: x = (u8 *)regs->a3; break;
+ }
+ goto set_x;
+ } else if (cur == 0xfc) {
+ /* RETS */
+ ret = probe_kernel_read(&x, sp, 4);
+ if (ret < 0)
+ return ret;
+ goto set_x;
+ } else if (cur == 0xfd) {
+ /* RTI */
+ ret = probe_kernel_read(&x, sp + 4, 4);
+ if (ret < 0)
+ return ret;
+ goto set_x;
+ } else {
+ x = pc + 2;
+ goto set_x;
+ }
+ break;
+
+ /* potential 3-byte conditional branches */
+ case 0xf8:
+ ret = probe_kernel_read(&cur, pc + 1, 1);
+ if (ret < 0)
+ return ret;
+ x = pc + 3;
+
+ if (cur >= 0xe8 && cur <= 0xeb) {
+ ret = probe_kernel_read(&arg, pc + 2, 1);
+ if (ret < 0)
+ return ret;
+ if (arg >= 0 && arg <= 3)
+ goto set_x;
+ y = pc + (s8)arg;
+ goto set_x_and_y;
+ }
+ goto set_x;
+
+ case 0xfa:
+ ret = probe_kernel_read(&cur, pc + 1, 1);
+ if (ret < 0)
+ return ret;
+
+ if (cur == 0xff) {
+ /* CALLS (d16,PC) */
+ ret = probe_kernel_read(&arg, pc + 2, 2);
+ if (ret < 0)
+ return ret;
+ x = pc + (s16)arg;
+ goto set_x;
+ }
+
+ x = pc + 4;
+ goto set_x;
+
+ case 0xfc:
+ ret = probe_kernel_read(&cur, pc + 1, 1);
+ if (ret < 0)
+ return ret;
+
+ if (cur == 0xff) {
+ /* CALLS (d32,PC) */
+ ret = probe_kernel_read(&arg, pc + 2, 4);
+ if (ret < 0)
+ return ret;
+ x = pc + (s32)arg;
+ goto set_x;
+ }
+
+ x = pc + 6;
+ goto set_x;
+ }
+
+ return 0;
+
+set_x:
+ kgdb_sstep_bp_addr[0] = x;
+ kgdb_sstep_bp_addr[1] = NULL;
+ ret = probe_kernel_read(&kgdb_sstep_bp[0], x, 1);
+ if (ret < 0)
+ return ret;
+ ret = probe_kernel_write(x, &arch_kgdb_ops.gdb_bpt_instr, 1);
+ if (ret < 0)
+ return ret;
+ kgdb_sstep_thread = current_thread_info();
+ debugger_local_cache_flushinv_one(x);
+ return ret;
+
+set_x_and_y:
+ kgdb_sstep_bp_addr[0] = x;
+ kgdb_sstep_bp_addr[1] = y;
+ ret = probe_kernel_read(&kgdb_sstep_bp[0], x, 1);
+ if (ret < 0)
+ return ret;
+ ret = probe_kernel_read(&kgdb_sstep_bp[1], y, 1);
+ if (ret < 0)
+ return ret;
+ ret = probe_kernel_write(x, &arch_kgdb_ops.gdb_bpt_instr, 1);
+ if (ret < 0)
+ return ret;
+ ret = probe_kernel_write(y, &arch_kgdb_ops.gdb_bpt_instr, 1);
+ if (ret < 0) {
+ probe_kernel_write(kgdb_sstep_bp_addr[0],
+ &kgdb_sstep_bp[0], 1);
+ } else {
+ kgdb_sstep_thread = current_thread_info();
+ }
+ debugger_local_cache_flushinv_one(x);
+ debugger_local_cache_flushinv_one(y);
+ return ret;
+}
+
+/*
+ * Remove emplaced single-step breakpoints, returning true if we hit one of
+ * them.
+ */
+static bool kgdb_arch_undo_singlestep(struct pt_regs *regs)
+{
+ bool hit = false;
+ u8 *x = kgdb_sstep_bp_addr[0], *y = kgdb_sstep_bp_addr[1];
+ u8 opcode;
+
+ if (kgdb_sstep_thread == current_thread_info()) {
+ if (x) {
+ if (x == (u8 *)regs->pc)
+ hit = true;
+ if (probe_kernel_read(&opcode, x,
+ 1) < 0 ||
+ opcode != 0xff)
+ BUG();
+ probe_kernel_write(x, &kgdb_sstep_bp[0], 1);
+ debugger_local_cache_flushinv_one(x);
+ }
+ if (y) {
+ if (y == (u8 *)regs->pc)
+ hit = true;
+ if (probe_kernel_read(&opcode, y,
+ 1) < 0 ||
+ opcode != 0xff)
+ BUG();
+ probe_kernel_write(y, &kgdb_sstep_bp[1], 1);
+ debugger_local_cache_flushinv_one(y);
+ }
+ }
+
+ kgdb_sstep_bp_addr[0] = NULL;
+ kgdb_sstep_bp_addr[1] = NULL;
+ kgdb_sstep_thread = NULL;
+ return hit;
+}
+
+/*
+ * Catch a single-step-pending thread being deleted and make sure the global
+ * single-step state is cleared. At this point the breakpoints should have
+ * been removed by __switch_to().
+ */
+void arch_release_thread_info(struct thread_info *ti)
+{
+ if (kgdb_sstep_thread == ti) {
+ kgdb_sstep_thread = NULL;
+
+ /* However, we may now be running in degraded mode, with most
+ * of the CPUs disabled until such a time as KGDB is reentered,
+ * so force immediate reentry */
+ kgdb_breakpoint();
+ }
+}
+
+/*
+ * Handle unknown packets and [CcsDk] packets
+ * - at this point breakpoints have been installed
+ */
+int kgdb_arch_handle_exception(int vector, int signo, int err_code,
+ char *remcom_in_buffer, char *remcom_out_buffer,
+ struct pt_regs *regs)
+{
+ long addr;
+ char *ptr;
+
+ switch (remcom_in_buffer[0]) {
+ case 'c':
+ case 's':
+ /* try to read optional parameter, pc unchanged if no parm */
+ ptr = &remcom_in_buffer[1];
+ if (kgdb_hex2long(&ptr, &addr))
+ regs->pc = addr;
+ case 'D':
+ case 'k':
+ atomic_set(&kgdb_cpu_doing_single_step, -1);
+
+ if (remcom_in_buffer[0] == 's') {
+ kgdb_arch_do_singlestep(regs);
+ kgdb_single_step = 1;
+ atomic_set(&kgdb_cpu_doing_single_step,
+ raw_smp_processor_id());
+ }
+ return 0;
+ }
+ return -1; /* this means that we do not want to exit from the handler */
+}
+
+/*
+ * Handle event interception
+ * - returns 0 if the exception should be skipped, -ERROR otherwise.
+ */
+int debugger_intercept(enum exception_code excep, int signo, int si_code,
+ struct pt_regs *regs)
+{
+ int ret;
+
+ if (kgdb_arch_undo_singlestep(regs)) {
+ excep = EXCEP_TRAP;
+ signo = SIGTRAP;
+ si_code = TRAP_TRACE;
+ }
+
+ ret = kgdb_handle_exception(excep, signo, si_code, regs);
+
+ debugger_local_cache_flushinv();
+
+ return ret;
+}
+
+/*
+ * Determine if we've hit a debugger special breakpoint
+ */
+int at_debugger_breakpoint(struct pt_regs *regs)
+{
+ return regs->pc == (unsigned long)&__arch_kgdb_breakpoint;
+}
+
+/*
+ * Initialise kgdb
+ */
+int kgdb_arch_init(void)
+{
+ return 0;
+}
+
+/*
+ * Do something, perhaps, but don't know what.
+ */
+void kgdb_arch_exit(void)
+{
+}
+
+#ifdef CONFIG_SMP
+void debugger_nmi_interrupt(struct pt_regs *regs, enum exception_code code)
+{
+ kgdb_nmicallback(arch_smp_processor_id(), regs);
+ debugger_local_cache_flushinv();
+}
+
+void kgdb_roundup_cpus(unsigned long flags)
+{
+ smp_jump_to_debugger();
+}
+#endif
diff --git a/arch/mn10300/kernel/kprobes.c b/arch/mn10300/kernel/kprobes.c
new file mode 100644
index 000000000..0311a7fce
--- /dev/null
+++ b/arch/mn10300/kernel/kprobes.c
@@ -0,0 +1,656 @@
+/* MN10300 Kernel probes implementation
+ *
+ * Copyright (C) 2005 Red Hat, Inc. All Rights Reserved.
+ * Written by Mark Salter (msalter@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public Licence as published by
+ * the Free Software Foundation; either version 2 of the Licence, 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 Licence for more details.
+ *
+ * You should have received a copy of the GNU General Public Licence
+ * 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/kprobes.h>
+#include <linux/ptrace.h>
+#include <linux/spinlock.h>
+#include <linux/preempt.h>
+#include <linux/kdebug.h>
+#include <asm/cacheflush.h>
+
+struct kretprobe_blackpoint kretprobe_blacklist[] = { { NULL, NULL } };
+const int kretprobe_blacklist_size = ARRAY_SIZE(kretprobe_blacklist);
+
+/* kprobe_status settings */
+#define KPROBE_HIT_ACTIVE 0x00000001
+#define KPROBE_HIT_SS 0x00000002
+
+static struct kprobe *cur_kprobe;
+static unsigned long cur_kprobe_orig_pc;
+static unsigned long cur_kprobe_next_pc;
+static int cur_kprobe_ss_flags;
+static unsigned long kprobe_status;
+static kprobe_opcode_t cur_kprobe_ss_buf[MAX_INSN_SIZE + 2];
+static unsigned long cur_kprobe_bp_addr;
+
+DEFINE_PER_CPU(struct kprobe *, current_kprobe) = NULL;
+
+
+/* singlestep flag bits */
+#define SINGLESTEP_BRANCH 1
+#define SINGLESTEP_PCREL 2
+
+#define READ_BYTE(p, valp) \
+ do { *(u8 *)(valp) = *(u8 *)(p); } while (0)
+
+#define READ_WORD16(p, valp) \
+ do { \
+ READ_BYTE((p), (valp)); \
+ READ_BYTE((u8 *)(p) + 1, (u8 *)(valp) + 1); \
+ } while (0)
+
+#define READ_WORD32(p, valp) \
+ do { \
+ READ_BYTE((p), (valp)); \
+ READ_BYTE((u8 *)(p) + 1, (u8 *)(valp) + 1); \
+ READ_BYTE((u8 *)(p) + 2, (u8 *)(valp) + 2); \
+ READ_BYTE((u8 *)(p) + 3, (u8 *)(valp) + 3); \
+ } while (0)
+
+
+static const u8 mn10300_insn_sizes[256] =
+{
+ /* 1 2 3 4 5 6 7 8 9 a b c d e f */
+ 1, 3, 3, 3, 1, 3, 3, 3, 1, 3, 3, 3, 1, 3, 3, 3, /* 0 */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 1 */
+ 2, 2, 2, 2, 3, 3, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3, /* 2 */
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 1, 1, 1, /* 3 */
+ 1, 1, 2, 2, 1, 1, 2, 2, 1, 1, 2, 2, 1, 1, 2, 2, /* 4 */
+ 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, /* 5 */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 6 */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 7 */
+ 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, /* 8 */
+ 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, /* 9 */
+ 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, /* a */
+ 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, /* b */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 2, /* c */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* d */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* e */
+ 0, 2, 2, 2, 2, 2, 2, 4, 0, 3, 0, 4, 0, 6, 7, 1 /* f */
+};
+
+#define LT (1 << 0)
+#define GT (1 << 1)
+#define GE (1 << 2)
+#define LE (1 << 3)
+#define CS (1 << 4)
+#define HI (1 << 5)
+#define CC (1 << 6)
+#define LS (1 << 7)
+#define EQ (1 << 8)
+#define NE (1 << 9)
+#define RA (1 << 10)
+#define VC (1 << 11)
+#define VS (1 << 12)
+#define NC (1 << 13)
+#define NS (1 << 14)
+
+static const u16 cond_table[] = {
+ /* V C N Z */
+ /* 0 0 0 0 */ (NE | NC | CC | VC | GE | GT | HI),
+ /* 0 0 0 1 */ (EQ | NC | CC | VC | GE | LE | LS),
+ /* 0 0 1 0 */ (NE | NS | CC | VC | LT | LE | HI),
+ /* 0 0 1 1 */ (EQ | NS | CC | VC | LT | LE | LS),
+ /* 0 1 0 0 */ (NE | NC | CS | VC | GE | GT | LS),
+ /* 0 1 0 1 */ (EQ | NC | CS | VC | GE | LE | LS),
+ /* 0 1 1 0 */ (NE | NS | CS | VC | LT | LE | LS),
+ /* 0 1 1 1 */ (EQ | NS | CS | VC | LT | LE | LS),
+ /* 1 0 0 0 */ (NE | NC | CC | VS | LT | LE | HI),
+ /* 1 0 0 1 */ (EQ | NC | CC | VS | LT | LE | LS),
+ /* 1 0 1 0 */ (NE | NS | CC | VS | GE | GT | HI),
+ /* 1 0 1 1 */ (EQ | NS | CC | VS | GE | LE | LS),
+ /* 1 1 0 0 */ (NE | NC | CS | VS | LT | LE | LS),
+ /* 1 1 0 1 */ (EQ | NC | CS | VS | LT | LE | LS),
+ /* 1 1 1 0 */ (NE | NS | CS | VS | GE | GT | LS),
+ /* 1 1 1 1 */ (EQ | NS | CS | VS | GE | LE | LS),
+};
+
+/*
+ * Calculate what the PC will be after executing next instruction
+ */
+static unsigned find_nextpc(struct pt_regs *regs, int *flags)
+{
+ unsigned size;
+ s8 x8;
+ s16 x16;
+ s32 x32;
+ u8 opc, *pc, *sp, *next;
+
+ next = 0;
+ *flags = SINGLESTEP_PCREL;
+
+ pc = (u8 *) regs->pc;
+ sp = (u8 *) (regs + 1);
+ opc = *pc;
+
+ size = mn10300_insn_sizes[opc];
+ if (size > 0) {
+ next = pc + size;
+ } else {
+ switch (opc) {
+ /* Bxx (d8,PC) */
+ case 0xc0 ... 0xca:
+ x8 = 2;
+ if (cond_table[regs->epsw & 0xf] & (1 << (opc & 0xf)))
+ x8 = (s8)pc[1];
+ next = pc + x8;
+ *flags |= SINGLESTEP_BRANCH;
+ break;
+
+ /* JMP (d16,PC) or CALL (d16,PC) */
+ case 0xcc:
+ case 0xcd:
+ READ_WORD16(pc + 1, &x16);
+ next = pc + x16;
+ *flags |= SINGLESTEP_BRANCH;
+ break;
+
+ /* JMP (d32,PC) or CALL (d32,PC) */
+ case 0xdc:
+ case 0xdd:
+ READ_WORD32(pc + 1, &x32);
+ next = pc + x32;
+ *flags |= SINGLESTEP_BRANCH;
+ break;
+
+ /* RETF */
+ case 0xde:
+ next = (u8 *)regs->mdr;
+ *flags &= ~SINGLESTEP_PCREL;
+ *flags |= SINGLESTEP_BRANCH;
+ break;
+
+ /* RET */
+ case 0xdf:
+ sp += pc[2];
+ READ_WORD32(sp, &x32);
+ next = (u8 *)x32;
+ *flags &= ~SINGLESTEP_PCREL;
+ *flags |= SINGLESTEP_BRANCH;
+ break;
+
+ case 0xf0:
+ next = pc + 2;
+ opc = pc[1];
+ if (opc >= 0xf0 && opc <= 0xf7) {
+ /* JMP (An) / CALLS (An) */
+ switch (opc & 3) {
+ case 0:
+ next = (u8 *)regs->a0;
+ break;
+ case 1:
+ next = (u8 *)regs->a1;
+ break;
+ case 2:
+ next = (u8 *)regs->a2;
+ break;
+ case 3:
+ next = (u8 *)regs->a3;
+ break;
+ }
+ *flags &= ~SINGLESTEP_PCREL;
+ *flags |= SINGLESTEP_BRANCH;
+ } else if (opc == 0xfc) {
+ /* RETS */
+ READ_WORD32(sp, &x32);
+ next = (u8 *)x32;
+ *flags &= ~SINGLESTEP_PCREL;
+ *flags |= SINGLESTEP_BRANCH;
+ } else if (opc == 0xfd) {
+ /* RTI */
+ READ_WORD32(sp + 4, &x32);
+ next = (u8 *)x32;
+ *flags &= ~SINGLESTEP_PCREL;
+ *flags |= SINGLESTEP_BRANCH;
+ }
+ break;
+
+ /* potential 3-byte conditional branches */
+ case 0xf8:
+ next = pc + 3;
+ opc = pc[1];
+ if (opc >= 0xe8 && opc <= 0xeb &&
+ (cond_table[regs->epsw & 0xf] &
+ (1 << ((opc & 0xf) + 3)))
+ ) {
+ READ_BYTE(pc+2, &x8);
+ next = pc + x8;
+ *flags |= SINGLESTEP_BRANCH;
+ }
+ break;
+
+ case 0xfa:
+ if (pc[1] == 0xff) {
+ /* CALLS (d16,PC) */
+ READ_WORD16(pc + 2, &x16);
+ next = pc + x16;
+ } else
+ next = pc + 4;
+ *flags |= SINGLESTEP_BRANCH;
+ break;
+
+ case 0xfc:
+ x32 = 6;
+ if (pc[1] == 0xff) {
+ /* CALLS (d32,PC) */
+ READ_WORD32(pc + 2, &x32);
+ }
+ next = pc + x32;
+ *flags |= SINGLESTEP_BRANCH;
+ break;
+ /* LXX (d8,PC) */
+ /* SETLB - loads the next four bytes into the LIR reg */
+ case 0xd0 ... 0xda:
+ case 0xdb:
+ panic("Can't singlestep Lxx/SETLB\n");
+ break;
+ }
+ }
+ return (unsigned)next;
+
+}
+
+/*
+ * set up out of place singlestep of some branching instructions
+ */
+static unsigned __kprobes singlestep_branch_setup(struct pt_regs *regs)
+{
+ u8 opc, *pc, *sp, *next;
+
+ next = NULL;
+ pc = (u8 *) regs->pc;
+ sp = (u8 *) (regs + 1);
+
+ switch (pc[0]) {
+ case 0xc0 ... 0xca: /* Bxx (d8,PC) */
+ case 0xcc: /* JMP (d16,PC) */
+ case 0xdc: /* JMP (d32,PC) */
+ case 0xf8: /* Bxx (d8,PC) 3-byte version */
+ /* don't really need to do anything except cause trap */
+ next = pc;
+ break;
+
+ case 0xcd: /* CALL (d16,PC) */
+ pc[1] = 5;
+ pc[2] = 0;
+ next = pc + 5;
+ break;
+
+ case 0xdd: /* CALL (d32,PC) */
+ pc[1] = 7;
+ pc[2] = 0;
+ pc[3] = 0;
+ pc[4] = 0;
+ next = pc + 7;
+ break;
+
+ case 0xde: /* RETF */
+ next = pc + 3;
+ regs->mdr = (unsigned) next;
+ break;
+
+ case 0xdf: /* RET */
+ sp += pc[2];
+ next = pc + 3;
+ *(unsigned *)sp = (unsigned) next;
+ break;
+
+ case 0xf0:
+ next = pc + 2;
+ opc = pc[1];
+ if (opc >= 0xf0 && opc <= 0xf3) {
+ /* CALLS (An) */
+ /* use CALLS (d16,PC) to avoid mucking with An */
+ pc[0] = 0xfa;
+ pc[1] = 0xff;
+ pc[2] = 4;
+ pc[3] = 0;
+ next = pc + 4;
+ } else if (opc >= 0xf4 && opc <= 0xf7) {
+ /* JMP (An) */
+ next = pc;
+ } else if (opc == 0xfc) {
+ /* RETS */
+ next = pc + 2;
+ *(unsigned *) sp = (unsigned) next;
+ } else if (opc == 0xfd) {
+ /* RTI */
+ next = pc + 2;
+ *(unsigned *)(sp + 4) = (unsigned) next;
+ }
+ break;
+
+ case 0xfa: /* CALLS (d16,PC) */
+ pc[2] = 4;
+ pc[3] = 0;
+ next = pc + 4;
+ break;
+
+ case 0xfc: /* CALLS (d32,PC) */
+ pc[2] = 6;
+ pc[3] = 0;
+ pc[4] = 0;
+ pc[5] = 0;
+ next = pc + 6;
+ break;
+
+ case 0xd0 ... 0xda: /* LXX (d8,PC) */
+ case 0xdb: /* SETLB */
+ panic("Can't singlestep Lxx/SETLB\n");
+ }
+
+ return (unsigned) next;
+}
+
+int __kprobes arch_prepare_kprobe(struct kprobe *p)
+{
+ return 0;
+}
+
+void __kprobes arch_copy_kprobe(struct kprobe *p)
+{
+ memcpy(p->ainsn.insn, p->addr, MAX_INSN_SIZE);
+}
+
+void __kprobes arch_arm_kprobe(struct kprobe *p)
+{
+ *p->addr = BREAKPOINT_INSTRUCTION;
+ flush_icache_range((unsigned long) p->addr,
+ (unsigned long) p->addr + sizeof(kprobe_opcode_t));
+}
+
+void __kprobes arch_disarm_kprobe(struct kprobe *p)
+{
+#ifndef CONFIG_MN10300_CACHE_SNOOP
+ mn10300_dcache_flush();
+ mn10300_icache_inv();
+#endif
+}
+
+void arch_remove_kprobe(struct kprobe *p)
+{
+}
+
+static inline
+void __kprobes disarm_kprobe(struct kprobe *p, struct pt_regs *regs)
+{
+ *p->addr = p->opcode;
+ regs->pc = (unsigned long) p->addr;
+#ifndef CONFIG_MN10300_CACHE_SNOOP
+ mn10300_dcache_flush();
+ mn10300_icache_inv();
+#endif
+}
+
+static inline
+void __kprobes prepare_singlestep(struct kprobe *p, struct pt_regs *regs)
+{
+ unsigned long nextpc;
+
+ cur_kprobe_orig_pc = regs->pc;
+ memcpy(cur_kprobe_ss_buf, &p->ainsn.insn[0], MAX_INSN_SIZE);
+ regs->pc = (unsigned long) cur_kprobe_ss_buf;
+
+ nextpc = find_nextpc(regs, &cur_kprobe_ss_flags);
+ if (cur_kprobe_ss_flags & SINGLESTEP_PCREL)
+ cur_kprobe_next_pc = cur_kprobe_orig_pc + (nextpc - regs->pc);
+ else
+ cur_kprobe_next_pc = nextpc;
+
+ /* branching instructions need special handling */
+ if (cur_kprobe_ss_flags & SINGLESTEP_BRANCH)
+ nextpc = singlestep_branch_setup(regs);
+
+ cur_kprobe_bp_addr = nextpc;
+
+ *(u8 *) nextpc = BREAKPOINT_INSTRUCTION;
+ mn10300_dcache_flush_range2((unsigned) cur_kprobe_ss_buf,
+ sizeof(cur_kprobe_ss_buf));
+ mn10300_icache_inv();
+}
+
+static inline int __kprobes kprobe_handler(struct pt_regs *regs)
+{
+ struct kprobe *p;
+ int ret = 0;
+ unsigned int *addr = (unsigned int *) regs->pc;
+
+ /* We're in an interrupt, but this is clear and BUG()-safe. */
+ preempt_disable();
+
+ /* Check we're not actually recursing */
+ if (kprobe_running()) {
+ /* We *are* holding lock here, so this is safe.
+ Disarm the probe we just hit, and ignore it. */
+ p = get_kprobe(addr);
+ if (p) {
+ disarm_kprobe(p, regs);
+ ret = 1;
+ } else {
+ p = cur_kprobe;
+ if (p->break_handler && p->break_handler(p, regs))
+ goto ss_probe;
+ }
+ /* If it's not ours, can't be delete race, (we hold lock). */
+ goto no_kprobe;
+ }
+
+ p = get_kprobe(addr);
+ if (!p) {
+ if (*addr != BREAKPOINT_INSTRUCTION) {
+ /* The breakpoint instruction was removed right after
+ * we hit it. Another cpu has removed either a
+ * probepoint or a debugger breakpoint at this address.
+ * In either case, no further handling of this
+ * interrupt is appropriate.
+ */
+ ret = 1;
+ }
+ /* Not one of ours: let kernel handle it */
+ goto no_kprobe;
+ }
+
+ kprobe_status = KPROBE_HIT_ACTIVE;
+ cur_kprobe = p;
+ if (p->pre_handler(p, regs)) {
+ /* handler has already set things up, so skip ss setup */
+ return 1;
+ }
+
+ss_probe:
+ prepare_singlestep(p, regs);
+ kprobe_status = KPROBE_HIT_SS;
+ return 1;
+
+no_kprobe:
+ preempt_enable_no_resched();
+ return ret;
+}
+
+/*
+ * Called after single-stepping. p->addr is the address of the
+ * instruction whose first byte has been replaced by the "breakpoint"
+ * instruction. To avoid the SMP problems that can occur when we
+ * temporarily put back the original opcode to single-step, we
+ * single-stepped a copy of the instruction. The address of this
+ * copy is p->ainsn.insn.
+ */
+static void __kprobes resume_execution(struct kprobe *p, struct pt_regs *regs)
+{
+ /* we may need to fixup regs/stack after singlestepping a call insn */
+ if (cur_kprobe_ss_flags & SINGLESTEP_BRANCH) {
+ regs->pc = cur_kprobe_orig_pc;
+ switch (p->ainsn.insn[0]) {
+ case 0xcd: /* CALL (d16,PC) */
+ *(unsigned *) regs->sp = regs->mdr = regs->pc + 5;
+ break;
+ case 0xdd: /* CALL (d32,PC) */
+ /* fixup mdr and return address on stack */
+ *(unsigned *) regs->sp = regs->mdr = regs->pc + 7;
+ break;
+ case 0xf0:
+ if (p->ainsn.insn[1] >= 0xf0 &&
+ p->ainsn.insn[1] <= 0xf3) {
+ /* CALLS (An) */
+ /* fixup MDR and return address on stack */
+ regs->mdr = regs->pc + 2;
+ *(unsigned *) regs->sp = regs->mdr;
+ }
+ break;
+
+ case 0xfa: /* CALLS (d16,PC) */
+ /* fixup MDR and return address on stack */
+ *(unsigned *) regs->sp = regs->mdr = regs->pc + 4;
+ break;
+
+ case 0xfc: /* CALLS (d32,PC) */
+ /* fixup MDR and return address on stack */
+ *(unsigned *) regs->sp = regs->mdr = regs->pc + 6;
+ break;
+ }
+ }
+
+ regs->pc = cur_kprobe_next_pc;
+ cur_kprobe_bp_addr = 0;
+}
+
+static inline int __kprobes post_kprobe_handler(struct pt_regs *regs)
+{
+ if (!kprobe_running())
+ return 0;
+
+ if (cur_kprobe->post_handler)
+ cur_kprobe->post_handler(cur_kprobe, regs, 0);
+
+ resume_execution(cur_kprobe, regs);
+ reset_current_kprobe();
+ preempt_enable_no_resched();
+ return 1;
+}
+
+/* Interrupts disabled, kprobe_lock held. */
+static inline
+int __kprobes kprobe_fault_handler(struct pt_regs *regs, int trapnr)
+{
+ if (cur_kprobe->fault_handler &&
+ cur_kprobe->fault_handler(cur_kprobe, regs, trapnr))
+ return 1;
+
+ if (kprobe_status & KPROBE_HIT_SS) {
+ resume_execution(cur_kprobe, regs);
+ reset_current_kprobe();
+ preempt_enable_no_resched();
+ }
+ return 0;
+}
+
+/*
+ * Wrapper routine to for handling exceptions.
+ */
+int __kprobes kprobe_exceptions_notify(struct notifier_block *self,
+ unsigned long val, void *data)
+{
+ struct die_args *args = data;
+
+ switch (val) {
+ case DIE_BREAKPOINT:
+ if (cur_kprobe_bp_addr != args->regs->pc) {
+ if (kprobe_handler(args->regs))
+ return NOTIFY_STOP;
+ } else {
+ if (post_kprobe_handler(args->regs))
+ return NOTIFY_STOP;
+ }
+ break;
+ case DIE_GPF:
+ if (kprobe_running() &&
+ kprobe_fault_handler(args->regs, args->trapnr))
+ return NOTIFY_STOP;
+ break;
+ default:
+ break;
+ }
+ return NOTIFY_DONE;
+}
+
+/* Jprobes support. */
+static struct pt_regs jprobe_saved_regs;
+static struct pt_regs *jprobe_saved_regs_location;
+static kprobe_opcode_t jprobe_saved_stack[MAX_STACK_SIZE];
+
+int __kprobes setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs)
+{
+ struct jprobe *jp = container_of(p, struct jprobe, kp);
+
+ jprobe_saved_regs_location = regs;
+ memcpy(&jprobe_saved_regs, regs, sizeof(struct pt_regs));
+
+ /* Save a whole stack frame, this gets arguments
+ * pushed onto the stack after using up all the
+ * arg registers.
+ */
+ memcpy(&jprobe_saved_stack, regs + 1, sizeof(jprobe_saved_stack));
+
+ /* setup return addr to the jprobe handler routine */
+ regs->pc = (unsigned long) jp->entry;
+ return 1;
+}
+
+void __kprobes jprobe_return(void)
+{
+ void *orig_sp = jprobe_saved_regs_location + 1;
+
+ preempt_enable_no_resched();
+ asm volatile(" mov %0,sp\n"
+ ".globl jprobe_return_bp_addr\n"
+ "jprobe_return_bp_addr:\n\t"
+ " .byte 0xff\n"
+ : : "d" (orig_sp));
+}
+
+extern void jprobe_return_bp_addr(void);
+
+int __kprobes longjmp_break_handler(struct kprobe *p, struct pt_regs *regs)
+{
+ u8 *addr = (u8 *) regs->pc;
+
+ if (addr == (u8 *) jprobe_return_bp_addr) {
+ if (jprobe_saved_regs_location != regs) {
+ printk(KERN_ERR"JPROBE:"
+ " Current regs (%p) does not match saved regs"
+ " (%p).\n",
+ regs, jprobe_saved_regs_location);
+ BUG();
+ }
+
+ /* Restore old register state.
+ */
+ memcpy(regs, &jprobe_saved_regs, sizeof(struct pt_regs));
+
+ memcpy(regs + 1, &jprobe_saved_stack,
+ sizeof(jprobe_saved_stack));
+ return 1;
+ }
+ return 0;
+}
+
+int __init arch_init_kprobes(void)
+{
+ return 0;
+}
diff --git a/arch/mn10300/kernel/mn10300-debug.c b/arch/mn10300/kernel/mn10300-debug.c
new file mode 100644
index 000000000..bd8196478
--- /dev/null
+++ b/arch/mn10300/kernel/mn10300-debug.c
@@ -0,0 +1,58 @@
+/* Debugging stuff for the MN10300-based processors
+ *
+ * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+#include <linux/sched.h>
+#include <asm/serial-regs.h>
+
+#undef MN10300_CONSOLE_ON_SERIO
+
+/*
+ * write a string directly through one of the serial ports on-board the MN10300
+ */
+#ifdef MN10300_CONSOLE_ON_SERIO
+void debug_to_serial_mnser(const char *p, int n)
+{
+ char ch;
+
+ for (; n > 0; n--) {
+ ch = *p++;
+
+#if MN10300_CONSOLE_ON_SERIO == 0
+ while (SC0STR & (SC01STR_TBF)) continue;
+ SC0TXB = ch;
+ while (SC0STR & (SC01STR_TBF)) continue;
+ if (ch == 0x0a) {
+ SC0TXB = 0x0d;
+ while (SC0STR & (SC01STR_TBF)) continue;
+ }
+
+#elif MN10300_CONSOLE_ON_SERIO == 1
+ while (SC1STR & (SC01STR_TBF)) continue;
+ SC1TXB = ch;
+ while (SC1STR & (SC01STR_TBF)) continue;
+ if (ch == 0x0a) {
+ SC1TXB = 0x0d;
+ while (SC1STR & (SC01STR_TBF)) continue;
+ }
+
+#elif MN10300_CONSOLE_ON_SERIO == 2
+ while (SC2STR & (SC2STR_TBF)) continue;
+ SC2TXB = ch;
+ while (SC2STR & (SC2STR_TBF)) continue;
+ if (ch == 0x0a) {
+ SC2TXB = 0x0d;
+ while (SC2STR & (SC2STR_TBF)) continue;
+ }
+
+#endif
+ }
+}
+#endif
+
diff --git a/arch/mn10300/kernel/mn10300-serial-low.S b/arch/mn10300/kernel/mn10300-serial-low.S
new file mode 100644
index 000000000..b95e76caf
--- /dev/null
+++ b/arch/mn10300/kernel/mn10300-serial-low.S
@@ -0,0 +1,194 @@
+###############################################################################
+#
+# Virtual DMA driver for MN10300 serial ports
+#
+# Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
+# Written by David Howells (dhowells@redhat.com)
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public Licence
+# as published by the Free Software Foundation; either version
+# 2 of the Licence, or (at your option) any later version.
+#
+###############################################################################
+#include <linux/sys.h>
+#include <linux/linkage.h>
+#include <asm/page.h>
+#include <asm/smp.h>
+#include <asm/cpu-regs.h>
+#include <asm/frame.inc>
+#include <asm/timer-regs.h>
+#include <proc/cache.h>
+#include <unit/timex.h>
+#include "mn10300-serial.h"
+
+#define SCxCTR 0x00
+#define SCxICR 0x04
+#define SCxTXB 0x08
+#define SCxRXB 0x09
+#define SCxSTR 0x0c
+#define SCxTIM 0x0d
+
+ .text
+
+###############################################################################
+#
+# serial port interrupt virtual DMA entry point
+# - intended to run at interrupt priority 1 (not affected by local_irq_disable)
+#
+###############################################################################
+ .balign L1_CACHE_BYTES
+ENTRY(mn10300_serial_vdma_interrupt)
+# or EPSW_IE,psw # permit overriding by
+ # debugging interrupts
+ movm [d2,d3,a2,a3,exreg0],(sp)
+
+ movhu (IAGR),a2 # see if which interrupt is
+ # pending
+ and IAGR_GN,a2
+ add a2,a2
+ add mn10300_serial_int_tbl,a2
+
+ mov (a2+),a3
+ mov (__iobase,a3),e2
+ mov (a2),a2
+ jmp (a2)
+
+###############################################################################
+#
+# serial port receive interrupt virtual DMA entry point
+# - intended to run at interrupt priority 1 (not affected by local_irq_disable)
+# - stores data/status byte pairs in the ring buffer
+# - induces a scheduler tick timer interrupt when done, which we then subvert
+# on entry:
+# A3 struct mn10300_serial_port *
+# E2 I/O port base
+#
+###############################################################################
+ENTRY(mn10300_serial_vdma_rx_handler)
+ mov (__rx_icr,a3),e3
+ mov GxICR_DETECT,d2
+ movbu d2,(e3) # ACK the interrupt
+ movhu (e3),d2 # flush
+
+ mov (__rx_inp,a3),d3
+ mov d3,a2
+ add 2,d3
+ and MNSC_BUFFER_SIZE-1,d3
+ mov (__rx_outp,a3),d2
+ cmp d3,d2
+ beq mnsc_vdma_rx_overflow
+
+ mov (__rx_buffer,a3),d2
+ add d2,a2
+ movhu (SCxSTR,e2),d2
+ movbu d2,(1,a2)
+ movbu (SCxRXB,e2),d2
+ movbu d2,(a2)
+ mov d3,(__rx_inp,a3)
+ bset MNSCx_RX_AVAIL,(__intr_flags,a3)
+
+mnsc_vdma_rx_done:
+ mov (__tm_icr,a3),a2
+ mov GxICR_LEVEL_6|GxICR_ENABLE|GxICR_REQUEST|GxICR_DETECT,d2
+ movhu d2,(a2) # request a slow interrupt
+ movhu (a2),d2 # flush
+
+ movm (sp),[d2,d3,a2,a3,exreg0]
+ rti
+
+mnsc_vdma_rx_overflow:
+ bset MNSCx_RX_OVERF,(__intr_flags,a3)
+ bra mnsc_vdma_rx_done
+
+###############################################################################
+#
+# serial port transmit interrupt virtual DMA entry point
+# - intended to run at interrupt priority 1 (not affected by local_irq_disable)
+# - retrieves data bytes from the ring buffer and passes them to the serial port
+# - induces a scheduler tick timer interrupt when done, which we then subvert
+# A3 struct mn10300_serial_port *
+# E2 I/O port base
+#
+###############################################################################
+ .balign L1_CACHE_BYTES
+ENTRY(mn10300_serial_vdma_tx_handler)
+ mov (__tx_icr,a3),e3
+ mov GxICR_DETECT,d2
+ movbu d2,(e3) # ACK the interrupt
+ movhu (e3),d2 # flush
+
+ btst 0xFF,(__tx_flags,a3) # handle transmit flags
+ bne mnsc_vdma_tx_flags
+
+ movbu (SCxSTR,e2),d2 # don't try and transmit a char if the
+ # buffer is not empty
+ btst SC01STR_TBF,d2 # (may have tried to jumpstart)
+ bne mnsc_vdma_tx_noint
+
+ movbu (__tx_xchar,a3),d2 # handle hi-pri XON/XOFF
+ or d2,d2
+ bne mnsc_vdma_tx_xchar
+
+ mov (__uart_state,a3),a2 # see if the TTY Tx queue has anything in it
+ mov (__xmit_tail,a2),d3
+ mov (__xmit_head,a2),d2
+ cmp d3,d2
+ beq mnsc_vdma_tx_empty
+
+ mov (__xmit_buffer,a2),d2 # get a char from the buffer and
+ # transmit it
+ movbu (d3,d2),d2
+ movbu d2,(SCxTXB,e2) # Tx
+
+ inc d3 # advance the buffer pointer
+ and __UART_XMIT_SIZE-1,d3
+ mov (__xmit_head,a2),d2
+ mov d3,(__xmit_tail,a2)
+
+ sub d3,d2 # see if we've written everything
+ beq mnsc_vdma_tx_empty
+
+ and __UART_XMIT_SIZE-1,d2 # see if we just made a hole
+ cmp __UART_XMIT_SIZE-2,d2
+ beq mnsc_vdma_tx_made_hole
+
+mnsc_vdma_tx_done:
+ mov (__tm_icr,a3),a2
+ mov GxICR_LEVEL_6|GxICR_ENABLE|GxICR_REQUEST|GxICR_DETECT,d2
+ movhu d2,(a2) # request a slow interrupt
+ movhu (a2),d2 # flush
+
+mnsc_vdma_tx_noint:
+ movm (sp),[d2,d3,a2,a3,exreg0]
+ rti
+
+mnsc_vdma_tx_empty:
+ mov +(NUM2GxICR_LEVEL(CONFIG_MN10300_SERIAL_IRQ_LEVEL)|GxICR_DETECT),d2
+ movhu d2,(e3) # disable the interrupt
+ movhu (e3),d2 # flush
+
+ bset MNSCx_TX_EMPTY,(__intr_flags,a3)
+ bra mnsc_vdma_tx_done
+
+mnsc_vdma_tx_flags:
+ btst MNSCx_TX_STOP,(__tx_flags,a3)
+ bne mnsc_vdma_tx_stop
+ movhu (SCxCTR,e2),d2 # turn on break mode
+ or SC01CTR_BKE,d2
+ movhu d2,(SCxCTR,e2)
+mnsc_vdma_tx_stop:
+ mov +(NUM2GxICR_LEVEL(CONFIG_MN10300_SERIAL_IRQ_LEVEL)|GxICR_DETECT),d2
+ movhu d2,(e3) # disable transmit interrupts on this
+ # channel
+ movhu (e3),d2 # flush
+ bra mnsc_vdma_tx_noint
+
+mnsc_vdma_tx_xchar:
+ bclr 0xff,(__tx_xchar,a3)
+ movbu d2,(SCxTXB,e2)
+ bra mnsc_vdma_tx_done
+
+mnsc_vdma_tx_made_hole:
+ bset MNSCx_TX_SPACE,(__intr_flags,a3)
+ bra mnsc_vdma_tx_done
diff --git a/arch/mn10300/kernel/mn10300-serial.c b/arch/mn10300/kernel/mn10300-serial.c
new file mode 100644
index 000000000..7ecf69879
--- /dev/null
+++ b/arch/mn10300/kernel/mn10300-serial.c
@@ -0,0 +1,1787 @@
+/* MN10300 On-chip serial port UART driver
+ *
+ * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+static const char serial_name[] = "MN10300 Serial driver";
+static const char serial_version[] = "mn10300_serial-1.0";
+static const char serial_revdate[] = "2007-11-06";
+
+#if defined(CONFIG_MN10300_TTYSM_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
+#include <linux/module.h>
+#include <linux/serial.h>
+#include <linux/circ_buf.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/major.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/console.h>
+#include <linux/sysrq.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/bitops.h>
+#include <asm/serial-regs.h>
+#include <unit/timex.h>
+#include "mn10300-serial.h"
+
+#ifdef CONFIG_SMP
+#undef GxICR
+#define GxICR(X) CROSS_GxICR(X, 0)
+#endif /* CONFIG_SMP */
+
+#define kenter(FMT, ...) \
+ printk(KERN_DEBUG "-->%s(" FMT ")\n", __func__, ##__VA_ARGS__)
+#define _enter(FMT, ...) \
+ no_printk(KERN_DEBUG "-->%s(" FMT ")\n", __func__, ##__VA_ARGS__)
+#define kdebug(FMT, ...) \
+ printk(KERN_DEBUG "--- " FMT "\n", ##__VA_ARGS__)
+#define _debug(FMT, ...) \
+ no_printk(KERN_DEBUG "--- " FMT "\n", ##__VA_ARGS__)
+#define kproto(FMT, ...) \
+ printk(KERN_DEBUG "### MNSERIAL " FMT " ###\n", ##__VA_ARGS__)
+#define _proto(FMT, ...) \
+ no_printk(KERN_DEBUG "### MNSERIAL " FMT " ###\n", ##__VA_ARGS__)
+
+#ifndef CODMSB
+/* c_cflag bit meaning */
+#define CODMSB 004000000000 /* change Transfer bit-order */
+#endif
+
+#define NR_UARTS 3
+
+#ifdef CONFIG_MN10300_TTYSM_CONSOLE
+static void mn10300_serial_console_write(struct console *co,
+ const char *s, unsigned count);
+static int __init mn10300_serial_console_setup(struct console *co,
+ char *options);
+
+static struct uart_driver mn10300_serial_driver;
+static struct console mn10300_serial_console = {
+ .name = "ttySM",
+ .write = mn10300_serial_console_write,
+ .device = uart_console_device,
+ .setup = mn10300_serial_console_setup,
+ .flags = CON_PRINTBUFFER,
+ .index = -1,
+ .data = &mn10300_serial_driver,
+};
+#endif
+
+static struct uart_driver mn10300_serial_driver = {
+ .owner = NULL,
+ .driver_name = "mn10300-serial",
+ .dev_name = "ttySM",
+ .major = TTY_MAJOR,
+ .minor = 128,
+ .nr = NR_UARTS,
+#ifdef CONFIG_MN10300_TTYSM_CONSOLE
+ .cons = &mn10300_serial_console,
+#endif
+};
+
+static unsigned int mn10300_serial_tx_empty(struct uart_port *);
+static void mn10300_serial_set_mctrl(struct uart_port *, unsigned int mctrl);
+static unsigned int mn10300_serial_get_mctrl(struct uart_port *);
+static void mn10300_serial_stop_tx(struct uart_port *);
+static void mn10300_serial_start_tx(struct uart_port *);
+static void mn10300_serial_send_xchar(struct uart_port *, char ch);
+static void mn10300_serial_stop_rx(struct uart_port *);
+static void mn10300_serial_enable_ms(struct uart_port *);
+static void mn10300_serial_break_ctl(struct uart_port *, int ctl);
+static int mn10300_serial_startup(struct uart_port *);
+static void mn10300_serial_shutdown(struct uart_port *);
+static void mn10300_serial_set_termios(struct uart_port *,
+ struct ktermios *new,
+ struct ktermios *old);
+static const char *mn10300_serial_type(struct uart_port *);
+static void mn10300_serial_release_port(struct uart_port *);
+static int mn10300_serial_request_port(struct uart_port *);
+static void mn10300_serial_config_port(struct uart_port *, int);
+static int mn10300_serial_verify_port(struct uart_port *,
+ struct serial_struct *);
+#ifdef CONFIG_CONSOLE_POLL
+static void mn10300_serial_poll_put_char(struct uart_port *, unsigned char);
+static int mn10300_serial_poll_get_char(struct uart_port *);
+#endif
+
+static const struct uart_ops mn10300_serial_ops = {
+ .tx_empty = mn10300_serial_tx_empty,
+ .set_mctrl = mn10300_serial_set_mctrl,
+ .get_mctrl = mn10300_serial_get_mctrl,
+ .stop_tx = mn10300_serial_stop_tx,
+ .start_tx = mn10300_serial_start_tx,
+ .send_xchar = mn10300_serial_send_xchar,
+ .stop_rx = mn10300_serial_stop_rx,
+ .enable_ms = mn10300_serial_enable_ms,
+ .break_ctl = mn10300_serial_break_ctl,
+ .startup = mn10300_serial_startup,
+ .shutdown = mn10300_serial_shutdown,
+ .set_termios = mn10300_serial_set_termios,
+ .type = mn10300_serial_type,
+ .release_port = mn10300_serial_release_port,
+ .request_port = mn10300_serial_request_port,
+ .config_port = mn10300_serial_config_port,
+ .verify_port = mn10300_serial_verify_port,
+#ifdef CONFIG_CONSOLE_POLL
+ .poll_put_char = mn10300_serial_poll_put_char,
+ .poll_get_char = mn10300_serial_poll_get_char,
+#endif
+};
+
+static irqreturn_t mn10300_serial_interrupt(int irq, void *dev_id);
+
+/*
+ * the first on-chip serial port: ttySM0 (aka SIF0)
+ */
+#ifdef CONFIG_MN10300_TTYSM0
+struct mn10300_serial_port mn10300_serial_port_sif0 = {
+ .uart.ops = &mn10300_serial_ops,
+ .uart.membase = (void __iomem *) &SC0CTR,
+ .uart.mapbase = (unsigned long) &SC0CTR,
+ .uart.iotype = UPIO_MEM,
+ .uart.irq = 0,
+ .uart.uartclk = 0, /* MN10300_IOCLK, */
+ .uart.fifosize = 1,
+ .uart.flags = UPF_BOOT_AUTOCONF,
+ .uart.line = 0,
+ .uart.type = PORT_MN10300,
+ .uart.lock =
+ __SPIN_LOCK_UNLOCKED(mn10300_serial_port_sif0.uart.lock),
+ .name = "ttySM0",
+ ._iobase = &SC0CTR,
+ ._control = &SC0CTR,
+ ._status = (volatile u8 *)&SC0STR,
+ ._intr = &SC0ICR,
+ ._rxb = &SC0RXB,
+ ._txb = &SC0TXB,
+ .rx_name = "ttySM0:Rx",
+ .tx_name = "ttySM0:Tx",
+#if defined(CONFIG_MN10300_TTYSM0_TIMER8)
+ .tm_name = "ttySM0:Timer8",
+ ._tmxmd = &TM8MD,
+ ._tmxbr = &TM8BR,
+ ._tmicr = &TM8ICR,
+ .tm_irq = TM8IRQ,
+ .div_timer = MNSCx_DIV_TIMER_16BIT,
+#elif defined(CONFIG_MN10300_TTYSM0_TIMER0)
+ .tm_name = "ttySM0:Timer0",
+ ._tmxmd = &TM0MD,
+ ._tmxbr = (volatile u16 *)&TM0BR,
+ ._tmicr = &TM0ICR,
+ .tm_irq = TM0IRQ,
+ .div_timer = MNSCx_DIV_TIMER_8BIT,
+#elif defined(CONFIG_MN10300_TTYSM0_TIMER2)
+ .tm_name = "ttySM0:Timer2",
+ ._tmxmd = &TM2MD,
+ ._tmxbr = (volatile u16 *)&TM2BR,
+ ._tmicr = &TM2ICR,
+ .tm_irq = TM2IRQ,
+ .div_timer = MNSCx_DIV_TIMER_8BIT,
+#else
+#error "Unknown config for ttySM0"
+#endif
+ .rx_irq = SC0RXIRQ,
+ .tx_irq = SC0TXIRQ,
+ .rx_icr = &GxICR(SC0RXIRQ),
+ .tx_icr = &GxICR(SC0TXIRQ),
+ .clock_src = MNSCx_CLOCK_SRC_IOCLK,
+ .options = 0,
+#ifdef CONFIG_GDBSTUB_ON_TTYSM0
+ .gdbstub = 1,
+#endif
+};
+#endif /* CONFIG_MN10300_TTYSM0 */
+
+/*
+ * the second on-chip serial port: ttySM1 (aka SIF1)
+ */
+#ifdef CONFIG_MN10300_TTYSM1
+struct mn10300_serial_port mn10300_serial_port_sif1 = {
+ .uart.ops = &mn10300_serial_ops,
+ .uart.membase = (void __iomem *) &SC1CTR,
+ .uart.mapbase = (unsigned long) &SC1CTR,
+ .uart.iotype = UPIO_MEM,
+ .uart.irq = 0,
+ .uart.uartclk = 0, /* MN10300_IOCLK, */
+ .uart.fifosize = 1,
+ .uart.flags = UPF_BOOT_AUTOCONF,
+ .uart.line = 1,
+ .uart.type = PORT_MN10300,
+ .uart.lock =
+ __SPIN_LOCK_UNLOCKED(mn10300_serial_port_sif1.uart.lock),
+ .name = "ttySM1",
+ ._iobase = &SC1CTR,
+ ._control = &SC1CTR,
+ ._status = (volatile u8 *)&SC1STR,
+ ._intr = &SC1ICR,
+ ._rxb = &SC1RXB,
+ ._txb = &SC1TXB,
+ .rx_name = "ttySM1:Rx",
+ .tx_name = "ttySM1:Tx",
+#if defined(CONFIG_MN10300_TTYSM1_TIMER9)
+ .tm_name = "ttySM1:Timer9",
+ ._tmxmd = &TM9MD,
+ ._tmxbr = &TM9BR,
+ ._tmicr = &TM9ICR,
+ .tm_irq = TM9IRQ,
+ .div_timer = MNSCx_DIV_TIMER_16BIT,
+#elif defined(CONFIG_MN10300_TTYSM1_TIMER3)
+ .tm_name = "ttySM1:Timer3",
+ ._tmxmd = &TM3MD,
+ ._tmxbr = (volatile u16 *)&TM3BR,
+ ._tmicr = &TM3ICR,
+ .tm_irq = TM3IRQ,
+ .div_timer = MNSCx_DIV_TIMER_8BIT,
+#elif defined(CONFIG_MN10300_TTYSM1_TIMER12)
+ .tm_name = "ttySM1/Timer12",
+ ._tmxmd = &TM12MD,
+ ._tmxbr = &TM12BR,
+ ._tmicr = &TM12ICR,
+ .tm_irq = TM12IRQ,
+ .div_timer = MNSCx_DIV_TIMER_16BIT,
+#else
+#error "Unknown config for ttySM1"
+#endif
+ .rx_irq = SC1RXIRQ,
+ .tx_irq = SC1TXIRQ,
+ .rx_icr = &GxICR(SC1RXIRQ),
+ .tx_icr = &GxICR(SC1TXIRQ),
+ .clock_src = MNSCx_CLOCK_SRC_IOCLK,
+ .options = 0,
+#ifdef CONFIG_GDBSTUB_ON_TTYSM1
+ .gdbstub = 1,
+#endif
+};
+#endif /* CONFIG_MN10300_TTYSM1 */
+
+/*
+ * the third on-chip serial port: ttySM2 (aka SIF2)
+ */
+#ifdef CONFIG_MN10300_TTYSM2
+struct mn10300_serial_port mn10300_serial_port_sif2 = {
+ .uart.ops = &mn10300_serial_ops,
+ .uart.membase = (void __iomem *) &SC2CTR,
+ .uart.mapbase = (unsigned long) &SC2CTR,
+ .uart.iotype = UPIO_MEM,
+ .uart.irq = 0,
+ .uart.uartclk = 0, /* MN10300_IOCLK, */
+ .uart.fifosize = 1,
+ .uart.flags = UPF_BOOT_AUTOCONF,
+ .uart.line = 2,
+#ifdef CONFIG_MN10300_TTYSM2_CTS
+ .uart.type = PORT_MN10300_CTS,
+#else
+ .uart.type = PORT_MN10300,
+#endif
+ .uart.lock =
+ __SPIN_LOCK_UNLOCKED(mn10300_serial_port_sif2.uart.lock),
+ .name = "ttySM2",
+ ._iobase = &SC2CTR,
+ ._control = &SC2CTR,
+ ._status = (volatile u8 *)&SC2STR,
+ ._intr = &SC2ICR,
+ ._rxb = &SC2RXB,
+ ._txb = &SC2TXB,
+ .rx_name = "ttySM2:Rx",
+ .tx_name = "ttySM2:Tx",
+#if defined(CONFIG_MN10300_TTYSM2_TIMER10)
+ .tm_name = "ttySM2/Timer10",
+ ._tmxmd = &TM10MD,
+ ._tmxbr = &TM10BR,
+ ._tmicr = &TM10ICR,
+ .tm_irq = TM10IRQ,
+ .div_timer = MNSCx_DIV_TIMER_16BIT,
+#elif defined(CONFIG_MN10300_TTYSM2_TIMER9)
+ .tm_name = "ttySM2/Timer9",
+ ._tmxmd = &TM9MD,
+ ._tmxbr = &TM9BR,
+ ._tmicr = &TM9ICR,
+ .tm_irq = TM9IRQ,
+ .div_timer = MNSCx_DIV_TIMER_16BIT,
+#elif defined(CONFIG_MN10300_TTYSM2_TIMER1)
+ .tm_name = "ttySM2/Timer1",
+ ._tmxmd = &TM1MD,
+ ._tmxbr = (volatile u16 *)&TM1BR,
+ ._tmicr = &TM1ICR,
+ .tm_irq = TM1IRQ,
+ .div_timer = MNSCx_DIV_TIMER_8BIT,
+#elif defined(CONFIG_MN10300_TTYSM2_TIMER3)
+ .tm_name = "ttySM2/Timer3",
+ ._tmxmd = &TM3MD,
+ ._tmxbr = (volatile u16 *)&TM3BR,
+ ._tmicr = &TM3ICR,
+ .tm_irq = TM3IRQ,
+ .div_timer = MNSCx_DIV_TIMER_8BIT,
+#else
+#error "Unknown config for ttySM2"
+#endif
+ .rx_irq = SC2RXIRQ,
+ .tx_irq = SC2TXIRQ,
+ .rx_icr = &GxICR(SC2RXIRQ),
+ .tx_icr = &GxICR(SC2TXIRQ),
+ .clock_src = MNSCx_CLOCK_SRC_IOCLK,
+#ifdef CONFIG_MN10300_TTYSM2_CTS
+ .options = MNSCx_OPT_CTS,
+#else
+ .options = 0,
+#endif
+#ifdef CONFIG_GDBSTUB_ON_TTYSM2
+ .gdbstub = 1,
+#endif
+};
+#endif /* CONFIG_MN10300_TTYSM2 */
+
+
+/*
+ * list of available serial ports
+ */
+struct mn10300_serial_port *mn10300_serial_ports[NR_UARTS + 1] = {
+#ifdef CONFIG_MN10300_TTYSM0
+ [0] = &mn10300_serial_port_sif0,
+#endif
+#ifdef CONFIG_MN10300_TTYSM1
+ [1] = &mn10300_serial_port_sif1,
+#endif
+#ifdef CONFIG_MN10300_TTYSM2
+ [2] = &mn10300_serial_port_sif2,
+#endif
+ [NR_UARTS] = NULL,
+};
+
+
+/*
+ * we abuse the serial ports' baud timers' interrupt lines to get the ability
+ * to deliver interrupts to userspace as we use the ports' interrupt lines to
+ * do virtual DMA on account of the ports having no hardware FIFOs
+ *
+ * we can generate an interrupt manually in the assembly stubs by writing to
+ * the enable and detect bits in the interrupt control register, so all we need
+ * to do here is disable the interrupt line
+ *
+ * note that we can't just leave the line enabled as the baud rate timer *also*
+ * generates interrupts
+ */
+static void mn10300_serial_mask_ack(unsigned int irq)
+{
+ unsigned long flags;
+ u16 tmp;
+
+ flags = arch_local_cli_save();
+ GxICR(irq) = GxICR_LEVEL_6;
+ tmp = GxICR(irq); /* flush write buffer */
+ arch_local_irq_restore(flags);
+}
+
+static void mn10300_serial_chip_mask_ack(struct irq_data *d)
+{
+ mn10300_serial_mask_ack(d->irq);
+}
+
+static void mn10300_serial_nop(struct irq_data *d)
+{
+}
+
+static struct irq_chip mn10300_serial_pic = {
+ .name = "mnserial",
+ .irq_ack = mn10300_serial_chip_mask_ack,
+ .irq_mask = mn10300_serial_chip_mask_ack,
+ .irq_mask_ack = mn10300_serial_chip_mask_ack,
+ .irq_unmask = mn10300_serial_nop,
+};
+
+static void mn10300_serial_low_mask(struct irq_data *d)
+{
+ unsigned long flags;
+ u16 tmp;
+
+ flags = arch_local_cli_save();
+ GxICR(d->irq) = NUM2GxICR_LEVEL(CONFIG_MN10300_SERIAL_IRQ_LEVEL);
+ tmp = GxICR(d->irq); /* flush write buffer */
+ arch_local_irq_restore(flags);
+}
+
+static void mn10300_serial_low_unmask(struct irq_data *d)
+{
+ unsigned long flags;
+ u16 tmp;
+
+ flags = arch_local_cli_save();
+ GxICR(d->irq) =
+ NUM2GxICR_LEVEL(CONFIG_MN10300_SERIAL_IRQ_LEVEL) | GxICR_ENABLE;
+ tmp = GxICR(d->irq); /* flush write buffer */
+ arch_local_irq_restore(flags);
+}
+
+static struct irq_chip mn10300_serial_low_pic = {
+ .name = "mnserial-low",
+ .irq_mask = mn10300_serial_low_mask,
+ .irq_unmask = mn10300_serial_low_unmask,
+};
+
+/*
+ * serial virtual DMA interrupt jump table
+ */
+struct mn10300_serial_int mn10300_serial_int_tbl[NR_IRQS];
+
+static void mn10300_serial_dis_tx_intr(struct mn10300_serial_port *port)
+{
+ int retries = 100;
+ u16 x;
+
+ /* nothing to do if irq isn't set up */
+ if (!mn10300_serial_int_tbl[port->tx_irq].port)
+ return;
+
+ port->tx_flags |= MNSCx_TX_STOP;
+ mb();
+
+ /*
+ * Here we wait for the irq to be disabled. Either it already is
+ * disabled or we wait some number of retries for the VDMA handler
+ * to disable it. The retries give the VDMA handler enough time to
+ * run to completion if it was already in progress. If the VDMA IRQ
+ * is enabled but the handler is not yet running when arrive here,
+ * the STOP flag will prevent the handler from conflicting with the
+ * driver code following this loop.
+ */
+ while ((*port->tx_icr & GxICR_ENABLE) && retries-- > 0)
+ ;
+ if (retries <= 0) {
+ *port->tx_icr =
+ NUM2GxICR_LEVEL(CONFIG_MN10300_SERIAL_IRQ_LEVEL);
+ x = *port->tx_icr;
+ }
+}
+
+static void mn10300_serial_en_tx_intr(struct mn10300_serial_port *port)
+{
+ u16 x;
+
+ /* nothing to do if irq isn't set up */
+ if (!mn10300_serial_int_tbl[port->tx_irq].port)
+ return;
+
+ /* stop vdma irq if not already stopped */
+ if (!(port->tx_flags & MNSCx_TX_STOP))
+ mn10300_serial_dis_tx_intr(port);
+
+ port->tx_flags &= ~MNSCx_TX_STOP;
+ mb();
+
+ *port->tx_icr =
+ NUM2GxICR_LEVEL(CONFIG_MN10300_SERIAL_IRQ_LEVEL) |
+ GxICR_ENABLE | GxICR_REQUEST | GxICR_DETECT;
+ x = *port->tx_icr;
+}
+
+static void mn10300_serial_dis_rx_intr(struct mn10300_serial_port *port)
+{
+ unsigned long flags;
+ u16 x;
+
+ flags = arch_local_cli_save();
+ *port->rx_icr = NUM2GxICR_LEVEL(CONFIG_MN10300_SERIAL_IRQ_LEVEL);
+ x = *port->rx_icr;
+ arch_local_irq_restore(flags);
+}
+
+/*
+ * multi-bit equivalent of test_and_clear_bit()
+ */
+static int mask_test_and_clear(volatile u8 *ptr, u8 mask)
+{
+ u32 epsw;
+ asm volatile(" bclr %1,(%2) \n"
+ " mov epsw,%0 \n"
+ : "=d"(epsw) : "d"(mask), "a"(ptr)
+ : "cc", "memory");
+ return !(epsw & EPSW_FLAG_Z);
+}
+
+/*
+ * receive chars from the ring buffer for this serial port
+ * - must do break detection here (not done in the UART)
+ */
+static void mn10300_serial_receive_interrupt(struct mn10300_serial_port *port)
+{
+ struct uart_icount *icount = &port->uart.icount;
+ struct tty_port *tport = &port->uart.state->port;
+ unsigned ix;
+ int count;
+ u8 st, ch, push, status, overrun;
+
+ _enter("%s", port->name);
+
+ push = 0;
+
+ count = CIRC_CNT(port->rx_inp, port->rx_outp, MNSC_BUFFER_SIZE);
+ count = tty_buffer_request_room(tport, count);
+ if (count == 0) {
+ if (!tport->low_latency)
+ tty_flip_buffer_push(tport);
+ return;
+ }
+
+try_again:
+ /* pull chars out of the hat */
+ ix = ACCESS_ONCE(port->rx_outp);
+ if (CIRC_CNT(port->rx_inp, ix, MNSC_BUFFER_SIZE) == 0) {
+ if (push && !tport->low_latency)
+ tty_flip_buffer_push(tport);
+ return;
+ }
+
+ smp_read_barrier_depends();
+ ch = port->rx_buffer[ix++];
+ st = port->rx_buffer[ix++];
+ smp_mb();
+ port->rx_outp = ix & (MNSC_BUFFER_SIZE - 1);
+ port->uart.icount.rx++;
+
+ st &= SC01STR_FEF | SC01STR_PEF | SC01STR_OEF;
+ status = 0;
+ overrun = 0;
+
+ /* the UART doesn't detect BREAK, so we have to do that ourselves
+ * - it starts as a framing error on a NUL character
+ * - then we count another two NUL characters before issuing TTY_BREAK
+ * - then we end on a normal char or one that has all the bottom bits
+ * zero and the top bits set
+ */
+ switch (port->rx_brk) {
+ case 0:
+ /* not breaking at the moment */
+ break;
+
+ case 1:
+ if (st & SC01STR_FEF && ch == 0) {
+ port->rx_brk = 2;
+ goto try_again;
+ }
+ goto not_break;
+
+ case 2:
+ if (st & SC01STR_FEF && ch == 0) {
+ port->rx_brk = 3;
+ _proto("Rx Break Detected");
+ icount->brk++;
+ if (uart_handle_break(&port->uart))
+ goto ignore_char;
+ status |= 1 << TTY_BREAK;
+ goto insert;
+ }
+ goto not_break;
+
+ default:
+ if (st & (SC01STR_FEF | SC01STR_PEF | SC01STR_OEF))
+ goto try_again; /* still breaking */
+
+ port->rx_brk = 0; /* end of the break */
+
+ switch (ch) {
+ case 0xFF:
+ case 0xFE:
+ case 0xFC:
+ case 0xF8:
+ case 0xF0:
+ case 0xE0:
+ case 0xC0:
+ case 0x80:
+ case 0x00:
+ /* discard char at probable break end */
+ goto try_again;
+ }
+ break;
+ }
+
+process_errors:
+ /* handle framing error */
+ if (st & SC01STR_FEF) {
+ if (ch == 0) {
+ /* framing error with NUL char is probably a BREAK */
+ port->rx_brk = 1;
+ goto try_again;
+ }
+
+ _proto("Rx Framing Error");
+ icount->frame++;
+ status |= 1 << TTY_FRAME;
+ }
+
+ /* handle parity error */
+ if (st & SC01STR_PEF) {
+ _proto("Rx Parity Error");
+ icount->parity++;
+ status = TTY_PARITY;
+ }
+
+ /* handle normal char */
+ if (status == 0) {
+ if (uart_handle_sysrq_char(&port->uart, ch))
+ goto ignore_char;
+ status = (1 << TTY_NORMAL);
+ }
+
+ /* handle overrun error */
+ if (st & SC01STR_OEF) {
+ if (port->rx_brk)
+ goto try_again;
+
+ _proto("Rx Overrun Error");
+ icount->overrun++;
+ overrun = 1;
+ }
+
+insert:
+ status &= port->uart.read_status_mask;
+
+ if (!overrun && !(status & port->uart.ignore_status_mask)) {
+ int flag;
+
+ if (status & (1 << TTY_BREAK))
+ flag = TTY_BREAK;
+ else if (status & (1 << TTY_PARITY))
+ flag = TTY_PARITY;
+ else if (status & (1 << TTY_FRAME))
+ flag = TTY_FRAME;
+ else
+ flag = TTY_NORMAL;
+
+ tty_insert_flip_char(tport, ch, flag);
+ }
+
+ /* overrun is special, since it's reported immediately, and doesn't
+ * affect the current character
+ */
+ if (overrun)
+ tty_insert_flip_char(tport, 0, TTY_OVERRUN);
+
+ count--;
+ if (count <= 0) {
+ if (!tport->low_latency)
+ tty_flip_buffer_push(tport);
+ return;
+ }
+
+ignore_char:
+ push = 1;
+ goto try_again;
+
+not_break:
+ port->rx_brk = 0;
+ goto process_errors;
+}
+
+/*
+ * handle an interrupt from the serial transmission "virtual DMA" driver
+ * - note: the interrupt routine will disable its own interrupts when the Tx
+ * buffer is empty
+ */
+static void mn10300_serial_transmit_interrupt(struct mn10300_serial_port *port)
+{
+ _enter("%s", port->name);
+
+ if (!port->uart.state || !port->uart.state->port.tty) {
+ mn10300_serial_dis_tx_intr(port);
+ return;
+ }
+
+ if (uart_tx_stopped(&port->uart) ||
+ uart_circ_empty(&port->uart.state->xmit))
+ mn10300_serial_dis_tx_intr(port);
+
+ if (uart_circ_chars_pending(&port->uart.state->xmit) < WAKEUP_CHARS)
+ uart_write_wakeup(&port->uart);
+}
+
+/*
+ * deal with a change in the status of the CTS line
+ */
+static void mn10300_serial_cts_changed(struct mn10300_serial_port *port, u8 st)
+{
+ u16 ctr;
+
+ port->tx_cts = st;
+ port->uart.icount.cts++;
+
+ /* flip the CTS state selector flag to interrupt when it changes
+ * back */
+ ctr = *port->_control;
+ ctr ^= SC2CTR_TWS;
+ *port->_control = ctr;
+
+ uart_handle_cts_change(&port->uart, st & SC2STR_CTS);
+ wake_up_interruptible(&port->uart.state->port.delta_msr_wait);
+}
+
+/*
+ * handle a virtual interrupt generated by the lower level "virtual DMA"
+ * routines (irq is the baud timer interrupt)
+ */
+static irqreturn_t mn10300_serial_interrupt(int irq, void *dev_id)
+{
+ struct mn10300_serial_port *port = dev_id;
+ u8 st;
+
+ spin_lock(&port->uart.lock);
+
+ if (port->intr_flags) {
+ _debug("INT %s: %x", port->name, port->intr_flags);
+
+ if (mask_test_and_clear(&port->intr_flags, MNSCx_RX_AVAIL))
+ mn10300_serial_receive_interrupt(port);
+
+ if (mask_test_and_clear(&port->intr_flags,
+ MNSCx_TX_SPACE | MNSCx_TX_EMPTY))
+ mn10300_serial_transmit_interrupt(port);
+ }
+
+ /* the only modem control line amongst the whole lot is CTS on
+ * serial port 2 */
+ if (port->type == PORT_MN10300_CTS) {
+ st = *port->_status;
+ if ((port->tx_cts ^ st) & SC2STR_CTS)
+ mn10300_serial_cts_changed(port, st);
+ }
+
+ spin_unlock(&port->uart.lock);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * return indication of whether the hardware transmit buffer is empty
+ */
+static unsigned int mn10300_serial_tx_empty(struct uart_port *_port)
+{
+ struct mn10300_serial_port *port =
+ container_of(_port, struct mn10300_serial_port, uart);
+
+ _enter("%s", port->name);
+
+ return (*port->_status & (SC01STR_TXF | SC01STR_TBF)) ?
+ 0 : TIOCSER_TEMT;
+}
+
+/*
+ * set the modem control lines (we don't have any)
+ */
+static void mn10300_serial_set_mctrl(struct uart_port *_port,
+ unsigned int mctrl)
+{
+ struct mn10300_serial_port *port __attribute__ ((unused)) =
+ container_of(_port, struct mn10300_serial_port, uart);
+
+ _enter("%s,%x", port->name, mctrl);
+}
+
+/*
+ * get the modem control line statuses
+ */
+static unsigned int mn10300_serial_get_mctrl(struct uart_port *_port)
+{
+ struct mn10300_serial_port *port =
+ container_of(_port, struct mn10300_serial_port, uart);
+
+ _enter("%s", port->name);
+
+ if (port->type == PORT_MN10300_CTS && !(*port->_status & SC2STR_CTS))
+ return TIOCM_CAR | TIOCM_DSR;
+
+ return TIOCM_CAR | TIOCM_CTS | TIOCM_DSR;
+}
+
+/*
+ * stop transmitting characters
+ */
+static void mn10300_serial_stop_tx(struct uart_port *_port)
+{
+ struct mn10300_serial_port *port =
+ container_of(_port, struct mn10300_serial_port, uart);
+
+ _enter("%s", port->name);
+
+ /* disable the virtual DMA */
+ mn10300_serial_dis_tx_intr(port);
+}
+
+/*
+ * start transmitting characters
+ * - jump-start transmission if it has stalled
+ * - enable the serial Tx interrupt (used by the virtual DMA controller)
+ * - force an interrupt to happen if necessary
+ */
+static void mn10300_serial_start_tx(struct uart_port *_port)
+{
+ struct mn10300_serial_port *port =
+ container_of(_port, struct mn10300_serial_port, uart);
+
+ _enter("%s{%lu}",
+ port->name,
+ CIRC_CNT(&port->uart.state->xmit.head,
+ &port->uart.state->xmit.tail,
+ UART_XMIT_SIZE));
+
+ /* kick the virtual DMA controller */
+ mn10300_serial_en_tx_intr(port);
+
+ _debug("CTR=%04hx ICR=%02hx STR=%04x TMD=%02hx TBR=%04hx ICR=%04hx",
+ *port->_control, *port->_intr, *port->_status,
+ *port->_tmxmd,
+ (port->div_timer == MNSCx_DIV_TIMER_8BIT) ?
+ *(volatile u8 *)port->_tmxbr : *port->_tmxbr,
+ *port->tx_icr);
+}
+
+/*
+ * transmit a high-priority XON/XOFF character
+ */
+static void mn10300_serial_send_xchar(struct uart_port *_port, char ch)
+{
+ struct mn10300_serial_port *port =
+ container_of(_port, struct mn10300_serial_port, uart);
+ unsigned long flags;
+
+ _enter("%s,%02x", port->name, ch);
+
+ if (likely(port->gdbstub)) {
+ port->tx_xchar = ch;
+ if (ch) {
+ spin_lock_irqsave(&port->uart.lock, flags);
+ mn10300_serial_en_tx_intr(port);
+ spin_unlock_irqrestore(&port->uart.lock, flags);
+ }
+ }
+}
+
+/*
+ * stop receiving characters
+ * - called whilst the port is being closed
+ */
+static void mn10300_serial_stop_rx(struct uart_port *_port)
+{
+ struct mn10300_serial_port *port =
+ container_of(_port, struct mn10300_serial_port, uart);
+
+ u16 ctr;
+
+ _enter("%s", port->name);
+
+ ctr = *port->_control;
+ ctr &= ~SC01CTR_RXE;
+ *port->_control = ctr;
+
+ mn10300_serial_dis_rx_intr(port);
+}
+
+/*
+ * enable modem status interrupts
+ */
+static void mn10300_serial_enable_ms(struct uart_port *_port)
+{
+ struct mn10300_serial_port *port =
+ container_of(_port, struct mn10300_serial_port, uart);
+
+ u16 ctr, cts;
+
+ _enter("%s", port->name);
+
+ if (port->type == PORT_MN10300_CTS) {
+ /* want to interrupt when CTS goes low if CTS is now high and
+ * vice versa
+ */
+ port->tx_cts = *port->_status;
+
+ cts = (port->tx_cts & SC2STR_CTS) ?
+ SC2CTR_TWE : SC2CTR_TWE | SC2CTR_TWS;
+
+ ctr = *port->_control;
+ ctr &= ~SC2CTR_TWS;
+ ctr |= cts;
+ *port->_control = ctr;
+
+ mn10300_serial_en_tx_intr(port);
+ }
+}
+
+/*
+ * transmit or cease transmitting a break signal
+ */
+static void mn10300_serial_break_ctl(struct uart_port *_port, int ctl)
+{
+ struct mn10300_serial_port *port =
+ container_of(_port, struct mn10300_serial_port, uart);
+ unsigned long flags;
+
+ _enter("%s,%d", port->name, ctl);
+
+ spin_lock_irqsave(&port->uart.lock, flags);
+ if (ctl) {
+ /* tell the virtual DMA handler to assert BREAK */
+ port->tx_flags |= MNSCx_TX_BREAK;
+ mn10300_serial_en_tx_intr(port);
+ } else {
+ port->tx_flags &= ~MNSCx_TX_BREAK;
+ *port->_control &= ~SC01CTR_BKE;
+ mn10300_serial_en_tx_intr(port);
+ }
+ spin_unlock_irqrestore(&port->uart.lock, flags);
+}
+
+/*
+ * grab the interrupts and enable the port for reception
+ */
+static int mn10300_serial_startup(struct uart_port *_port)
+{
+ struct mn10300_serial_port *port =
+ container_of(_port, struct mn10300_serial_port, uart);
+ struct mn10300_serial_int *pint;
+
+ _enter("%s{%d}", port->name, port->gdbstub);
+
+ if (unlikely(port->gdbstub))
+ return -EBUSY;
+
+ /* allocate an Rx buffer for the virtual DMA handler */
+ port->rx_buffer = kmalloc(MNSC_BUFFER_SIZE, GFP_KERNEL);
+ if (!port->rx_buffer)
+ return -ENOMEM;
+
+ port->rx_inp = port->rx_outp = 0;
+ port->tx_flags = 0;
+
+ /* finally, enable the device */
+ *port->_intr = SC01ICR_TI;
+ *port->_control |= SC01CTR_TXE | SC01CTR_RXE;
+
+ pint = &mn10300_serial_int_tbl[port->rx_irq];
+ pint->port = port;
+ pint->vdma = mn10300_serial_vdma_rx_handler;
+ pint = &mn10300_serial_int_tbl[port->tx_irq];
+ pint->port = port;
+ pint->vdma = mn10300_serial_vdma_tx_handler;
+
+ irq_set_chip(port->rx_irq, &mn10300_serial_low_pic);
+ irq_set_chip(port->tx_irq, &mn10300_serial_low_pic);
+ irq_set_chip(port->tm_irq, &mn10300_serial_pic);
+
+ if (request_irq(port->rx_irq, mn10300_serial_interrupt,
+ IRQF_NOBALANCING,
+ port->rx_name, port) < 0)
+ goto error;
+
+ if (request_irq(port->tx_irq, mn10300_serial_interrupt,
+ IRQF_NOBALANCING,
+ port->tx_name, port) < 0)
+ goto error2;
+
+ if (request_irq(port->tm_irq, mn10300_serial_interrupt,
+ IRQF_NOBALANCING,
+ port->tm_name, port) < 0)
+ goto error3;
+ mn10300_serial_mask_ack(port->tm_irq);
+
+ return 0;
+
+error3:
+ free_irq(port->tx_irq, port);
+error2:
+ free_irq(port->rx_irq, port);
+error:
+ kfree(port->rx_buffer);
+ port->rx_buffer = NULL;
+ return -EBUSY;
+}
+
+/*
+ * shutdown the port and release interrupts
+ */
+static void mn10300_serial_shutdown(struct uart_port *_port)
+{
+ unsigned long flags;
+ u16 x;
+ struct mn10300_serial_port *port =
+ container_of(_port, struct mn10300_serial_port, uart);
+
+ _enter("%s", port->name);
+
+ spin_lock_irqsave(&_port->lock, flags);
+ mn10300_serial_dis_tx_intr(port);
+
+ *port->rx_icr = NUM2GxICR_LEVEL(CONFIG_MN10300_SERIAL_IRQ_LEVEL);
+ x = *port->rx_icr;
+ port->tx_flags = 0;
+ spin_unlock_irqrestore(&_port->lock, flags);
+
+ /* disable the serial port and its baud rate timer */
+ *port->_control &= ~(SC01CTR_TXE | SC01CTR_RXE | SC01CTR_BKE);
+ *port->_tmxmd = 0;
+
+ if (port->rx_buffer) {
+ void *buf = port->rx_buffer;
+ port->rx_buffer = NULL;
+ kfree(buf);
+ }
+
+ /* disable all intrs */
+ free_irq(port->tm_irq, port);
+ free_irq(port->rx_irq, port);
+ free_irq(port->tx_irq, port);
+
+ mn10300_serial_int_tbl[port->tx_irq].port = NULL;
+ mn10300_serial_int_tbl[port->rx_irq].port = NULL;
+}
+
+/*
+ * this routine is called to set the UART divisor registers to match the
+ * specified baud rate for a serial port.
+ */
+static void mn10300_serial_change_speed(struct mn10300_serial_port *port,
+ struct ktermios *new,
+ struct ktermios *old)
+{
+ unsigned long flags;
+ unsigned long ioclk = port->ioclk;
+ unsigned cflag;
+ int baud, bits, xdiv, tmp;
+ u16 tmxbr, scxctr;
+ u8 tmxmd, battempt;
+ u8 div_timer = port->div_timer;
+
+ _enter("%s{%lu}", port->name, ioclk);
+
+ /* byte size and parity */
+ cflag = new->c_cflag;
+ switch (cflag & CSIZE) {
+ case CS7: scxctr = SC01CTR_CLN_7BIT; bits = 9; break;
+ case CS8: scxctr = SC01CTR_CLN_8BIT; bits = 10; break;
+ default: scxctr = SC01CTR_CLN_8BIT; bits = 10; break;
+ }
+
+ if (cflag & CSTOPB) {
+ scxctr |= SC01CTR_STB_2BIT;
+ bits++;
+ }
+
+ if (cflag & PARENB) {
+ bits++;
+ if (cflag & PARODD)
+ scxctr |= SC01CTR_PB_ODD;
+#ifdef CMSPAR
+ else if (cflag & CMSPAR)
+ scxctr |= SC01CTR_PB_FIXED0;
+#endif
+ else
+ scxctr |= SC01CTR_PB_EVEN;
+ }
+
+ /* Determine divisor based on baud rate */
+ battempt = 0;
+
+ switch (port->uart.line) {
+#ifdef CONFIG_MN10300_TTYSM0
+ case 0: /* ttySM0 */
+#if defined(CONFIG_MN10300_TTYSM0_TIMER8)
+ scxctr |= SC0CTR_CK_TM8UFLOW_8;
+#elif defined(CONFIG_MN10300_TTYSM0_TIMER0)
+ scxctr |= SC0CTR_CK_TM0UFLOW_8;
+#elif defined(CONFIG_MN10300_TTYSM0_TIMER2)
+ scxctr |= SC0CTR_CK_TM2UFLOW_8;
+#else
+#error "Unknown config for ttySM0"
+#endif
+ break;
+#endif /* CONFIG_MN10300_TTYSM0 */
+
+#ifdef CONFIG_MN10300_TTYSM1
+ case 1: /* ttySM1 */
+#if defined(CONFIG_AM33_2) || defined(CONFIG_AM33_3)
+#if defined(CONFIG_MN10300_TTYSM1_TIMER9)
+ scxctr |= SC1CTR_CK_TM9UFLOW_8;
+#elif defined(CONFIG_MN10300_TTYSM1_TIMER3)
+ scxctr |= SC1CTR_CK_TM3UFLOW_8;
+#else
+#error "Unknown config for ttySM1"
+#endif
+#else /* CONFIG_AM33_2 || CONFIG_AM33_3 */
+#if defined(CONFIG_MN10300_TTYSM1_TIMER12)
+ scxctr |= SC1CTR_CK_TM12UFLOW_8;
+#else
+#error "Unknown config for ttySM1"
+#endif
+#endif /* CONFIG_AM33_2 || CONFIG_AM33_3 */
+ break;
+#endif /* CONFIG_MN10300_TTYSM1 */
+
+#ifdef CONFIG_MN10300_TTYSM2
+ case 2: /* ttySM2 */
+#if defined(CONFIG_AM33_2)
+#if defined(CONFIG_MN10300_TTYSM2_TIMER10)
+ scxctr |= SC2CTR_CK_TM10UFLOW;
+#else
+#error "Unknown config for ttySM2"
+#endif
+#else /* CONFIG_AM33_2 */
+#if defined(CONFIG_MN10300_TTYSM2_TIMER9)
+ scxctr |= SC2CTR_CK_TM9UFLOW_8;
+#elif defined(CONFIG_MN10300_TTYSM2_TIMER1)
+ scxctr |= SC2CTR_CK_TM1UFLOW_8;
+#elif defined(CONFIG_MN10300_TTYSM2_TIMER3)
+ scxctr |= SC2CTR_CK_TM3UFLOW_8;
+#else
+#error "Unknown config for ttySM2"
+#endif
+#endif /* CONFIG_AM33_2 */
+ break;
+#endif /* CONFIG_MN10300_TTYSM2 */
+
+ default:
+ break;
+ }
+
+try_alternative:
+ baud = uart_get_baud_rate(&port->uart, new, old, 0,
+ port->ioclk / 8);
+
+ _debug("ALT %d [baud %d]", battempt, baud);
+
+ if (!baud)
+ baud = 9600; /* B0 transition handled in rs_set_termios */
+ xdiv = 1;
+ if (baud == 134) {
+ baud = 269; /* 134 is really 134.5 */
+ xdiv = 2;
+ }
+
+ if (baud == 38400 &&
+ (port->uart.flags & UPF_SPD_MASK) == UPF_SPD_CUST
+ ) {
+ _debug("CUSTOM %u", port->uart.custom_divisor);
+
+ if (div_timer == MNSCx_DIV_TIMER_16BIT) {
+ if (port->uart.custom_divisor <= 65535) {
+ tmxmd = TM8MD_SRC_IOCLK;
+ tmxbr = port->uart.custom_divisor;
+ port->uart.uartclk = ioclk;
+ goto timer_okay;
+ }
+ if (port->uart.custom_divisor / 8 <= 65535) {
+ tmxmd = TM8MD_SRC_IOCLK_8;
+ tmxbr = port->uart.custom_divisor / 8;
+ port->uart.custom_divisor = tmxbr * 8;
+ port->uart.uartclk = ioclk / 8;
+ goto timer_okay;
+ }
+ if (port->uart.custom_divisor / 32 <= 65535) {
+ tmxmd = TM8MD_SRC_IOCLK_32;
+ tmxbr = port->uart.custom_divisor / 32;
+ port->uart.custom_divisor = tmxbr * 32;
+ port->uart.uartclk = ioclk / 32;
+ goto timer_okay;
+ }
+
+ } else if (div_timer == MNSCx_DIV_TIMER_8BIT) {
+ if (port->uart.custom_divisor <= 255) {
+ tmxmd = TM2MD_SRC_IOCLK;
+ tmxbr = port->uart.custom_divisor;
+ port->uart.uartclk = ioclk;
+ goto timer_okay;
+ }
+ if (port->uart.custom_divisor / 8 <= 255) {
+ tmxmd = TM2MD_SRC_IOCLK_8;
+ tmxbr = port->uart.custom_divisor / 8;
+ port->uart.custom_divisor = tmxbr * 8;
+ port->uart.uartclk = ioclk / 8;
+ goto timer_okay;
+ }
+ if (port->uart.custom_divisor / 32 <= 255) {
+ tmxmd = TM2MD_SRC_IOCLK_32;
+ tmxbr = port->uart.custom_divisor / 32;
+ port->uart.custom_divisor = tmxbr * 32;
+ port->uart.uartclk = ioclk / 32;
+ goto timer_okay;
+ }
+ }
+ }
+
+ switch (div_timer) {
+ case MNSCx_DIV_TIMER_16BIT:
+ port->uart.uartclk = ioclk;
+ tmxmd = TM8MD_SRC_IOCLK;
+ tmxbr = tmp = (ioclk / (baud * xdiv) + 4) / 8 - 1;
+ if (tmp > 0 && tmp <= 65535)
+ goto timer_okay;
+
+ port->uart.uartclk = ioclk / 8;
+ tmxmd = TM8MD_SRC_IOCLK_8;
+ tmxbr = tmp = (ioclk / (baud * 8 * xdiv) + 4) / 8 - 1;
+ if (tmp > 0 && tmp <= 65535)
+ goto timer_okay;
+
+ port->uart.uartclk = ioclk / 32;
+ tmxmd = TM8MD_SRC_IOCLK_32;
+ tmxbr = tmp = (ioclk / (baud * 32 * xdiv) + 4) / 8 - 1;
+ if (tmp > 0 && tmp <= 65535)
+ goto timer_okay;
+ break;
+
+ case MNSCx_DIV_TIMER_8BIT:
+ port->uart.uartclk = ioclk;
+ tmxmd = TM2MD_SRC_IOCLK;
+ tmxbr = tmp = (ioclk / (baud * xdiv) + 4) / 8 - 1;
+ if (tmp > 0 && tmp <= 255)
+ goto timer_okay;
+
+ port->uart.uartclk = ioclk / 8;
+ tmxmd = TM2MD_SRC_IOCLK_8;
+ tmxbr = tmp = (ioclk / (baud * 8 * xdiv) + 4) / 8 - 1;
+ if (tmp > 0 && tmp <= 255)
+ goto timer_okay;
+
+ port->uart.uartclk = ioclk / 32;
+ tmxmd = TM2MD_SRC_IOCLK_32;
+ tmxbr = tmp = (ioclk / (baud * 32 * xdiv) + 4) / 8 - 1;
+ if (tmp > 0 && tmp <= 255)
+ goto timer_okay;
+ break;
+
+ default:
+ BUG();
+ return;
+ }
+
+ /* refuse to change to a baud rate we can't support */
+ _debug("CAN'T SUPPORT");
+
+ switch (battempt) {
+ case 0:
+ if (old) {
+ new->c_cflag &= ~CBAUD;
+ new->c_cflag |= (old->c_cflag & CBAUD);
+ battempt = 1;
+ goto try_alternative;
+ }
+
+ case 1:
+ /* as a last resort, if the quotient is zero, default to 9600
+ * bps */
+ new->c_cflag &= ~CBAUD;
+ new->c_cflag |= B9600;
+ battempt = 2;
+ goto try_alternative;
+
+ default:
+ /* hmmm... can't seem to support 9600 either
+ * - we could try iterating through the speeds we know about to
+ * find the lowest
+ */
+ new->c_cflag &= ~CBAUD;
+ new->c_cflag |= B0;
+
+ if (div_timer == MNSCx_DIV_TIMER_16BIT)
+ tmxmd = TM8MD_SRC_IOCLK_32;
+ else if (div_timer == MNSCx_DIV_TIMER_8BIT)
+ tmxmd = TM2MD_SRC_IOCLK_32;
+ tmxbr = 1;
+
+ port->uart.uartclk = ioclk / 32;
+ break;
+ }
+timer_okay:
+
+ _debug("UARTCLK: %u / %hu", port->uart.uartclk, tmxbr);
+
+ /* make the changes */
+ spin_lock_irqsave(&port->uart.lock, flags);
+
+ uart_update_timeout(&port->uart, new->c_cflag, baud);
+
+ /* set the timer to produce the required baud rate */
+ switch (div_timer) {
+ case MNSCx_DIV_TIMER_16BIT:
+ *port->_tmxmd = 0;
+ *port->_tmxbr = tmxbr;
+ *port->_tmxmd = TM8MD_INIT_COUNTER;
+ *port->_tmxmd = tmxmd | TM8MD_COUNT_ENABLE;
+ break;
+
+ case MNSCx_DIV_TIMER_8BIT:
+ *port->_tmxmd = 0;
+ *(volatile u8 *) port->_tmxbr = (u8) tmxbr;
+ *port->_tmxmd = TM2MD_INIT_COUNTER;
+ *port->_tmxmd = tmxmd | TM2MD_COUNT_ENABLE;
+ break;
+ }
+
+ /* CTS flow control flag and modem status interrupts */
+ scxctr &= ~(SC2CTR_TWE | SC2CTR_TWS);
+
+ if (port->type == PORT_MN10300_CTS && cflag & CRTSCTS) {
+ /* want to interrupt when CTS goes low if CTS is now
+ * high and vice versa
+ */
+ port->tx_cts = *port->_status;
+
+ if (port->tx_cts & SC2STR_CTS)
+ scxctr |= SC2CTR_TWE;
+ else
+ scxctr |= SC2CTR_TWE | SC2CTR_TWS;
+ }
+
+ /* set up parity check flag */
+ port->uart.read_status_mask = (1 << TTY_NORMAL) | (1 << TTY_OVERRUN);
+ if (new->c_iflag & INPCK)
+ port->uart.read_status_mask |=
+ (1 << TTY_PARITY) | (1 << TTY_FRAME);
+ if (new->c_iflag & (BRKINT | PARMRK))
+ port->uart.read_status_mask |= (1 << TTY_BREAK);
+
+ /* characters to ignore */
+ port->uart.ignore_status_mask = 0;
+ if (new->c_iflag & IGNPAR)
+ port->uart.ignore_status_mask |=
+ (1 << TTY_PARITY) | (1 << TTY_FRAME);
+ if (new->c_iflag & IGNBRK) {
+ port->uart.ignore_status_mask |= (1 << TTY_BREAK);
+ /*
+ * If we're ignoring parity and break indicators,
+ * ignore overruns to (for real raw support).
+ */
+ if (new->c_iflag & IGNPAR)
+ port->uart.ignore_status_mask |= (1 << TTY_OVERRUN);
+ }
+
+ /* Ignore all characters if CREAD is not set */
+ if ((new->c_cflag & CREAD) == 0)
+ port->uart.ignore_status_mask |= (1 << TTY_NORMAL);
+
+ scxctr |= SC01CTR_TXE | SC01CTR_RXE;
+ scxctr |= *port->_control & SC01CTR_BKE;
+ *port->_control = scxctr;
+
+ spin_unlock_irqrestore(&port->uart.lock, flags);
+}
+
+/*
+ * set the terminal I/O parameters
+ */
+static void mn10300_serial_set_termios(struct uart_port *_port,
+ struct ktermios *new,
+ struct ktermios *old)
+{
+ struct mn10300_serial_port *port =
+ container_of(_port, struct mn10300_serial_port, uart);
+
+ _enter("%s,%p,%p", port->name, new, old);
+
+ mn10300_serial_change_speed(port, new, old);
+
+ /* handle turning off CRTSCTS */
+ if (!(new->c_cflag & CRTSCTS)) {
+ u16 ctr = *port->_control;
+ ctr &= ~SC2CTR_TWE;
+ *port->_control = ctr;
+ }
+
+ /* change Transfer bit-order (LSB/MSB) */
+ if (new->c_cflag & CODMSB)
+ *port->_control |= SC01CTR_OD_MSBFIRST; /* MSB MODE */
+ else
+ *port->_control &= ~SC01CTR_OD_MSBFIRST; /* LSB MODE */
+}
+
+/*
+ * return description of port type
+ */
+static const char *mn10300_serial_type(struct uart_port *_port)
+{
+ struct mn10300_serial_port *port =
+ container_of(_port, struct mn10300_serial_port, uart);
+
+ if (port->uart.type == PORT_MN10300_CTS)
+ return "MN10300 SIF_CTS";
+
+ return "MN10300 SIF";
+}
+
+/*
+ * release I/O and memory regions in use by port
+ */
+static void mn10300_serial_release_port(struct uart_port *_port)
+{
+ struct mn10300_serial_port *port =
+ container_of(_port, struct mn10300_serial_port, uart);
+
+ _enter("%s", port->name);
+
+ release_mem_region((unsigned long) port->_iobase, 16);
+}
+
+/*
+ * request I/O and memory regions for port
+ */
+static int mn10300_serial_request_port(struct uart_port *_port)
+{
+ struct mn10300_serial_port *port =
+ container_of(_port, struct mn10300_serial_port, uart);
+
+ _enter("%s", port->name);
+
+ request_mem_region((unsigned long) port->_iobase, 16, port->name);
+ return 0;
+}
+
+/*
+ * configure the type and reserve the ports
+ */
+static void mn10300_serial_config_port(struct uart_port *_port, int type)
+{
+ struct mn10300_serial_port *port =
+ container_of(_port, struct mn10300_serial_port, uart);
+
+ _enter("%s", port->name);
+
+ port->uart.type = PORT_MN10300;
+
+ if (port->options & MNSCx_OPT_CTS)
+ port->uart.type = PORT_MN10300_CTS;
+
+ mn10300_serial_request_port(_port);
+}
+
+/*
+ * verify serial parameters are suitable for this port type
+ */
+static int mn10300_serial_verify_port(struct uart_port *_port,
+ struct serial_struct *ss)
+{
+ struct mn10300_serial_port *port =
+ container_of(_port, struct mn10300_serial_port, uart);
+ void *mapbase = (void *) (unsigned long) port->uart.mapbase;
+
+ _enter("%s", port->name);
+
+ /* these things may not be changed */
+ if (ss->irq != port->uart.irq ||
+ ss->port != port->uart.iobase ||
+ ss->io_type != port->uart.iotype ||
+ ss->iomem_base != mapbase ||
+ ss->iomem_reg_shift != port->uart.regshift ||
+ ss->hub6 != port->uart.hub6 ||
+ ss->xmit_fifo_size != port->uart.fifosize)
+ return -EINVAL;
+
+ /* type may be changed on a port that supports CTS */
+ if (ss->type != port->uart.type) {
+ if (!(port->options & MNSCx_OPT_CTS))
+ return -EINVAL;
+
+ if (ss->type != PORT_MN10300 &&
+ ss->type != PORT_MN10300_CTS)
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * initialise the MN10300 on-chip UARTs
+ */
+static int __init mn10300_serial_init(void)
+{
+ struct mn10300_serial_port *port;
+ int ret, i;
+
+ printk(KERN_INFO "%s version %s (%s)\n",
+ serial_name, serial_version, serial_revdate);
+
+#if defined(CONFIG_MN10300_TTYSM2) && defined(CONFIG_AM33_2)
+ {
+ int tmp;
+ SC2TIM = 8; /* make the baud base of timer 2 IOCLK/8 */
+ tmp = SC2TIM;
+ }
+#endif
+
+ set_intr_stub(NUM2EXCEP_IRQ_LEVEL(CONFIG_MN10300_SERIAL_IRQ_LEVEL),
+ mn10300_serial_vdma_interrupt);
+
+ ret = uart_register_driver(&mn10300_serial_driver);
+ if (!ret) {
+ for (i = 0 ; i < NR_PORTS ; i++) {
+ port = mn10300_serial_ports[i];
+ if (!port || port->gdbstub)
+ continue;
+
+ switch (port->clock_src) {
+ case MNSCx_CLOCK_SRC_IOCLK:
+ port->ioclk = MN10300_IOCLK;
+ break;
+
+#ifdef MN10300_IOBCLK
+ case MNSCx_CLOCK_SRC_IOBCLK:
+ port->ioclk = MN10300_IOBCLK;
+ break;
+#endif
+ default:
+ BUG();
+ }
+
+ ret = uart_add_one_port(&mn10300_serial_driver,
+ &port->uart);
+
+ if (ret < 0) {
+ _debug("ERROR %d", -ret);
+ break;
+ }
+ }
+
+ if (ret)
+ uart_unregister_driver(&mn10300_serial_driver);
+ }
+
+ return ret;
+}
+
+__initcall(mn10300_serial_init);
+
+
+#ifdef CONFIG_MN10300_TTYSM_CONSOLE
+
+/*
+ * print a string to the serial port without disturbing the real user of the
+ * port too much
+ * - the console must be locked by the caller
+ */
+static void mn10300_serial_console_write(struct console *co,
+ const char *s, unsigned count)
+{
+ struct mn10300_serial_port *port;
+ unsigned i;
+ u16 scxctr;
+ u8 tmxmd;
+ unsigned long flags;
+ int locked = 1;
+
+ port = mn10300_serial_ports[co->index];
+
+ local_irq_save(flags);
+ if (port->uart.sysrq) {
+ /* mn10300_serial_interrupt() already took the lock */
+ locked = 0;
+ } else if (oops_in_progress) {
+ locked = spin_trylock(&port->uart.lock);
+ } else
+ spin_lock(&port->uart.lock);
+
+ /* firstly hijack the serial port from the "virtual DMA" controller */
+ mn10300_serial_dis_tx_intr(port);
+
+ /* the transmitter may be disabled */
+ scxctr = *port->_control;
+ if (!(scxctr & SC01CTR_TXE)) {
+ /* restart the UART clock */
+ tmxmd = *port->_tmxmd;
+
+ switch (port->div_timer) {
+ case MNSCx_DIV_TIMER_16BIT:
+ *port->_tmxmd = 0;
+ *port->_tmxmd = TM8MD_INIT_COUNTER;
+ *port->_tmxmd = tmxmd | TM8MD_COUNT_ENABLE;
+ break;
+
+ case MNSCx_DIV_TIMER_8BIT:
+ *port->_tmxmd = 0;
+ *port->_tmxmd = TM2MD_INIT_COUNTER;
+ *port->_tmxmd = tmxmd | TM2MD_COUNT_ENABLE;
+ break;
+ }
+
+ /* enable the transmitter */
+ *port->_control = (scxctr & ~SC01CTR_BKE) | SC01CTR_TXE;
+
+ } else if (scxctr & SC01CTR_BKE) {
+ /* stop transmitting BREAK */
+ *port->_control = (scxctr & ~SC01CTR_BKE);
+ }
+
+ /* send the chars into the serial port (with LF -> LFCR conversion) */
+ for (i = 0; i < count; i++) {
+ char ch = *s++;
+
+ while (*port->_status & SC01STR_TBF)
+ continue;
+ *port->_txb = ch;
+
+ if (ch == 0x0a) {
+ while (*port->_status & SC01STR_TBF)
+ continue;
+ *port->_txb = 0xd;
+ }
+ }
+
+ /* can't let the transmitter be turned off if it's actually
+ * transmitting */
+ while (*port->_status & (SC01STR_TXF | SC01STR_TBF))
+ continue;
+
+ /* disable the transmitter if we re-enabled it */
+ if (!(scxctr & SC01CTR_TXE))
+ *port->_control = scxctr;
+
+ mn10300_serial_en_tx_intr(port);
+
+ if (locked)
+ spin_unlock(&port->uart.lock);
+ local_irq_restore(flags);
+}
+
+/*
+ * set up a serial port as a console
+ * - 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 mn10300_serial_console_setup(struct console *co,
+ char *options)
+{
+ struct mn10300_serial_port *port;
+ int i, parity = 'n', baud = 9600, bits = 8, flow = 0;
+
+ for (i = 0 ; i < NR_PORTS ; i++) {
+ port = mn10300_serial_ports[i];
+ if (port && !port->gdbstub && port->uart.line == co->index)
+ goto found_device;
+ }
+
+ return -ENODEV;
+
+found_device:
+ switch (port->clock_src) {
+ case MNSCx_CLOCK_SRC_IOCLK:
+ port->ioclk = MN10300_IOCLK;
+ break;
+
+#ifdef MN10300_IOBCLK
+ case MNSCx_CLOCK_SRC_IOBCLK:
+ port->ioclk = MN10300_IOBCLK;
+ break;
+#endif
+ default:
+ BUG();
+ }
+
+ if (options)
+ uart_parse_options(options, &baud, &parity, &bits, &flow);
+
+ return uart_set_options(&port->uart, co, baud, parity, bits, flow);
+}
+
+/*
+ * register console
+ */
+static int __init mn10300_serial_console_init(void)
+{
+ register_console(&mn10300_serial_console);
+ return 0;
+}
+
+console_initcall(mn10300_serial_console_init);
+#endif
+
+#ifdef CONFIG_CONSOLE_POLL
+/*
+ * Polled character reception for the kernel debugger
+ */
+static int mn10300_serial_poll_get_char(struct uart_port *_port)
+{
+ struct mn10300_serial_port *port =
+ container_of(_port, struct mn10300_serial_port, uart);
+ unsigned ix;
+ u8 st, ch;
+
+ _enter("%s", port->name);
+
+ if (mn10300_serial_int_tbl[port->rx_irq].port != NULL) {
+ do {
+ /* pull chars out of the hat */
+ ix = ACCESS_ONCE(port->rx_outp);
+ if (CIRC_CNT(port->rx_inp, ix, MNSC_BUFFER_SIZE) == 0)
+ return NO_POLL_CHAR;
+
+ smp_read_barrier_depends();
+ ch = port->rx_buffer[ix++];
+ st = port->rx_buffer[ix++];
+ smp_mb();
+ port->rx_outp = ix & (MNSC_BUFFER_SIZE - 1);
+
+ } while (st & (SC01STR_FEF | SC01STR_PEF | SC01STR_OEF));
+ } else {
+ do {
+ st = *port->_status;
+ if (st & (SC01STR_FEF | SC01STR_PEF | SC01STR_OEF))
+ continue;
+ } while (!(st & SC01STR_RBF));
+
+ ch = *port->_rxb;
+ }
+
+ return ch;
+}
+
+
+/*
+ * Polled character transmission for the kernel debugger
+ */
+static void mn10300_serial_poll_put_char(struct uart_port *_port,
+ unsigned char ch)
+{
+ struct mn10300_serial_port *port =
+ container_of(_port, struct mn10300_serial_port, uart);
+ u8 intr, tmp;
+
+ /* wait for the transmitter to finish anything it might be doing (and
+ * this includes the virtual DMA handler, so it might take a while) */
+ while (*port->_status & (SC01STR_TBF | SC01STR_TXF))
+ continue;
+
+ /* disable the Tx ready interrupt */
+ intr = *port->_intr;
+ *port->_intr = intr & ~SC01ICR_TI;
+ tmp = *port->_intr;
+
+ if (ch == 0x0a) {
+ *port->_txb = 0x0d;
+ while (*port->_status & SC01STR_TBF)
+ continue;
+ }
+
+ *port->_txb = ch;
+ while (*port->_status & SC01STR_TBF)
+ continue;
+
+ /* restore the Tx interrupt flag */
+ *port->_intr = intr;
+ tmp = *port->_intr;
+}
+
+#endif /* CONFIG_CONSOLE_POLL */
diff --git a/arch/mn10300/kernel/mn10300-serial.h b/arch/mn10300/kernel/mn10300-serial.h
new file mode 100644
index 000000000..01791c68e
--- /dev/null
+++ b/arch/mn10300/kernel/mn10300-serial.h
@@ -0,0 +1,130 @@
+/* MN10300 On-chip serial port driver definitions
+ *
+ * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+#ifndef _MN10300_SERIAL_H
+#define _MN10300_SERIAL_H
+
+#ifndef __ASSEMBLY__
+#include <linux/serial_core.h>
+#include <linux/termios.h>
+#endif
+
+#include <asm/page.h>
+#include <asm/serial-regs.h>
+
+#define NR_PORTS 3 /* should be set 3 or 9 or 16 */
+
+#define MNSC_BUFFER_SIZE +(PAGE_SIZE / 2)
+
+/* intr_flags bits */
+#define MNSCx_RX_AVAIL 0x01
+#define MNSCx_RX_OVERF 0x02
+#define MNSCx_TX_SPACE 0x04
+#define MNSCx_TX_EMPTY 0x08
+
+/* tx_flags bits */
+#define MNSCx_TX_BREAK 0x01
+#define MNSCx_TX_STOP 0x02
+
+#ifndef __ASSEMBLY__
+
+struct mn10300_serial_port {
+ char *rx_buffer; /* reception buffer base */
+ unsigned rx_inp; /* pointer to rx input offset */
+ unsigned rx_outp; /* pointer to rx output offset */
+ u8 tx_xchar; /* high-priority XON/XOFF buffer */
+ u8 tx_flags; /* transmit break/stop request */
+ u8 intr_flags; /* interrupt flags */
+ volatile u16 *rx_icr; /* Rx interrupt control register */
+ volatile u16 *tx_icr; /* Tx interrupt control register */
+ int rx_irq; /* reception IRQ */
+ int tx_irq; /* transmission IRQ */
+ int tm_irq; /* timer IRQ */
+
+ const char *name; /* name of serial port */
+ const char *rx_name; /* Rx interrupt handler name of serial port */
+ const char *tx_name; /* Tx interrupt handler name of serial port */
+ const char *tm_name; /* Timer interrupt handler name */
+ unsigned short type; /* type of serial port */
+ unsigned char isconsole; /* T if it's a console */
+ volatile void *_iobase; /* pointer to base of I/O control regs */
+ volatile u16 *_control; /* control register pointer */
+ volatile u8 *_status; /* status register pointer */
+ volatile u8 *_intr; /* interrupt register pointer */
+ volatile u8 *_rxb; /* receive buffer register pointer */
+ volatile u8 *_txb; /* transmit buffer register pointer */
+ volatile u16 *_tmicr; /* timer interrupt control register */
+ volatile u8 *_tmxmd; /* baud rate timer mode register */
+ volatile u16 *_tmxbr; /* baud rate timer base register */
+
+ /* this must come down here so that assembly can use BSET to access the
+ * above fields */
+ struct uart_port uart;
+
+ unsigned short rx_brk; /* current break reception status */
+ u16 tx_cts; /* current CTS status */
+ int gdbstub; /* preemptively stolen by GDB stub */
+
+ u8 clock_src; /* clock source */
+#define MNSCx_CLOCK_SRC_IOCLK 0
+#define MNSCx_CLOCK_SRC_IOBCLK 1
+
+ u8 div_timer; /* timer used as divisor */
+#define MNSCx_DIV_TIMER_16BIT 0
+#define MNSCx_DIV_TIMER_8BIT 1
+
+ u16 options; /* options */
+#define MNSCx_OPT_CTS 0x0001
+
+ unsigned long ioclk; /* base clock rate */
+};
+
+#ifdef CONFIG_MN10300_TTYSM0
+extern struct mn10300_serial_port mn10300_serial_port_sif0;
+#endif
+
+#ifdef CONFIG_MN10300_TTYSM1
+extern struct mn10300_serial_port mn10300_serial_port_sif1;
+#endif
+
+#ifdef CONFIG_MN10300_TTYSM2
+extern struct mn10300_serial_port mn10300_serial_port_sif2;
+#endif
+
+extern struct mn10300_serial_port *mn10300_serial_ports[];
+
+struct mn10300_serial_int {
+ struct mn10300_serial_port *port;
+ asmlinkage void (*vdma)(void);
+};
+
+extern struct mn10300_serial_int mn10300_serial_int_tbl[];
+
+extern asmlinkage void mn10300_serial_vdma_interrupt(void);
+extern asmlinkage void mn10300_serial_vdma_rx_handler(void);
+extern asmlinkage void mn10300_serial_vdma_tx_handler(void);
+
+#endif /* __ASSEMBLY__ */
+
+#if defined(CONFIG_GDBSTUB_ON_TTYSM0)
+#define SCgSTR SC0STR
+#define SCgRXB SC0RXB
+#define SCgRXIRQ SC0RXIRQ
+#elif defined(CONFIG_GDBSTUB_ON_TTYSM1)
+#define SCgSTR SC1STR
+#define SCgRXB SC1RXB
+#define SCgRXIRQ SC1RXIRQ
+#elif defined(CONFIG_GDBSTUB_ON_TTYSM2)
+#define SCgSTR SC2STR
+#define SCgRXB SC2RXB
+#define SCgRXIRQ SC2RXIRQ
+#endif
+
+#endif /* _MN10300_SERIAL_H */
diff --git a/arch/mn10300/kernel/mn10300-watchdog-low.S b/arch/mn10300/kernel/mn10300-watchdog-low.S
new file mode 100644
index 000000000..f2f5c9cfa
--- /dev/null
+++ b/arch/mn10300/kernel/mn10300-watchdog-low.S
@@ -0,0 +1,66 @@
+###############################################################################
+#
+# MN10300 Watchdog interrupt handler
+#
+# Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
+# Written by David Howells (dhowells@redhat.com)
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public Licence
+# as published by the Free Software Foundation; either version
+# 2 of the Licence, or (at your option) any later version.
+#
+###############################################################################
+#include <linux/sys.h>
+#include <linux/linkage.h>
+#include <asm/intctl-regs.h>
+#include <asm/timer-regs.h>
+#include <asm/frame.inc>
+#include <linux/threads.h>
+
+ .text
+
+###############################################################################
+#
+# Watchdog handler entry point
+# - special non-maskable interrupt
+#
+###############################################################################
+ .globl watchdog_handler
+ .type watchdog_handler,@function
+watchdog_handler:
+ add -4,sp
+ SAVE_ALL
+
+ mov 0xffffffff,d0
+ mov d0,(REG_ORIG_D0,fp)
+
+ mov fp,d0
+ lsr 2,d1
+ call watchdog_interrupt[],0 # watchdog_interrupt(regs,irq)
+
+ jmp ret_from_intr
+
+ .size watchdog_handler,.-watchdog_handler
+
+###############################################################################
+#
+# Watchdog touch entry point
+# - kept to absolute minimum (unfortunately, it's prototyped in linux/nmi.h so
+# we can't inline it)
+#
+###############################################################################
+ .globl touch_nmi_watchdog
+ .type touch_nmi_watchdog,@function
+touch_nmi_watchdog:
+ clr d0
+ clr d1
+ mov watchdog_alert_counter, a0
+ setlb
+ mov d0, (a0+)
+ inc d1
+ cmp NR_CPUS, d1
+ lne
+ ret [],0
+
+ .size touch_nmi_watchdog,.-touch_nmi_watchdog
diff --git a/arch/mn10300/kernel/mn10300-watchdog.c b/arch/mn10300/kernel/mn10300-watchdog.c
new file mode 100644
index 000000000..a2d8e6938
--- /dev/null
+++ b/arch/mn10300/kernel/mn10300-watchdog.c
@@ -0,0 +1,205 @@
+/* MN10300 Watchdog timer
+ *
+ * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ * - Derived from arch/i386/kernel/nmi.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/kernel_stat.h>
+#include <linux/nmi.h>
+#include <asm/processor.h>
+#include <linux/atomic.h>
+#include <asm/intctl-regs.h>
+#include <asm/rtc-regs.h>
+#include <asm/div64.h>
+#include <asm/smp.h>
+#include <asm/gdb-stub.h>
+#include <proc/clock.h>
+
+static DEFINE_SPINLOCK(watchdog_print_lock);
+static unsigned int watchdog;
+static unsigned int watchdog_hz = 1;
+unsigned int watchdog_alert_counter[NR_CPUS];
+
+EXPORT_SYMBOL(touch_nmi_watchdog);
+
+/*
+ * the best way to detect whether a CPU has a 'hard lockup' problem
+ * is to check its timer makes IRQ counts. If they are not
+ * changing then that CPU has some problem.
+ *
+ * since NMIs dont listen to _any_ locks, we have to be extremely
+ * careful not to rely on unsafe variables. The printk might lock
+ * up though, so we have to break up any console locks first ...
+ * [when there will be more tty-related locks, break them up
+ * here too!]
+ */
+static unsigned int last_irq_sums[NR_CPUS];
+
+int __init check_watchdog(void)
+{
+ irq_cpustat_t tmp[1];
+
+ printk(KERN_INFO "Testing Watchdog... ");
+
+ memcpy(tmp, irq_stat, sizeof(tmp));
+ local_irq_enable();
+ mdelay((10 * 1000) / watchdog_hz); /* wait 10 ticks */
+ local_irq_disable();
+
+ if (nmi_count(0) - tmp[0].__nmi_count <= 5) {
+ printk(KERN_WARNING "CPU#%d: Watchdog appears to be stuck!\n",
+ 0);
+ return -1;
+ }
+
+ printk(KERN_INFO "OK.\n");
+
+ /* now that we know it works we can reduce NMI frequency to something
+ * more reasonable; makes a difference in some configs
+ */
+ watchdog_hz = 1;
+
+ return 0;
+}
+
+static int __init setup_watchdog(char *str)
+{
+ unsigned tmp;
+ int opt;
+ u8 ctr;
+
+ get_option(&str, &opt);
+ if (opt != 1)
+ return 0;
+
+ watchdog = opt;
+ if (watchdog) {
+ set_intr_stub(EXCEP_WDT, watchdog_handler);
+ ctr = WDCTR_WDCK_65536th;
+ WDCTR = WDCTR_WDRST | ctr;
+ WDCTR = ctr;
+ tmp = WDCTR;
+
+ tmp = __muldiv64u(1 << (16 + ctr * 2), 1000000, MN10300_WDCLK);
+ tmp = 1000000000 / tmp;
+ watchdog_hz = (tmp + 500) / 1000;
+ }
+
+ return 1;
+}
+
+__setup("watchdog=", setup_watchdog);
+
+void __init watchdog_go(void)
+{
+ u8 wdt;
+
+ if (watchdog) {
+ printk(KERN_INFO "Watchdog: running at %uHz\n", watchdog_hz);
+ wdt = WDCTR & ~WDCTR_WDCNE;
+ WDCTR = wdt | WDCTR_WDRST;
+ wdt = WDCTR;
+ WDCTR = wdt | WDCTR_WDCNE;
+ wdt = WDCTR;
+
+ check_watchdog();
+ }
+}
+
+#ifdef CONFIG_SMP
+static void watchdog_dump_register(void *dummy)
+{
+ printk(KERN_ERR "--- Register Dump (CPU%d) ---\n", CPUID);
+ show_registers(current_frame());
+}
+#endif
+
+asmlinkage
+void watchdog_interrupt(struct pt_regs *regs, enum exception_code excep)
+{
+ /*
+ * Since current-> is always on the stack, and we always switch
+ * the stack NMI-atomically, it's safe to use smp_processor_id().
+ */
+ int sum, cpu;
+ int irq = NMIIRQ;
+ u8 wdt, tmp;
+
+ wdt = WDCTR & ~WDCTR_WDCNE;
+ WDCTR = wdt;
+ tmp = WDCTR;
+ NMICR = NMICR_WDIF;
+
+ nmi_count(smp_processor_id())++;
+ kstat_incr_irq_this_cpu(irq);
+
+ for_each_online_cpu(cpu) {
+
+ sum = irq_stat[cpu].__irq_count;
+
+ if ((last_irq_sums[cpu] == sum)
+#if defined(CONFIG_GDBSTUB) && defined(CONFIG_SMP)
+ && !(CHK_GDBSTUB_BUSY()
+ || atomic_read(&cpu_doing_single_step))
+#endif
+ ) {
+ /*
+ * Ayiee, looks like this CPU is stuck ...
+ * wait a few IRQs (5 seconds) before doing the oops ...
+ */
+ watchdog_alert_counter[cpu]++;
+ if (watchdog_alert_counter[cpu] == 5 * watchdog_hz) {
+ spin_lock(&watchdog_print_lock);
+ /*
+ * We are in trouble anyway, lets at least try
+ * to get a message out.
+ */
+ bust_spinlocks(1);
+ printk(KERN_ERR
+ "NMI Watchdog detected LOCKUP on CPU%d,"
+ " pc %08lx, registers:\n",
+ cpu, regs->pc);
+#ifdef CONFIG_SMP
+ printk(KERN_ERR
+ "--- Register Dump (CPU%d) ---\n",
+ CPUID);
+#endif
+ show_registers(regs);
+#ifdef CONFIG_SMP
+ smp_nmi_call_function(watchdog_dump_register,
+ NULL, 1);
+#endif
+ printk(KERN_NOTICE "console shuts up ...\n");
+ console_silent();
+ spin_unlock(&watchdog_print_lock);
+ bust_spinlocks(0);
+#ifdef CONFIG_GDBSTUB
+ if (CHK_GDBSTUB_BUSY_AND_ACTIVE())
+ gdbstub_exception(regs, excep);
+ else
+ gdbstub_intercept(regs, excep);
+#endif
+ do_exit(SIGSEGV);
+ }
+ } else {
+ last_irq_sums[cpu] = sum;
+ watchdog_alert_counter[cpu] = 0;
+ }
+ }
+
+ WDCTR = wdt | WDCTR_WDRST;
+ tmp = WDCTR;
+ WDCTR = wdt | WDCTR_WDCNE;
+ tmp = WDCTR;
+}
diff --git a/arch/mn10300/kernel/mn10300_ksyms.c b/arch/mn10300/kernel/mn10300_ksyms.c
new file mode 100644
index 000000000..f9eb9753a
--- /dev/null
+++ b/arch/mn10300/kernel/mn10300_ksyms.c
@@ -0,0 +1,42 @@
+/* MN10300 Miscellaneous and library kernel exports
+ *
+ * Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd.
+ * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+#include <linux/module.h>
+#include <asm/uaccess.h>
+#include <asm/pgtable.h>
+
+
+EXPORT_SYMBOL(empty_zero_page);
+
+EXPORT_SYMBOL(change_bit);
+EXPORT_SYMBOL(test_and_change_bit);
+
+EXPORT_SYMBOL(memcpy);
+EXPORT_SYMBOL(memmove);
+EXPORT_SYMBOL(memset);
+
+EXPORT_SYMBOL(strncpy_from_user);
+EXPORT_SYMBOL(__strncpy_from_user);
+EXPORT_SYMBOL(clear_user);
+EXPORT_SYMBOL(__clear_user);
+EXPORT_SYMBOL(__generic_copy_from_user);
+EXPORT_SYMBOL(__generic_copy_to_user);
+EXPORT_SYMBOL(strnlen_user);
+
+extern u64 __ashrdi3(u64, unsigned);
+extern u64 __ashldi3(u64, unsigned);
+extern u64 __lshrdi3(u64, unsigned);
+extern s64 __negdi2(s64);
+extern int __ucmpdi2(u64, u64);
+EXPORT_SYMBOL(__ashrdi3);
+EXPORT_SYMBOL(__ashldi3);
+EXPORT_SYMBOL(__lshrdi3);
+EXPORT_SYMBOL(__negdi2);
+EXPORT_SYMBOL(__ucmpdi2);
diff --git a/arch/mn10300/kernel/module.c b/arch/mn10300/kernel/module.c
new file mode 100644
index 000000000..216ad23c9
--- /dev/null
+++ b/arch/mn10300/kernel/module.c
@@ -0,0 +1,156 @@
+/* MN10300 Kernel module helper routines
+ *
+ * Copyright (C) 2007, 2008, 2009 Red Hat, Inc. All Rights Reserved.
+ * Written by Mark Salter (msalter@redhat.com)
+ * - Derived from arch/i386/kernel/module.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public Licence as published by
+ * the Free Software Foundation; either version 2 of the Licence, 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 Licence for more details.
+ *
+ * You should have received a copy of the GNU General Public Licence
+ * 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>
+#include <linux/bug.h>
+
+#if 0
+#define DEBUGP printk
+#else
+#define DEBUGP(fmt, ...)
+#endif
+
+static void reloc_put16(uint8_t *p, uint32_t val)
+{
+ p[0] = val & 0xff;
+ p[1] = (val >> 8) & 0xff;
+}
+
+static void reloc_put24(uint8_t *p, uint32_t val)
+{
+ reloc_put16(p, val);
+ p[2] = (val >> 16) & 0xff;
+}
+
+static void reloc_put32(uint8_t *p, uint32_t val)
+{
+ reloc_put16(p, val);
+ reloc_put16(p+2, val >> 16);
+}
+
+/*
+ * apply a RELA relocation
+ */
+int apply_relocate_add(Elf32_Shdr *sechdrs,
+ const char *strtab,
+ unsigned int symindex,
+ unsigned int relsec,
+ struct module *me)
+{
+ unsigned int i, sym_diff_seen = 0;
+ Elf32_Rela *rel = (void *)sechdrs[relsec].sh_addr;
+ Elf32_Sym *sym;
+ Elf32_Addr relocation, sym_diff_val = 0;
+ uint8_t *location;
+ uint32_t value;
+
+ 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 the relocation is referring to (note that
+ * all undefined symbols have been resolved by the caller) */
+ sym = (Elf32_Sym *)sechdrs[symindex].sh_addr
+ + ELF32_R_SYM(rel[i].r_info);
+
+ /* this is the adjustment to be made */
+ relocation = sym->st_value + rel[i].r_addend;
+
+ if (sym_diff_seen) {
+ switch (ELF32_R_TYPE(rel[i].r_info)) {
+ case R_MN10300_32:
+ case R_MN10300_24:
+ case R_MN10300_16:
+ case R_MN10300_8:
+ relocation -= sym_diff_val;
+ sym_diff_seen = 0;
+ break;
+ default:
+ printk(KERN_ERR "module %s: Unexpected SYM_DIFF relocation: %u\n",
+ me->name, ELF32_R_TYPE(rel[i].r_info));
+ return -ENOEXEC;
+ }
+ }
+
+ switch (ELF32_R_TYPE(rel[i].r_info)) {
+ /* for the first four relocation types, we simply
+ * store the adjustment at the location given */
+ case R_MN10300_32:
+ reloc_put32(location, relocation);
+ break;
+ case R_MN10300_24:
+ reloc_put24(location, relocation);
+ break;
+ case R_MN10300_16:
+ reloc_put16(location, relocation);
+ break;
+ case R_MN10300_8:
+ *location = relocation;
+ break;
+
+ /* for the next three relocation types, we write the
+ * adjustment with the address subtracted over the
+ * value at the location given */
+ case R_MN10300_PCREL32:
+ value = relocation - (uint32_t) location;
+ reloc_put32(location, value);
+ break;
+ case R_MN10300_PCREL16:
+ value = relocation - (uint32_t) location;
+ reloc_put16(location, value);
+ break;
+ case R_MN10300_PCREL8:
+ *location = relocation - (uint32_t) location;
+ break;
+
+ case R_MN10300_SYM_DIFF:
+ /* This is used to adjust the next reloc as required
+ * by relaxation. */
+ sym_diff_seen = 1;
+ sym_diff_val = sym->st_value;
+ break;
+
+ case R_MN10300_ALIGN:
+ /* Just ignore the ALIGN relocs.
+ * Only interesting if kernel performed relaxation. */
+ continue;
+
+ default:
+ printk(KERN_ERR "module %s: Unknown relocation: %u\n",
+ me->name, ELF32_R_TYPE(rel[i].r_info));
+ return -ENOEXEC;
+ }
+ }
+ if (sym_diff_seen) {
+ printk(KERN_ERR "module %s: Nothing follows SYM_DIFF relocation: %u\n",
+ me->name, ELF32_R_TYPE(rel[i].r_info));
+ return -ENOEXEC;
+ }
+ return 0;
+}
diff --git a/arch/mn10300/kernel/process.c b/arch/mn10300/kernel/process.c
new file mode 100644
index 000000000..3707da583
--- /dev/null
+++ b/arch/mn10300/kernel/process.c
@@ -0,0 +1,192 @@
+/* MN10300 Process handling code
+ *
+ * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/smp.h>
+#include <linux/stddef.h>
+#include <linux/unistd.h>
+#include <linux/ptrace.h>
+#include <linux/user.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/reboot.h>
+#include <linux/percpu.h>
+#include <linux/err.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/rcupdate.h>
+#include <asm/uaccess.h>
+#include <asm/pgtable.h>
+#include <asm/io.h>
+#include <asm/processor.h>
+#include <asm/mmu_context.h>
+#include <asm/fpu.h>
+#include <asm/reset-regs.h>
+#include <asm/gdb-stub.h>
+#include "internal.h"
+
+/*
+ * return saved PC of a blocked thread.
+ */
+unsigned long thread_saved_pc(struct task_struct *tsk)
+{
+ return ((unsigned long *) tsk->thread.sp)[3];
+}
+
+/*
+ * power off function, if any
+ */
+void (*pm_power_off)(void);
+EXPORT_SYMBOL(pm_power_off);
+
+/*
+ * On SMP it's slightly faster (but much more power-consuming!)
+ * to poll the ->work.need_resched flag instead of waiting for the
+ * cross-CPU IPI to arrive. Use this option with caution.
+ *
+ * tglx: No idea why this depends on HOTPLUG_CPU !?!
+ */
+#if !defined(CONFIG_SMP) || defined(CONFIG_HOTPLUG_CPU)
+void arch_cpu_idle(void)
+{
+ safe_halt();
+}
+#endif
+
+void release_segments(struct mm_struct *mm)
+{
+}
+
+void machine_restart(char *cmd)
+{
+#ifdef CONFIG_KERNEL_DEBUGGER
+ gdbstub_exit(0);
+#endif
+
+#ifdef mn10300_unit_hard_reset
+ mn10300_unit_hard_reset();
+#else
+ mn10300_proc_hard_reset();
+#endif
+}
+
+void machine_halt(void)
+{
+#ifdef CONFIG_KERNEL_DEBUGGER
+ gdbstub_exit(0);
+#endif
+}
+
+void machine_power_off(void)
+{
+#ifdef CONFIG_KERNEL_DEBUGGER
+ gdbstub_exit(0);
+#endif
+}
+
+void show_regs(struct pt_regs *regs)
+{
+ show_regs_print_info(KERN_DEFAULT);
+}
+
+/*
+ * free current thread data structures etc..
+ */
+void exit_thread(void)
+{
+ exit_fpu();
+}
+
+void flush_thread(void)
+{
+ flush_fpu();
+}
+
+void release_thread(struct task_struct *dead_task)
+{
+}
+
+/*
+ * we do not have to muck with descriptors here, that is
+ * done in switch_mm() as needed.
+ */
+void copy_segments(struct task_struct *p, struct mm_struct *new_mm)
+{
+}
+
+/*
+ * this gets called so that we can store lazy state into memory and copy the
+ * current task into the new thread.
+ */
+int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src)
+{
+ unlazy_fpu(src);
+ *dst = *src;
+ return 0;
+}
+
+/*
+ * set up the kernel stack for a new thread and copy arch-specific thread
+ * control information
+ */
+int copy_thread(unsigned long clone_flags,
+ unsigned long c_usp, unsigned long ustk_size,
+ struct task_struct *p)
+{
+ struct thread_info *ti = task_thread_info(p);
+ struct pt_regs *c_regs;
+ unsigned long c_ksp;
+
+ c_ksp = (unsigned long) task_stack_page(p) + THREAD_SIZE;
+
+ /* allocate the userspace exception frame and set it up */
+ c_ksp -= sizeof(struct pt_regs);
+ c_regs = (struct pt_regs *) c_ksp;
+ c_ksp -= 12; /* allocate function call ABI slack */
+
+ /* set up things up so the scheduler can start the new task */
+ p->thread.uregs = c_regs;
+ ti->frame = c_regs;
+ p->thread.a3 = (unsigned long) c_regs;
+ p->thread.sp = c_ksp;
+ p->thread.wchan = p->thread.pc;
+ p->thread.usp = c_usp;
+
+ if (unlikely(p->flags & PF_KTHREAD)) {
+ memset(c_regs, 0, sizeof(struct pt_regs));
+ c_regs->a0 = c_usp; /* function */
+ c_regs->d0 = ustk_size; /* argument */
+ local_save_flags(c_regs->epsw);
+ c_regs->epsw |= EPSW_IE | EPSW_IM_7;
+ p->thread.pc = (unsigned long) ret_from_kernel_thread;
+ return 0;
+ }
+ *c_regs = *current_pt_regs();
+ if (c_usp)
+ c_regs->sp = c_usp;
+ c_regs->epsw &= ~EPSW_FE; /* my FPU */
+
+ /* the new TLS pointer is passed in as arg #5 to sys_clone() */
+ if (clone_flags & CLONE_SETTLS)
+ c_regs->e2 = current_frame()->d3;
+
+ p->thread.pc = (unsigned long) ret_from_fork;
+
+ return 0;
+}
+
+unsigned long get_wchan(struct task_struct *p)
+{
+ return p->thread.wchan;
+}
diff --git a/arch/mn10300/kernel/profile-low.S b/arch/mn10300/kernel/profile-low.S
new file mode 100644
index 000000000..94ffac12d
--- /dev/null
+++ b/arch/mn10300/kernel/profile-low.S
@@ -0,0 +1,72 @@
+###############################################################################
+#
+# Fast profiling interrupt handler
+#
+# Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
+# Written by David Howells (dhowells@redhat.com)
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public Licence
+# as published by the Free Software Foundation; either version
+# 2 of the Licence, or (at your option) any later version.
+#
+###############################################################################
+#include <linux/sys.h>
+#include <linux/linkage.h>
+#include <asm/segment.h>
+#include <asm/smp.h>
+#include <asm/intctl-regs.h>
+#include <asm/timer-regs.h>
+
+#define pi break
+
+ .balign 4
+counter:
+ .long -1
+
+###############################################################################
+#
+# Profiling interrupt entry point
+# - intended to run at interrupt priority 1
+#
+###############################################################################
+ENTRY(profile_handler)
+ movm [d2,d3,a2],(sp)
+
+ # ignore userspace
+ mov (12,sp),d2
+ and EPSW_nSL,d2
+ bne out
+
+ # do nothing if there's no buffer
+ mov (prof_buffer),a2
+ and a2,a2
+ beq out
+ or 0x20000000,a2
+
+ # calculate relative position in text segment
+ mov (16,sp),d2
+ sub _stext,d2
+ mov (prof_shift),d3
+ lsr d3,d2
+ mov (prof_len),d3
+ cmp d3,d2
+ bcc outside_text
+
+ # increment the appropriate profile bucket
+do_inc:
+ asl2 d2
+ mov (a2,d2),d3
+ inc d3
+ mov d3,(a2,d2)
+out:
+ mov GxICR_DETECT,d2
+ movbu d2,(TM11ICR) # ACK the interrupt
+ movbu (TM11ICR),d2
+ movm (sp),[d2,d3,a2]
+ rti
+
+outside_text:
+ sub 1,d3
+ mov d3,d2
+ bra do_inc
diff --git a/arch/mn10300/kernel/profile.c b/arch/mn10300/kernel/profile.c
new file mode 100644
index 000000000..4f342f75d
--- /dev/null
+++ b/arch/mn10300/kernel/profile.c
@@ -0,0 +1,51 @@
+/* MN10300 Profiling setup
+ *
+ * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+/*
+ * initialise the profiling if enabled
+ * - using with gdbstub will give anomalous results
+ * - can't be used with gdbstub if running at IRQ priority 0
+ */
+static __init int profile_init(void)
+{
+ u16 tmp;
+
+ if (!prof_buffer)
+ return 0;
+
+ /* use timer 11 to drive the profiling interrupts */
+ set_intr_stub(EXCEP_IRQ_LEVEL0, profile_handler);
+
+ /* set IRQ priority at which to run */
+ set_intr_level(TM11IRQ, GxICR_LEVEL_0);
+
+ /* set up timer 11
+ * - source: (IOCLK 33MHz)*2 = 66MHz
+ * - frequency: (33330000*2) / 8 / 20625 = 202Hz
+ */
+ TM11BR = 20625 - 1;
+ TM11MD = TM8MD_SRC_IOCLK_8;
+ TM11MD |= TM8MD_INIT_COUNTER;
+ TM11MD &= ~TM8MD_INIT_COUNTER;
+ TM11MD |= TM8MD_COUNT_ENABLE;
+
+ TM11ICR |= GxICR_ENABLE;
+ tmp = TM11ICR;
+
+ printk(KERN_INFO "Profiling initiated on timer 11, priority 0, %uHz\n",
+ MN10300_IOCLK / 8 / (TM11BR + 1));
+ printk(KERN_INFO "Profile histogram stored %p-%p\n",
+ prof_buffer, (u8 *)(prof_buffer + prof_len) - 1);
+
+ return 0;
+}
+
+__initcall(profile_init);
diff --git a/arch/mn10300/kernel/ptrace.c b/arch/mn10300/kernel/ptrace.c
new file mode 100644
index 000000000..5bd58514e
--- /dev/null
+++ b/arch/mn10300/kernel/ptrace.c
@@ -0,0 +1,385 @@
+/* MN10300 Process tracing
+ *
+ * Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd.
+ * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
+ * Modified by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/smp.h>
+#include <linux/errno.h>
+#include <linux/ptrace.h>
+#include <linux/user.h>
+#include <linux/regset.h>
+#include <linux/elf.h>
+#include <linux/tracehook.h>
+#include <asm/uaccess.h>
+#include <asm/pgtable.h>
+#include <asm/processor.h>
+#include <asm/cacheflush.h>
+#include <asm/fpu.h>
+#include <asm/asm-offsets.h>
+
+/*
+ * translate ptrace register IDs into struct pt_regs offsets
+ */
+static const u8 ptrace_regid_to_frame[] = {
+ [PT_A3 << 2] = REG_A3,
+ [PT_A2 << 2] = REG_A2,
+ [PT_D3 << 2] = REG_D3,
+ [PT_D2 << 2] = REG_D2,
+ [PT_MCVF << 2] = REG_MCVF,
+ [PT_MCRL << 2] = REG_MCRL,
+ [PT_MCRH << 2] = REG_MCRH,
+ [PT_MDRQ << 2] = REG_MDRQ,
+ [PT_E1 << 2] = REG_E1,
+ [PT_E0 << 2] = REG_E0,
+ [PT_E7 << 2] = REG_E7,
+ [PT_E6 << 2] = REG_E6,
+ [PT_E5 << 2] = REG_E5,
+ [PT_E4 << 2] = REG_E4,
+ [PT_E3 << 2] = REG_E3,
+ [PT_E2 << 2] = REG_E2,
+ [PT_SP << 2] = REG_SP,
+ [PT_LAR << 2] = REG_LAR,
+ [PT_LIR << 2] = REG_LIR,
+ [PT_MDR << 2] = REG_MDR,
+ [PT_A1 << 2] = REG_A1,
+ [PT_A0 << 2] = REG_A0,
+ [PT_D1 << 2] = REG_D1,
+ [PT_D0 << 2] = REG_D0,
+ [PT_ORIG_D0 << 2] = REG_ORIG_D0,
+ [PT_EPSW << 2] = REG_EPSW,
+ [PT_PC << 2] = REG_PC,
+};
+
+static inline int get_stack_long(struct task_struct *task, int offset)
+{
+ return *(unsigned long *)
+ ((unsigned long) task->thread.uregs + offset);
+}
+
+static inline
+int put_stack_long(struct task_struct *task, int offset, unsigned long data)
+{
+ unsigned long stack;
+
+ stack = (unsigned long) task->thread.uregs + offset;
+ *(unsigned long *) stack = data;
+ return 0;
+}
+
+/*
+ * retrieve the contents of MN10300 userspace general registers
+ */
+static int genregs_get(struct task_struct *target,
+ const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
+{
+ const struct pt_regs *regs = task_pt_regs(target);
+ int ret;
+
+ /* we need to skip regs->next */
+ ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ regs, 0, PT_ORIG_D0 * sizeof(long));
+ if (ret < 0)
+ return ret;
+
+ ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ &regs->orig_d0, PT_ORIG_D0 * sizeof(long),
+ NR_PTREGS * sizeof(long));
+ if (ret < 0)
+ return ret;
+
+ return user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
+ NR_PTREGS * sizeof(long), -1);
+}
+
+/*
+ * update the contents of the MN10300 userspace general registers
+ */
+static int genregs_set(struct task_struct *target,
+ const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
+{
+ struct pt_regs *regs = task_pt_regs(target);
+ unsigned long tmp;
+ int ret;
+
+ /* we need to skip regs->next */
+ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ regs, 0, PT_ORIG_D0 * sizeof(long));
+ if (ret < 0)
+ return ret;
+
+ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ &regs->orig_d0, PT_ORIG_D0 * sizeof(long),
+ PT_EPSW * sizeof(long));
+ if (ret < 0)
+ return ret;
+
+ /* we need to mask off changes to EPSW */
+ tmp = regs->epsw;
+ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ &tmp, PT_EPSW * sizeof(long),
+ PT_PC * sizeof(long));
+ tmp &= EPSW_FLAG_V | EPSW_FLAG_C | EPSW_FLAG_N | EPSW_FLAG_Z;
+ tmp |= regs->epsw & ~(EPSW_FLAG_V | EPSW_FLAG_C | EPSW_FLAG_N |
+ EPSW_FLAG_Z);
+ regs->epsw = tmp;
+
+ if (ret < 0)
+ return ret;
+
+ /* and finally load the PC */
+ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ &regs->pc, PT_PC * sizeof(long),
+ NR_PTREGS * sizeof(long));
+
+ if (ret < 0)
+ return ret;
+
+ return user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
+ NR_PTREGS * sizeof(long), -1);
+}
+
+/*
+ * retrieve the contents of MN10300 userspace FPU registers
+ */
+static int fpuregs_get(struct task_struct *target,
+ const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
+{
+ const struct fpu_state_struct *fpregs = &target->thread.fpu_state;
+ int ret;
+
+ unlazy_fpu(target);
+
+ ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ fpregs, 0, sizeof(*fpregs));
+ if (ret < 0)
+ return ret;
+
+ return user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
+ sizeof(*fpregs), -1);
+}
+
+/*
+ * update the contents of the MN10300 userspace FPU registers
+ */
+static int fpuregs_set(struct task_struct *target,
+ const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
+{
+ struct fpu_state_struct fpu_state = target->thread.fpu_state;
+ int ret;
+
+ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ &fpu_state, 0, sizeof(fpu_state));
+ if (ret < 0)
+ return ret;
+
+ fpu_kill_state(target);
+ target->thread.fpu_state = fpu_state;
+ set_using_fpu(target);
+
+ return user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
+ sizeof(fpu_state), -1);
+}
+
+/*
+ * determine if the FPU registers have actually been used
+ */
+static int fpuregs_active(struct task_struct *target,
+ const struct user_regset *regset)
+{
+ return is_using_fpu(target) ? regset->n : 0;
+}
+
+/*
+ * Define the register sets available on the MN10300 under Linux
+ */
+enum mn10300_regset {
+ REGSET_GENERAL,
+ REGSET_FPU,
+};
+
+static const struct user_regset mn10300_regsets[] = {
+ /*
+ * General register format is:
+ * A3, A2, D3, D2, MCVF, MCRL, MCRH, MDRQ
+ * E1, E0, E7...E2, SP, LAR, LIR, MDR
+ * A1, A0, D1, D0, ORIG_D0, EPSW, PC
+ */
+ [REGSET_GENERAL] = {
+ .core_note_type = NT_PRSTATUS,
+ .n = ELF_NGREG,
+ .size = sizeof(long),
+ .align = sizeof(long),
+ .get = genregs_get,
+ .set = genregs_set,
+ },
+ /*
+ * FPU register format is:
+ * FS0-31, FPCR
+ */
+ [REGSET_FPU] = {
+ .core_note_type = NT_PRFPREG,
+ .n = sizeof(struct fpu_state_struct) / sizeof(long),
+ .size = sizeof(long),
+ .align = sizeof(long),
+ .get = fpuregs_get,
+ .set = fpuregs_set,
+ .active = fpuregs_active,
+ },
+};
+
+static const struct user_regset_view user_mn10300_native_view = {
+ .name = "mn10300",
+ .e_machine = EM_MN10300,
+ .regsets = mn10300_regsets,
+ .n = ARRAY_SIZE(mn10300_regsets),
+};
+
+const struct user_regset_view *task_user_regset_view(struct task_struct *task)
+{
+ return &user_mn10300_native_view;
+}
+
+/*
+ * set the single-step bit
+ */
+void user_enable_single_step(struct task_struct *child)
+{
+#ifndef CONFIG_MN10300_USING_JTAG
+ struct user *dummy = NULL;
+ long tmp;
+
+ tmp = get_stack_long(child, (unsigned long) &dummy->regs.epsw);
+ tmp |= EPSW_T;
+ put_stack_long(child, (unsigned long) &dummy->regs.epsw, tmp);
+#endif
+}
+
+/*
+ * make sure the single-step bit is not set
+ */
+void user_disable_single_step(struct task_struct *child)
+{
+#ifndef CONFIG_MN10300_USING_JTAG
+ struct user *dummy = NULL;
+ long tmp;
+
+ tmp = get_stack_long(child, (unsigned long) &dummy->regs.epsw);
+ tmp &= ~EPSW_T;
+ put_stack_long(child, (unsigned long) &dummy->regs.epsw, tmp);
+#endif
+}
+
+void ptrace_disable(struct task_struct *child)
+{
+ user_disable_single_step(child);
+}
+
+/*
+ * handle the arch-specific side of process tracing
+ */
+long arch_ptrace(struct task_struct *child, long request,
+ unsigned long addr, unsigned long data)
+{
+ unsigned long tmp;
+ int ret;
+ unsigned long __user *datap = (unsigned long __user *) data;
+
+ switch (request) {
+ /* read the word at location addr in the USER area. */
+ case PTRACE_PEEKUSR:
+ ret = -EIO;
+ if ((addr & 3) || addr > sizeof(struct user) - 3)
+ break;
+
+ tmp = 0; /* Default return condition */
+ if (addr < NR_PTREGS << 2)
+ tmp = get_stack_long(child,
+ ptrace_regid_to_frame[addr]);
+ ret = put_user(tmp, datap);
+ break;
+
+ /* write the word at location addr in the USER area */
+ case PTRACE_POKEUSR:
+ ret = -EIO;
+ if ((addr & 3) || addr > sizeof(struct user) - 3)
+ break;
+
+ ret = 0;
+ if (addr < NR_PTREGS << 2)
+ ret = put_stack_long(child, ptrace_regid_to_frame[addr],
+ data);
+ break;
+
+ case PTRACE_GETREGS: /* Get all integer regs from the child. */
+ return copy_regset_to_user(child, &user_mn10300_native_view,
+ REGSET_GENERAL,
+ 0, NR_PTREGS * sizeof(long),
+ datap);
+
+ case PTRACE_SETREGS: /* Set all integer regs in the child. */
+ return copy_regset_from_user(child, &user_mn10300_native_view,
+ REGSET_GENERAL,
+ 0, NR_PTREGS * sizeof(long),
+ datap);
+
+ case PTRACE_GETFPREGS: /* Get the child FPU state. */
+ return copy_regset_to_user(child, &user_mn10300_native_view,
+ REGSET_FPU,
+ 0, sizeof(struct fpu_state_struct),
+ datap);
+
+ case PTRACE_SETFPREGS: /* Set the child FPU state. */
+ return copy_regset_from_user(child, &user_mn10300_native_view,
+ REGSET_FPU,
+ 0, sizeof(struct fpu_state_struct),
+ datap);
+
+ default:
+ ret = ptrace_request(child, request, addr, data);
+ break;
+ }
+
+ return ret;
+}
+
+/*
+ * handle tracing of system call entry
+ * - return the revised system call number or ULONG_MAX to cause ENOSYS
+ */
+asmlinkage unsigned long syscall_trace_entry(struct pt_regs *regs)
+{
+ if (tracehook_report_syscall_entry(regs))
+ /* tracing decided this syscall should not happen, so
+ * We'll return a bogus call number to get an ENOSYS
+ * error, but leave the original number in
+ * regs->orig_d0
+ */
+ return ULONG_MAX;
+
+ return regs->orig_d0;
+}
+
+/*
+ * handle tracing of system call exit
+ */
+asmlinkage void syscall_trace_exit(struct pt_regs *regs)
+{
+ tracehook_report_syscall_exit(regs, 0);
+}
diff --git a/arch/mn10300/kernel/rtc.c b/arch/mn10300/kernel/rtc.c
new file mode 100644
index 000000000..48d7058b3
--- /dev/null
+++ b/arch/mn10300/kernel/rtc.c
@@ -0,0 +1,132 @@
+/* MN10300 RTC management
+ *
+ * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/mc146818rtc.h>
+#include <linux/bcd.h>
+#include <linux/timex.h>
+#include <asm/rtc-regs.h>
+#include <asm/rtc.h>
+
+DEFINE_SPINLOCK(rtc_lock);
+EXPORT_SYMBOL(rtc_lock);
+
+/*
+ * Read the current RTC time
+ */
+void read_persistent_clock(struct timespec *ts)
+{
+ struct rtc_time tm;
+
+ get_rtc_time(&tm);
+
+ ts->tv_nsec = 0;
+ ts->tv_sec = mktime(tm.tm_year, tm.tm_mon, tm.tm_mday,
+ tm.tm_hour, tm.tm_min, tm.tm_sec);
+
+ /* if rtc is way off in the past, set something reasonable */
+ if (ts->tv_sec < 0)
+ ts->tv_sec = mktime(2009, 1, 1, 12, 0, 0);
+}
+
+/*
+ * In order to set the CMOS clock precisely, set_rtc_mmss has to be called 500
+ * ms after the second nowtime has started, because when nowtime is written
+ * into the registers of the CMOS clock, it will jump to the next second
+ * precisely 500 ms later. Check the Motorola MC146818A or Dallas DS12887 data
+ * sheet for details.
+ *
+ * BUG: This routine does not handle hour overflow properly; it just
+ * sets the minutes. Usually you'll only notice that after reboot!
+ */
+static int set_rtc_mmss(unsigned long nowtime)
+{
+ unsigned char save_control, save_freq_select;
+ int retval = 0;
+ int real_seconds, real_minutes, cmos_minutes;
+
+ /* gets recalled with irq locally disabled */
+ spin_lock(&rtc_lock);
+ save_control = CMOS_READ(RTC_CONTROL); /* tell the clock it's being
+ * set */
+ CMOS_WRITE(save_control | RTC_SET, RTC_CONTROL);
+
+ save_freq_select = CMOS_READ(RTC_FREQ_SELECT); /* stop and reset
+ * prescaler */
+ CMOS_WRITE(save_freq_select | RTC_DIV_RESET2, RTC_FREQ_SELECT);
+
+ cmos_minutes = CMOS_READ(RTC_MINUTES);
+ if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD)
+ cmos_minutes = bcd2bin(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 = nowtime % 60;
+ real_minutes = nowtime / 60;
+ if (((abs(real_minutes - cmos_minutes) + 15) / 30) & 1)
+ /* correct for half hour time zone */
+ real_minutes += 30;
+ real_minutes %= 60;
+
+ if (abs(real_minutes - cmos_minutes) < 30) {
+ if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) {
+ real_seconds = bin2bcd(real_seconds);
+ real_minutes = bin2bcd(real_minutes);
+ }
+ CMOS_WRITE(real_seconds, RTC_SECONDS);
+ CMOS_WRITE(real_minutes, RTC_MINUTES);
+ } else {
+ printk_once(KERN_NOTICE
+ "set_rtc_mmss: can't update from %d to %d\n",
+ cmos_minutes, real_minutes);
+ retval = -1;
+ }
+
+ /* The following flags have to be released exactly in this order,
+ * otherwise the DS12887 (popular MC146818A clone with integrated
+ * battery and quartz) will not reset the oscillator and will not
+ * update precisely 500 ms later. You won't find this mentioned in
+ * the Dallas Semiconductor data sheets, but who believes data
+ * sheets anyway ... -- Markus Kuhn
+ */
+ CMOS_WRITE(save_control, RTC_CONTROL);
+ CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT);
+ spin_unlock(&rtc_lock);
+
+ return retval;
+}
+
+int update_persistent_clock(struct timespec now)
+{
+ return set_rtc_mmss(now.tv_sec);
+}
+
+/*
+ * calibrate the TSC clock against the RTC
+ */
+void __init calibrate_clock(void)
+{
+ unsigned char status;
+
+ /* make sure the RTC is running and is set to operate in 24hr mode */
+ status = RTSRC;
+ RTCRB |= RTCRB_SET;
+ RTCRB |= RTCRB_TM_24HR;
+ RTCRB &= ~RTCRB_DM_BINARY;
+ RTCRA |= RTCRA_DVR;
+ RTCRA &= ~RTCRA_DVR;
+ RTCRB &= ~RTCRB_SET;
+}
diff --git a/arch/mn10300/kernel/setup.c b/arch/mn10300/kernel/setup.c
new file mode 100644
index 000000000..2ad7f32fa
--- /dev/null
+++ b/arch/mn10300/kernel/setup.c
@@ -0,0 +1,283 @@
+/* MN10300 Arch-specific initialisation
+ *
+ * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/stddef.h>
+#include <linux/unistd.h>
+#include <linux/ptrace.h>
+#include <linux/user.h>
+#include <linux/tty.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/bootmem.h>
+#include <linux/seq_file.h>
+#include <linux/cpu.h>
+#include <asm/processor.h>
+#include <linux/console.h>
+#include <asm/uaccess.h>
+#include <asm/setup.h>
+#include <asm/io.h>
+#include <asm/smp.h>
+#include <proc/proc.h>
+#include <asm/fpu.h>
+#include <asm/sections.h>
+
+struct mn10300_cpuinfo boot_cpu_data;
+
+static char __initdata cmd_line[COMMAND_LINE_SIZE];
+char redboot_command_line[COMMAND_LINE_SIZE] =
+ "console=ttyS0,115200 root=/dev/mtdblock3 rw";
+
+char __initdata redboot_platform_name[COMMAND_LINE_SIZE];
+
+static struct resource code_resource = {
+ .start = 0x100000,
+ .end = 0,
+ .name = "Kernel code",
+};
+
+static struct resource data_resource = {
+ .start = 0,
+ .end = 0,
+ .name = "Kernel data",
+};
+
+static unsigned long __initdata phys_memory_base;
+static unsigned long __initdata phys_memory_end;
+static unsigned long __initdata memory_end;
+unsigned long memory_size;
+
+struct thread_info *__current_ti = &init_thread_union.thread_info;
+struct task_struct *__current = &init_task;
+
+#define mn10300_known_cpus 5
+static const char *const mn10300_cputypes[] = {
+ "am33-1",
+ "am33-2",
+ "am34-1",
+ "am33-3",
+ "am34-2",
+ "unknown"
+};
+
+/*
+ * Pick out the memory size. We look for mem=size,
+ * where size is "size[KkMm]"
+ */
+static int __init early_mem(char *p)
+{
+ memory_size = memparse(p, &p);
+
+ if (memory_size == 0)
+ panic("Memory size not known\n");
+
+ return 0;
+}
+early_param("mem", early_mem);
+
+/*
+ * architecture specific setup
+ */
+void __init setup_arch(char **cmdline_p)
+{
+ unsigned long bootmap_size;
+ unsigned long kstart_pfn, start_pfn, free_pfn, end_pfn;
+
+ cpu_init();
+ unit_setup();
+ smp_init_cpus();
+
+ /* save unparsed command line copy for /proc/cmdline */
+ strlcpy(boot_command_line, redboot_command_line, COMMAND_LINE_SIZE);
+
+ /* populate cmd_line too for later use, preserving boot_command_line */
+ strlcpy(cmd_line, boot_command_line, COMMAND_LINE_SIZE);
+ *cmdline_p = cmd_line;
+
+ parse_early_param();
+
+ memory_end = (unsigned long) CONFIG_KERNEL_RAM_BASE_ADDRESS +
+ memory_size;
+ if (memory_end > phys_memory_end)
+ memory_end = phys_memory_end;
+
+ 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;
+
+ start_pfn = (CONFIG_KERNEL_RAM_BASE_ADDRESS >> PAGE_SHIFT);
+ kstart_pfn = PFN_UP(__pa(&_text));
+ free_pfn = PFN_UP(__pa(&_end));
+ end_pfn = PFN_DOWN(__pa(memory_end));
+
+ bootmap_size = init_bootmem_node(&contig_page_data,
+ free_pfn,
+ start_pfn,
+ end_pfn);
+
+ if (kstart_pfn > start_pfn)
+ free_bootmem(PFN_PHYS(start_pfn),
+ PFN_PHYS(kstart_pfn - start_pfn));
+
+ free_bootmem(PFN_PHYS(free_pfn),
+ PFN_PHYS(end_pfn - free_pfn));
+
+ /* If interrupt vector table is in main ram, then we need to
+ reserve the page it is occupying. */
+ if (CONFIG_INTERRUPT_VECTOR_BASE >= CONFIG_KERNEL_RAM_BASE_ADDRESS &&
+ CONFIG_INTERRUPT_VECTOR_BASE < memory_end)
+ reserve_bootmem(CONFIG_INTERRUPT_VECTOR_BASE, PAGE_SIZE,
+ BOOTMEM_DEFAULT);
+
+ reserve_bootmem(PAGE_ALIGN(PFN_PHYS(free_pfn)), bootmap_size,
+ BOOTMEM_DEFAULT);
+
+#ifdef CONFIG_VT
+#if defined(CONFIG_VGA_CONSOLE)
+ conswitchp = &vga_con;
+#elif defined(CONFIG_DUMMY_CONSOLE)
+ conswitchp = &dummy_con;
+#endif
+#endif
+
+ paging_init();
+}
+
+/*
+ * perform CPU initialisation
+ */
+void __init cpu_init(void)
+{
+ unsigned long cpurev = CPUREV, type;
+
+ type = (CPUREV & CPUREV_TYPE) >> CPUREV_TYPE_S;
+ if (type > mn10300_known_cpus)
+ type = mn10300_known_cpus;
+
+ printk(KERN_INFO "Panasonic %s, rev %ld\n",
+ mn10300_cputypes[type],
+ (cpurev & CPUREV_REVISION) >> CPUREV_REVISION_S);
+
+ get_mem_info(&phys_memory_base, &memory_size);
+ phys_memory_end = phys_memory_base + memory_size;
+
+ fpu_init_state();
+}
+
+static struct cpu cpu_devices[NR_CPUS];
+
+static int __init topology_init(void)
+{
+ int i;
+
+ for_each_present_cpu(i)
+ register_cpu(&cpu_devices[i], i);
+
+ return 0;
+}
+
+subsys_initcall(topology_init);
+
+/*
+ * Get CPU information for use by the procfs.
+ */
+static int show_cpuinfo(struct seq_file *m, void *v)
+{
+#ifdef CONFIG_SMP
+ struct mn10300_cpuinfo *c = v;
+ unsigned long cpu_id = c - cpu_data;
+ unsigned long cpurev = c->type, type, icachesz, dcachesz;
+#else /* CONFIG_SMP */
+ unsigned long cpu_id = 0;
+ unsigned long cpurev = CPUREV, type, icachesz, dcachesz;
+#endif /* CONFIG_SMP */
+
+#ifdef CONFIG_SMP
+ if (!cpu_online(cpu_id))
+ return 0;
+#endif
+
+ type = (cpurev & CPUREV_TYPE) >> CPUREV_TYPE_S;
+ if (type > mn10300_known_cpus)
+ type = mn10300_known_cpus;
+
+ icachesz =
+ ((cpurev & CPUREV_ICWAY ) >> CPUREV_ICWAY_S) *
+ ((cpurev & CPUREV_ICSIZE) >> CPUREV_ICSIZE_S) *
+ 1024;
+
+ dcachesz =
+ ((cpurev & CPUREV_DCWAY ) >> CPUREV_DCWAY_S) *
+ ((cpurev & CPUREV_DCSIZE) >> CPUREV_DCSIZE_S) *
+ 1024;
+
+ seq_printf(m,
+ "processor : %ld\n"
+ "vendor_id : " PROCESSOR_VENDOR_NAME "\n"
+ "cpu core : %s\n"
+ "cpu rev : %lu\n"
+ "model name : " PROCESSOR_MODEL_NAME "\n"
+ "icache size: %lu\n"
+ "dcache size: %lu\n",
+ cpu_id,
+ mn10300_cputypes[type],
+ (cpurev & CPUREV_REVISION) >> CPUREV_REVISION_S,
+ icachesz,
+ dcachesz
+ );
+
+ seq_printf(m,
+ "ioclk speed: %lu.%02luMHz\n"
+ "bogomips : %lu.%02lu\n\n",
+ MN10300_IOCLK / 1000000,
+ (MN10300_IOCLK / 10000) % 100,
+#ifdef CONFIG_SMP
+ c->loops_per_jiffy / (500000 / HZ),
+ (c->loops_per_jiffy / (5000 / HZ)) % 100
+#else /* CONFIG_SMP */
+ loops_per_jiffy / (500000 / HZ),
+ (loops_per_jiffy / (5000 / HZ)) % 100
+#endif /* CONFIG_SMP */
+ );
+
+ 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)
+{
+}
+
+const struct seq_operations cpuinfo_op = {
+ .start = c_start,
+ .next = c_next,
+ .stop = c_stop,
+ .show = show_cpuinfo,
+};
diff --git a/arch/mn10300/kernel/sigframe.h b/arch/mn10300/kernel/sigframe.h
new file mode 100644
index 000000000..0decba28a
--- /dev/null
+++ b/arch/mn10300/kernel/sigframe.h
@@ -0,0 +1,33 @@
+/* MN10300 Signal frame definitions
+ *
+ * Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd.
+ * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+struct sigframe
+{
+ void (*pretcode)(void);
+ int sig;
+ struct sigcontext *psc;
+ struct sigcontext sc;
+ struct fpucontext fpuctx;
+ unsigned long extramask[_NSIG_WORDS-1];
+ char retcode[8];
+};
+
+struct rt_sigframe
+{
+ void (*pretcode)(void);
+ int sig;
+ struct siginfo *pinfo;
+ void *puc;
+ struct siginfo info;
+ struct ucontext uc;
+ struct fpucontext fpuctx;
+ char retcode[8];
+};
diff --git a/arch/mn10300/kernel/signal.c b/arch/mn10300/kernel/signal.c
new file mode 100644
index 000000000..dfd0301cf
--- /dev/null
+++ b/arch/mn10300/kernel/signal.c
@@ -0,0 +1,431 @@
+/* MN10300 Signal handling
+ *
+ * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/smp.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/suspend.h>
+#include <linux/tracehook.h>
+#include <asm/cacheflush.h>
+#include <asm/ucontext.h>
+#include <asm/uaccess.h>
+#include <asm/fpu.h>
+#include "sigframe.h"
+
+#define DEBUG_SIG 0
+
+/*
+ * do a signal return; undo the signal stack.
+ */
+static int restore_sigcontext(struct pt_regs *regs,
+ struct sigcontext __user *sc, long *_d0)
+{
+ unsigned int err = 0;
+
+ /* Always make any pending restarted system calls return -EINTR */
+ current->restart_block.fn = do_no_restart_syscall;
+
+ if (is_using_fpu(current))
+ fpu_kill_state(current);
+
+#define COPY(x) err |= __get_user(regs->x, &sc->x)
+ COPY(d1); COPY(d2); COPY(d3);
+ COPY(a0); COPY(a1); COPY(a2); COPY(a3);
+ COPY(e0); COPY(e1); COPY(e2); COPY(e3);
+ COPY(e4); COPY(e5); COPY(e6); COPY(e7);
+ COPY(lar); COPY(lir);
+ COPY(mdr); COPY(mdrq);
+ COPY(mcvf); COPY(mcrl); COPY(mcrh);
+ COPY(sp); COPY(pc);
+#undef COPY
+
+ {
+ unsigned int tmpflags;
+#ifndef CONFIG_MN10300_USING_JTAG
+#define USER_EPSW (EPSW_FLAG_Z | EPSW_FLAG_N | EPSW_FLAG_C | EPSW_FLAG_V | \
+ EPSW_T | EPSW_nAR)
+#else
+#define USER_EPSW (EPSW_FLAG_Z | EPSW_FLAG_N | EPSW_FLAG_C | EPSW_FLAG_V | \
+ EPSW_nAR)
+#endif
+ err |= __get_user(tmpflags, &sc->epsw);
+ regs->epsw = (regs->epsw & ~USER_EPSW) |
+ (tmpflags & USER_EPSW);
+ regs->orig_d0 = -1; /* disable syscall checks */
+ }
+
+ {
+ struct fpucontext *buf;
+ err |= __get_user(buf, &sc->fpucontext);
+ if (buf) {
+ if (verify_area(VERIFY_READ, buf, sizeof(*buf)))
+ goto badframe;
+ err |= fpu_restore_sigcontext(buf);
+ }
+ }
+
+ err |= __get_user(*_d0, &sc->d0);
+ return err;
+
+badframe:
+ return 1;
+}
+
+/*
+ * standard signal return syscall
+ */
+asmlinkage long sys_sigreturn(void)
+{
+ struct sigframe __user *frame;
+ sigset_t set;
+ long d0;
+
+ frame = (struct sigframe __user *) current_frame()->sp;
+ if (verify_area(VERIFY_READ, frame, sizeof(*frame)))
+ goto badframe;
+ if (__get_user(set.sig[0], &frame->sc.oldmask))
+ goto badframe;
+
+ if (_NSIG_WORDS > 1 &&
+ __copy_from_user(&set.sig[1], &frame->extramask,
+ sizeof(frame->extramask)))
+ goto badframe;
+
+ set_current_blocked(&set);
+
+ if (restore_sigcontext(current_frame(), &frame->sc, &d0))
+ goto badframe;
+
+ return d0;
+
+badframe:
+ force_sig(SIGSEGV, current);
+ return 0;
+}
+
+/*
+ * realtime signal return syscall
+ */
+asmlinkage long sys_rt_sigreturn(void)
+{
+ struct rt_sigframe __user *frame;
+ sigset_t set;
+ long d0;
+
+ frame = (struct rt_sigframe __user *) current_frame()->sp;
+ if (verify_area(VERIFY_READ, frame, sizeof(*frame)))
+ goto badframe;
+ if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set)))
+ goto badframe;
+
+ set_current_blocked(&set);
+
+ if (restore_sigcontext(current_frame(), &frame->uc.uc_mcontext, &d0))
+ goto badframe;
+
+ if (restore_altstack(&frame->uc.uc_stack))
+ goto badframe;
+
+ return d0;
+
+badframe:
+ force_sig(SIGSEGV, current);
+ return 0;
+}
+
+/*
+ * store the userspace context into a signal frame
+ */
+static int setup_sigcontext(struct sigcontext __user *sc,
+ struct fpucontext *fpuctx,
+ struct pt_regs *regs,
+ unsigned long mask)
+{
+ int tmp, err = 0;
+
+#define COPY(x) err |= __put_user(regs->x, &sc->x)
+ COPY(d0); COPY(d1); COPY(d2); COPY(d3);
+ COPY(a0); COPY(a1); COPY(a2); COPY(a3);
+ COPY(e0); COPY(e1); COPY(e2); COPY(e3);
+ COPY(e4); COPY(e5); COPY(e6); COPY(e7);
+ COPY(lar); COPY(lir);
+ COPY(mdr); COPY(mdrq);
+ COPY(mcvf); COPY(mcrl); COPY(mcrh);
+ COPY(sp); COPY(epsw); COPY(pc);
+#undef COPY
+
+ tmp = fpu_setup_sigcontext(fpuctx);
+ if (tmp < 0)
+ err = 1;
+ else
+ err |= __put_user(tmp ? fpuctx : NULL, &sc->fpucontext);
+
+ /* non-iBCS2 extensions.. */
+ err |= __put_user(mask, &sc->oldmask);
+
+ return err;
+}
+
+/*
+ * determine which stack to use..
+ */
+static inline void __user *get_sigframe(struct ksignal *ksig,
+ struct pt_regs *regs,
+ size_t frame_size)
+{
+ unsigned long sp = sigsp(regs->sp, ksig);
+
+ return (void __user *) ((sp - frame_size) & ~7UL);
+}
+
+/*
+ * set up a normal signal frame
+ */
+static int setup_frame(struct ksignal *ksig, sigset_t *set,
+ struct pt_regs *regs)
+{
+ struct sigframe __user *frame;
+ int sig = ksig->sig;
+
+ frame = get_sigframe(ksig, regs, sizeof(*frame));
+
+ if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
+ return -EFAULT;
+
+ if (__put_user(sig, &frame->sig) < 0 ||
+ __put_user(&frame->sc, &frame->psc) < 0)
+ return -EFAULT;
+
+ if (setup_sigcontext(&frame->sc, &frame->fpuctx, regs, set->sig[0]))
+ return -EFAULT;
+
+ if (_NSIG_WORDS > 1) {
+ if (__copy_to_user(frame->extramask, &set->sig[1],
+ sizeof(frame->extramask)))
+ return -EFAULT;
+ }
+
+ /* set up to return from userspace. If provided, use a stub already in
+ * userspace */
+ if (ksig->ka.sa.sa_flags & SA_RESTORER) {
+ if (__put_user(ksig->ka.sa.sa_restorer, &frame->pretcode))
+ return -EFAULT;
+ } else {
+ if (__put_user((void (*)(void))frame->retcode,
+ &frame->pretcode))
+ return -EFAULT;
+ /* this is mov $,d0; syscall 0 */
+ if (__put_user(0x2c, (char *)(frame->retcode + 0)) ||
+ __put_user(__NR_sigreturn, (char *)(frame->retcode + 1)) ||
+ __put_user(0x00, (char *)(frame->retcode + 2)) ||
+ __put_user(0xf0, (char *)(frame->retcode + 3)) ||
+ __put_user(0xe0, (char *)(frame->retcode + 4)))
+ return -EFAULT;
+ flush_icache_range((unsigned long) frame->retcode,
+ (unsigned long) frame->retcode + 5);
+ }
+
+ /* set up registers for signal handler */
+ regs->sp = (unsigned long) frame;
+ regs->pc = (unsigned long) ksig->ka.sa.sa_handler;
+ regs->d0 = sig;
+ regs->d1 = (unsigned long) &frame->sc;
+
+#if DEBUG_SIG
+ printk(KERN_DEBUG "SIG deliver %d (%s:%d): sp=%p pc=%lx ra=%p\n",
+ sig, current->comm, current->pid, frame, regs->pc,
+ frame->pretcode);
+#endif
+
+ return 0;
+}
+
+/*
+ * set up a realtime signal frame
+ */
+static int setup_rt_frame(struct ksignal *ksig, sigset_t *set,
+ struct pt_regs *regs)
+{
+ struct rt_sigframe __user *frame;
+ int sig = ksig->sig;
+
+ frame = get_sigframe(ksig, regs, sizeof(*frame));
+
+ if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
+ return -EFAULT;
+
+ if (__put_user(sig, &frame->sig) ||
+ __put_user(&frame->info, &frame->pinfo) ||
+ __put_user(&frame->uc, &frame->puc) ||
+ copy_siginfo_to_user(&frame->info, &ksig->info))
+ return -EFAULT;
+
+ /* create the ucontext. */
+ if (__put_user(0, &frame->uc.uc_flags) ||
+ __put_user(0, &frame->uc.uc_link) ||
+ __save_altstack(&frame->uc.uc_stack, regs->sp) ||
+ setup_sigcontext(&frame->uc.uc_mcontext,
+ &frame->fpuctx, regs, set->sig[0]) ||
+ __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)))
+ return -EFAULT;
+
+ /* set up to return from userspace. If provided, use a stub already in
+ * userspace */
+ if (ksig->ka.sa.sa_flags & SA_RESTORER) {
+ if (__put_user(ksig->ka.sa.sa_restorer, &frame->pretcode))
+ return -EFAULT;
+
+ } else {
+ if (__put_user((void(*)(void))frame->retcode,
+ &frame->pretcode) ||
+ /* This is mov $,d0; syscall 0 */
+ __put_user(0x2c, (char *)(frame->retcode + 0)) ||
+ __put_user(__NR_rt_sigreturn,
+ (char *)(frame->retcode + 1)) ||
+ __put_user(0x00, (char *)(frame->retcode + 2)) ||
+ __put_user(0xf0, (char *)(frame->retcode + 3)) ||
+ __put_user(0xe0, (char *)(frame->retcode + 4)))
+ return -EFAULT;
+
+ flush_icache_range((u_long) frame->retcode,
+ (u_long) frame->retcode + 5);
+ }
+
+ /* Set up registers for signal handler */
+ regs->sp = (unsigned long) frame;
+ regs->pc = (unsigned long) ksig->ka.sa.sa_handler;
+ regs->d0 = sig;
+ regs->d1 = (long) &frame->info;
+
+#if DEBUG_SIG
+ printk(KERN_DEBUG "SIG deliver %d (%s:%d): sp=%p pc=%lx ra=%p\n",
+ sig, current->comm, current->pid, frame, regs->pc,
+ frame->pretcode);
+#endif
+
+ return 0;
+}
+
+static inline void stepback(struct pt_regs *regs)
+{
+ regs->pc -= 2;
+ regs->orig_d0 = -1;
+}
+
+/*
+ * handle the actual delivery of a signal to userspace
+ */
+static int handle_signal(struct ksignal *ksig, struct pt_regs *regs)
+{
+ sigset_t *oldset = sigmask_to_save();
+ int ret;
+
+ /* Are we from a system call? */
+ if (regs->orig_d0 >= 0) {
+ /* If so, check system call restarting.. */
+ switch (regs->d0) {
+ case -ERESTART_RESTARTBLOCK:
+ case -ERESTARTNOHAND:
+ regs->d0 = -EINTR;
+ break;
+
+ case -ERESTARTSYS:
+ if (!(ksig->ka.sa.sa_flags & SA_RESTART)) {
+ regs->d0 = -EINTR;
+ break;
+ }
+
+ /* fallthrough */
+ case -ERESTARTNOINTR:
+ regs->d0 = regs->orig_d0;
+ stepback(regs);
+ }
+ }
+
+ /* Set up the stack frame */
+ if (ksig->ka.sa.sa_flags & SA_SIGINFO)
+ ret = setup_rt_frame(ksig, oldset, regs);
+ else
+ ret = setup_frame(ksig, oldset, regs);
+
+ signal_setup_done(ret, ksig, test_thread_flag(TIF_SINGLESTEP));
+ return 0;
+}
+
+/*
+ * handle a potential signal
+ */
+static void do_signal(struct pt_regs *regs)
+{
+ struct ksignal ksig;
+
+ if (get_signal(&ksig)) {
+ handle_signal(&ksig, regs);
+ return;
+ }
+
+ /* did we come from a system call? */
+ if (regs->orig_d0 >= 0) {
+ /* restart the system call - no handlers present */
+ switch (regs->d0) {
+ case -ERESTARTNOHAND:
+ case -ERESTARTSYS:
+ case -ERESTARTNOINTR:
+ regs->d0 = regs->orig_d0;
+ stepback(regs);
+ break;
+
+ case -ERESTART_RESTARTBLOCK:
+ regs->d0 = __NR_restart_syscall;
+ stepback(regs);
+ break;
+ }
+ }
+
+ /* if there's no signal to deliver, we just put the saved sigmask
+ * back */
+ restore_saved_sigmask();
+}
+
+/*
+ * notification of userspace execution resumption
+ * - triggered by current->work.notify_resume
+ */
+asmlinkage void do_notify_resume(struct pt_regs *regs, u32 thread_info_flags)
+{
+ /* Pending single-step? */
+ if (thread_info_flags & _TIF_SINGLESTEP) {
+#ifndef CONFIG_MN10300_USING_JTAG
+ regs->epsw |= EPSW_T;
+ clear_thread_flag(TIF_SINGLESTEP);
+#else
+ BUG(); /* no h/w single-step if using JTAG unit */
+#endif
+ }
+
+ /* deal with pending signal delivery */
+ if (thread_info_flags & _TIF_SIGPENDING)
+ do_signal(regs);
+
+ if (thread_info_flags & _TIF_NOTIFY_RESUME) {
+ clear_thread_flag(TIF_NOTIFY_RESUME);
+ tracehook_notify_resume(current_frame());
+ }
+}
diff --git a/arch/mn10300/kernel/smp-low.S b/arch/mn10300/kernel/smp-low.S
new file mode 100644
index 000000000..71f1b2faa
--- /dev/null
+++ b/arch/mn10300/kernel/smp-low.S
@@ -0,0 +1,97 @@
+/* SMP IPI low-level handler
+ *
+ * Copyright (C) 2006-2007 Matsushita Electric Industrial Co., Ltd.
+ * All Rights Reserved.
+ *
+ * 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/sys.h>
+#include <linux/linkage.h>
+#include <asm/smp.h>
+#include <asm/thread_info.h>
+#include <asm/cpu-regs.h>
+#include <asm/intctl-regs.h>
+#include <proc/smp-regs.h>
+#include <asm/asm-offsets.h>
+#include <asm/frame.inc>
+
+ .am33_2
+
+###############################################################################
+#
+# IPI interrupt handler
+#
+###############################################################################
+ .globl mn10300_low_ipi_handler
+mn10300_low_ipi_handler:
+ add -4,sp
+ mov d0,(sp)
+ movhu (IAGR),d0
+ and IAGR_GN,d0
+ lsr 0x2,d0
+#ifdef CONFIG_MN10300_CACHE_ENABLED
+ cmp FLUSH_CACHE_IPI,d0
+ beq mn10300_flush_cache_ipi
+#endif
+ cmp SMP_BOOT_IRQ,d0
+ beq mn10300_smp_boot_ipi
+ /* OTHERS */
+ mov (sp),d0
+ add 4,sp
+#ifdef CONFIG_GDBSTUB
+ jmp gdbstub_io_rx_handler
+#else
+ jmp end
+#endif
+
+###############################################################################
+#
+# Cache flush IPI interrupt handler
+#
+###############################################################################
+#ifdef CONFIG_MN10300_CACHE_ENABLED
+mn10300_flush_cache_ipi:
+ mov (sp),d0
+ add 4,sp
+
+ /* FLUSH_CACHE_IPI */
+ add -4,sp
+ SAVE_ALL
+ mov GxICR_DETECT,d2
+ movbu d2,(GxICR(FLUSH_CACHE_IPI)) # ACK the interrupt
+ movhu (GxICR(FLUSH_CACHE_IPI)),d2
+ call smp_cache_interrupt[],0
+ RESTORE_ALL
+ jmp end
+#endif
+
+###############################################################################
+#
+# SMP boot CPU IPI interrupt handler
+#
+###############################################################################
+mn10300_smp_boot_ipi:
+ /* clear interrupt */
+ movhu (GxICR(SMP_BOOT_IRQ)),d0
+ and ~GxICR_REQUEST,d0
+ movhu d0,(GxICR(SMP_BOOT_IRQ))
+ mov (sp),d0
+ add 4,sp
+
+ # get stack
+ mov (CPUID),a0
+ add -1,a0
+ add a0,a0
+ add a0,a0
+ mov (start_stack,a0),a0
+ mov a0,sp
+ jmp initialize_secondary
+
+
+# Jump here after RTI to suppress the icache lookahead
+end:
diff --git a/arch/mn10300/kernel/smp.c b/arch/mn10300/kernel/smp.c
new file mode 100644
index 000000000..f98419371
--- /dev/null
+++ b/arch/mn10300/kernel/smp.c
@@ -0,0 +1,1185 @@
+/* SMP support routines.
+ *
+ * Copyright (C) 2006-2008 Panasonic Corporation
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/init.h>
+#include <linux/jiffies.h>
+#include <linux/cpumask.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/profile.h>
+#include <linux/smp.h>
+#include <linux/cpu.h>
+#include <asm/tlbflush.h>
+#include <asm/bitops.h>
+#include <asm/processor.h>
+#include <asm/bug.h>
+#include <asm/exceptions.h>
+#include <asm/hardirq.h>
+#include <asm/fpu.h>
+#include <asm/mmu_context.h>
+#include <asm/thread_info.h>
+#include <asm/cpu-regs.h>
+#include <asm/intctl-regs.h>
+#include "internal.h"
+
+#ifdef CONFIG_HOTPLUG_CPU
+#include <asm/cacheflush.h>
+
+static unsigned long sleep_mode[NR_CPUS];
+
+static void run_sleep_cpu(unsigned int cpu);
+static void run_wakeup_cpu(unsigned int cpu);
+#endif /* CONFIG_HOTPLUG_CPU */
+
+/*
+ * Debug Message function
+ */
+
+#undef DEBUG_SMP
+#ifdef DEBUG_SMP
+#define Dprintk(fmt, ...) printk(KERN_DEBUG fmt, ##__VA_ARGS__)
+#else
+#define Dprintk(fmt, ...) no_printk(KERN_DEBUG fmt, ##__VA_ARGS__)
+#endif
+
+/* timeout value in msec for smp_nmi_call_function. zero is no timeout. */
+#define CALL_FUNCTION_NMI_IPI_TIMEOUT 0
+
+/*
+ * Structure and data for smp_nmi_call_function().
+ */
+struct nmi_call_data_struct {
+ smp_call_func_t func;
+ void *info;
+ cpumask_t started;
+ cpumask_t finished;
+ int wait;
+ char size_alignment[0]
+ __attribute__ ((__aligned__(SMP_CACHE_BYTES)));
+} __attribute__ ((__aligned__(SMP_CACHE_BYTES)));
+
+static DEFINE_SPINLOCK(smp_nmi_call_lock);
+static struct nmi_call_data_struct *nmi_call_data;
+
+/*
+ * Data structures and variables
+ */
+static cpumask_t cpu_callin_map; /* Bitmask of callin CPUs */
+static cpumask_t cpu_callout_map; /* Bitmask of callout CPUs */
+cpumask_t cpu_boot_map; /* Bitmask of boot APs */
+unsigned long start_stack[NR_CPUS - 1];
+
+/*
+ * Per CPU parameters
+ */
+struct mn10300_cpuinfo cpu_data[NR_CPUS] __cacheline_aligned;
+
+static int cpucount; /* The count of boot CPUs */
+static cpumask_t smp_commenced_mask;
+cpumask_t cpu_initialized __initdata = CPU_MASK_NONE;
+
+/*
+ * Function Prototypes
+ */
+static int do_boot_cpu(int);
+static void smp_show_cpu_info(int cpu_id);
+static void smp_callin(void);
+static void smp_online(void);
+static void smp_store_cpu_info(int);
+static void smp_cpu_init(void);
+static void smp_tune_scheduling(void);
+static void send_IPI_mask(const cpumask_t *cpumask, int irq);
+static void init_ipi(void);
+
+/*
+ * IPI Initialization interrupt definitions
+ */
+static void mn10300_ipi_disable(unsigned int irq);
+static void mn10300_ipi_enable(unsigned int irq);
+static void mn10300_ipi_chip_disable(struct irq_data *d);
+static void mn10300_ipi_chip_enable(struct irq_data *d);
+static void mn10300_ipi_ack(struct irq_data *d);
+static void mn10300_ipi_nop(struct irq_data *d);
+
+static struct irq_chip mn10300_ipi_type = {
+ .name = "cpu_ipi",
+ .irq_disable = mn10300_ipi_chip_disable,
+ .irq_enable = mn10300_ipi_chip_enable,
+ .irq_ack = mn10300_ipi_ack,
+ .irq_eoi = mn10300_ipi_nop
+};
+
+static irqreturn_t smp_reschedule_interrupt(int irq, void *dev_id);
+static irqreturn_t smp_call_function_interrupt(int irq, void *dev_id);
+
+static struct irqaction reschedule_ipi = {
+ .handler = smp_reschedule_interrupt,
+ .flags = IRQF_NOBALANCING,
+ .name = "smp reschedule IPI"
+};
+static struct irqaction call_function_ipi = {
+ .handler = smp_call_function_interrupt,
+ .flags = IRQF_NOBALANCING,
+ .name = "smp call function IPI"
+};
+
+#if !defined(CONFIG_GENERIC_CLOCKEVENTS) || defined(CONFIG_GENERIC_CLOCKEVENTS_BROADCAST)
+static irqreturn_t smp_ipi_timer_interrupt(int irq, void *dev_id);
+static struct irqaction local_timer_ipi = {
+ .handler = smp_ipi_timer_interrupt,
+ .flags = IRQF_NOBALANCING,
+ .name = "smp local timer IPI"
+};
+#endif
+
+/**
+ * init_ipi - Initialise the IPI mechanism
+ */
+static void init_ipi(void)
+{
+ unsigned long flags;
+ u16 tmp16;
+
+ /* set up the reschedule IPI */
+ irq_set_chip_and_handler(RESCHEDULE_IPI, &mn10300_ipi_type,
+ handle_percpu_irq);
+ setup_irq(RESCHEDULE_IPI, &reschedule_ipi);
+ set_intr_level(RESCHEDULE_IPI, RESCHEDULE_GxICR_LV);
+ mn10300_ipi_enable(RESCHEDULE_IPI);
+
+ /* set up the call function IPI */
+ irq_set_chip_and_handler(CALL_FUNC_SINGLE_IPI, &mn10300_ipi_type,
+ handle_percpu_irq);
+ setup_irq(CALL_FUNC_SINGLE_IPI, &call_function_ipi);
+ set_intr_level(CALL_FUNC_SINGLE_IPI, CALL_FUNCTION_GxICR_LV);
+ mn10300_ipi_enable(CALL_FUNC_SINGLE_IPI);
+
+ /* set up the local timer IPI */
+#if !defined(CONFIG_GENERIC_CLOCKEVENTS) || \
+ defined(CONFIG_GENERIC_CLOCKEVENTS_BROADCAST)
+ irq_set_chip_and_handler(LOCAL_TIMER_IPI, &mn10300_ipi_type,
+ handle_percpu_irq);
+ setup_irq(LOCAL_TIMER_IPI, &local_timer_ipi);
+ set_intr_level(LOCAL_TIMER_IPI, LOCAL_TIMER_GxICR_LV);
+ mn10300_ipi_enable(LOCAL_TIMER_IPI);
+#endif
+
+#ifdef CONFIG_MN10300_CACHE_ENABLED
+ /* set up the cache flush IPI */
+ irq_set_chip(FLUSH_CACHE_IPI, &mn10300_ipi_type);
+ flags = arch_local_cli_save();
+ __set_intr_stub(NUM2EXCEP_IRQ_LEVEL(FLUSH_CACHE_GxICR_LV),
+ mn10300_low_ipi_handler);
+ GxICR(FLUSH_CACHE_IPI) = FLUSH_CACHE_GxICR_LV | GxICR_DETECT;
+ mn10300_ipi_enable(FLUSH_CACHE_IPI);
+ arch_local_irq_restore(flags);
+#endif
+
+ /* set up the NMI call function IPI */
+ irq_set_chip(CALL_FUNCTION_NMI_IPI, &mn10300_ipi_type);
+ flags = arch_local_cli_save();
+ GxICR(CALL_FUNCTION_NMI_IPI) = GxICR_NMI | GxICR_ENABLE | GxICR_DETECT;
+ tmp16 = GxICR(CALL_FUNCTION_NMI_IPI);
+ arch_local_irq_restore(flags);
+
+ /* set up the SMP boot IPI */
+ flags = arch_local_cli_save();
+ __set_intr_stub(NUM2EXCEP_IRQ_LEVEL(SMP_BOOT_GxICR_LV),
+ mn10300_low_ipi_handler);
+ arch_local_irq_restore(flags);
+
+#ifdef CONFIG_KERNEL_DEBUGGER
+ irq_set_chip(DEBUGGER_NMI_IPI, &mn10300_ipi_type);
+#endif
+}
+
+/**
+ * mn10300_ipi_shutdown - Shut down handling of an IPI
+ * @irq: The IPI to be shut down.
+ */
+static void mn10300_ipi_shutdown(unsigned int irq)
+{
+ unsigned long flags;
+ u16 tmp;
+
+ flags = arch_local_cli_save();
+
+ tmp = GxICR(irq);
+ GxICR(irq) = (tmp & GxICR_LEVEL) | GxICR_DETECT;
+ tmp = GxICR(irq);
+
+ arch_local_irq_restore(flags);
+}
+
+/**
+ * mn10300_ipi_enable - Enable an IPI
+ * @irq: The IPI to be enabled.
+ */
+static void mn10300_ipi_enable(unsigned int irq)
+{
+ unsigned long flags;
+ u16 tmp;
+
+ flags = arch_local_cli_save();
+
+ tmp = GxICR(irq);
+ GxICR(irq) = (tmp & GxICR_LEVEL) | GxICR_ENABLE;
+ tmp = GxICR(irq);
+
+ arch_local_irq_restore(flags);
+}
+
+static void mn10300_ipi_chip_enable(struct irq_data *d)
+{
+ mn10300_ipi_enable(d->irq);
+}
+
+/**
+ * mn10300_ipi_disable - Disable an IPI
+ * @irq: The IPI to be disabled.
+ */
+static void mn10300_ipi_disable(unsigned int irq)
+{
+ unsigned long flags;
+ u16 tmp;
+
+ flags = arch_local_cli_save();
+
+ tmp = GxICR(irq);
+ GxICR(irq) = tmp & GxICR_LEVEL;
+ tmp = GxICR(irq);
+
+ arch_local_irq_restore(flags);
+}
+
+static void mn10300_ipi_chip_disable(struct irq_data *d)
+{
+ mn10300_ipi_disable(d->irq);
+}
+
+
+/**
+ * mn10300_ipi_ack - Acknowledge an IPI interrupt in the PIC
+ * @irq: The IPI to be acknowledged.
+ *
+ * Clear the interrupt detection flag for the IPI on the appropriate interrupt
+ * channel in the PIC.
+ */
+static void mn10300_ipi_ack(struct irq_data *d)
+{
+ unsigned int irq = d->irq;
+ unsigned long flags;
+ u16 tmp;
+
+ flags = arch_local_cli_save();
+ GxICR_u8(irq) = GxICR_DETECT;
+ tmp = GxICR(irq);
+ arch_local_irq_restore(flags);
+}
+
+/**
+ * mn10300_ipi_nop - Dummy IPI action
+ * @irq: The IPI to be acted upon.
+ */
+static void mn10300_ipi_nop(struct irq_data *d)
+{
+}
+
+/**
+ * send_IPI_mask - Send IPIs to all CPUs in list
+ * @cpumask: The list of CPUs to target.
+ * @irq: The IPI request to be sent.
+ *
+ * Send the specified IPI to all the CPUs in the list, not waiting for them to
+ * finish before returning. The caller is responsible for synchronisation if
+ * that is needed.
+ */
+static void send_IPI_mask(const cpumask_t *cpumask, int irq)
+{
+ int i;
+ u16 tmp;
+
+ for (i = 0; i < NR_CPUS; i++) {
+ if (cpumask_test_cpu(i, cpumask)) {
+ /* send IPI */
+ tmp = CROSS_GxICR(irq, i);
+ CROSS_GxICR(irq, i) =
+ tmp | GxICR_REQUEST | GxICR_DETECT;
+ tmp = CROSS_GxICR(irq, i); /* flush write buffer */
+ }
+ }
+}
+
+/**
+ * send_IPI_self - Send an IPI to this CPU.
+ * @irq: The IPI request to be sent.
+ *
+ * Send the specified IPI to the current CPU.
+ */
+void send_IPI_self(int irq)
+{
+ send_IPI_mask(cpumask_of(smp_processor_id()), irq);
+}
+
+/**
+ * send_IPI_allbutself - Send IPIs to all the other CPUs.
+ * @irq: The IPI request to be sent.
+ *
+ * Send the specified IPI to all CPUs in the system barring the current one,
+ * not waiting for them to finish before returning. The caller is responsible
+ * for synchronisation if that is needed.
+ */
+void send_IPI_allbutself(int irq)
+{
+ cpumask_t cpumask;
+
+ cpumask_copy(&cpumask, cpu_online_mask);
+ cpumask_clear_cpu(smp_processor_id(), &cpumask);
+ send_IPI_mask(&cpumask, irq);
+}
+
+void arch_send_call_function_ipi_mask(const struct cpumask *mask)
+{
+ BUG();
+ /*send_IPI_mask(mask, CALL_FUNCTION_IPI);*/
+}
+
+void arch_send_call_function_single_ipi(int cpu)
+{
+ send_IPI_mask(cpumask_of(cpu), CALL_FUNC_SINGLE_IPI);
+}
+
+/**
+ * smp_send_reschedule - Send reschedule IPI to a CPU
+ * @cpu: The CPU to target.
+ */
+void smp_send_reschedule(int cpu)
+{
+ send_IPI_mask(cpumask_of(cpu), RESCHEDULE_IPI);
+}
+
+/**
+ * smp_nmi_call_function - Send a call function NMI IPI to all CPUs
+ * @func: The function to ask to be run.
+ * @info: The context data to pass to that function.
+ * @wait: If true, wait (atomically) until function is run on all CPUs.
+ *
+ * Send a non-maskable request to all CPUs in the system, requesting them to
+ * run the specified function with the given context data, and, potentially, to
+ * wait for completion of that function on all CPUs.
+ *
+ * Returns 0 if successful, -ETIMEDOUT if we were asked to wait, but hit the
+ * timeout.
+ */
+int smp_nmi_call_function(smp_call_func_t func, void *info, int wait)
+{
+ struct nmi_call_data_struct data;
+ unsigned long flags;
+ unsigned int cnt;
+ int cpus, ret = 0;
+
+ cpus = num_online_cpus() - 1;
+ if (cpus < 1)
+ return 0;
+
+ data.func = func;
+ data.info = info;
+ cpumask_copy(&data.started, cpu_online_mask);
+ cpumask_clear_cpu(smp_processor_id(), &data.started);
+ data.wait = wait;
+ if (wait)
+ data.finished = data.started;
+
+ spin_lock_irqsave(&smp_nmi_call_lock, flags);
+ nmi_call_data = &data;
+ smp_mb();
+
+ /* Send a message to all other CPUs and wait for them to respond */
+ send_IPI_allbutself(CALL_FUNCTION_NMI_IPI);
+
+ /* Wait for response */
+ if (CALL_FUNCTION_NMI_IPI_TIMEOUT > 0) {
+ for (cnt = 0;
+ cnt < CALL_FUNCTION_NMI_IPI_TIMEOUT &&
+ !cpumask_empty(&data.started);
+ cnt++)
+ mdelay(1);
+
+ if (wait && cnt < CALL_FUNCTION_NMI_IPI_TIMEOUT) {
+ for (cnt = 0;
+ cnt < CALL_FUNCTION_NMI_IPI_TIMEOUT &&
+ !cpumask_empty(&data.finished);
+ cnt++)
+ mdelay(1);
+ }
+
+ if (cnt >= CALL_FUNCTION_NMI_IPI_TIMEOUT)
+ ret = -ETIMEDOUT;
+
+ } else {
+ /* If timeout value is zero, wait until cpumask has been
+ * cleared */
+ while (!cpumask_empty(&data.started))
+ barrier();
+ if (wait)
+ while (!cpumask_empty(&data.finished))
+ barrier();
+ }
+
+ spin_unlock_irqrestore(&smp_nmi_call_lock, flags);
+ return ret;
+}
+
+/**
+ * smp_jump_to_debugger - Make other CPUs enter the debugger by sending an IPI
+ *
+ * Send a non-maskable request to all other CPUs in the system, instructing
+ * them to jump into the debugger. The caller is responsible for checking that
+ * the other CPUs responded to the instruction.
+ *
+ * The caller should make sure that this CPU's debugger IPI is disabled.
+ */
+void smp_jump_to_debugger(void)
+{
+ if (num_online_cpus() > 1)
+ /* Send a message to all other CPUs */
+ send_IPI_allbutself(DEBUGGER_NMI_IPI);
+}
+
+/**
+ * stop_this_cpu - Callback to stop a CPU.
+ * @unused: Callback context (ignored).
+ */
+void stop_this_cpu(void *unused)
+{
+ static volatile int stopflag;
+ unsigned long flags;
+
+#ifdef CONFIG_GDBSTUB
+ /* In case of single stepping smp_send_stop by other CPU,
+ * clear procindebug to avoid deadlock.
+ */
+ atomic_set(&procindebug[smp_processor_id()], 0);
+#endif /* CONFIG_GDBSTUB */
+
+ flags = arch_local_cli_save();
+ set_cpu_online(smp_processor_id(), false);
+
+ while (!stopflag)
+ cpu_relax();
+
+ set_cpu_online(smp_processor_id(), true);
+ arch_local_irq_restore(flags);
+}
+
+/**
+ * smp_send_stop - Send a stop request to all CPUs.
+ */
+void smp_send_stop(void)
+{
+ smp_nmi_call_function(stop_this_cpu, NULL, 0);
+}
+
+/**
+ * smp_reschedule_interrupt - Reschedule IPI handler
+ * @irq: The interrupt number.
+ * @dev_id: The device ID.
+ *
+ * Returns IRQ_HANDLED to indicate we handled the interrupt successfully.
+ */
+static irqreturn_t smp_reschedule_interrupt(int irq, void *dev_id)
+{
+ scheduler_ipi();
+ return IRQ_HANDLED;
+}
+
+/**
+ * smp_call_function_interrupt - Call function IPI handler
+ * @irq: The interrupt number.
+ * @dev_id: The device ID.
+ *
+ * Returns IRQ_HANDLED to indicate we handled the interrupt successfully.
+ */
+static irqreturn_t smp_call_function_interrupt(int irq, void *dev_id)
+{
+ /* generic_smp_call_function_interrupt(); */
+ generic_smp_call_function_single_interrupt();
+ return IRQ_HANDLED;
+}
+
+/**
+ * smp_nmi_call_function_interrupt - Non-maskable call function IPI handler
+ */
+void smp_nmi_call_function_interrupt(void)
+{
+ smp_call_func_t func = nmi_call_data->func;
+ void *info = nmi_call_data->info;
+ int wait = nmi_call_data->wait;
+
+ /* Notify the initiating CPU that I've grabbed the data and am about to
+ * execute the function
+ */
+ smp_mb();
+ cpumask_clear_cpu(smp_processor_id(), &nmi_call_data->started);
+ (*func)(info);
+
+ if (wait) {
+ smp_mb();
+ cpumask_clear_cpu(smp_processor_id(),
+ &nmi_call_data->finished);
+ }
+}
+
+#if !defined(CONFIG_GENERIC_CLOCKEVENTS) || \
+ defined(CONFIG_GENERIC_CLOCKEVENTS_BROADCAST)
+/**
+ * smp_ipi_timer_interrupt - Local timer IPI handler
+ * @irq: The interrupt number.
+ * @dev_id: The device ID.
+ *
+ * Returns IRQ_HANDLED to indicate we handled the interrupt successfully.
+ */
+static irqreturn_t smp_ipi_timer_interrupt(int irq, void *dev_id)
+{
+ return local_timer_interrupt();
+}
+#endif
+
+void __init smp_init_cpus(void)
+{
+ int i;
+ for (i = 0; i < NR_CPUS; i++) {
+ set_cpu_possible(i, true);
+ set_cpu_present(i, true);
+ }
+}
+
+/**
+ * smp_cpu_init - Initialise AP in start_secondary.
+ *
+ * For this Application Processor, set up init_mm, initialise FPU and set
+ * interrupt level 0-6 setting.
+ */
+static void __init smp_cpu_init(void)
+{
+ unsigned long flags;
+ int cpu_id = smp_processor_id();
+ u16 tmp16;
+
+ if (test_and_set_bit(cpu_id, &cpu_initialized)) {
+ printk(KERN_WARNING "CPU#%d already initialized!\n", cpu_id);
+ for (;;)
+ local_irq_enable();
+ }
+ printk(KERN_INFO "Initializing CPU#%d\n", cpu_id);
+
+ atomic_inc(&init_mm.mm_count);
+ current->active_mm = &init_mm;
+ BUG_ON(current->mm);
+
+ enter_lazy_tlb(&init_mm, current);
+
+ /* Force FPU initialization */
+ clear_using_fpu(current);
+
+ GxICR(CALL_FUNC_SINGLE_IPI) = CALL_FUNCTION_GxICR_LV | GxICR_DETECT;
+ mn10300_ipi_enable(CALL_FUNC_SINGLE_IPI);
+
+ GxICR(LOCAL_TIMER_IPI) = LOCAL_TIMER_GxICR_LV | GxICR_DETECT;
+ mn10300_ipi_enable(LOCAL_TIMER_IPI);
+
+ GxICR(RESCHEDULE_IPI) = RESCHEDULE_GxICR_LV | GxICR_DETECT;
+ mn10300_ipi_enable(RESCHEDULE_IPI);
+
+#ifdef CONFIG_MN10300_CACHE_ENABLED
+ GxICR(FLUSH_CACHE_IPI) = FLUSH_CACHE_GxICR_LV | GxICR_DETECT;
+ mn10300_ipi_enable(FLUSH_CACHE_IPI);
+#endif
+
+ mn10300_ipi_shutdown(SMP_BOOT_IRQ);
+
+ /* Set up the non-maskable call function IPI */
+ flags = arch_local_cli_save();
+ GxICR(CALL_FUNCTION_NMI_IPI) = GxICR_NMI | GxICR_ENABLE | GxICR_DETECT;
+ tmp16 = GxICR(CALL_FUNCTION_NMI_IPI);
+ arch_local_irq_restore(flags);
+}
+
+/**
+ * smp_prepare_cpu_init - Initialise CPU in startup_secondary
+ *
+ * Set interrupt level 0-6 setting and init ICR of the kernel debugger.
+ */
+void smp_prepare_cpu_init(void)
+{
+ int loop;
+
+ /* Set the interrupt vector registers */
+ IVAR0 = EXCEP_IRQ_LEVEL0;
+ IVAR1 = EXCEP_IRQ_LEVEL1;
+ IVAR2 = EXCEP_IRQ_LEVEL2;
+ IVAR3 = EXCEP_IRQ_LEVEL3;
+ IVAR4 = EXCEP_IRQ_LEVEL4;
+ IVAR5 = EXCEP_IRQ_LEVEL5;
+ IVAR6 = EXCEP_IRQ_LEVEL6;
+
+ /* Disable all interrupts and set to priority 6 (lowest) */
+ for (loop = 0; loop < GxICR_NUM_IRQS; loop++)
+ GxICR(loop) = GxICR_LEVEL_6 | GxICR_DETECT;
+
+#ifdef CONFIG_KERNEL_DEBUGGER
+ /* initialise the kernel debugger interrupt */
+ do {
+ unsigned long flags;
+ u16 tmp16;
+
+ flags = arch_local_cli_save();
+ GxICR(DEBUGGER_NMI_IPI) = GxICR_NMI | GxICR_ENABLE | GxICR_DETECT;
+ tmp16 = GxICR(DEBUGGER_NMI_IPI);
+ arch_local_irq_restore(flags);
+ } while (0);
+#endif
+}
+
+/**
+ * start_secondary - Activate a secondary CPU (AP)
+ * @unused: Thread parameter (ignored).
+ */
+int __init start_secondary(void *unused)
+{
+ smp_cpu_init();
+ smp_callin();
+ while (!cpumask_test_cpu(smp_processor_id(), &smp_commenced_mask))
+ cpu_relax();
+
+ local_flush_tlb();
+ preempt_disable();
+ smp_online();
+
+#ifdef CONFIG_GENERIC_CLOCKEVENTS
+ init_clockevents();
+#endif
+ cpu_startup_entry(CPUHP_ONLINE);
+ return 0;
+}
+
+/**
+ * smp_prepare_cpus - Boot up secondary CPUs (APs)
+ * @max_cpus: Maximum number of CPUs to boot.
+ *
+ * Call do_boot_cpu, and boot up APs.
+ */
+void __init smp_prepare_cpus(unsigned int max_cpus)
+{
+ int phy_id;
+
+ /* Setup boot CPU information */
+ smp_store_cpu_info(0);
+ smp_tune_scheduling();
+
+ init_ipi();
+
+ /* If SMP should be disabled, then finish */
+ if (max_cpus == 0) {
+ printk(KERN_INFO "SMP mode deactivated.\n");
+ goto smp_done;
+ }
+
+ /* Boot secondary CPUs (for which phy_id > 0) */
+ for (phy_id = 0; phy_id < NR_CPUS; phy_id++) {
+ /* Don't boot primary CPU */
+ if (max_cpus <= cpucount + 1)
+ continue;
+ if (phy_id != 0)
+ do_boot_cpu(phy_id);
+ set_cpu_possible(phy_id, true);
+ smp_show_cpu_info(phy_id);
+ }
+
+smp_done:
+ Dprintk("Boot done.\n");
+}
+
+/**
+ * smp_store_cpu_info - Save a CPU's information
+ * @cpu: The CPU to save for.
+ *
+ * Save boot_cpu_data and jiffy for the specified CPU.
+ */
+static void __init smp_store_cpu_info(int cpu)
+{
+ struct mn10300_cpuinfo *ci = &cpu_data[cpu];
+
+ *ci = boot_cpu_data;
+ ci->loops_per_jiffy = loops_per_jiffy;
+ ci->type = CPUREV;
+}
+
+/**
+ * smp_tune_scheduling - Set time slice value
+ *
+ * Nothing to do here.
+ */
+static void __init smp_tune_scheduling(void)
+{
+}
+
+/**
+ * do_boot_cpu: Boot up one CPU
+ * @phy_id: Physical ID of CPU to boot.
+ *
+ * Send an IPI to a secondary CPU to boot it. Returns 0 on success, 1
+ * otherwise.
+ */
+static int __init do_boot_cpu(int phy_id)
+{
+ struct task_struct *idle;
+ unsigned long send_status, callin_status;
+ int timeout, cpu_id;
+
+ send_status = GxICR_REQUEST;
+ callin_status = 0;
+ timeout = 0;
+ cpu_id = phy_id;
+
+ cpucount++;
+
+ /* Create idle thread for this CPU */
+ idle = fork_idle(cpu_id);
+ if (IS_ERR(idle))
+ panic("Failed fork for CPU#%d.", cpu_id);
+
+ idle->thread.pc = (unsigned long)start_secondary;
+
+ printk(KERN_NOTICE "Booting CPU#%d\n", cpu_id);
+ start_stack[cpu_id - 1] = idle->thread.sp;
+
+ task_thread_info(idle)->cpu = cpu_id;
+
+ /* Send boot IPI to AP */
+ send_IPI_mask(cpumask_of(phy_id), SMP_BOOT_IRQ);
+
+ Dprintk("Waiting for send to finish...\n");
+
+ /* Wait for AP's IPI receive in 100[ms] */
+ do {
+ udelay(1000);
+ send_status =
+ CROSS_GxICR(SMP_BOOT_IRQ, phy_id) & GxICR_REQUEST;
+ } while (send_status == GxICR_REQUEST && timeout++ < 100);
+
+ Dprintk("Waiting for cpu_callin_map.\n");
+
+ if (send_status == 0) {
+ /* Allow AP to start initializing */
+ cpumask_set_cpu(cpu_id, &cpu_callout_map);
+
+ /* Wait for setting cpu_callin_map */
+ timeout = 0;
+ do {
+ udelay(1000);
+ callin_status = cpumask_test_cpu(cpu_id,
+ &cpu_callin_map);
+ } while (callin_status == 0 && timeout++ < 5000);
+
+ if (callin_status == 0)
+ Dprintk("Not responding.\n");
+ } else {
+ printk(KERN_WARNING "IPI not delivered.\n");
+ }
+
+ if (send_status == GxICR_REQUEST || callin_status == 0) {
+ cpumask_clear_cpu(cpu_id, &cpu_callout_map);
+ cpumask_clear_cpu(cpu_id, &cpu_callin_map);
+ cpumask_clear_cpu(cpu_id, &cpu_initialized);
+ cpucount--;
+ return 1;
+ }
+ return 0;
+}
+
+/**
+ * smp_show_cpu_info - Show SMP CPU information
+ * @cpu: The CPU of interest.
+ */
+static void __init smp_show_cpu_info(int cpu)
+{
+ struct mn10300_cpuinfo *ci = &cpu_data[cpu];
+
+ printk(KERN_INFO
+ "CPU#%d : ioclk speed: %lu.%02luMHz : bogomips : %lu.%02lu\n",
+ cpu,
+ MN10300_IOCLK / 1000000,
+ (MN10300_IOCLK / 10000) % 100,
+ ci->loops_per_jiffy / (500000 / HZ),
+ (ci->loops_per_jiffy / (5000 / HZ)) % 100);
+}
+
+/**
+ * smp_callin - Set cpu_callin_map of the current CPU ID
+ */
+static void __init smp_callin(void)
+{
+ unsigned long timeout;
+ int cpu;
+
+ cpu = smp_processor_id();
+ timeout = jiffies + (2 * HZ);
+
+ if (cpumask_test_cpu(cpu, &cpu_callin_map)) {
+ printk(KERN_ERR "CPU#%d already present.\n", cpu);
+ BUG();
+ }
+ Dprintk("CPU#%d waiting for CALLOUT\n", cpu);
+
+ /* Wait for AP startup 2s total */
+ while (time_before(jiffies, timeout)) {
+ if (cpumask_test_cpu(cpu, &cpu_callout_map))
+ break;
+ cpu_relax();
+ }
+
+ if (!time_before(jiffies, timeout)) {
+ printk(KERN_ERR
+ "BUG: CPU#%d started up but did not get a callout!\n",
+ cpu);
+ BUG();
+ }
+
+#ifdef CONFIG_CALIBRATE_DELAY
+ calibrate_delay(); /* Get our bogomips */
+#endif
+
+ /* Save our processor parameters */
+ smp_store_cpu_info(cpu);
+
+ /* Allow the boot processor to continue */
+ cpumask_set_cpu(cpu, &cpu_callin_map);
+}
+
+/**
+ * smp_online - Set cpu_online_mask
+ */
+static void __init smp_online(void)
+{
+ int cpu;
+
+ cpu = smp_processor_id();
+
+ notify_cpu_starting(cpu);
+
+ set_cpu_online(cpu, true);
+
+ local_irq_enable();
+}
+
+/**
+ * smp_cpus_done -
+ * @max_cpus: Maximum CPU count.
+ *
+ * Do nothing.
+ */
+void __init smp_cpus_done(unsigned int max_cpus)
+{
+}
+
+/*
+ * smp_prepare_boot_cpu - Set up stuff for the boot processor.
+ *
+ * Set up the cpu_online_mask, cpu_callout_map and cpu_callin_map of the boot
+ * processor (CPU 0).
+ */
+void smp_prepare_boot_cpu(void)
+{
+ cpumask_set_cpu(0, &cpu_callout_map);
+ cpumask_set_cpu(0, &cpu_callin_map);
+ current_thread_info()->cpu = 0;
+}
+
+/*
+ * initialize_secondary - Initialise a secondary CPU (Application Processor).
+ *
+ * Set SP register and jump to thread's PC address.
+ */
+void initialize_secondary(void)
+{
+ asm volatile (
+ "mov %0,sp \n"
+ "jmp (%1) \n"
+ :
+ : "a"(current->thread.sp), "a"(current->thread.pc));
+}
+
+/**
+ * __cpu_up - Set smp_commenced_mask for the nominated CPU
+ * @cpu: The target CPU.
+ */
+int __cpu_up(unsigned int cpu, struct task_struct *tidle)
+{
+ int timeout;
+
+#ifdef CONFIG_HOTPLUG_CPU
+ if (sleep_mode[cpu])
+ run_wakeup_cpu(cpu);
+#endif /* CONFIG_HOTPLUG_CPU */
+
+ cpumask_set_cpu(cpu, &smp_commenced_mask);
+
+ /* Wait 5s total for a response */
+ for (timeout = 0 ; timeout < 5000 ; timeout++) {
+ if (cpu_online(cpu))
+ break;
+ udelay(1000);
+ }
+
+ BUG_ON(!cpu_online(cpu));
+ return 0;
+}
+
+/**
+ * setup_profiling_timer - Set up the profiling timer
+ * @multiplier - The frequency multiplier to use
+ *
+ * The frequency of the profiling timer can be changed by writing a multiplier
+ * value into /proc/profile.
+ */
+int setup_profiling_timer(unsigned int multiplier)
+{
+ return -EINVAL;
+}
+
+/*
+ * CPU hotplug routines
+ */
+#ifdef CONFIG_HOTPLUG_CPU
+
+static DEFINE_PER_CPU(struct cpu, cpu_devices);
+
+static int __init topology_init(void)
+{
+ int cpu, ret;
+
+ for_each_cpu(cpu) {
+ ret = register_cpu(&per_cpu(cpu_devices, cpu), cpu, NULL);
+ if (ret)
+ printk(KERN_WARNING
+ "topology_init: register_cpu %d failed (%d)\n",
+ cpu, ret);
+ }
+ return 0;
+}
+
+subsys_initcall(topology_init);
+
+int __cpu_disable(void)
+{
+ int cpu = smp_processor_id();
+ if (cpu == 0)
+ return -EBUSY;
+
+ migrate_irqs();
+ cpumask_clear_cpu(cpu, &mm_cpumask(current->active_mm));
+ return 0;
+}
+
+void __cpu_die(unsigned int cpu)
+{
+ run_sleep_cpu(cpu);
+}
+
+#ifdef CONFIG_MN10300_CACHE_ENABLED
+static inline void hotplug_cpu_disable_cache(void)
+{
+ int tmp;
+ asm volatile(
+ " movhu (%1),%0 \n"
+ " and %2,%0 \n"
+ " movhu %0,(%1) \n"
+ "1: movhu (%1),%0 \n"
+ " btst %3,%0 \n"
+ " bne 1b \n"
+ : "=&r"(tmp)
+ : "a"(&CHCTR),
+ "i"(~(CHCTR_ICEN | CHCTR_DCEN)),
+ "i"(CHCTR_ICBUSY | CHCTR_DCBUSY)
+ : "memory", "cc");
+}
+
+static inline void hotplug_cpu_enable_cache(void)
+{
+ int tmp;
+ asm volatile(
+ "movhu (%1),%0 \n"
+ "or %2,%0 \n"
+ "movhu %0,(%1) \n"
+ : "=&r"(tmp)
+ : "a"(&CHCTR),
+ "i"(CHCTR_ICEN | CHCTR_DCEN)
+ : "memory", "cc");
+}
+
+static inline void hotplug_cpu_invalidate_cache(void)
+{
+ int tmp;
+ asm volatile (
+ "movhu (%1),%0 \n"
+ "or %2,%0 \n"
+ "movhu %0,(%1) \n"
+ : "=&r"(tmp)
+ : "a"(&CHCTR),
+ "i"(CHCTR_ICINV | CHCTR_DCINV)
+ : "cc");
+}
+
+#else /* CONFIG_MN10300_CACHE_ENABLED */
+#define hotplug_cpu_disable_cache() do {} while (0)
+#define hotplug_cpu_enable_cache() do {} while (0)
+#define hotplug_cpu_invalidate_cache() do {} while (0)
+#endif /* CONFIG_MN10300_CACHE_ENABLED */
+
+/**
+ * hotplug_cpu_nmi_call_function - Call a function on other CPUs for hotplug
+ * @cpumask: List of target CPUs.
+ * @func: The function to call on those CPUs.
+ * @info: The context data for the function to be called.
+ * @wait: Whether to wait for the calls to complete.
+ *
+ * Non-maskably call a function on another CPU for hotplug purposes.
+ *
+ * This function must be called with maskable interrupts disabled.
+ */
+static int hotplug_cpu_nmi_call_function(cpumask_t cpumask,
+ smp_call_func_t func, void *info,
+ int wait)
+{
+ /*
+ * The address and the size of nmi_call_func_mask_data
+ * need to be aligned on L1_CACHE_BYTES.
+ */
+ static struct nmi_call_data_struct nmi_call_func_mask_data
+ __cacheline_aligned;
+ unsigned long start, end;
+
+ start = (unsigned long)&nmi_call_func_mask_data;
+ end = start + sizeof(struct nmi_call_data_struct);
+
+ nmi_call_func_mask_data.func = func;
+ nmi_call_func_mask_data.info = info;
+ nmi_call_func_mask_data.started = cpumask;
+ nmi_call_func_mask_data.wait = wait;
+ if (wait)
+ nmi_call_func_mask_data.finished = cpumask;
+
+ spin_lock(&smp_nmi_call_lock);
+ nmi_call_data = &nmi_call_func_mask_data;
+ mn10300_local_dcache_flush_range(start, end);
+ smp_wmb();
+
+ send_IPI_mask(cpumask, CALL_FUNCTION_NMI_IPI);
+
+ do {
+ mn10300_local_dcache_inv_range(start, end);
+ barrier();
+ } while (!cpumask_empty(&nmi_call_func_mask_data.started));
+
+ if (wait) {
+ do {
+ mn10300_local_dcache_inv_range(start, end);
+ barrier();
+ } while (!cpumask_empty(&nmi_call_func_mask_data.finished));
+ }
+
+ spin_unlock(&smp_nmi_call_lock);
+ return 0;
+}
+
+static void restart_wakeup_cpu(void)
+{
+ unsigned int cpu = smp_processor_id();
+
+ cpumask_set_cpu(cpu, &cpu_callin_map);
+ local_flush_tlb();
+ set_cpu_online(cpu, true);
+ smp_wmb();
+}
+
+static void prepare_sleep_cpu(void *unused)
+{
+ sleep_mode[smp_processor_id()] = 1;
+ smp_mb();
+ mn10300_local_dcache_flush_inv();
+ hotplug_cpu_disable_cache();
+ hotplug_cpu_invalidate_cache();
+}
+
+/* when this function called, IE=0, NMID=0. */
+static void sleep_cpu(void *unused)
+{
+ unsigned int cpu_id = smp_processor_id();
+ /*
+ * CALL_FUNCTION_NMI_IPI for wakeup_cpu() shall not be requested,
+ * before this cpu goes in SLEEP mode.
+ */
+ do {
+ smp_mb();
+ __sleep_cpu();
+ } while (sleep_mode[cpu_id]);
+ restart_wakeup_cpu();
+}
+
+static void run_sleep_cpu(unsigned int cpu)
+{
+ unsigned long flags;
+ cpumask_t cpumask;
+
+ cpumask_copy(&cpumask, &cpumask_of(cpu));
+ flags = arch_local_cli_save();
+ hotplug_cpu_nmi_call_function(cpumask, prepare_sleep_cpu, NULL, 1);
+ hotplug_cpu_nmi_call_function(cpumask, sleep_cpu, NULL, 0);
+ udelay(1); /* delay for the cpu to sleep. */
+ arch_local_irq_restore(flags);
+}
+
+static void wakeup_cpu(void)
+{
+ hotplug_cpu_invalidate_cache();
+ hotplug_cpu_enable_cache();
+ smp_mb();
+ sleep_mode[smp_processor_id()] = 0;
+}
+
+static void run_wakeup_cpu(unsigned int cpu)
+{
+ unsigned long flags;
+
+ flags = arch_local_cli_save();
+#if NR_CPUS == 2
+ mn10300_local_dcache_flush_inv();
+#else
+ /*
+ * Before waking up the cpu,
+ * all online cpus should stop and flush D-Cache for global data.
+ */
+#error not support NR_CPUS > 2, when CONFIG_HOTPLUG_CPU=y.
+#endif
+ hotplug_cpu_nmi_call_function(cpumask_of(cpu), wakeup_cpu, NULL, 1);
+ arch_local_irq_restore(flags);
+}
+
+#endif /* CONFIG_HOTPLUG_CPU */
diff --git a/arch/mn10300/kernel/switch_to.S b/arch/mn10300/kernel/switch_to.S
new file mode 100644
index 000000000..de3e74fc9
--- /dev/null
+++ b/arch/mn10300/kernel/switch_to.S
@@ -0,0 +1,179 @@
+###############################################################################
+#
+# MN10300 Context switch operation
+#
+# Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
+# Written by David Howells (dhowells@redhat.com)
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public Licence
+# as published by the Free Software Foundation; either version
+# 2 of the Licence, or (at your option) any later version.
+#
+###############################################################################
+#include <linux/sys.h>
+#include <linux/linkage.h>
+#include <asm/thread_info.h>
+#include <asm/cpu-regs.h>
+#ifdef CONFIG_SMP
+#include <proc/smp-regs.h>
+#endif /* CONFIG_SMP */
+
+ .text
+
+###############################################################################
+#
+# struct task_struct *__switch_to(struct thread_struct *prev,
+# struct thread_struct *next,
+# struct task_struct *prev_task)
+#
+###############################################################################
+ENTRY(__switch_to)
+ movm [d2,d3,a2,a3,exreg1],(sp)
+ or EPSW_NMID,epsw
+
+ mov (44,sp),d2
+
+ mov d0,a0
+ mov d1,a1
+
+ # save prev context
+ mov __switch_back,d0
+ mov sp,a2
+ mov a2,(THREAD_SP,a0)
+ mov a3,(THREAD_A3,a0)
+
+#ifdef CONFIG_KGDB
+ btst 0xff,(kgdb_single_step)
+ bne __switch_to__lift_sstep_bp
+__switch_to__continue:
+#endif
+ mov d0,(THREAD_PC,a0)
+
+ mov (THREAD_A3,a1),a3
+ mov (THREAD_SP,a1),a2
+
+ # switch
+ mov a2,sp
+
+ # load next context
+ GET_THREAD_INFO a2
+ mov a2,(__current_ti)
+ mov (TI_task,a2),a2
+ mov a2,(__current)
+#ifdef CONFIG_MN10300_CURRENT_IN_E2
+ mov a2,e2
+#endif
+
+ mov (THREAD_PC,a1),a2
+ mov d2,d0 # for ret_from_fork
+ mov d0,a0 # for __switch_to
+
+ jmp (a2)
+
+__switch_back:
+ and ~EPSW_NMID,epsw
+ ret [d2,d3,a2,a3,exreg1],32
+
+#ifdef CONFIG_KGDB
+###############################################################################
+#
+# Lift the single-step breakpoints when the task being traced is switched out
+# A0 = prev
+# A1 = next
+#
+###############################################################################
+__switch_to__lift_sstep_bp:
+ add -12,sp
+ mov a0,e4
+ mov a1,e5
+
+ # Clear the single-step flag to prevent us coming this way until we get
+ # switched back in
+ bclr 0xff,(kgdb_single_step)
+
+ # Remove first breakpoint
+ mov (kgdb_sstep_bp_addr),a2
+ cmp 0,a2
+ beq 1f
+ movbu (kgdb_sstep_bp),d0
+ movbu d0,(a2)
+#if defined(CONFIG_MN10300_CACHE_FLUSH_ICACHE) || defined(CONFIG_MN10300_CACHE_INV_ICACHE)
+ mov a2,d0
+ mov a2,d1
+ add 1,d1
+ calls flush_icache_range
+#endif
+1:
+
+ # Remove second breakpoint
+ mov (kgdb_sstep_bp_addr+4),a2
+ cmp 0,a2
+ beq 2f
+ movbu (kgdb_sstep_bp+1),d0
+ movbu d0,(a2)
+#if defined(CONFIG_MN10300_CACHE_FLUSH_ICACHE) || defined(CONFIG_MN10300_CACHE_INV_ICACHE)
+ mov a2,d0
+ mov a2,d1
+ add 1,d1
+ calls flush_icache_range
+#endif
+2:
+
+ # Change the resumption address and return
+ mov __switch_back__reinstall_sstep_bp,d0
+ mov e4,a0
+ mov e5,a1
+ add 12,sp
+ bra __switch_to__continue
+
+###############################################################################
+#
+# Reinstall the single-step breakpoints when the task being traced is switched
+# back in (A1 points to the new thread_struct).
+#
+###############################################################################
+__switch_back__reinstall_sstep_bp:
+ add -12,sp
+ mov a0,e4 # save the return value
+ mov 0xff,d3
+
+ # Reinstall first breakpoint
+ mov (kgdb_sstep_bp_addr),a2
+ cmp 0,a2
+ beq 1f
+ movbu (a2),d0
+ movbu d0,(kgdb_sstep_bp)
+ movbu d3,(a2)
+#if defined(CONFIG_MN10300_CACHE_FLUSH_ICACHE) || defined(CONFIG_MN10300_CACHE_INV_ICACHE)
+ mov a2,d0
+ mov a2,d1
+ add 1,d1
+ calls flush_icache_range
+#endif
+1:
+
+ # Reinstall second breakpoint
+ mov (kgdb_sstep_bp_addr+4),a2
+ cmp 0,a2
+ beq 2f
+ movbu (a2),d0
+ movbu d0,(kgdb_sstep_bp+1)
+ movbu d3,(a2)
+#if defined(CONFIG_MN10300_CACHE_FLUSH_ICACHE) || defined(CONFIG_MN10300_CACHE_INV_ICACHE)
+ mov a2,d0
+ mov a2,d1
+ add 1,d1
+ calls flush_icache_range
+#endif
+2:
+
+ mov d3,(kgdb_single_step)
+
+ # Restore the return value (the previous thread_struct pointer)
+ mov e4,a0
+ mov a0,d0
+ add 12,sp
+ bra __switch_back
+
+#endif /* CONFIG_KGDB */
diff --git a/arch/mn10300/kernel/sys_mn10300.c b/arch/mn10300/kernel/sys_mn10300.c
new file mode 100644
index 000000000..815f1355f
--- /dev/null
+++ b/arch/mn10300/kernel/sys_mn10300.c
@@ -0,0 +1,33 @@
+/* MN10300 Weird system calls
+ *
+ * Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd.
+ * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/syscalls.h>
+#include <linux/mm.h>
+#include <linux/smp.h>
+#include <linux/sem.h>
+#include <linux/msg.h>
+#include <linux/shm.h>
+#include <linux/stat.h>
+#include <linux/mman.h>
+#include <linux/file.h>
+#include <linux/tty.h>
+
+#include <asm/uaccess.h>
+
+asmlinkage long old_mmap(unsigned long addr, unsigned long len,
+ unsigned long prot, unsigned long flags,
+ unsigned long fd, unsigned long offset)
+{
+ if (offset & ~PAGE_MASK)
+ return -EINVAL;
+ return sys_mmap_pgoff(addr, len, prot, flags, fd, offset >> PAGE_SHIFT);
+}
diff --git a/arch/mn10300/kernel/time.c b/arch/mn10300/kernel/time.c
new file mode 100644
index 000000000..67c6416a5
--- /dev/null
+++ b/arch/mn10300/kernel/time.c
@@ -0,0 +1,124 @@
+/* MN10300 Low level time management
+ *
+ * Copyright (C) 2007-2008 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ * - Derived from arch/i386/kernel/time.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/time.h>
+#include <linux/init.h>
+#include <linux/smp.h>
+#include <linux/profile.h>
+#include <linux/cnt32_to_63.h>
+#include <linux/clocksource.h>
+#include <linux/clockchips.h>
+#include <asm/irq.h>
+#include <asm/div64.h>
+#include <asm/processor.h>
+#include <asm/intctl-regs.h>
+#include <asm/rtc.h>
+#include "internal.h"
+
+static unsigned long mn10300_last_tsc; /* time-stamp counter at last time
+ * interrupt occurred */
+
+static unsigned long sched_clock_multiplier;
+
+/*
+ * scheduler clock - returns current time in nanosec units.
+ */
+unsigned long long sched_clock(void)
+{
+ union {
+ unsigned long long ll;
+ unsigned l[2];
+ } tsc64, result;
+ unsigned long tmp;
+ unsigned product[3]; /* 96-bit intermediate value */
+
+ /* cnt32_to_63() is not safe with preemption */
+ preempt_disable();
+
+ /* expand the tsc to 64-bits.
+ * - sched_clock() must be called once a minute or better or the
+ * following will go horribly wrong - see cnt32_to_63()
+ */
+ tsc64.ll = cnt32_to_63(get_cycles()) & 0x7fffffffffffffffULL;
+
+ preempt_enable();
+
+ /* scale the 64-bit TSC value to a nanosecond value via a 96-bit
+ * intermediate
+ */
+ asm("mulu %2,%0,%3,%0 \n" /* LSW * mult -> 0:%3:%0 */
+ "mulu %2,%1,%2,%1 \n" /* MSW * mult -> %2:%1:0 */
+ "add %3,%1 \n"
+ "addc 0,%2 \n" /* result in %2:%1:%0 */
+ : "=r"(product[0]), "=r"(product[1]), "=r"(product[2]), "=r"(tmp)
+ : "0"(tsc64.l[0]), "1"(tsc64.l[1]), "2"(sched_clock_multiplier)
+ : "cc");
+
+ result.l[0] = product[1] << 16 | product[0] >> 16;
+ result.l[1] = product[2] << 16 | product[1] >> 16;
+
+ return result.ll;
+}
+
+/*
+ * initialise the scheduler clock
+ */
+static void __init mn10300_sched_clock_init(void)
+{
+ sched_clock_multiplier =
+ __muldiv64u(NSEC_PER_SEC, 1 << 16, MN10300_TSCCLK);
+}
+
+/**
+ * local_timer_interrupt - Local timer interrupt handler
+ *
+ * Handle local timer interrupts for this CPU. They may have been propagated
+ * to this CPU from the CPU that actually gets them by way of an IPI.
+ */
+irqreturn_t local_timer_interrupt(void)
+{
+ profile_tick(CPU_PROFILING);
+ update_process_times(user_mode(get_irq_regs()));
+ return IRQ_HANDLED;
+}
+
+/*
+ * initialise the various timers used by the main part of the kernel
+ */
+void __init time_init(void)
+{
+ /* we need the prescalar running to be able to use IOCLK/8
+ * - IOCLK runs at 1/4 (ST5 open) or 1/8 (ST5 closed) internal CPU clock
+ * - IOCLK runs at Fosc rate (crystal speed)
+ */
+ TMPSCNT |= TMPSCNT_ENABLE;
+
+ init_clocksource();
+
+ printk(KERN_INFO
+ "timestamp counter I/O clock running at %lu.%02lu"
+ " (calibrated against RTC)\n",
+ MN10300_TSCCLK / 1000000, (MN10300_TSCCLK / 10000) % 100);
+
+ mn10300_last_tsc = read_timestamp_counter();
+
+ init_clockevents();
+
+#ifdef CONFIG_MN10300_WD_TIMER
+ /* start the watchdog timer */
+ watchdog_go();
+#endif
+
+ mn10300_sched_clock_init();
+}
diff --git a/arch/mn10300/kernel/traps.c b/arch/mn10300/kernel/traps.c
new file mode 100644
index 000000000..a7a987c79
--- /dev/null
+++ b/arch/mn10300/kernel/traps.c
@@ -0,0 +1,616 @@
+/* MN10300 Exception handling
+ *
+ * Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd.
+ * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
+ * Modified by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+#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/init.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/kallsyms.h>
+#include <linux/pci.h>
+#include <linux/kdebug.h>
+#include <linux/bug.h>
+#include <linux/irq.h>
+#include <linux/export.h>
+#include <asm/processor.h>
+#include <linux/uaccess.h>
+#include <asm/io.h>
+#include <linux/atomic.h>
+#include <asm/smp.h>
+#include <asm/pgalloc.h>
+#include <asm/cacheflush.h>
+#include <asm/cpu-regs.h>
+#include <asm/busctl-regs.h>
+#include <unit/leds.h>
+#include <asm/fpu.h>
+#include <asm/sections.h>
+#include <asm/debugger.h>
+#include "internal.h"
+
+#if (CONFIG_INTERRUPT_VECTOR_BASE & 0xffffff)
+#error "INTERRUPT_VECTOR_BASE not aligned to 16MiB boundary!"
+#endif
+
+int kstack_depth_to_print = 24;
+
+spinlock_t die_lock = __SPIN_LOCK_UNLOCKED(die_lock);
+
+struct exception_to_signal_map {
+ u8 signo;
+ u32 si_code;
+};
+
+static const struct exception_to_signal_map exception_to_signal_map[256] = {
+ /* MMU exceptions */
+ [EXCEP_ITLBMISS >> 3] = { 0, 0 },
+ [EXCEP_DTLBMISS >> 3] = { 0, 0 },
+ [EXCEP_IAERROR >> 3] = { 0, 0 },
+ [EXCEP_DAERROR >> 3] = { 0, 0 },
+
+ /* system exceptions */
+ [EXCEP_TRAP >> 3] = { SIGTRAP, TRAP_BRKPT },
+ [EXCEP_ISTEP >> 3] = { SIGTRAP, TRAP_TRACE }, /* Monitor */
+ [EXCEP_IBREAK >> 3] = { SIGTRAP, TRAP_HWBKPT }, /* Monitor */
+ [EXCEP_OBREAK >> 3] = { SIGTRAP, TRAP_HWBKPT }, /* Monitor */
+ [EXCEP_PRIVINS >> 3] = { SIGILL, ILL_PRVOPC },
+ [EXCEP_UNIMPINS >> 3] = { SIGILL, ILL_ILLOPC },
+ [EXCEP_UNIMPEXINS >> 3] = { SIGILL, ILL_ILLOPC },
+ [EXCEP_MEMERR >> 3] = { SIGSEGV, SEGV_ACCERR },
+ [EXCEP_MISALIGN >> 3] = { SIGBUS, BUS_ADRALN },
+ [EXCEP_BUSERROR >> 3] = { SIGBUS, BUS_ADRERR },
+ [EXCEP_ILLINSACC >> 3] = { SIGSEGV, SEGV_ACCERR },
+ [EXCEP_ILLDATACC >> 3] = { SIGSEGV, SEGV_ACCERR },
+ [EXCEP_IOINSACC >> 3] = { SIGSEGV, SEGV_ACCERR },
+ [EXCEP_PRIVINSACC >> 3] = { SIGSEGV, SEGV_ACCERR }, /* userspace */
+ [EXCEP_PRIVDATACC >> 3] = { SIGSEGV, SEGV_ACCERR }, /* userspace */
+ [EXCEP_DATINSACC >> 3] = { SIGSEGV, SEGV_ACCERR },
+ [EXCEP_DOUBLE_FAULT >> 3] = { SIGILL, ILL_BADSTK },
+
+ /* FPU exceptions */
+ [EXCEP_FPU_DISABLED >> 3] = { SIGILL, ILL_COPROC },
+ [EXCEP_FPU_UNIMPINS >> 3] = { SIGILL, ILL_COPROC },
+ [EXCEP_FPU_OPERATION >> 3] = { SIGFPE, FPE_INTDIV },
+
+ /* interrupts */
+ [EXCEP_WDT >> 3] = { SIGALRM, 0 },
+ [EXCEP_NMI >> 3] = { SIGQUIT, 0 },
+ [EXCEP_IRQ_LEVEL0 >> 3] = { SIGINT, 0 },
+ [EXCEP_IRQ_LEVEL1 >> 3] = { 0, 0 },
+ [EXCEP_IRQ_LEVEL2 >> 3] = { 0, 0 },
+ [EXCEP_IRQ_LEVEL3 >> 3] = { 0, 0 },
+ [EXCEP_IRQ_LEVEL4 >> 3] = { 0, 0 },
+ [EXCEP_IRQ_LEVEL5 >> 3] = { 0, 0 },
+ [EXCEP_IRQ_LEVEL6 >> 3] = { 0, 0 },
+
+ /* system calls */
+ [EXCEP_SYSCALL0 >> 3] = { 0, 0 },
+ [EXCEP_SYSCALL1 >> 3] = { SIGILL, ILL_ILLTRP },
+ [EXCEP_SYSCALL2 >> 3] = { SIGILL, ILL_ILLTRP },
+ [EXCEP_SYSCALL3 >> 3] = { SIGILL, ILL_ILLTRP },
+ [EXCEP_SYSCALL4 >> 3] = { SIGILL, ILL_ILLTRP },
+ [EXCEP_SYSCALL5 >> 3] = { SIGILL, ILL_ILLTRP },
+ [EXCEP_SYSCALL6 >> 3] = { SIGILL, ILL_ILLTRP },
+ [EXCEP_SYSCALL7 >> 3] = { SIGILL, ILL_ILLTRP },
+ [EXCEP_SYSCALL8 >> 3] = { SIGILL, ILL_ILLTRP },
+ [EXCEP_SYSCALL9 >> 3] = { SIGILL, ILL_ILLTRP },
+ [EXCEP_SYSCALL10 >> 3] = { SIGILL, ILL_ILLTRP },
+ [EXCEP_SYSCALL11 >> 3] = { SIGILL, ILL_ILLTRP },
+ [EXCEP_SYSCALL12 >> 3] = { SIGILL, ILL_ILLTRP },
+ [EXCEP_SYSCALL13 >> 3] = { SIGILL, ILL_ILLTRP },
+ [EXCEP_SYSCALL14 >> 3] = { SIGILL, ILL_ILLTRP },
+ [EXCEP_SYSCALL15 >> 3] = { SIGABRT, 0 },
+};
+
+/*
+ * Handle kernel exceptions.
+ *
+ * See if there's a fixup handler we can force a jump to when an exception
+ * happens due to something kernel code did
+ */
+int die_if_no_fixup(const char *str, struct pt_regs *regs,
+ enum exception_code code)
+{
+ u8 opcode;
+ int signo, si_code;
+
+ if (user_mode(regs))
+ return 0;
+
+ peripheral_leds_display_exception(code);
+
+ signo = exception_to_signal_map[code >> 3].signo;
+ si_code = exception_to_signal_map[code >> 3].si_code;
+
+ switch (code) {
+ /* see if we can fixup the kernel accessing memory */
+ case EXCEP_ITLBMISS:
+ case EXCEP_DTLBMISS:
+ case EXCEP_IAERROR:
+ case EXCEP_DAERROR:
+ case EXCEP_MEMERR:
+ case EXCEP_MISALIGN:
+ case EXCEP_BUSERROR:
+ case EXCEP_ILLDATACC:
+ case EXCEP_IOINSACC:
+ case EXCEP_PRIVINSACC:
+ case EXCEP_PRIVDATACC:
+ case EXCEP_DATINSACC:
+ if (fixup_exception(regs))
+ return 1;
+ break;
+
+ case EXCEP_TRAP:
+ case EXCEP_UNIMPINS:
+ if (probe_kernel_read(&opcode, (u8 *)regs->pc, 1) < 0)
+ break;
+ if (opcode == 0xff) {
+ if (notify_die(DIE_BREAKPOINT, str, regs, code, 0, 0))
+ return 1;
+ if (at_debugger_breakpoint(regs))
+ regs->pc++;
+ signo = SIGTRAP;
+ si_code = TRAP_BRKPT;
+ }
+ break;
+
+ case EXCEP_SYSCALL1 ... EXCEP_SYSCALL14:
+ /* syscall return addr is _after_ the instruction */
+ regs->pc -= 2;
+ break;
+
+ case EXCEP_SYSCALL15:
+ if (report_bug(regs->pc, regs) == BUG_TRAP_TYPE_WARN)
+ return 1;
+
+ /* syscall return addr is _after_ the instruction */
+ regs->pc -= 2;
+ break;
+
+ default:
+ break;
+ }
+
+ if (debugger_intercept(code, signo, si_code, regs) == 0)
+ return 1;
+
+ if (notify_die(DIE_GPF, str, regs, code, 0, 0))
+ return 1;
+
+ /* make the process die as the last resort */
+ die(str, regs, code);
+}
+
+/*
+ * General exception handler
+ */
+asmlinkage void handle_exception(struct pt_regs *regs, u32 intcode)
+{
+ siginfo_t info;
+
+ /* deal with kernel exceptions here */
+ if (die_if_no_fixup(NULL, regs, intcode))
+ return;
+
+ /* otherwise it's a userspace exception */
+ info.si_signo = exception_to_signal_map[intcode >> 3].signo;
+ info.si_code = exception_to_signal_map[intcode >> 3].si_code;
+ info.si_errno = 0;
+ info.si_addr = (void *) regs->pc;
+ force_sig_info(info.si_signo, &info, current);
+}
+
+/*
+ * handle NMI
+ */
+asmlinkage void nmi(struct pt_regs *regs, enum exception_code code)
+{
+ /* see if gdbstub wants to deal with it */
+ if (debugger_intercept(code, SIGQUIT, 0, regs))
+ return;
+
+ printk(KERN_WARNING "--- Register Dump ---\n");
+ show_registers(regs);
+ printk(KERN_WARNING "---------------------\n");
+}
+
+/*
+ * show a stack trace from the specified stack pointer
+ */
+void show_trace(unsigned long *sp)
+{
+ unsigned long bottom, stack, addr, fp, raslot;
+
+ printk(KERN_EMERG "\nCall Trace:\n");
+
+ //stack = (unsigned long)sp;
+ asm("mov sp,%0" : "=a"(stack));
+ asm("mov a3,%0" : "=r"(fp));
+
+ raslot = ULONG_MAX;
+ bottom = (stack + THREAD_SIZE) & ~(THREAD_SIZE - 1);
+ for (; stack < bottom; stack += sizeof(addr)) {
+ addr = *(unsigned long *)stack;
+ if (stack == fp) {
+ if (addr > stack && addr < bottom) {
+ fp = addr;
+ raslot = stack + sizeof(addr);
+ continue;
+ }
+ fp = 0;
+ raslot = ULONG_MAX;
+ }
+
+ if (__kernel_text_address(addr)) {
+ printk(" [<%08lx>]", addr);
+ if (stack >= raslot)
+ raslot = ULONG_MAX;
+ else
+ printk(" ?");
+ print_symbol(" %s", addr);
+ printk("\n");
+ }
+ }
+
+ printk("\n");
+}
+
+/*
+ * show the raw stack from the specified stack pointer
+ */
+void show_stack(struct task_struct *task, unsigned long *sp)
+{
+ unsigned long *stack;
+ int i;
+
+ if (!sp)
+ sp = (unsigned long *) &sp;
+
+ stack = sp;
+ printk(KERN_EMERG "Stack:");
+ for (i = 0; i < kstack_depth_to_print; i++) {
+ if (((long) stack & (THREAD_SIZE - 1)) == 0)
+ break;
+ if ((i % 8) == 0)
+ printk(KERN_EMERG " ");
+ printk("%08lx ", *stack++);
+ }
+
+ show_trace(sp);
+}
+
+/*
+ * dump the register file in the specified exception frame
+ */
+void show_registers_only(struct pt_regs *regs)
+{
+ unsigned long ssp;
+
+ ssp = (unsigned long) regs + sizeof(*regs);
+
+ printk(KERN_EMERG "PC: %08lx EPSW: %08lx SSP: %08lx mode: %s\n",
+ regs->pc, regs->epsw, ssp, user_mode(regs) ? "User" : "Super");
+ printk(KERN_EMERG "d0: %08lx d1: %08lx d2: %08lx d3: %08lx\n",
+ regs->d0, regs->d1, regs->d2, regs->d3);
+ printk(KERN_EMERG "a0: %08lx a1: %08lx a2: %08lx a3: %08lx\n",
+ regs->a0, regs->a1, regs->a2, regs->a3);
+ printk(KERN_EMERG "e0: %08lx e1: %08lx e2: %08lx e3: %08lx\n",
+ regs->e0, regs->e1, regs->e2, regs->e3);
+ printk(KERN_EMERG "e4: %08lx e5: %08lx e6: %08lx e7: %08lx\n",
+ regs->e4, regs->e5, regs->e6, regs->e7);
+ printk(KERN_EMERG "lar: %08lx lir: %08lx mdr: %08lx usp: %08lx\n",
+ regs->lar, regs->lir, regs->mdr, regs->sp);
+ printk(KERN_EMERG "cvf: %08lx crl: %08lx crh: %08lx drq: %08lx\n",
+ regs->mcvf, regs->mcrl, regs->mcrh, regs->mdrq);
+ printk(KERN_EMERG "threadinfo=%p task=%p)\n",
+ current_thread_info(), current);
+
+ if ((unsigned long) current >= PAGE_OFFSET &&
+ (unsigned long) current < (unsigned long)high_memory)
+ printk(KERN_EMERG "Process %s (pid: %d)\n",
+ current->comm, current->pid);
+
+#ifdef CONFIG_SMP
+ printk(KERN_EMERG "CPUID: %08x\n", CPUID);
+#endif
+ printk(KERN_EMERG "CPUP: %04hx\n", CPUP);
+ printk(KERN_EMERG "TBR: %08x\n", TBR);
+ printk(KERN_EMERG "DEAR: %08x\n", DEAR);
+ printk(KERN_EMERG "sISR: %08x\n", sISR);
+ printk(KERN_EMERG "NMICR: %04hx\n", NMICR);
+ printk(KERN_EMERG "BCBERR: %08x\n", BCBERR);
+ printk(KERN_EMERG "BCBEAR: %08x\n", BCBEAR);
+ printk(KERN_EMERG "MMUFCR: %08x\n", MMUFCR);
+ printk(KERN_EMERG "IPTEU : %08x IPTEL2: %08x\n", IPTEU, IPTEL2);
+ printk(KERN_EMERG "DPTEU: %08x DPTEL2: %08x\n", DPTEU, DPTEL2);
+}
+
+/*
+ * dump the registers and the stack
+ */
+void show_registers(struct pt_regs *regs)
+{
+ unsigned long sp;
+ int i;
+
+ show_registers_only(regs);
+
+ if (!user_mode(regs))
+ sp = (unsigned long) regs + sizeof(*regs);
+ else
+ sp = regs->sp;
+
+ /* when in-kernel, we also print out the stack and code at the
+ * time of the fault..
+ */
+ if (!user_mode(regs)) {
+ printk(KERN_EMERG "\n");
+ show_stack(current, (unsigned long *) sp);
+
+#if 0
+ printk(KERN_EMERG "\nCode: ");
+ if (regs->pc < PAGE_OFFSET)
+ goto bad;
+
+ for (i = 0; i < 20; i++) {
+ unsigned char c;
+ if (__get_user(c, &((unsigned char *) regs->pc)[i]))
+ goto bad;
+ printk("%02x ", c);
+ }
+#else
+ i = 0;
+#endif
+ }
+
+ printk("\n");
+ return;
+
+#if 0
+bad:
+ printk(KERN_EMERG " Bad PC value.");
+ break;
+#endif
+}
+
+/*
+ *
+ */
+void show_trace_task(struct task_struct *tsk)
+{
+ unsigned long sp = tsk->thread.sp;
+
+ /* User space on another CPU? */
+ if ((sp ^ (unsigned long) tsk) & (PAGE_MASK << 1))
+ return;
+
+ show_trace((unsigned long *) sp);
+}
+
+/*
+ * note the untimely death of part of the kernel
+ */
+void die(const char *str, struct pt_regs *regs, enum exception_code code)
+{
+ console_verbose();
+ spin_lock_irq(&die_lock);
+ printk(KERN_EMERG "\n%s: %04x\n",
+ str, code & 0xffff);
+ show_registers(regs);
+
+ if (regs->pc >= 0x02000000 && regs->pc < 0x04000000 &&
+ (regs->epsw & (EPSW_IM | EPSW_IE)) != (EPSW_IM | EPSW_IE)) {
+ printk(KERN_EMERG "Exception in usermode interrupt handler\n");
+ printk(KERN_EMERG "\nPlease connect to kernel debugger !!\n");
+ asm volatile ("0: bra 0b");
+ }
+
+ spin_unlock_irq(&die_lock);
+ do_exit(SIGSEGV);
+}
+
+/*
+ * display the register file when the stack pointer gets clobbered
+ */
+asmlinkage void do_double_fault(struct pt_regs *regs)
+{
+ struct task_struct *tsk = current;
+
+ strcpy(tsk->comm, "emergency tsk");
+ tsk->pid = 0;
+ console_verbose();
+ printk(KERN_EMERG "--- double fault ---\n");
+ show_registers(regs);
+}
+
+/*
+ * asynchronous bus error (external, usually I/O DMA)
+ */
+asmlinkage void io_bus_error(u32 bcberr, u32 bcbear, struct pt_regs *regs)
+{
+ console_verbose();
+
+ printk(KERN_EMERG "Asynchronous I/O Bus Error\n");
+ printk(KERN_EMERG "==========================\n");
+
+ if (bcberr & BCBERR_BEME)
+ printk(KERN_EMERG "- Multiple recorded errors\n");
+
+ printk(KERN_EMERG "- Faulting Buses:%s%s%s\n",
+ bcberr & BCBERR_BEMR_CI ? " CPU-Ins-Fetch" : "",
+ bcberr & BCBERR_BEMR_CD ? " CPU-Data" : "",
+ bcberr & BCBERR_BEMR_DMA ? " DMA" : "");
+
+ printk(KERN_EMERG "- %s %s access made to %s at address %08x\n",
+ bcberr & BCBERR_BEBST ? "Burst" : "Single",
+ bcberr & BCBERR_BERW ? "Read" : "Write",
+ bcberr & BCBERR_BESB_MON ? "Monitor Space" :
+ bcberr & BCBERR_BESB_IO ? "Internal CPU I/O Space" :
+ bcberr & BCBERR_BESB_EX ? "External I/O Bus" :
+ bcberr & BCBERR_BESB_OPEX ? "External Memory Bus" :
+ "On Chip Memory",
+ bcbear
+ );
+
+ printk(KERN_EMERG "- Detected by the %s\n",
+ bcberr&BCBERR_BESD ? "Bus Control Unit" : "Slave Bus");
+
+#ifdef CONFIG_PCI
+#define BRIDGEREGB(X) (*(volatile __u8 *)(0xBE040000 + (X)))
+#define BRIDGEREGW(X) (*(volatile __u16 *)(0xBE040000 + (X)))
+#define BRIDGEREGL(X) (*(volatile __u32 *)(0xBE040000 + (X)))
+
+ printk(KERN_EMERG "- PCI Memory Paging Reg: %08x\n",
+ *(volatile __u32 *) (0xBFFFFFF4));
+ printk(KERN_EMERG "- PCI Bridge Base Address 0: %08x\n",
+ BRIDGEREGL(PCI_BASE_ADDRESS_0));
+ printk(KERN_EMERG "- PCI Bridge AMPCI Base Address: %08x\n",
+ BRIDGEREGL(0x48));
+ printk(KERN_EMERG "- PCI Bridge Command: %04hx\n",
+ BRIDGEREGW(PCI_COMMAND));
+ printk(KERN_EMERG "- PCI Bridge Status: %04hx\n",
+ BRIDGEREGW(PCI_STATUS));
+ printk(KERN_EMERG "- PCI Bridge Int Status: %08hx\n",
+ BRIDGEREGL(0x4c));
+#endif
+
+ printk(KERN_EMERG "\n");
+ show_registers(regs);
+
+ panic("Halted due to asynchronous I/O Bus Error\n");
+}
+
+/*
+ * handle an exception for which a handler has not yet been installed
+ */
+asmlinkage void uninitialised_exception(struct pt_regs *regs,
+ enum exception_code code)
+{
+
+ /* see if gdbstub wants to deal with it */
+ if (debugger_intercept(code, SIGSYS, 0, regs) == 0)
+ return;
+
+ peripheral_leds_display_exception(code);
+ printk(KERN_EMERG "Uninitialised Exception 0x%04x\n", code & 0xFFFF);
+ show_registers(regs);
+
+ for (;;)
+ continue;
+}
+
+/*
+ * set an interrupt stub to jump to a handler
+ * ! NOTE: this does *not* flush the caches
+ */
+void __init __set_intr_stub(enum exception_code code, void *handler)
+{
+ unsigned long addr;
+ u8 *vector = (u8 *)(CONFIG_INTERRUPT_VECTOR_BASE + code);
+
+ addr = (unsigned long) handler - (unsigned long) vector;
+ vector[0] = 0xdc; /* JMP handler */
+ vector[1] = addr;
+ vector[2] = addr >> 8;
+ vector[3] = addr >> 16;
+ vector[4] = addr >> 24;
+ vector[5] = 0xcb;
+ vector[6] = 0xcb;
+ vector[7] = 0xcb;
+}
+
+/*
+ * set an interrupt stub to jump to a handler
+ */
+void __init set_intr_stub(enum exception_code code, void *handler)
+{
+ unsigned long addr;
+ u8 *vector = (u8 *)(CONFIG_INTERRUPT_VECTOR_BASE + code);
+ unsigned long flags;
+
+ addr = (unsigned long) handler - (unsigned long) vector;
+
+ flags = arch_local_cli_save();
+
+ vector[0] = 0xdc; /* JMP handler */
+ vector[1] = addr;
+ vector[2] = addr >> 8;
+ vector[3] = addr >> 16;
+ vector[4] = addr >> 24;
+ vector[5] = 0xcb;
+ vector[6] = 0xcb;
+ vector[7] = 0xcb;
+
+ arch_local_irq_restore(flags);
+
+#ifndef CONFIG_MN10300_CACHE_SNOOP
+ mn10300_dcache_flush_inv();
+ mn10300_icache_inv();
+#endif
+}
+
+/*
+ * initialise the exception table
+ */
+void __init trap_init(void)
+{
+ set_excp_vector(EXCEP_TRAP, handle_exception);
+ set_excp_vector(EXCEP_ISTEP, handle_exception);
+ set_excp_vector(EXCEP_IBREAK, handle_exception);
+ set_excp_vector(EXCEP_OBREAK, handle_exception);
+
+ set_excp_vector(EXCEP_PRIVINS, handle_exception);
+ set_excp_vector(EXCEP_UNIMPINS, handle_exception);
+ set_excp_vector(EXCEP_UNIMPEXINS, handle_exception);
+ set_excp_vector(EXCEP_MEMERR, handle_exception);
+ set_excp_vector(EXCEP_MISALIGN, misalignment);
+ set_excp_vector(EXCEP_BUSERROR, handle_exception);
+ set_excp_vector(EXCEP_ILLINSACC, handle_exception);
+ set_excp_vector(EXCEP_ILLDATACC, handle_exception);
+ set_excp_vector(EXCEP_IOINSACC, handle_exception);
+ set_excp_vector(EXCEP_PRIVINSACC, handle_exception);
+ set_excp_vector(EXCEP_PRIVDATACC, handle_exception);
+ set_excp_vector(EXCEP_DATINSACC, handle_exception);
+ set_excp_vector(EXCEP_FPU_UNIMPINS, handle_exception);
+ set_excp_vector(EXCEP_FPU_OPERATION, fpu_exception);
+
+ set_excp_vector(EXCEP_NMI, nmi);
+
+ set_excp_vector(EXCEP_SYSCALL1, handle_exception);
+ set_excp_vector(EXCEP_SYSCALL2, handle_exception);
+ set_excp_vector(EXCEP_SYSCALL3, handle_exception);
+ set_excp_vector(EXCEP_SYSCALL4, handle_exception);
+ set_excp_vector(EXCEP_SYSCALL5, handle_exception);
+ set_excp_vector(EXCEP_SYSCALL6, handle_exception);
+ set_excp_vector(EXCEP_SYSCALL7, handle_exception);
+ set_excp_vector(EXCEP_SYSCALL8, handle_exception);
+ set_excp_vector(EXCEP_SYSCALL9, handle_exception);
+ set_excp_vector(EXCEP_SYSCALL10, handle_exception);
+ set_excp_vector(EXCEP_SYSCALL11, handle_exception);
+ set_excp_vector(EXCEP_SYSCALL12, handle_exception);
+ set_excp_vector(EXCEP_SYSCALL13, handle_exception);
+ set_excp_vector(EXCEP_SYSCALL14, handle_exception);
+ set_excp_vector(EXCEP_SYSCALL15, handle_exception);
+}
+
+/*
+ * determine if a program counter value is a valid bug address
+ */
+int is_valid_bugaddr(unsigned long pc)
+{
+ return pc >= PAGE_OFFSET;
+}
diff --git a/arch/mn10300/kernel/vmlinux.lds.S b/arch/mn10300/kernel/vmlinux.lds.S
new file mode 100644
index 000000000..13c4814c2
--- /dev/null
+++ b/arch/mn10300/kernel/vmlinux.lds.S
@@ -0,0 +1,93 @@
+/* MN10300 Main kernel linker script
+ *
+ * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+#define __VMLINUX_LDS__
+#include <asm-generic/vmlinux.lds.h>
+#include <asm/thread_info.h>
+#include <asm/page.h>
+
+OUTPUT_FORMAT("elf32-am33lin", "elf32-am33lin", "elf32-am33lin")
+OUTPUT_ARCH(mn10300)
+ENTRY(_start)
+jiffies = jiffies_64;
+#ifndef CONFIG_MN10300_CURRENT_IN_E2
+current = __current;
+#endif
+SECTIONS
+{
+ . = CONFIG_KERNEL_TEXT_ADDRESS;
+ /* read-only */
+ _stext = .;
+ _text = .; /* Text and read-only data */
+ .text : {
+ HEAD_TEXT
+ TEXT_TEXT
+ SCHED_TEXT
+ LOCK_TEXT
+ KPROBES_TEXT
+ *(.fixup)
+ *(.gnu.warning)
+ } = 0xcb
+
+ _etext = .; /* End of text section */
+
+ EXCEPTION_TABLE(16)
+ BUG_TABLE
+
+ RO_DATA(PAGE_SIZE)
+
+ /* writeable */
+ _sdata = .; /* Start of rw data section */
+ RW_DATA_SECTION(32, PAGE_SIZE, THREAD_SIZE)
+ _edata = .;
+
+ /* might get freed after init */
+ . = ALIGN(PAGE_SIZE);
+ .smp_locks : AT(ADDR(.smp_locks) - LOAD_OFFSET) {
+ __smp_locks = .;
+ *(.smp_locks)
+ __smp_locks_end = .;
+ }
+
+ /* will be freed after init */
+ . = ALIGN(PAGE_SIZE); /* Init code and data */
+ __init_begin = .;
+ INIT_TEXT_SECTION(PAGE_SIZE)
+ INIT_DATA_SECTION(16)
+ . = ALIGN(4);
+ __alt_instructions = .;
+ .altinstructions : { *(.altinstructions) }
+ __alt_instructions_end = .;
+ .altinstr_replacement : { *(.altinstr_replacement) }
+ /* .exit.text is discard at runtime, not link time, to deal with references
+ from .altinstructions and .eh_frame */
+ .exit.text : { EXIT_TEXT; }
+ .exit.data : { EXIT_DATA; }
+
+ PERCPU_SECTION(32)
+ . = ALIGN(PAGE_SIZE);
+ __init_end = .;
+ /* freed after init ends here */
+
+ BSS_SECTION(0, PAGE_SIZE, 4)
+
+ _end = . ;
+
+ /* This is where the kernel creates the early boot page tables */
+ . = ALIGN(PAGE_SIZE);
+ pg0 = .;
+
+ STABS_DEBUG
+
+ DWARF_DEBUG
+
+ /* Sections to be discarded */
+ DISCARDS
+}