summary refs log tree commit diff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2011-03-24 10:07:50 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2011-03-24 10:07:50 -0700
commit3dab04e6978e358ad2307bca563fabd6c5d2c58b (patch)
tree893e9bc5041e7f722722fe13a3b145396f2554d8
parent6d1e9a42e7176bbce9348274784b2e5f69223936 (diff)
parent5a4b65ab506398ba5a35c37e06edddd387cc0add (diff)
downloadlinux-3dab04e6978e358ad2307bca563fabd6c5d2c58b.tar.gz
Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-2.6-mn10300
* 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-2.6-mn10300:
  MN10300: gcc 4.6 vs am33 inline assembly
  MN10300: Deprecate gdbstub
  MN10300: Allow KGDB to use the MN10300 serial ports
  MN10300: Emulate single stepping in KGDB on MN10300
  MN10300: Generalise kernel debugger kernel halt, reboot or power off hook
  KGDB: Notify GDB of machine halt, reboot or power off
  MN10300: Use KGDB
  MN10300: Create generic kernel debugger hooks
  MN10300: Create general kernel debugger cache flushing
  MN10300: Introduce a general config option for kernel debugger hooks
  MN10300: The icache invalidate functions should disable the icache first
  MN10300: gdbstub: Restrict single-stepping to non-preemptable non-SMP configs
-rw-r--r--arch/mn10300/Kconfig10
-rw-r--r--arch/mn10300/Kconfig.debug17
-rw-r--r--arch/mn10300/include/asm/debugger.h43
-rw-r--r--arch/mn10300/include/asm/div64.h21
-rw-r--r--arch/mn10300/include/asm/fpu.h2
-rw-r--r--arch/mn10300/include/asm/irqflags.h2
-rw-r--r--arch/mn10300/include/asm/kgdb.h81
-rw-r--r--arch/mn10300/include/asm/smp.h6
-rw-r--r--arch/mn10300/include/asm/thread_info.h4
-rw-r--r--arch/mn10300/kernel/Makefile5
-rw-r--r--arch/mn10300/kernel/entry.S67
-rw-r--r--arch/mn10300/kernel/fpu.c18
-rw-r--r--arch/mn10300/kernel/gdb-cache.S105
-rw-r--r--arch/mn10300/kernel/gdb-io-ttysm.c8
-rw-r--r--arch/mn10300/kernel/gdb-stub.c41
-rw-r--r--arch/mn10300/kernel/internal.h7
-rw-r--r--arch/mn10300/kernel/irq.c2
-rw-r--r--arch/mn10300/kernel/kgdb.c502
-rw-r--r--arch/mn10300/kernel/mn10300-serial.c75
-rw-r--r--arch/mn10300/kernel/process.c6
-rw-r--r--arch/mn10300/kernel/smp.c26
-rw-r--r--arch/mn10300/kernel/switch_to.S111
-rw-r--r--arch/mn10300/kernel/traps.c406
-rw-r--r--arch/mn10300/mm/Kconfig.cache46
-rw-r--r--arch/mn10300/mm/Makefile9
-rw-r--r--arch/mn10300/mm/cache-dbg-flush-by-reg.S160
-rw-r--r--arch/mn10300/mm/cache-dbg-flush-by-tag.S114
-rw-r--r--arch/mn10300/mm/cache-dbg-inv-by-reg.S69
-rw-r--r--arch/mn10300/mm/cache-dbg-inv-by-tag.S120
-rw-r--r--arch/mn10300/mm/cache-dbg-inv.S47
-rw-r--r--arch/mn10300/mm/cache-flush-by-tag.S13
-rw-r--r--arch/mn10300/mm/cache-inv-by-reg.S22
-rw-r--r--arch/mn10300/mm/cache-inv-by-tag.S86
-rw-r--r--arch/mn10300/mm/cache.inc133
-rw-r--r--arch/mn10300/mm/fault.c9
-rw-r--r--arch/mn10300/proc-mn103e010/include/proc/cache.h1
-rw-r--r--arch/mn10300/proc-mn2ws0050/include/proc/cache.h1
-rw-r--r--include/linux/kgdb.h1
-rw-r--r--kernel/debug/gdbstub.c30
39 files changed, 1924 insertions, 502 deletions
diff --git a/arch/mn10300/Kconfig b/arch/mn10300/Kconfig
index 10971be43061..d8ab97a73db2 100644
--- a/arch/mn10300/Kconfig
+++ b/arch/mn10300/Kconfig
@@ -3,6 +3,8 @@ config MN10300
 	select HAVE_OPROFILE
 	select HAVE_GENERIC_HARDIRQS
 	select GENERIC_HARDIRQS_NO_DEPRECATED
+	select HAVE_ARCH_TRACEHOOK
+	select HAVE_ARCH_KGDB
 
 config AM33_2
 	def_bool n
@@ -401,9 +403,9 @@ comment "[!] NOTE: A lower number/level indicates a higher priority (0 is highes
 comment "____Non-maskable interrupt levels____"
 comment "The following must be set to a higher priority than local_irq_disable() and on-chip serial"
 
-config GDBSTUB_IRQ_LEVEL
-	int "GDBSTUB interrupt priority"
-	depends on GDBSTUB
+config DEBUGGER_IRQ_LEVEL
+	int "DEBUGGER interrupt priority"
+	depends on KERNEL_DEBUGGER
 	range 0 1 if LINUX_CLI_LEVEL = 2
 	range 0 2 if LINUX_CLI_LEVEL = 3
 	range 0 3 if LINUX_CLI_LEVEL = 4
@@ -437,7 +439,7 @@ config LINUX_CLI_LEVEL
 	  EPSW.IM from 7.  Any interrupt is permitted for which the level is
 	  lower than EPSW.IM.
 
-	  Certain interrupts, such as GDBSTUB and virtual MN10300 on-chip
+	  Certain interrupts, such as DEBUGGER and virtual MN10300 on-chip
 	  serial DMA interrupts are allowed to interrupt normal disabled
 	  sections.
 
diff --git a/arch/mn10300/Kconfig.debug b/arch/mn10300/Kconfig.debug
index ce83c74b3fd7..bdbfd444a9ff 100644
--- a/arch/mn10300/Kconfig.debug
+++ b/arch/mn10300/Kconfig.debug
@@ -36,7 +36,7 @@ config KPROBES
 
 config GDBSTUB
 	bool "Remote GDB kernel debugging"
-	depends on DEBUG_KERNEL
+	depends on DEBUG_KERNEL && DEPRECATED
 	select DEBUG_INFO
 	select FRAME_POINTER
 	help
@@ -46,6 +46,9 @@ config GDBSTUB
 	  RAM to avoid excessive linking time. This is only useful for kernel
 	  hackers. If unsure, say N.
 
+	  This is deprecated in favour of KGDB and will be removed in a later
+	  version.
+
 config GDBSTUB_IMMEDIATE
 	bool "Break into GDB stub immediately"
 	depends on GDBSTUB
@@ -54,6 +57,14 @@ config GDBSTUB_IMMEDIATE
 	  possible, leaving the program counter at the beginning of
 	  start_kernel() in init/main.c.
 
+config GDBSTUB_ALLOW_SINGLE_STEP
+	bool "Allow software single-stepping in GDB stub"
+	depends on GDBSTUB && !SMP && !PREEMPT
+	help
+	  Allow GDB stub to perform software single-stepping through the
+	  kernel.  This doesn't work very well on SMP or preemptible kernels as
+	  it uses temporary breakpoints to emulate single-stepping.
+
 config GDB_CONSOLE
 	bool "Console output to GDB"
 	depends on GDBSTUB
@@ -142,3 +153,7 @@ config GDBSTUB_ON_TTYSx
 	default y
 
 endmenu
+
+config KERNEL_DEBUGGER
+	def_bool y
+	depends on GDBSTUB || KGDB
diff --git a/arch/mn10300/include/asm/debugger.h b/arch/mn10300/include/asm/debugger.h
new file mode 100644
index 000000000000..e1d3b083696c
--- /dev/null
+++ b/arch/mn10300/include/asm/debugger.h
@@ -0,0 +1,43 @@
+/* Kernel debugger for MN10300
+ *
+ * Copyright (C) 2011 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 _ASM_DEBUGGER_H
+#define _ASM_DEBUGGER_H
+
+#if defined(CONFIG_KERNEL_DEBUGGER)
+
+extern int debugger_intercept(enum exception_code, int, int, struct pt_regs *);
+extern int at_debugger_breakpoint(struct pt_regs *);
+
+#ifndef CONFIG_MN10300_DEBUGGER_CACHE_NO_FLUSH
+extern void debugger_local_cache_flushinv(void);
+extern void debugger_local_cache_flushinv_one(u8 *);
+#else
+static inline void debugger_local_cache_flushinv(void) {}
+static inline void debugger_local_cache_flushinv_one(u8 *addr) {}
+#endif
+
+#else /* CONFIG_KERNEL_DEBUGGER */
+
+static inline int debugger_intercept(enum exception_code excep,
+				     int signo, int si_code,
+				     struct pt_regs *regs)
+{
+	return 0;
+}
+
+static inline int at_debugger_breakpoint(struct pt_regs *regs)
+{
+	return 0;
+}
+
+#endif /* CONFIG_KERNEL_DEBUGGER */
+#endif /* _ASM_DEBUGGER_H */
diff --git a/arch/mn10300/include/asm/div64.h b/arch/mn10300/include/asm/div64.h
index 34dcb8e68309..503efab2a516 100644
--- a/arch/mn10300/include/asm/div64.h
+++ b/arch/mn10300/include/asm/div64.h
@@ -16,6 +16,19 @@
 extern void ____unhandled_size_in_do_div___(void);
 
 /*
+ * Beginning with gcc 4.6, the MDR register is represented explicitly.  We
+ * must, therefore, at least explicitly clobber the register when we make
+ * changes to it.  The following assembly fragments *could* be rearranged in
+ * order to leave the moves to/from the MDR register to the compiler, but the
+ * gains would be minimal at best.
+ */
+#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
+# define CLOBBER_MDR_CC		"mdr", "cc"
+#else
+# define CLOBBER_MDR_CC		"cc"
+#endif
+
+/*
  * divide n by base, leaving the result in n and returning the remainder
  * - we can do this quite efficiently on the MN10300 by cascading the divides
  *   through the MDR register
@@ -29,7 +42,7 @@ extern void ____unhandled_size_in_do_div___(void);
 		    "mov	mdr,%1	\n"				\
 		    : "+r"(n), "=d"(__rem)				\
 		    : "r"(base), "1"(__rem)				\
-		    : "cc"						\
+		    : CLOBBER_MDR_CC					\
 		    );							\
 	} else if (sizeof(n) <= 8) {					\
 		union {							\
@@ -48,7 +61,7 @@ extern void ____unhandled_size_in_do_div___(void);
 		    : "=d"(__rem), "=r"(__quot.w[1]), "=r"(__quot.w[0])	\
 		    : "r"(base), "0"(__rem), "1"(__quot.w[1]),		\
 		      "2"(__quot.w[0])					\
-		    : "cc"						\
+		    : CLOBBER_MDR_CC					\
 		    );							\
 		n = __quot.l;						\
 	} else {							\
@@ -72,7 +85,7 @@ unsigned __muldiv64u(unsigned val, unsigned mult, unsigned div)
 					 * MDR = MDR:val%div */
 	    : "=r"(result)
 	    : "0"(val), "ir"(mult), "r"(div)
-	    : "cc"
+	    : CLOBBER_MDR_CC
 	    );
 
 	return result;
@@ -93,7 +106,7 @@ signed __muldiv64s(signed val, signed mult, signed div)
 					 * MDR = MDR:val%div */
 	    : "=r"(result)
 	    : "0"(val), "ir"(mult), "r"(div)
-	    : "cc"
+	    : CLOBBER_MDR_CC
 	    );
 
 	return result;
diff --git a/arch/mn10300/include/asm/fpu.h b/arch/mn10300/include/asm/fpu.h
index b7625de8eade..738ff72659d5 100644
--- a/arch/mn10300/include/asm/fpu.h
+++ b/arch/mn10300/include/asm/fpu.h
@@ -55,7 +55,6 @@ static inline void clear_using_fpu(struct task_struct *tsk)
 
 extern asmlinkage void fpu_kill_state(struct task_struct *);
 extern asmlinkage void fpu_exception(struct pt_regs *, enum exception_code);
-extern asmlinkage void fpu_invalid_op(struct pt_regs *, enum exception_code);
 extern asmlinkage void fpu_init_state(void);
 extern asmlinkage void fpu_save(struct fpu_state_struct *);
 extern int fpu_setup_sigcontext(struct fpucontext *buf);
@@ -113,7 +112,6 @@ static inline void flush_fpu(void)
 
 extern asmlinkage
 void unexpected_fpu_exception(struct pt_regs *, enum exception_code);
-#define fpu_invalid_op unexpected_fpu_exception
 #define fpu_exception unexpected_fpu_exception
 
 struct task_struct;
diff --git a/arch/mn10300/include/asm/irqflags.h b/arch/mn10300/include/asm/irqflags.h
index 7a7ae12c7119..678f68d5f37b 100644
--- a/arch/mn10300/include/asm/irqflags.h
+++ b/arch/mn10300/include/asm/irqflags.h
@@ -20,7 +20,7 @@
 /*
  * interrupt control
  * - "disabled": run in IM1/2
- *   - level 0 - GDB stub
+ *   - level 0 - kernel debugger
  *   - level 1 - virtual serial DMA (if present)
  *   - level 5 - normal interrupt priority
  *   - level 6 - timer interrupt
diff --git a/arch/mn10300/include/asm/kgdb.h b/arch/mn10300/include/asm/kgdb.h
new file mode 100644
index 000000000000..eb245f18a708
--- /dev/null
+++ b/arch/mn10300/include/asm/kgdb.h
@@ -0,0 +1,81 @@
+/* Kernel debugger 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.
+ */
+
+#ifndef _ASM_KGDB_H
+#define _ASM_KGDB_H
+
+/*
+ * BUFMAX defines the maximum number of characters in inbound/outbound
+ * buffers at least NUMREGBYTES*2 are needed for register packets
+ * Longer buffer is needed to list all threads
+ */
+#define BUFMAX			1024
+
+/*
+ * Note that this register image is in a different order than the register
+ * image that Linux produces at interrupt time.
+ */
+enum regnames {
+	GDB_FR_D0		= 0,
+	GDB_FR_D1		= 1,
+	GDB_FR_D2		= 2,
+	GDB_FR_D3		= 3,
+	GDB_FR_A0		= 4,
+	GDB_FR_A1		= 5,
+	GDB_FR_A2		= 6,
+	GDB_FR_A3		= 7,
+
+	GDB_FR_SP		= 8,
+	GDB_FR_PC		= 9,
+	GDB_FR_MDR		= 10,
+	GDB_FR_EPSW		= 11,
+	GDB_FR_LIR		= 12,
+	GDB_FR_LAR		= 13,
+	GDB_FR_MDRQ		= 14,
+
+	GDB_FR_E0		= 15,
+	GDB_FR_E1		= 16,
+	GDB_FR_E2		= 17,
+	GDB_FR_E3		= 18,
+	GDB_FR_E4		= 19,
+	GDB_FR_E5		= 20,
+	GDB_FR_E6		= 21,
+	GDB_FR_E7		= 22,
+
+	GDB_FR_SSP		= 23,
+	GDB_FR_MSP		= 24,
+	GDB_FR_USP		= 25,
+	GDB_FR_MCRH		= 26,
+	GDB_FR_MCRL		= 27,
+	GDB_FR_MCVF		= 28,
+
+	GDB_FR_FPCR		= 29,
+	GDB_FR_DUMMY0		= 30,
+	GDB_FR_DUMMY1		= 31,
+
+	GDB_FR_FS0		= 32,
+
+	GDB_FR_SIZE		= 64,
+};
+
+#define GDB_ORIG_D0		41
+#define NUMREGBYTES		(GDB_FR_SIZE*4)
+
+static inline void arch_kgdb_breakpoint(void)
+{
+	asm(".globl __arch_kgdb_breakpoint; __arch_kgdb_breakpoint: break");
+}
+extern u8 __arch_kgdb_breakpoint;
+
+#define BREAK_INSTR_SIZE	1
+#define CACHE_FLUSH_IS_SAFE	1
+
+#endif /* _ASM_KGDB_H */
diff --git a/arch/mn10300/include/asm/smp.h b/arch/mn10300/include/asm/smp.h
index a3930e43a958..6745dbe64944 100644
--- a/arch/mn10300/include/asm/smp.h
+++ b/arch/mn10300/include/asm/smp.h
@@ -34,7 +34,7 @@
 #define LOCAL_TIMER_IPI		193
 #define FLUSH_CACHE_IPI		194
 #define CALL_FUNCTION_NMI_IPI	195
-#define GDB_NMI_IPI		196
+#define DEBUGGER_NMI_IPI	196
 
 #define SMP_BOOT_IRQ		195
 
@@ -43,6 +43,7 @@
 #define LOCAL_TIMER_GxICR_LV	GxICR_LEVEL_4
 #define FLUSH_CACHE_GxICR_LV	GxICR_LEVEL_0
 #define SMP_BOOT_GxICR_LV	GxICR_LEVEL_0
+#define DEBUGGER_GxICR_LV	CONFIG_DEBUGGER_IRQ_LEVEL
 
 #define TIME_OUT_COUNT_BOOT_IPI	100
 #define DELAY_TIME_BOOT_IPI	75000
@@ -61,8 +62,9 @@
  * An alternate way of dealing with this could be to use the EPSW.S bits to
  * cache this information for systems with up to four CPUs.
  */
+#define arch_smp_processor_id()	(CPUID)
 #if 0
-#define raw_smp_processor_id()	(CPUID)
+#define raw_smp_processor_id()	(arch_smp_processor_id())
 #else
 #define raw_smp_processor_id()	(current_thread_info()->cpu)
 #endif
diff --git a/arch/mn10300/include/asm/thread_info.h b/arch/mn10300/include/asm/thread_info.h
index 8d53f09c878d..87c213002d4c 100644
--- a/arch/mn10300/include/asm/thread_info.h
+++ b/arch/mn10300/include/asm/thread_info.h
@@ -131,7 +131,11 @@ static inline unsigned long current_stack_pointer(void)
 		kmalloc_node(THREAD_SIZE, GFP_KERNEL, node)
 #endif
 
+#ifndef CONFIG_KGDB
 #define free_thread_info(ti)	kfree((ti))
+#else
+extern void free_thread_info(struct thread_info *);
+#endif
 #define get_thread_info(ti)	get_task_struct((ti)->task)
 #define put_thread_info(ti)	put_task_struct((ti)->task)
 
diff --git a/arch/mn10300/kernel/Makefile b/arch/mn10300/kernel/Makefile
index a06a2e10051d..47ed30fe8178 100644
--- a/arch/mn10300/kernel/Makefile
+++ b/arch/mn10300/kernel/Makefile
@@ -21,11 +21,8 @@ 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
 
-ifeq ($(CONFIG_MN10300_CACHE_ENABLED),y)
-obj-$(CONFIG_GDBSTUB) += gdb-cache.o
-endif
-
 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/entry.S b/arch/mn10300/kernel/entry.S
index f00b9bafcd3e..fb93ad720b82 100644
--- a/arch/mn10300/kernel/entry.S
+++ b/arch/mn10300/kernel/entry.S
@@ -266,7 +266,11 @@ ENTRY(raw_bus_error)
 
 ###############################################################################
 #
-# Miscellaneous exception entry points
+# 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)
@@ -281,7 +285,7 @@ ENTRY(nmi_handler)
 	and	NMIAGR_GN,d0
 	lsr	0x2,d0
 	cmp	CALL_FUNCTION_NMI_IPI,d0
-	bne	5f			# if not call function, jump
+	bne	nmi_not_smp_callfunc	# if not call function, jump
 
 	# function call nmi ipi
 	add	4,sp			# no need to store TBR
@@ -295,59 +299,38 @@ ENTRY(nmi_handler)
 	call	smp_nmi_call_function_interrupt[],0
 	RESTORE_ALL
 
-5:
-#ifdef CONFIG_GDBSTUB
-	cmp	GDB_NMI_IPI,d0
-	bne	3f			# if not gdb nmi ipi, jump
+nmi_not_smp_callfunc:
+#ifdef CONFIG_KERNEL_DEBUGGER
+	cmp	DEBUGGER_NMI_IPI,d0
+	bne	nmi_not_debugger	# if not kernel debugger NMI IPI, jump
 
-	# gdb nmi ipi
+	# kernel debugger NMI IPI
 	add	4,sp			# no need to store TBR
 	mov	GxICR_DETECT,d0		# clear NMI
-	movbu	d0,(GxICR(GDB_NMI_IPI))
-	movhu	(GxICR(GDB_NMI_IPI)),d0
+	movbu	d0,(GxICR(DEBUGGER_NMI_IPI))
+	movhu	(GxICR(DEBUGGER_NMI_IPI)),d0
 	and	~EPSW_NMID,epsw		# enable NMI
-#ifdef CONFIG_MN10300_CACHE_ENABLED
-	mov	(gdbstub_nmi_opr_type),d0
-	cmp	GDBSTUB_NMI_CACHE_PURGE,d0
-	bne	4f			# if not gdb cache purge, jump
-
-	# gdb cache purge nmi ipi
-	add	-20,sp
-	mov	d1,(4,sp)
-	mov	a0,(8,sp)
-	mov	a1,(12,sp)
-	mov	mdr,d0
-	mov	d0,(16,sp)
-	call	gdbstub_local_purge_cache[],0
-	mov	0x1,d0
-	mov	(CPUID),d1
-	asl	d1,d0
-	mov	gdbstub_nmi_cpumask,a0
-	bclr	d0,(a0)
-	mov	(4,sp),d1
-	mov	(8,sp),a0
-	mov	(12,sp),a1
-	mov	(16,sp),d0
-	mov	d0,mdr
-	add	20,sp
-	mov	(sp),d0
-	add	4,sp
-	rti
-4:
-#endif /* CONFIG_MN10300_CACHE_ENABLED */
-	# gdb wait nmi ipi
+
 	mov     (sp),d0
 	SAVE_ALL
-	call    gdbstub_nmi_wait[],0
+	mov	fp,d0			# arg 0: stacked register file
+	mov	a2,d1			# arg 1: exception number
+	call    debugger_nmi_interrupt[],0
 	RESTORE_ALL
-3:
-#endif /* CONFIG_GDBSTUB */
+
+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)
diff --git a/arch/mn10300/kernel/fpu.c b/arch/mn10300/kernel/fpu.c
index 5f9c3fa19a85..bb5fa7df6c44 100644
--- a/arch/mn10300/kernel/fpu.c
+++ b/arch/mn10300/kernel/fpu.c
@@ -70,24 +70,6 @@ asmlinkage void fpu_exception(struct pt_regs *regs, enum exception_code code)
 }
 
 /*
- * handle an FPU invalid_op exception
- * - Derived from DO_EINFO() macro in arch/mn10300/kernel/traps.c
- */
-asmlinkage void fpu_invalid_op(struct pt_regs *regs, enum exception_code code)
-{
-	siginfo_t info;
-
-	if (!user_mode(regs))
-		die_if_no_fixup("FPU invalid opcode", regs, code);
-
-	info.si_signo = SIGILL;
-	info.si_errno = 0;
-	info.si_code = ILL_COPROC;
-	info.si_addr = (void *) regs->pc;
-	force_sig_info(info.si_signo, &info, current);
-}
-
-/*
  * save the FPU state to a signal context
  */
 int fpu_setup_sigcontext(struct fpucontext *fpucontext)
diff --git a/arch/mn10300/kernel/gdb-cache.S b/arch/mn10300/kernel/gdb-cache.S
deleted file mode 100644
index 1108badc3d32..000000000000
--- a/arch/mn10300/kernel/gdb-cache.S
+++ /dev/null
@@ -1,105 +0,0 @@
-###############################################################################
-#
-# MN10300 Low-level cache purging routines for gdbstub
-#
-# 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 cache purge
-#
-###############################################################################
-	.type	gdbstub_purge_cache,@function
-ENTRY(gdbstub_purge_cache)
-	#######################################################################
-	# read the addresses tagged in the cache's tag RAM and attempt to flush
-	# those addresses specifically
-	# - we rely on the hardware to filter out invalid tag entry addresses
-	mov	DCACHE_TAG(0,0),a0		# dcache tag RAM access address
-	mov	DCACHE_PURGE(0,0),a1		# dcache purge request address
-	mov	L1_CACHE_NWAYS*L1_CACHE_NENTRIES,d1  # total number of entries
-
-mn10300_dcache_flush_loop:
-	mov	(a0),d0
-	and	L1_CACHE_TAG_ADDRESS|L1_CACHE_TAG_ENTRY,d0
-	or	L1_CACHE_TAG_VALID,d0		# retain valid entries in the
-						# cache
-	mov	d0,(a1)				# conditional purge
-
-mn10300_dcache_flush_skip:
-	add	L1_CACHE_BYTES,a0
-	add	L1_CACHE_BYTES,a1
-	add	-1,d1
-	bne	mn10300_dcache_flush_loop
-
-;; 	# unconditionally flush and invalidate the dcache
-;; 	mov	DCACHE_PURGE(0,0),a1		# dcache purge request address
-;; 	mov	L1_CACHE_NWAYS*L1_CACHE_NENTRIES,d1	# total number of
-;;							# entries
-;;
-;; gdbstub_purge_cache__dcache_loop:
-;; 	mov	(a1),d0				# unconditional purge
-;;
-;; 	add	L1_CACHE_BYTES,a1
-;; 	add	-1,d1
-;; 	bne	gdbstub_purge_cache__dcache_loop
-
-	#######################################################################
-	# now invalidate the icache
-	mov	CHCTR,a0
-	movhu	(a0),a1
-
-	mov	epsw,d1
-	and	~EPSW_IE,epsw
-	nop
-	nop
-
-	# disable the icache
-	and	~CHCTR_ICEN,d0
-	movhu	d0,(a0)
-
-	# and wait for it to calm down
-	setlb
-	movhu	(a0),d0
-	btst	CHCTR_ICBUSY,d0
-	lne
-
-	# invalidate
-	or	CHCTR_ICINV,d0
-	movhu	d0,(a0)
-
-	# wait for the cache to finish
-	mov	CHCTR,a0
-	setlb
-	movhu	(a0),d0
-	btst	CHCTR_ICBUSY,d0
-	lne
-
-	# and reenable it
-	movhu	a1,(a0)
-	movhu	(a0),d0			# read back to flush
-					# (SIGILLs all over without this)
-
-	mov	d1,epsw
-
-	ret	[],0
-
-	.size	gdbstub_purge_cache,.-gdbstub_purge_cache
diff --git a/arch/mn10300/kernel/gdb-io-ttysm.c b/arch/mn10300/kernel/gdb-io-ttysm.c
index abdeea153c89..c859cacbb9c3 100644
--- a/arch/mn10300/kernel/gdb-io-ttysm.c
+++ b/arch/mn10300/kernel/gdb-io-ttysm.c
@@ -59,10 +59,10 @@ void __init gdbstub_io_init(void)
 
 	/* we want to get serial receive interrupts */
 	set_intr_level(gdbstub_port->rx_irq,
-		NUM2GxICR_LEVEL(CONFIG_GDBSTUB_IRQ_LEVEL));
+		NUM2GxICR_LEVEL(CONFIG_DEBUGGER_IRQ_LEVEL));
 	set_intr_level(gdbstub_port->tx_irq,
-		NUM2GxICR_LEVEL(CONFIG_GDBSTUB_IRQ_LEVEL));
-	set_intr_stub(NUM2EXCEP_IRQ_LEVEL(CONFIG_GDBSTUB_IRQ_LEVEL),
+		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;
@@ -88,7 +88,7 @@ void __init gdbstub_io_init(void)
 
 	/* permit level 0 IRQs only */
 	arch_local_change_intr_mask_level(
-		NUM2EPSW_IM(CONFIG_GDBSTUB_IRQ_LEVEL + 1));
+		NUM2EPSW_IM(CONFIG_DEBUGGER_IRQ_LEVEL + 1));
 }
 
 /*
diff --git a/arch/mn10300/kernel/gdb-stub.c b/arch/mn10300/kernel/gdb-stub.c
index b169d99d9f20..538266b2c9bc 100644
--- a/arch/mn10300/kernel/gdb-stub.c
+++ b/arch/mn10300/kernel/gdb-stub.c
@@ -133,7 +133,7 @@
 #include <asm/system.h>
 #include <asm/gdb-stub.h>
 #include <asm/exceptions.h>
-#include <asm/cacheflush.h>
+#include <asm/debugger.h>
 #include <asm/serial-regs.h>
 #include <asm/busctl-regs.h>
 #include <unit/leds.h>
@@ -405,6 +405,7 @@ static int hexToInt(char **ptr, int *intValue)
 	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
@@ -729,6 +730,7 @@ static int gdbstub_single_step(struct pt_regs *regs)
 	__gdbstub_restore_bp();
 	return -EFAULT;
 }
+#endif /* CONFIG_GDBSTUB_ALLOW_SINGLE_STEP */
 
 #ifdef CONFIG_GDBSTUB_CONSOLE
 
@@ -1171,7 +1173,7 @@ int gdbstub_clear_breakpoint(u8 *addr, int len)
 
 /*
  * This function does all command processing for interfacing to gdb
- * - returns 1 if the exception should be skipped, 0 otherwise.
+ * - returns 0 if the exception should be skipped, -ERROR otherwise.
  */
 static int gdbstub(struct pt_regs *regs, enum exception_code excep)
 {
@@ -1186,7 +1188,7 @@ static int gdbstub(struct pt_regs *regs, enum exception_code excep)
 	int loop;
 
 	if (excep == EXCEP_FPU_DISABLED)
-		return 0;
+		return -ENOTSUPP;
 
 	gdbstub_flush_caches = 0;
 
@@ -1195,7 +1197,7 @@ static int gdbstub(struct pt_regs *regs, enum exception_code excep)
 	asm volatile("mov mdr,%0" : "=d"(mdr));
 	local_save_flags(epsw);
 	arch_local_change_intr_mask_level(
-		NUM2EPSW_IM(CONFIG_GDBSTUB_IRQ_LEVEL + 1));
+		NUM2EPSW_IM(CONFIG_DEBUGGER_IRQ_LEVEL + 1));
 
 	gdbstub_store_fpu();
 
@@ -1208,11 +1210,13 @@ static int gdbstub(struct pt_regs *regs, enum exception_code excep)
 	/* 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;
@@ -1548,17 +1552,21 @@ packet_waiting:
 			 * Step to next instruction
 			 */
 		case 's':
-			/*
-			 * using the T flag doesn't seem to perform single
+			/* 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)
@@ -1657,7 +1665,7 @@ done:
 	 * NB: We flush both caches, just to be sure...
 	 */
 	if (gdbstub_flush_caches)
-		gdbstub_purge_cache();
+		debugger_local_cache_flushinv();
 
 	gdbstub_load_fpu();
 	mn10300_set_gdbleds(0);
@@ -1667,14 +1675,23 @@ done:
 	touch_softlockup_watchdog();
 
 	local_irq_restore(epsw);
-	return 1;
+	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 gdbstub_intercept(struct pt_regs *regs,
-				 enum exception_code excep)
+asmlinkage int debugger_intercept(enum exception_code excep,
+				  int signo, int si_code, struct pt_regs *regs)
 {
 	static u8 notfirst = 1;
 	int ret;
@@ -1688,7 +1705,7 @@ asmlinkage int gdbstub_intercept(struct pt_regs *regs,
 		asm("mov mdr,%0" : "=d"(mdr));
 
 		gdbstub_entry(
-			"--> gdbstub_intercept(%p,%04x) [MDR=%lx PC=%lx]\n",
+			"--> debugger_intercept(%p,%04x) [MDR=%lx PC=%lx]\n",
 			regs, excep, mdr, regs->pc);
 
 		gdbstub_entry(
@@ -1722,7 +1739,7 @@ asmlinkage int gdbstub_intercept(struct pt_regs *regs,
 
 	ret = gdbstub(regs, excep);
 
-	gdbstub_entry("<-- gdbstub_intercept()\n");
+	gdbstub_entry("<-- debugger_intercept()\n");
 	gdbstub_busy = 0;
 	return ret;
 }
diff --git a/arch/mn10300/kernel/internal.h b/arch/mn10300/kernel/internal.h
index ea946613f46d..a5ac755dd69f 100644
--- a/arch/mn10300/kernel/internal.h
+++ b/arch/mn10300/kernel/internal.h
@@ -30,6 +30,13 @@ 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/irq.c b/arch/mn10300/kernel/irq.c
index f09fed5e6afc..5f7fc3eb45e6 100644
--- a/arch/mn10300/kernel/irq.c
+++ b/arch/mn10300/kernel/irq.c
@@ -153,7 +153,7 @@ mn10300_cpupic_setaffinity(struct irq_data *d, const struct cpumask *mask,
 	case LOCAL_TIMER_IPI:
 	case FLUSH_CACHE_IPI:
 	case CALL_FUNCTION_NMI_IPI:
-	case GDB_NMI_IPI:
+	case DEBUGGER_NMI_IPI:
 #ifdef CONFIG_MN10300_TTYSM0
 	case SC0RXIRQ:
 	case SC0TXIRQ:
diff --git a/arch/mn10300/kernel/kgdb.c b/arch/mn10300/kernel/kgdb.c
new file mode 100644
index 000000000000..f6c981db2a36
--- /dev/null
+++ b/arch/mn10300/kernel/kgdb.c
@@ -0,0 +1,502 @@
+/* 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 free_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();
+	}
+	kfree(ti);
+}
+
+/*
+ * 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/mn10300-serial.c b/arch/mn10300/kernel/mn10300-serial.c
index 93c53739cfc9..efca426a2ed4 100644
--- a/arch/mn10300/kernel/mn10300-serial.c
+++ b/arch/mn10300/kernel/mn10300-serial.c
@@ -119,6 +119,10 @@ 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,
@@ -138,6 +142,10 @@ static const struct uart_ops mn10300_serial_ops = {
 	.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);
@@ -1634,3 +1642,70 @@ static int __init mn10300_serial_console_init(void)
 
 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);
+
+	do {
+		/* pull chars out of the hat */
+		ix = port->rx_outp;
+		if (ix == port->rx_inp)
+			return NO_POLL_CHAR;
+
+		ch = port->rx_buffer[ix++];
+		st = port->rx_buffer[ix++];
+		smp_rmb();
+		port->rx_outp = ix & (MNSC_BUFFER_SIZE - 1);
+
+	} while (st & (SC01STR_FEF | SC01STR_PEF | SC01STR_OEF));
+
+	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) {
+		*(u8 *) port->_txb = 0x0d;
+		while (*port->_status & SC01STR_TBF)
+			continue;
+	}
+
+	*(u8 *) 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/process.c b/arch/mn10300/kernel/process.c
index e1b14a6ed544..28eec3102535 100644
--- a/arch/mn10300/kernel/process.c
+++ b/arch/mn10300/kernel/process.c
@@ -135,7 +135,7 @@ void release_segments(struct mm_struct *mm)
 
 void machine_restart(char *cmd)
 {
-#ifdef CONFIG_GDBSTUB
+#ifdef CONFIG_KERNEL_DEBUGGER
 	gdbstub_exit(0);
 #endif
 
@@ -148,14 +148,14 @@ void machine_restart(char *cmd)
 
 void machine_halt(void)
 {
-#ifdef CONFIG_GDBSTUB
+#ifdef CONFIG_KERNEL_DEBUGGER
 	gdbstub_exit(0);
 #endif
 }
 
 void machine_power_off(void)
 {
-#ifdef CONFIG_GDBSTUB
+#ifdef CONFIG_KERNEL_DEBUGGER
 	gdbstub_exit(0);
 #endif
 }
diff --git a/arch/mn10300/kernel/smp.c b/arch/mn10300/kernel/smp.c
index 1ebb79f1650d..51c02f97dcea 100644
--- a/arch/mn10300/kernel/smp.c
+++ b/arch/mn10300/kernel/smp.c
@@ -440,6 +440,22 @@ int smp_nmi_call_function(smp_call_func_t func, void *info, int wait)
 }
 
 /**
+ * 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).
  */
@@ -603,7 +619,7 @@ static void __init smp_cpu_init(void)
 /**
  * smp_prepare_cpu_init - Initialise CPU in startup_secondary
  *
- * Set interrupt level 0-6 setting and init ICR of gdbstub.
+ * Set interrupt level 0-6 setting and init ICR of the kernel debugger.
  */
 void smp_prepare_cpu_init(void)
 {
@@ -622,15 +638,15 @@ void smp_prepare_cpu_init(void)
 	for (loop = 0; loop < GxICR_NUM_IRQS; loop++)
 		GxICR(loop) = GxICR_LEVEL_6 | GxICR_DETECT;
 
-#ifdef CONFIG_GDBSTUB
-	/* initialise GDB-stub */
+#ifdef CONFIG_KERNEL_DEBUGGER
+	/* initialise the kernel debugger interrupt */
 	do {
 		unsigned long flags;
 		u16 tmp16;
 
 		flags = arch_local_cli_save();
-		GxICR(GDB_NMI_IPI) = GxICR_NMI | GxICR_ENABLE | GxICR_DETECT;
-		tmp16 = GxICR(GDB_NMI_IPI);
+		GxICR(DEBUGGER_NMI_IPI) = GxICR_NMI | GxICR_ENABLE | GxICR_DETECT;
+		tmp16 = GxICR(DEBUGGER_NMI_IPI);
 		arch_local_irq_restore(flags);
 	} while (0);
 #endif
diff --git a/arch/mn10300/kernel/switch_to.S b/arch/mn10300/kernel/switch_to.S
index 9074d0fb8788..de3e74fc9ea0 100644
--- a/arch/mn10300/kernel/switch_to.S
+++ b/arch/mn10300/kernel/switch_to.S
@@ -39,11 +39,17 @@ ENTRY(__switch_to)
 
 	# save prev context
 	mov	__switch_back,d0
-	mov	d0,(THREAD_PC,a0)
 	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
 
@@ -68,3 +74,106 @@ ENTRY(__switch_to)
 __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/traps.c b/arch/mn10300/kernel/traps.c
index b90c3f160c77..f03cb278828f 100644
--- a/arch/mn10300/kernel/traps.c
+++ b/arch/mn10300/kernel/traps.c
@@ -38,8 +38,9 @@
 #include <asm/busctl-regs.h>
 #include <unit/leds.h>
 #include <asm/fpu.h>
-#include <asm/gdb-stub.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!"
@@ -49,63 +50,169 @@ int kstack_depth_to_print = 24;
 
 spinlock_t die_lock = __SPIN_LOCK_UNLOCKED(die_lock);
 
-ATOMIC_NOTIFIER_HEAD(mn10300_die_chain);
+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 },
+};
 
 /*
- * These constants are for searching for possible module text
- * segments. MODULE_RANGE is a guess of how much space is likely
- * to be vmalloced.
+ * 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
  */
-#define MODULE_RANGE (8 * 1024 * 1024)
-
-#define DO_ERROR(signr, prologue, str, name)			\
-asmlinkage void name(struct pt_regs *regs, u32 intcode)		\
-{								\
-	prologue;						\
-	if (die_if_no_fixup(str, regs, intcode))		\
-		return;						\
-	force_sig(signr, current);				\
-}
+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;
 
-#define DO_EINFO(signr, prologue, str, name, sicode)			\
-asmlinkage void name(struct pt_regs *regs, u32 intcode)			\
-{									\
-	siginfo_t info;							\
-	prologue;							\
-	if (die_if_no_fixup(str, regs, intcode))			\
-		return;							\
-	info.si_signo = signr;						\
-	if (signr == SIGILL && sicode == ILL_ILLOPC) {			\
-		uint8_t opcode;						\
-		if (get_user(opcode, (uint8_t __user *)regs->pc) == 0)	\
-			if (opcode == 0xff)				\
-				info.si_signo = SIGTRAP;		\
-	}								\
-	info.si_errno = 0;						\
-	info.si_code = sicode;						\
-	info.si_addr = (void *) regs->pc;				\
-	force_sig_info(info.si_signo, &info, current);			\
+	case EXCEP_TRAP:
+	case EXCEP_UNIMPINS:
+		if (get_user(opcode, (uint8_t __user *)regs->pc) != 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);
 }
 
-DO_ERROR(SIGTRAP, {}, "trap",			trap);
-DO_ERROR(SIGSEGV, {}, "ibreak",			ibreak);
-DO_ERROR(SIGSEGV, {}, "obreak",			obreak);
-DO_EINFO(SIGSEGV, {}, "access error",		access_error,	SEGV_ACCERR);
-DO_EINFO(SIGSEGV, {}, "insn access error",	insn_acc_error,	SEGV_ACCERR);
-DO_EINFO(SIGSEGV, {}, "data access error",	data_acc_error,	SEGV_ACCERR);
-DO_EINFO(SIGILL,  {}, "privileged opcode",	priv_op,	ILL_PRVOPC);
-DO_EINFO(SIGILL,  {}, "invalid opcode",		invalid_op,	ILL_ILLOPC);
-DO_EINFO(SIGILL,  {}, "invalid ex opcode",	invalid_exop,	ILL_ILLOPC);
-DO_EINFO(SIGBUS,  {}, "invalid address",	mem_error,	BUS_ADRERR);
-DO_EINFO(SIGBUS,  {}, "bus error",		bus_error,	BUS_ADRERR);
-
-DO_ERROR(SIGTRAP,
-#ifndef CONFIG_MN10300_USING_JTAG
-	 DCR &= ~0x0001,
-#else
-	 {},
-#endif
-	 "single step", istep);
+/*
+ * 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
@@ -113,10 +220,8 @@ DO_ERROR(SIGTRAP,
 asmlinkage void nmi(struct pt_regs *regs, enum exception_code code)
 {
 	/* see if gdbstub wants to deal with it */
-#ifdef CONFIG_GDBSTUB
-	if (gdbstub_intercept(regs, code))
+	if (debugger_intercept(code, SIGQUIT, 0, regs))
 		return;
-#endif
 
 	printk(KERN_WARNING "--- Register Dump ---\n");
 	show_registers(regs);
@@ -128,29 +233,36 @@ asmlinkage void nmi(struct pt_regs *regs, enum exception_code code)
  */
 void show_trace(unsigned long *sp)
 {
-	unsigned long *stack, addr, module_start, module_end;
-	int i;
-
-	printk(KERN_EMERG "\nCall Trace:");
-
-	stack = sp;
-	i = 0;
-	module_start = VMALLOC_START;
-	module_end = VMALLOC_END;
+	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;
+		}
 
-	while (((long) stack & (THREAD_SIZE - 1)) != 0) {
-		addr = *stack++;
 		if (__kernel_text_address(addr)) {
-#if 1
 			printk(" [<%08lx>]", addr);
+			if (stack >= raslot)
+				raslot = ULONG_MAX;
+			else
+				printk(" ?");
 			print_symbol(" %s", addr);
 			printk("\n");
-#else
-			if ((i % 6) == 0)
-				printk(KERN_EMERG "  ");
-			printk("[<%08lx>] ", addr);
-			i++;
-#endif
 		}
 	}
 
@@ -323,86 +435,6 @@ void die(const char *str, struct pt_regs *regs, enum exception_code code)
 }
 
 /*
- * 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)
-{
-	if (user_mode(regs))
-		return 0;
-
-	peripheral_leds_display_exception(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;
-	case EXCEP_UNIMPINS:
-		if (regs->pc && *(uint8_t *)regs->pc == 0xff)
-			if (notify_die(DIE_BREAKPOINT, str, regs, code, 0, 0))
-				return 1;
-		break;
-	default:
-		break;
-	}
-
-	/* see if gdbstub wants to deal with it */
-#ifdef CONFIG_GDBSTUB
-	if (gdbstub_intercept(regs, code))
-		return 1;
-#endif
-
-	if (notify_die(DIE_GPF, str, regs, code, 0, 0))
-		return 1;
-
-	/* make the process die as the last resort */
-	die(str, regs, code);
-}
-
-/*
- * handle unsupported syscall instructions (syscall 1-15)
- */
-static asmlinkage void unsupported_syscall(struct pt_regs *regs,
-					   enum exception_code code)
-{
-	struct task_struct *tsk = current;
-	siginfo_t info;
-
-	/* catch a kernel BUG() */
-	if (code == EXCEP_SYSCALL15 && !user_mode(regs)) {
-		if (report_bug(regs->pc, regs) == BUG_TRAP_TYPE_BUG) {
-#ifdef CONFIG_GDBSTUB
-			gdbstub_intercept(regs, code);
-#endif
-		}
-	}
-
-	regs->pc -= 2; /* syscall return addr is _after_ the instruction */
-
-	die_if_no_fixup("An unsupported syscall insn was used by the kernel\n",
-			regs, code);
-
-	info.si_signo	= SIGILL;
-	info.si_errno	= ENOSYS;
-	info.si_code	= ILL_ILLTRP;
-	info.si_addr	= (void *) regs->pc;
-	force_sig_info(SIGILL, &info, tsk);
-}
-
-/*
  * display the register file when the stack pointer gets clobbered
  */
 asmlinkage void do_double_fault(struct pt_regs *regs)
@@ -481,10 +513,8 @@ asmlinkage void uninitialised_exception(struct pt_regs *regs,
 {
 
 	/* see if gdbstub wants to deal with it */
-#ifdef CONFIG_GDBSTUB
-	if (gdbstub_intercept(regs, code))
+	if (debugger_intercept(code, SIGSYS, 0, regs) == 0)
 		return;
-#endif
 
 	peripheral_leds_display_exception(code);
 	printk(KERN_EMERG "Uninitialised Exception 0x%04x\n", code & 0xFFFF);
@@ -549,43 +579,43 @@ void __init set_intr_stub(enum exception_code code, void *handler)
  */
 void __init trap_init(void)
 {
-	set_excp_vector(EXCEP_TRAP,		trap);
-	set_excp_vector(EXCEP_ISTEP,		istep);
-	set_excp_vector(EXCEP_IBREAK,		ibreak);
-	set_excp_vector(EXCEP_OBREAK,		obreak);
-
-	set_excp_vector(EXCEP_PRIVINS,		priv_op);
-	set_excp_vector(EXCEP_UNIMPINS,		invalid_op);
-	set_excp_vector(EXCEP_UNIMPEXINS,	invalid_exop);
-	set_excp_vector(EXCEP_MEMERR,		mem_error);
+	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,		bus_error);
-	set_excp_vector(EXCEP_ILLINSACC,	insn_acc_error);
-	set_excp_vector(EXCEP_ILLDATACC,	data_acc_error);
-	set_excp_vector(EXCEP_IOINSACC,		insn_acc_error);
-	set_excp_vector(EXCEP_PRIVINSACC,	insn_acc_error);
-	set_excp_vector(EXCEP_PRIVDATACC,	data_acc_error);
-	set_excp_vector(EXCEP_DATINSACC,	insn_acc_error);
-	set_excp_vector(EXCEP_FPU_UNIMPINS,	fpu_invalid_op);
+	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,		unsupported_syscall);
-	set_excp_vector(EXCEP_SYSCALL2,		unsupported_syscall);
-	set_excp_vector(EXCEP_SYSCALL3,		unsupported_syscall);
-	set_excp_vector(EXCEP_SYSCALL4,		unsupported_syscall);
-	set_excp_vector(EXCEP_SYSCALL5,		unsupported_syscall);
-	set_excp_vector(EXCEP_SYSCALL6,		unsupported_syscall);
-	set_excp_vector(EXCEP_SYSCALL7,		unsupported_syscall);
-	set_excp_vector(EXCEP_SYSCALL8,		unsupported_syscall);
-	set_excp_vector(EXCEP_SYSCALL9,		unsupported_syscall);
-	set_excp_vector(EXCEP_SYSCALL10,	unsupported_syscall);
-	set_excp_vector(EXCEP_SYSCALL11,	unsupported_syscall);
-	set_excp_vector(EXCEP_SYSCALL12,	unsupported_syscall);
-	set_excp_vector(EXCEP_SYSCALL13,	unsupported_syscall);
-	set_excp_vector(EXCEP_SYSCALL14,	unsupported_syscall);
-	set_excp_vector(EXCEP_SYSCALL15,	unsupported_syscall);
+	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);
 }
 
 /*
diff --git a/arch/mn10300/mm/Kconfig.cache b/arch/mn10300/mm/Kconfig.cache
index c4fd923a55a0..bfbe52691f2c 100644
--- a/arch/mn10300/mm/Kconfig.cache
+++ b/arch/mn10300/mm/Kconfig.cache
@@ -99,3 +99,49 @@ config MN10300_CACHE_INV_ICACHE
 	help
 	  Set if we need the icache to be invalidated, even if the dcache is in
 	  write-through mode and doesn't need flushing.
+
+#
+# The kernel debugger gets its own separate cache flushing functions
+#
+config MN10300_DEBUGGER_CACHE_FLUSH_BY_TAG
+	def_bool y if KERNEL_DEBUGGER && \
+			MN10300_CACHE_WBACK && \
+			!MN10300_CACHE_SNOOP && \
+			MN10300_CACHE_MANAGE_BY_TAG
+	help
+	  Set if the debugger needs to flush the dcache and invalidate the
+	  icache using the cache tag registers to make breakpoints work.
+
+config MN10300_DEBUGGER_CACHE_FLUSH_BY_REG
+	def_bool y if KERNEL_DEBUGGER && \
+			MN10300_CACHE_WBACK && \
+			!MN10300_CACHE_SNOOP && \
+			MN10300_CACHE_MANAGE_BY_REG
+	help
+	  Set if the debugger needs to flush the dcache and invalidate the
+	  icache using automatic purge registers to make breakpoints work.
+
+config MN10300_DEBUGGER_CACHE_INV_BY_TAG
+	def_bool y if KERNEL_DEBUGGER && \
+			MN10300_CACHE_WTHRU && \
+			!MN10300_CACHE_SNOOP && \
+			MN10300_CACHE_MANAGE_BY_TAG
+	help
+	  Set if the debugger needs to invalidate the icache using the cache
+	  tag registers to make breakpoints work.
+
+config MN10300_DEBUGGER_CACHE_INV_BY_REG
+	def_bool y if KERNEL_DEBUGGER && \
+			MN10300_CACHE_WTHRU && \
+			!MN10300_CACHE_SNOOP && \
+			MN10300_CACHE_MANAGE_BY_REG
+	help
+	  Set if the debugger needs to invalidate the icache using automatic
+	  purge registers to make breakpoints work.
+
+config MN10300_DEBUGGER_CACHE_NO_FLUSH
+	def_bool y if KERNEL_DEBUGGER && \
+			(MN10300_CACHE_DISABLED || MN10300_CACHE_SNOOP)
+	help
+	  Set if the debugger does not need to flush the dcache and/or
+	  invalidate the icache to make breakpoints work.
diff --git a/arch/mn10300/mm/Makefile b/arch/mn10300/mm/Makefile
index 203fee23f7d7..11f38466ac28 100644
--- a/arch/mn10300/mm/Makefile
+++ b/arch/mn10300/mm/Makefile
@@ -13,6 +13,15 @@ cacheflush-$(CONFIG_MN10300_CACHE_INV_BY_REG) += cache-inv-by-reg.o
 cacheflush-$(CONFIG_MN10300_CACHE_FLUSH_BY_TAG) += cache-flush-by-tag.o
 cacheflush-$(CONFIG_MN10300_CACHE_FLUSH_BY_REG) += cache-flush-by-reg.o
 
+cacheflush-$(CONFIG_MN10300_DEBUGGER_CACHE_FLUSH_BY_TAG) += \
+	cache-dbg-flush-by-tag.o cache-dbg-inv-by-tag.o
+cacheflush-$(CONFIG_MN10300_DEBUGGER_CACHE_FLUSH_BY_REG) += \
+	cache-dbg-flush-by-reg.o
+cacheflush-$(CONFIG_MN10300_DEBUGGER_CACHE_INV_BY_TAG) += \
+	cache-dbg-inv-by-tag.o cache-dbg-inv.o
+cacheflush-$(CONFIG_MN10300_DEBUGGER_CACHE_INV_BY_REG) += \
+	cache-dbg-inv-by-reg.o cache-dbg-inv.o
+
 cacheflush-$(CONFIG_MN10300_CACHE_DISABLED) := cache-disabled.o
 
 obj-y := \
diff --git a/arch/mn10300/mm/cache-dbg-flush-by-reg.S b/arch/mn10300/mm/cache-dbg-flush-by-reg.S
new file mode 100644
index 000000000000..665919f2ab62
--- /dev/null
+++ b/arch/mn10300/mm/cache-dbg-flush-by-reg.S
@@ -0,0 +1,160 @@
+/* MN10300 CPU cache invalidation routines, using automatic purge registers
+ *
+ * Copyright (C) 2011 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/page.h>
+#include <asm/cache.h>
+#include <asm/irqflags.h>
+#include <asm/cacheflush.h>
+#include "cache.inc"
+
+	.am33_2
+
+###############################################################################
+#
+# void debugger_local_cache_flushinv(void)
+# Flush the entire data cache back to RAM and invalidate the icache
+#
+###############################################################################
+	ALIGN
+	.globl	debugger_local_cache_flushinv
+        .type	debugger_local_cache_flushinv,@function
+debugger_local_cache_flushinv:
+	#
+	# firstly flush the dcache
+	#
+	movhu	(CHCTR),d0
+	btst	CHCTR_DCEN|CHCTR_ICEN,d0
+	beq	debugger_local_cache_flushinv_end
+
+	mov	DCPGCR,a0
+
+	mov	epsw,d1
+	and	~EPSW_IE,epsw
+	or	EPSW_NMID,epsw
+	nop
+
+	btst	CHCTR_DCEN,d0
+	beq	debugger_local_cache_flushinv_no_dcache
+
+	# wait for busy bit of area purge
+	setlb
+	mov	(a0),d0
+	btst	DCPGCR_DCPGBSY,d0
+	lne
+
+	# set mask
+	clr	d0
+	mov	d0,(DCPGMR)
+
+	# area purge
+	#
+	# DCPGCR = DCPGCR_DCP
+	#
+	mov	DCPGCR_DCP,d0
+	mov	d0,(a0)
+
+	# wait for busy bit of area purge
+	setlb
+	mov	(a0),d0
+	btst	DCPGCR_DCPGBSY,d0
+	lne
+
+debugger_local_cache_flushinv_no_dcache:
+	#
+	# secondly, invalidate the icache if it is enabled
+	#
+	mov	CHCTR,a0
+	movhu	(a0),d0
+	btst	CHCTR_ICEN,d0
+	beq	debugger_local_cache_flushinv_done
+
+	invalidate_icache 0
+
+debugger_local_cache_flushinv_done:
+	mov	d1,epsw
+
+debugger_local_cache_flushinv_end:
+	ret	[],0
+	.size	debugger_local_cache_flushinv,.-debugger_local_cache_flushinv
+
+###############################################################################
+#
+# void debugger_local_cache_flushinv_one(u8 *addr)
+#
+# Invalidate one particular cacheline if it's in the icache
+#
+###############################################################################
+	ALIGN
+	.globl	debugger_local_cache_flushinv_one
+	.type	debugger_local_cache_flushinv_one,@function
+debugger_local_cache_flushinv_one:
+	movhu	(CHCTR),d1
+	btst	CHCTR_DCEN|CHCTR_ICEN,d1
+	beq	debugger_local_cache_flushinv_one_end
+	btst	CHCTR_DCEN,d1
+	beq	debugger_local_cache_flushinv_one_no_dcache
+
+	# round cacheline addr down
+	and	L1_CACHE_TAG_MASK,d0
+	mov	d0,a1
+	mov	d0,d1
+
+	# determine the dcache purge control reg address
+	mov	DCACHE_PURGE(0,0),a0
+	and	L1_CACHE_TAG_ENTRY,d0
+	add	d0,a0
+
+	# retain valid entries in the cache
+	or	L1_CACHE_TAG_VALID,d1
+
+	# conditionally purge this line in all ways
+	mov	d1,(L1_CACHE_WAYDISP*0,a0)
+
+debugger_local_cache_flushinv_no_dcache:
+	#
+	# now try to flush the icache
+	#
+	mov	CHCTR,a0
+	movhu	(a0),d0
+	btst	CHCTR_ICEN,d0
+	beq	mn10300_local_icache_inv_range_reg_end
+
+	LOCAL_CLI_SAVE(d1)
+
+	mov	ICIVCR,a0
+
+	# wait for the invalidator to quiesce
+	setlb
+	mov	(a0),d0
+	btst	ICIVCR_ICIVBSY,d0
+	lne
+
+	# set the mask
+	mov	L1_CACHE_TAG_MASK,d0
+	mov	d0,(ICIVMR)
+
+	# invalidate the cache line at the given address
+	or	ICIVCR_ICI,a1
+	mov	a1,(a0)
+
+	# wait for the invalidator to quiesce again
+	setlb
+	mov	(a0),d0
+	btst	ICIVCR_ICIVBSY,d0
+	lne
+
+	LOCAL_IRQ_RESTORE(d1)
+
+debugger_local_cache_flushinv_one_end:
+	ret	[],0
+	.size	debugger_local_cache_flushinv_one,.-debugger_local_cache_flushinv_one
diff --git a/arch/mn10300/mm/cache-dbg-flush-by-tag.S b/arch/mn10300/mm/cache-dbg-flush-by-tag.S
new file mode 100644
index 000000000000..bf56930e6e70
--- /dev/null
+++ b/arch/mn10300/mm/cache-dbg-flush-by-tag.S
@@ -0,0 +1,114 @@
+/* MN10300 CPU cache invalidation routines, using direct tag flushing
+ *
+ * Copyright (C) 2011 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/page.h>
+#include <asm/cache.h>
+#include <asm/irqflags.h>
+#include <asm/cacheflush.h>
+#include "cache.inc"
+
+	.am33_2
+
+###############################################################################
+#
+# void debugger_local_cache_flushinv(void)
+#
+# Flush the entire data cache back to RAM and invalidate the icache
+#
+###############################################################################
+	ALIGN
+	.globl	debugger_local_cache_flushinv
+        .type	debugger_local_cache_flushinv,@function
+debugger_local_cache_flushinv:
+	#
+	# firstly flush the dcache
+	#
+	movhu	(CHCTR),d0
+	btst	CHCTR_DCEN|CHCTR_ICEN,d0
+	beq	debugger_local_cache_flushinv_end
+
+	btst	CHCTR_DCEN,d0
+	beq	debugger_local_cache_flushinv_no_dcache
+
+	# read the addresses tagged in the cache's tag RAM and attempt to flush
+	# those addresses specifically
+	# - we rely on the hardware to filter out invalid tag entry addresses
+	mov	DCACHE_TAG(0,0),a0		# dcache tag RAM access address
+	mov	DCACHE_PURGE(0,0),a1		# dcache purge request address
+	mov	L1_CACHE_NWAYS*L1_CACHE_NENTRIES,e0  # total number of entries
+
+mn10300_local_dcache_flush_loop:
+	mov	(a0),d0
+	and	L1_CACHE_TAG_MASK,d0
+	or	L1_CACHE_TAG_VALID,d0		# retain valid entries in the
+						# cache
+	mov	d0,(a1)				# conditional purge
+
+	add	L1_CACHE_BYTES,a0
+	add	L1_CACHE_BYTES,a1
+	add	-1,e0
+	bne	mn10300_local_dcache_flush_loop
+
+debugger_local_cache_flushinv_no_dcache:
+	#
+	# secondly, invalidate the icache if it is enabled
+	#
+	mov	CHCTR,a0
+	movhu	(a0),d0
+	btst	CHCTR_ICEN,d0
+	beq	debugger_local_cache_flushinv_end
+
+	invalidate_icache 1
+
+debugger_local_cache_flushinv_end:
+	ret	[],0
+	.size	debugger_local_cache_flushinv,.-debugger_local_cache_flushinv
+
+###############################################################################
+#
+# void debugger_local_cache_flushinv_one(u8 *addr)
+#
+# Invalidate one particular cacheline if it's in the icache
+#
+###############################################################################
+	ALIGN
+	.globl	debugger_local_cache_flushinv_one
+	.type	debugger_local_cache_flushinv_one,@function
+debugger_local_cache_flushinv_one:
+	movhu	(CHCTR),d1
+	btst	CHCTR_DCEN|CHCTR_ICEN,d1
+	beq	debugger_local_cache_flushinv_one_end
+	btst	CHCTR_DCEN,d1
+	beq	debugger_local_cache_flushinv_one_icache
+
+	# round cacheline addr down
+	and	L1_CACHE_TAG_MASK,d0
+	mov	d0,a1
+
+	# determine the dcache purge control reg address
+	mov	DCACHE_PURGE(0,0),a0
+	and	L1_CACHE_TAG_ENTRY,d0
+	add	d0,a0
+
+	# retain valid entries in the cache
+	or	L1_CACHE_TAG_VALID,a1
+
+	# conditionally purge this line in all ways
+	mov	a1,(L1_CACHE_WAYDISP*0,a0)
+
+	# now go and do the icache
+	bra	debugger_local_cache_flushinv_one_icache
+
+debugger_local_cache_flushinv_one_end:
+	ret	[],0
+	.size	debugger_local_cache_flushinv_one,.-debugger_local_cache_flushinv_one
diff --git a/arch/mn10300/mm/cache-dbg-inv-by-reg.S b/arch/mn10300/mm/cache-dbg-inv-by-reg.S
new file mode 100644
index 000000000000..c4e6252941b1
--- /dev/null
+++ b/arch/mn10300/mm/cache-dbg-inv-by-reg.S
@@ -0,0 +1,69 @@
+/* MN10300 CPU cache invalidation routines, using automatic purge registers
+ *
+ * Copyright (C) 2011 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/cache.h>
+#include <asm/irqflags.h>
+#include <asm/cacheflush.h>
+#include "cache.inc"
+
+	.am33_2
+
+	.globl	debugger_local_cache_flushinv_one
+
+###############################################################################
+#
+# void debugger_local_cache_flushinv_one(u8 *addr)
+#
+# Invalidate one particular cacheline if it's in the icache
+#
+###############################################################################
+	ALIGN
+	.globl	debugger_local_cache_flushinv_one
+	.type	debugger_local_cache_flushinv_one,@function
+debugger_local_cache_flushinv_one:
+	mov	d0,a1
+
+	mov	CHCTR,a0
+	movhu	(a0),d0
+	btst	CHCTR_ICEN,d0
+	beq	mn10300_local_icache_inv_range_reg_end
+
+	LOCAL_CLI_SAVE(d1)
+
+	mov	ICIVCR,a0
+
+	# wait for the invalidator to quiesce
+	setlb
+	mov	(a0),d0
+	btst	ICIVCR_ICIVBSY,d0
+	lne
+
+	# set the mask
+	mov	~L1_CACHE_TAG_MASK,d0
+	mov	d0,(ICIVMR)
+
+	# invalidate the cache line at the given address
+	and	~L1_CACHE_TAG_MASK,a1
+	or	ICIVCR_ICI,a1
+	mov	a1,(a0)
+
+	# wait for the invalidator to quiesce again
+	setlb
+	mov	(a0),d0
+	btst	ICIVCR_ICIVBSY,d0
+	lne
+
+	LOCAL_IRQ_RESTORE(d1)
+
+mn10300_local_icache_inv_range_reg_end:
+	ret	[],0
+	.size	debugger_local_cache_flushinv_one,.-debugger_local_cache_flushinv_one
diff --git a/arch/mn10300/mm/cache-dbg-inv-by-tag.S b/arch/mn10300/mm/cache-dbg-inv-by-tag.S
new file mode 100644
index 000000000000..d8ec821e5f88
--- /dev/null
+++ b/arch/mn10300/mm/cache-dbg-inv-by-tag.S
@@ -0,0 +1,120 @@
+/* MN10300 CPU cache invalidation routines, using direct tag flushing
+ *
+ * Copyright (C) 2011 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/page.h>
+#include <asm/cache.h>
+#include <asm/irqflags.h>
+#include <asm/cacheflush.h>
+#include "cache.inc"
+
+	.am33_2
+
+	.globl	debugger_local_cache_flushinv_one_icache
+
+###############################################################################
+#
+# void debugger_local_cache_flushinv_one(u8 *addr)
+#
+# Invalidate one particular cacheline if it's in the icache
+#
+###############################################################################
+	ALIGN
+	.globl	debugger_local_cache_flushinv_one_icache
+	.type	debugger_local_cache_flushinv_one_icache,@function
+debugger_local_cache_flushinv_one_icache:
+	movm	[d3,a2],(sp)
+
+	mov	CHCTR,a2
+	movhu	(a2),d0
+	btst	CHCTR_ICEN,d0
+	beq	debugger_local_cache_flushinv_one_icache_end
+
+	mov	d0,a1
+	and	L1_CACHE_TAG_MASK,a1
+
+	# read the tags from the tag RAM, and if they indicate a matching valid
+	# cache line then we invalidate that line
+	mov	ICACHE_TAG(0,0),a0
+	mov	a1,d0
+	and	L1_CACHE_TAG_ENTRY,d0
+	add	d0,a0				# starting icache tag RAM
+						# access address
+
+	and	~(L1_CACHE_DISPARITY-1),a1	# determine comparator base
+	or	L1_CACHE_TAG_VALID,a1
+	mov	L1_CACHE_TAG_ADDRESS|L1_CACHE_TAG_VALID,d1
+
+	LOCAL_CLI_SAVE(d3)
+
+	# disable the icache
+	movhu	(a2),d0
+	and	~CHCTR_ICEN,d0
+	movhu	d0,(a2)
+
+	# and wait for it to calm down
+	setlb
+	movhu	(a2),d0
+	btst	CHCTR_ICBUSY,d0
+	lne
+
+	# check all the way tags for this cache entry
+	mov	(a0),d0				# read the tag in the way 0 slot
+	xor	a1,d0
+	and	d1,d0
+	beq	debugger_local_icache_kill	# jump if matched
+
+	add	L1_CACHE_WAYDISP,a0
+	mov	(a0),d0				# read the tag in the way 1 slot
+	xor	a1,d0
+	and	d1,d0
+	beq	debugger_local_icache_kill	# jump if matched
+
+	add	L1_CACHE_WAYDISP,a0
+	mov	(a0),d0				# read the tag in the way 2 slot
+	xor	a1,d0
+	and	d1,d0
+	beq	debugger_local_icache_kill	# jump if matched
+
+	add	L1_CACHE_WAYDISP,a0
+	mov	(a0),d0				# read the tag in the way 3 slot
+	xor	a1,d0
+	and	d1,d0
+	bne	debugger_local_icache_finish	# jump if not matched
+
+debugger_local_icache_kill:
+	mov	d0,(a0)				# kill the tag (D0 is 0 at this point)
+
+debugger_local_icache_finish:
+	# wait for the cache to finish what it's doing
+	setlb
+	movhu	(a2),d0
+	btst	CHCTR_ICBUSY,d0
+	lne
+
+	# and reenable it
+	or	CHCTR_ICEN,d0
+	movhu	d0,(a2)
+	movhu	(a2),d0
+
+	# re-enable interrupts
+	LOCAL_IRQ_RESTORE(d3)
+
+debugger_local_cache_flushinv_one_icache_end:
+	ret	[d3,a2],8
+	.size	debugger_local_cache_flushinv_one_icache,.-debugger_local_cache_flushinv_one_icache
+
+#ifdef CONFIG_MN10300_DEBUGGER_CACHE_INV_BY_TAG
+	.globl	debugger_local_cache_flushinv_one
+	.type	debugger_local_cache_flushinv_one,@function
+debugger_local_cache_flushinv_one = debugger_local_cache_flushinv_one_icache
+#endif
diff --git a/arch/mn10300/mm/cache-dbg-inv.S b/arch/mn10300/mm/cache-dbg-inv.S
new file mode 100644
index 000000000000..eba2d6dca066
--- /dev/null
+++ b/arch/mn10300/mm/cache-dbg-inv.S
@@ -0,0 +1,47 @@
+/* MN10300 CPU cache invalidation routines
+ *
+ * Copyright (C) 2011 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/page.h>
+#include <asm/cache.h>
+#include <asm/irqflags.h>
+#include <asm/cacheflush.h>
+#include "cache.inc"
+
+	.am33_2
+
+	.globl	debugger_local_cache_flushinv
+
+###############################################################################
+#
+# void debugger_local_cache_flushinv(void)
+#
+# Invalidate the entire icache
+#
+###############################################################################
+	ALIGN
+	.globl	debugger_local_cache_flushinv
+        .type	debugger_local_cache_flushinv,@function
+debugger_local_cache_flushinv:
+	#
+	# we only need to invalidate the icache in this cache mode
+	#
+	mov	CHCTR,a0
+	movhu	(a0),d0
+	btst	CHCTR_ICEN,d0
+	beq	debugger_local_cache_flushinv_end
+
+	invalidate_icache 1
+
+debugger_local_cache_flushinv_end:
+	ret	[],0
+	.size	debugger_local_cache_flushinv,.-debugger_local_cache_flushinv
diff --git a/arch/mn10300/mm/cache-flush-by-tag.S b/arch/mn10300/mm/cache-flush-by-tag.S
index 5cd6a27dd63e..1ddc06849242 100644
--- a/arch/mn10300/mm/cache-flush-by-tag.S
+++ b/arch/mn10300/mm/cache-flush-by-tag.S
@@ -62,7 +62,7 @@ mn10300_local_dcache_flush:
 
 mn10300_local_dcache_flush_loop:
 	mov	(a0),d0
-	and	L1_CACHE_TAG_ADDRESS|L1_CACHE_TAG_ENTRY,d0
+	and	L1_CACHE_TAG_MASK,d0
 	or	L1_CACHE_TAG_VALID,d0		# retain valid entries in the
 						# cache
 	mov	d0,(a1)				# conditional purge
@@ -112,11 +112,11 @@ mn10300_local_dcache_flush_range:
 1:
 
 	# round start addr down
-	and	L1_CACHE_TAG_ADDRESS|L1_CACHE_TAG_ENTRY,d0
+	and	L1_CACHE_TAG_MASK,d0
 	mov	d0,a1
 
 	add	L1_CACHE_BYTES,d1			# round end addr up
-	and	L1_CACHE_TAG_ADDRESS|L1_CACHE_TAG_ENTRY,d1
+	and	L1_CACHE_TAG_MASK,d1
 
 	# write a request to flush all instances of an address from the cache
 	mov	DCACHE_PURGE(0,0),a0
@@ -215,12 +215,11 @@ mn10300_local_dcache_flush_inv_range:
 	bra	mn10300_local_dcache_flush_inv
 1:
 
-	and	L1_CACHE_TAG_ADDRESS|L1_CACHE_TAG_ENTRY,d0	# round start
-								# addr down
+	and	L1_CACHE_TAG_MASK,d0		# round start addr down
 	mov	d0,a1
 
-	add	L1_CACHE_BYTES,d1			# round end addr up
-	and	L1_CACHE_TAG_ADDRESS|L1_CACHE_TAG_ENTRY,d1
+	add	L1_CACHE_BYTES,d1		# round end addr up
+	and	L1_CACHE_TAG_MASK,d1
 
 	# write a request to flush and invalidate all instances of an address
 	# from the cache
diff --git a/arch/mn10300/mm/cache-inv-by-reg.S b/arch/mn10300/mm/cache-inv-by-reg.S
index c8950861ed77..a60825b91e77 100644
--- a/arch/mn10300/mm/cache-inv-by-reg.S
+++ b/arch/mn10300/mm/cache-inv-by-reg.S
@@ -15,6 +15,7 @@
 #include <asm/cache.h>
 #include <asm/irqflags.h>
 #include <asm/cacheflush.h>
+#include "cache.inc"
 
 #define mn10300_local_dcache_inv_range_intr_interval \
 	+((1 << MN10300_DCACHE_INV_RANGE_INTR_LOG2_INTERVAL) - 1)
@@ -62,10 +63,7 @@ mn10300_local_icache_inv:
 	btst	CHCTR_ICEN,d0
 	beq	mn10300_local_icache_inv_end
 
-	# invalidate
-	or	CHCTR_ICINV,d0
-	movhu	d0,(a0)
-	movhu	(a0),d0
+	invalidate_icache 1
 
 mn10300_local_icache_inv_end:
 	ret	[],0
@@ -87,11 +85,8 @@ mn10300_local_dcache_inv:
 	btst	CHCTR_DCEN,d0
 	beq	mn10300_local_dcache_inv_end
 
-	# invalidate
-	or	CHCTR_DCINV,d0
-	movhu	d0,(a0)
-	movhu	(a0),d0
-
+	invalidate_dcache 1
+	
 mn10300_local_dcache_inv_end:
 	ret	[],0
 	.size	mn10300_local_dcache_inv,.-mn10300_local_dcache_inv
@@ -121,9 +116,9 @@ mn10300_local_dcache_inv_range:
 	# and if they're not cacheline-aligned, we must flush any bits outside
 	# the range that share cachelines with stuff inside the range
 #ifdef CONFIG_MN10300_CACHE_WBACK
-	btst	~(L1_CACHE_BYTES-1),d0
+	btst	~L1_CACHE_TAG_MASK,d0
 	bne	1f
-	btst	~(L1_CACHE_BYTES-1),d1
+	btst	~L1_CACHE_TAG_MASK,d1
 	beq	2f
 1:
 	bra	mn10300_local_dcache_flush_inv_range
@@ -141,12 +136,11 @@ mn10300_local_dcache_inv_range:
 	# writeback mode, in which case we would be in flush and invalidate by
 	# now
 #ifndef CONFIG_MN10300_CACHE_WBACK
-	and	L1_CACHE_TAG_ADDRESS|L1_CACHE_TAG_ENTRY,d0	# round start
-								# addr down
+	and	L1_CACHE_TAG_MASK,d0	# round start addr down
 
 	mov	L1_CACHE_BYTES-1,d2
 	add	d2,d1
-	and	L1_CACHE_TAG_ADDRESS|L1_CACHE_TAG_ENTRY,d1	# round end addr up
+	and	L1_CACHE_TAG_MASK,d1	# round end addr up
 #endif /* !CONFIG_MN10300_CACHE_WBACK */
 
 	sub	d0,d1,d2		# calculate the total size
diff --git a/arch/mn10300/mm/cache-inv-by-tag.S b/arch/mn10300/mm/cache-inv-by-tag.S
index e9713b40c0ff..ccedce9c144d 100644
--- a/arch/mn10300/mm/cache-inv-by-tag.S
+++ b/arch/mn10300/mm/cache-inv-by-tag.S
@@ -15,6 +15,7 @@
 #include <asm/cache.h>
 #include <asm/irqflags.h>
 #include <asm/cacheflush.h>
+#include "cache.inc"
 
 #define mn10300_local_dcache_inv_range_intr_interval \
 	+((1 << MN10300_DCACHE_INV_RANGE_INTR_LOG2_INTERVAL) - 1)
@@ -70,43 +71,7 @@ mn10300_local_icache_inv:
 	btst	CHCTR_ICEN,d0
 	beq	mn10300_local_icache_inv_end
 
-#if defined(CONFIG_AM33_2) || defined(CONFIG_AM33_3)
-	LOCAL_CLI_SAVE(d1)
-
-	# disable the icache
-	and	~CHCTR_ICEN,d0
-	movhu	d0,(a0)
-
-	# and wait for it to calm down
-	setlb
-	movhu	(a0),d0
-	btst	CHCTR_ICBUSY,d0
-	lne
-
-	# invalidate
-	or	CHCTR_ICINV,d0
-	movhu	d0,(a0)
-
-	# wait for the cache to finish
-	mov	CHCTR,a0
-	setlb
-	movhu	(a0),d0
-	btst	CHCTR_ICBUSY,d0
-	lne
-
-	# and reenable it
-	and	~CHCTR_ICINV,d0
-	or	CHCTR_ICEN,d0
-	movhu	d0,(a0)
-	movhu	(a0),d0
-
-	LOCAL_IRQ_RESTORE(d1)
-#else /* CONFIG_AM33_2 || CONFIG_AM33_3 */
-	# invalidate
-	or	CHCTR_ICINV,d0
-	movhu	d0,(a0)
-	movhu	(a0),d0
-#endif /* CONFIG_AM33_2 || CONFIG_AM33_3 */
+	invalidate_icache 1
 
 mn10300_local_icache_inv_end:
 	ret	[],0
@@ -128,43 +93,7 @@ mn10300_local_dcache_inv:
 	btst	CHCTR_DCEN,d0
 	beq	mn10300_local_dcache_inv_end
 
-#if defined(CONFIG_AM33_2) || defined(CONFIG_AM33_3)
-	LOCAL_CLI_SAVE(d1)
-
-	# disable the dcache
-	and	~CHCTR_DCEN,d0
-	movhu	d0,(a0)
-
-	# and wait for it to calm down
-	setlb
-	movhu	(a0),d0
-	btst	CHCTR_DCBUSY,d0
-	lne
-
-	# invalidate
-	or	CHCTR_DCINV,d0
-	movhu	d0,(a0)
-
-	# wait for the cache to finish
-	mov	CHCTR,a0
-	setlb
-	movhu	(a0),d0
-	btst	CHCTR_DCBUSY,d0
-	lne
-
-	# and reenable it
-	and	~CHCTR_DCINV,d0
-	or	CHCTR_DCEN,d0
-	movhu	d0,(a0)
-	movhu	(a0),d0
-
-	LOCAL_IRQ_RESTORE(d1)
-#else /* CONFIG_AM33_2 || CONFIG_AM33_3 */
-	# invalidate
-	or	CHCTR_DCINV,d0
-	movhu	d0,(a0)
-	movhu	(a0),d0
-#endif /* CONFIG_AM33_2 || CONFIG_AM33_3 */
+	invalidate_dcache 1
 
 mn10300_local_dcache_inv_end:
 	ret	[],0
@@ -195,9 +124,9 @@ mn10300_local_dcache_inv_range:
 	# and if they're not cacheline-aligned, we must flush any bits outside
 	# the range that share cachelines with stuff inside the range
 #ifdef CONFIG_MN10300_CACHE_WBACK
-	btst	~(L1_CACHE_BYTES-1),d0
+	btst	~L1_CACHE_TAG_MASK,d0
 	bne	1f
-	btst	~(L1_CACHE_BYTES-1),d1
+	btst	~L1_CACHE_TAG_MASK,d1
 	beq	2f
 1:
 	bra	mn10300_local_dcache_flush_inv_range
@@ -212,11 +141,10 @@ mn10300_local_dcache_inv_range:
 	beq	mn10300_local_dcache_inv_range_end
 
 #ifndef CONFIG_MN10300_CACHE_WBACK
-	and	L1_CACHE_TAG_ADDRESS|L1_CACHE_TAG_ENTRY,d0	# round start
-								# addr down
+	and	L1_CACHE_TAG_MASK,d0		# round start addr down
 
 	add	L1_CACHE_BYTES,d1		# round end addr up
-	and	L1_CACHE_TAG_ADDRESS|L1_CACHE_TAG_ENTRY,d1
+	and	L1_CACHE_TAG_MASK,d1
 #endif /* !CONFIG_MN10300_CACHE_WBACK */
 	mov	d0,a1
 
diff --git a/arch/mn10300/mm/cache.inc b/arch/mn10300/mm/cache.inc
new file mode 100644
index 000000000000..394a119b9c73
--- /dev/null
+++ b/arch/mn10300/mm/cache.inc
@@ -0,0 +1,133 @@
+/* MN10300 CPU core caching macros -*- asm -*-
+ *
+ * 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.
+ */
+
+
+###############################################################################
+#
+# Invalidate the instruction cache.
+#	A0: Should hold CHCTR
+#	D0: Should have been read from CHCTR
+#	D1: Will be clobbered
+#
+# On some cores it is necessary to disable the icache whilst we do this.
+#
+###############################################################################
+	.macro invalidate_icache,disable_irq
+
+#if defined(CONFIG_AM33_2) || defined(CONFIG_AM33_3)
+	.if \disable_irq
+	# don't want an interrupt routine seeing a disabled cache
+	mov	epsw,d1
+	and	~EPSW_IE,epsw
+	or	EPSW_NMID,epsw
+	nop
+	nop
+	.endif
+
+	# disable the icache
+	and	~CHCTR_ICEN,d0
+	movhu	d0,(a0)
+
+	# and wait for it to calm down
+	setlb
+	movhu	(a0),d0
+	btst	CHCTR_ICBUSY,d0
+	lne
+
+	# invalidate
+	or	CHCTR_ICINV,d0
+	movhu	d0,(a0)
+
+	# wait for the cache to finish
+	setlb
+	movhu	(a0),d0
+	btst	CHCTR_ICBUSY,d0
+	lne
+
+	# and reenable it
+	or	CHCTR_ICEN,d0
+	movhu	d0,(a0)
+	movhu	(a0),d0
+
+	.if \disable_irq
+	LOCAL_IRQ_RESTORE(d1)
+	.endif
+
+#else /* CONFIG_AM33_2 || CONFIG_AM33_3 */
+
+	# invalidate
+	or	CHCTR_ICINV,d0
+	movhu	d0,(a0)
+	movhu	(a0),d0
+
+#endif /* CONFIG_AM33_2 || CONFIG_AM33_3 */
+	.endm
+
+###############################################################################
+#
+# Invalidate the data cache.
+#	A0: Should hold CHCTR
+#	D0: Should have been read from CHCTR
+#	D1: Will be clobbered
+#
+# On some cores it is necessary to disable the dcache whilst we do this.
+#
+###############################################################################
+	.macro invalidate_dcache,disable_irq
+
+#if defined(CONFIG_AM33_2) || defined(CONFIG_AM33_3)
+	.if \disable_irq
+	# don't want an interrupt routine seeing a disabled cache
+	mov	epsw,d1
+	and	~EPSW_IE,epsw
+	or	EPSW_NMID,epsw
+	nop
+	nop
+	.endif
+	
+	# disable the dcache
+	and	~CHCTR_DCEN,d0
+	movhu	d0,(a0)
+
+	# and wait for it to calm down
+	setlb
+	movhu	(a0),d0
+	btst	CHCTR_DCBUSY,d0
+	lne
+
+	# invalidate
+	or	CHCTR_DCINV,d0
+	movhu	d0,(a0)
+
+	# wait for the cache to finish
+	setlb
+	movhu	(a0),d0
+	btst	CHCTR_DCBUSY,d0
+	lne
+
+	# and reenable it
+	or	CHCTR_DCEN,d0
+	movhu	d0,(a0)
+	movhu	(a0),d0
+
+	.if \disable_irq
+	LOCAL_IRQ_RESTORE(d1)
+	.endif
+
+#else /* CONFIG_AM33_2 || CONFIG_AM33_3 */
+
+	# invalidate
+	or	CHCTR_DCINV,d0
+	movhu	d0,(a0)
+	movhu	(a0),d0
+
+#endif /* CONFIG_AM33_2 || CONFIG_AM33_3 */
+	.endm
diff --git a/arch/mn10300/mm/fault.c b/arch/mn10300/mm/fault.c
index 59c3da49d9d9..0945409a8022 100644
--- a/arch/mn10300/mm/fault.c
+++ b/arch/mn10300/mm/fault.c
@@ -28,8 +28,9 @@
 #include <asm/uaccess.h>
 #include <asm/pgalloc.h>
 #include <asm/hardirq.h>
-#include <asm/gdb-stub.h>
 #include <asm/cpu-regs.h>
+#include <asm/debugger.h>
+#include <asm/gdb-stub.h>
 
 /*
  * Unlock any spinlocks which will prevent us from getting the
@@ -306,10 +307,8 @@ no_context:
 	printk(" printing pc:\n");
 	printk(KERN_ALERT "%08lx\n", regs->pc);
 
-#ifdef CONFIG_GDBSTUB
-	gdbstub_intercept(
-		regs, fault_code & 0x00010000 ? EXCEP_IAERROR : EXCEP_DAERROR);
-#endif
+	debugger_intercept(fault_code & 0x00010000 ? EXCEP_IAERROR : EXCEP_DAERROR,
+			   SIGSEGV, SEGV_ACCERR, regs);
 
 	page = PTBR;
 	page = ((unsigned long *) __va(page))[address >> 22];
diff --git a/arch/mn10300/proc-mn103e010/include/proc/cache.h b/arch/mn10300/proc-mn103e010/include/proc/cache.h
index c1528004163c..967d144f307e 100644
--- a/arch/mn10300/proc-mn103e010/include/proc/cache.h
+++ b/arch/mn10300/proc-mn103e010/include/proc/cache.h
@@ -23,6 +23,7 @@
 #define L1_CACHE_TAG_DIRTY	0x00000008	/* data cache tag dirty bit */
 #define L1_CACHE_TAG_ENTRY	0x00000ff0	/* cache tag entry address mask */
 #define L1_CACHE_TAG_ADDRESS	0xfffff000	/* cache tag line address mask */
+#define L1_CACHE_TAG_MASK	+(L1_CACHE_TAG_ADDRESS|L1_CACHE_TAG_ENTRY)
 
 /*
  * specification of the interval between interrupt checking intervals whilst
diff --git a/arch/mn10300/proc-mn2ws0050/include/proc/cache.h b/arch/mn10300/proc-mn2ws0050/include/proc/cache.h
index cafd7b5b55b4..bcb5df2d892f 100644
--- a/arch/mn10300/proc-mn2ws0050/include/proc/cache.h
+++ b/arch/mn10300/proc-mn2ws0050/include/proc/cache.h
@@ -29,6 +29,7 @@
 #define L1_CACHE_TAG_DIRTY	0x00000008	/* data cache tag dirty bit */
 #define L1_CACHE_TAG_ENTRY	0x00000fe0	/* cache tag entry address mask */
 #define L1_CACHE_TAG_ADDRESS	0xfffff000	/* cache tag line address mask */
+#define L1_CACHE_TAG_MASK	+(L1_CACHE_TAG_ADDRESS|L1_CACHE_TAG_ENTRY)
 
 /*
  * specification of the interval between interrupt checking intervals whilst
diff --git a/include/linux/kgdb.h b/include/linux/kgdb.h
index 092e4250a458..10ca03d0a250 100644
--- a/include/linux/kgdb.h
+++ b/include/linux/kgdb.h
@@ -297,6 +297,7 @@ extern int
 kgdb_handle_exception(int ex_vector, int signo, int err_code,
 		      struct pt_regs *regs);
 extern int kgdb_nmicallback(int cpu, void *regs);
+extern void gdbstub_exit(int status);
 
 extern int			kgdb_single_step;
 extern atomic_t			kgdb_active;
diff --git a/kernel/debug/gdbstub.c b/kernel/debug/gdbstub.c
index 481a7bd2dfe7..a11db956dd62 100644
--- a/kernel/debug/gdbstub.c
+++ b/kernel/debug/gdbstub.c
@@ -1093,3 +1093,33 @@ int gdbstub_state(struct kgdb_state *ks, char *cmd)
 	put_packet(remcom_out_buffer);
 	return 0;
 }
+
+/**
+ * gdbstub_exit - Send an exit message to GDB
+ * @status: The exit code to report.
+ */
+void gdbstub_exit(int status)
+{
+	unsigned char checksum, ch, buffer[3];
+	int loop;
+
+	buffer[0] = 'W';
+	buffer[1] = hex_asc_hi(status);
+	buffer[2] = hex_asc_lo(status);
+
+	dbg_io_ops->write_char('$');
+	checksum = 0;
+
+	for (loop = 0; loop < 3; loop++) {
+		ch = buffer[loop];
+		checksum += ch;
+		dbg_io_ops->write_char(ch);
+	}
+
+	dbg_io_ops->write_char('#');
+	dbg_io_ops->write_char(hex_asc_hi(checksum));
+	dbg_io_ops->write_char(hex_asc_lo(checksum));
+
+	/* make sure the output is flushed, lest the bootloader clobber it */
+	dbg_io_ops->flush();
+}