Linux-2.6.12-rc2

Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.

Let it rip!
diff --git a/arch/frv/Kconfig b/arch/frv/Kconfig
new file mode 100644
index 0000000..2b19372
--- /dev/null
+++ b/arch/frv/Kconfig
@@ -0,0 +1,357 @@
+#
+# For a description of the syntax of this configuration file,
+# see Documentation/kbuild/kconfig-language.txt.
+#
+config FRV
+	bool
+	default y
+
+config UID16
+	bool
+	default y
+
+config RWSEM_GENERIC_SPINLOCK
+	bool
+	default y
+
+config RWSEM_XCHGADD_ALGORITHM
+	bool
+
+config GENERIC_FIND_NEXT_BIT
+	bool
+	default y
+
+config GENERIC_CALIBRATE_DELAY
+	bool
+	default n
+
+config GENERIC_HARDIRQS
+	bool
+	default n
+
+mainmenu "Fujitsu FR-V Kernel Configuration"
+
+source "init/Kconfig"
+
+
+menu "Fujitsu FR-V system setup"
+
+config MMU
+	bool "MMU support"
+	help
+	  This options switches on and off support for the FR-V MMU
+	  (effectively switching between vmlinux and uClinux). Not all FR-V
+	  CPUs support this. Currently only the FR451 has a sufficiently
+	  featured MMU.
+
+config FRV_OUTOFLINE_ATOMIC_OPS
+	bool "Out-of-line the FRV atomic operations"
+	default n
+	help
+	  Setting this option causes the FR-V atomic operations to be mostly
+	  implemented out-of-line.
+
+	  See Documentation/fujitsu/frv/atomic-ops.txt for more information.
+
+config HIGHMEM
+	bool "High memory support"
+	depends on MMU
+	default y
+	help
+	  If you wish to use more than 256MB of memory with your MMU based
+	  system, you will need to select this option. The kernel can only see
+	  the memory between 0xC0000000 and 0xD0000000 directly... everything
+	  else must be kmapped.
+
+	  The arch is, however, capable of supporting up to 3GB of SDRAM.
+
+config HIGHPTE
+	bool "Allocate page tables in highmem"
+	depends on HIGHMEM
+	default y
+	help
+	  The VM uses one page of memory for each page table.  For systems
+	  with a lot of RAM, this can be wasteful of precious low memory.
+	  Setting this option will put user-space page tables in high memory.
+
+choice
+	prompt "uClinux kernel load address"
+	depends on !MMU
+	default UCPAGE_OFFSET_C0000000
+	help
+	  This option sets the base address for the uClinux kernel. The kernel
+	  will rearrange the SDRAM layout to start at this address, and move
+	  itself to start there. It must be greater than 0, and it must be
+	  sufficiently less than 0xE0000000 that the SDRAM does not intersect
+	  the I/O region.
+
+	  The base address must also be aligned such that the SDRAM controller
+	  can decode it. For instance, a 512MB SDRAM bank must be 512MB aligned.
+
+config UCPAGE_OFFSET_20000000
+       bool "0x20000000"
+
+config UCPAGE_OFFSET_40000000
+       bool "0x40000000"
+
+config UCPAGE_OFFSET_60000000
+       bool "0x60000000"
+
+config UCPAGE_OFFSET_80000000
+       bool "0x80000000"
+
+config UCPAGE_OFFSET_A0000000
+       bool "0xA0000000"
+
+config UCPAGE_OFFSET_C0000000
+       bool "0xC0000000 (Recommended)"
+
+endchoice
+
+config PROTECT_KERNEL
+	bool "Protect core kernel against userspace"
+	depends on !MMU
+	default y
+	help
+	  Selecting this option causes the uClinux kernel to change the
+	  permittivity of DAMPR register covering the core kernel image to
+	  prevent userspace accessing the underlying memory directly.
+
+choice
+	prompt "CPU Caching mode"
+	default FRV_DEFL_CACHE_WBACK
+	help
+	  This option determines the default caching mode for the kernel.
+
+	  Write-Back caching mode involves the all reads and writes causing
+	  the affected cacheline to be read into the cache first before being
+	  operated upon. Memory is not then updated by a write until the cache
+	  is filled and a cacheline needs to be displaced from the cache to
+	  make room. Only at that point is it written back.
+
+	  Write-Behind caching is similar to Write-Back caching, except that a
+	  write won't fetch a cacheline into the cache if there isn't already
+	  one there; it will write directly to memory instead.
+
+	  Write-Through caching only fetches cachelines from memory on a
+	  read. Writes always get written directly to memory. If the affected
+	  cacheline is also in cache, it will be updated too.
+
+	  The final option is to turn of caching entirely.
+
+	  Note that not all CPUs support Write-Behind caching. If the CPU on
+	  which the kernel is running doesn't, it'll fall back to Write-Back
+	  caching.
+
+config FRV_DEFL_CACHE_WBACK
+	bool "Write-Back"
+
+config FRV_DEFL_CACHE_WBEHIND
+	bool "Write-Behind"
+
+config FRV_DEFL_CACHE_WTHRU
+	bool "Write-Through"
+
+config FRV_DEFL_CACHE_DISABLED
+	bool "Disabled"
+
+endchoice
+
+menu "CPU core support"
+
+config CPU_FR401
+	bool "Include FR401 core support"
+	depends on !MMU
+	default y
+	help
+	  This enables support for the FR401, FR401A and FR403 CPUs
+
+config CPU_FR405
+	bool "Include FR405 core support"
+	depends on !MMU
+	default y
+	help
+	  This enables support for the FR405 CPU
+
+config CPU_FR451
+	bool "Include FR451 core support"
+	default y
+	help
+	  This enables support for the FR451 CPU
+
+config CPU_FR451_COMPILE
+	bool "Specifically compile for FR451 core"
+	depends on CPU_FR451 && !CPU_FR401 && !CPU_FR405 && !CPU_FR551
+	default y
+	help
+	  This causes appropriate flags to be passed to the compiler to
+	  optimise for the FR451 CPU
+
+config CPU_FR551
+	bool "Include FR551 core support"
+	depends on !MMU
+	default y
+	help
+	  This enables support for the FR555 CPU
+
+config CPU_FR551_COMPILE
+	bool "Specifically compile for FR551 core"
+	depends on CPU_FR551 && !CPU_FR401 && !CPU_FR405 && !CPU_FR451
+	default y
+	help
+	  This causes appropriate flags to be passed to the compiler to
+	  optimise for the FR555 CPU
+
+config FRV_L1_CACHE_SHIFT
+	int
+	default "5" if CPU_FR401 || CPU_FR405 || CPU_FR451
+	default "6" if CPU_FR551
+
+endmenu
+
+choice
+	prompt "System support"
+	default MB93091_VDK
+
+config MB93091_VDK
+	bool "MB93091 CPU board with or without motherboard"
+
+config MB93093_PDK
+	bool "MB93093 PDK unit"
+
+endchoice
+
+if MB93091_VDK
+choice
+	prompt "Motherboard support"
+	default MB93090_MB00
+
+config MB93090_MB00
+	bool "Use the MB93090-MB00 motherboard"
+	help
+	  Select this option if the MB93091 CPU board is going to be used with
+	  a MB93090-MB00 VDK motherboard
+
+config MB93091_NO_MB
+	bool "Use standalone"
+	help
+	  Select this option if the MB93091 CPU board is going to be used
+	  without a motherboard
+
+endchoice
+endif
+
+choice
+	prompt "GP-Relative data support"
+	default GPREL_DATA_8
+	help
+	  This option controls what data, if any, should be placed in the GP
+	  relative data sections. Using this means that the compiler can
+	  generate accesses to the data using GR16-relative addressing which
+	  is faster than absolute instructions and saves space (2 instructions
+	  per access).
+
+	  However, the GPREL region is limited in size because the immediate
+	  value used in the load and store instructions is limited to a 12-bit
+	  signed number.
+
+	  So if the linker starts complaining that accesses to GPREL data are
+	  out of range, try changing this option from the default.
+
+	  Note that modules will always be compiled with this feature disabled
+	  as the module data will not be in range of the GP base address.
+
+config GPREL_DATA_8
+	bool "Put data objects of up to 8 bytes into GP-REL"
+
+config GPREL_DATA_4
+	bool "Put data objects of up to 4 bytes into GP-REL"
+
+config GPREL_DATA_NONE
+	bool "Don't use GP-REL"
+
+endchoice
+
+config PCI
+	bool "Use PCI"
+	depends on MB93090_MB00
+	default y
+	help
+	  Some FR-V systems (such as the MB93090-MB00 VDK) have PCI
+	  onboard. If you have one of these boards and you wish to use the PCI
+	  facilities, say Y here.
+
+	  The PCI-HOWTO, available from
+	  <http://www.tldp.org/docs.html#howto>, contains valuable
+	  information about which PCI hardware does work under Linux and which
+	  doesn't.
+
+config RESERVE_DMA_COHERENT
+	bool "Reserve DMA coherent memory"
+	depends on PCI && !MMU
+	default y
+	help
+	  Many PCI drivers require access to uncached memory for DMA device
+	  communications (such as is done with some Ethernet buffer rings). If
+	  a fully featured MMU is available, this can be done through page
+	  table settings, but if not, a region has to be set aside and marked
+	  with a special DAMPR register.
+
+	  Setting this option causes uClinux to set aside a portion of the
+	  available memory for use in this manner. The memory will then be
+	  unavailable for normal kernel use.
+
+source "drivers/pci/Kconfig"
+
+config PCMCIA
+	tristate "Use PCMCIA"
+	help
+	  Say Y here if you want to attach PCMCIA- or PC-cards to your FR-V
+	  board.  These are credit-card size devices such as network cards,
+	  modems or hard drives often used with laptops computers.  There are
+	  actually two varieties of these cards: the older 16 bit PCMCIA cards
+	  and the newer 32 bit CardBus cards.  If you want to use CardBus
+	  cards, you need to say Y here and also to "CardBus support" below.
+
+	  To use your PC-cards, you will need supporting software from David
+	  Hinds pcmcia-cs package (see the file <file:Documentation/Changes>
+	  for location).  Please also read the PCMCIA-HOWTO, available from
+	  <http://www.tldp.org/docs.html#howto>.
+
+	  To compile this driver as modules, choose M here: the
+	  modules will be called pcmcia_core and ds.
+
+#config MATH_EMULATION
+#	bool "Math emulation support (EXPERIMENTAL)"
+#	depends on EXPERIMENTAL
+#	help
+#	  At some point in the future, this will cause floating-point math
+#	  instructions to be emulated by the kernel on machines that lack a
+#	  floating-point math coprocessor.  Thrill-seekers and chronically
+#	  sleep-deprived psychotic hacker types can say Y now, everyone else
+#	  should probably wait a while.
+
+menu "Power management options"
+source kernel/power/Kconfig
+endmenu
+
+endmenu
+
+
+menu "Executable formats"
+
+source "fs/Kconfig.binfmt"
+
+endmenu
+
+source "drivers/Kconfig"
+
+source "fs/Kconfig"
+
+source "arch/frv/Kconfig.debug"
+
+source "security/Kconfig"
+
+source "crypto/Kconfig"
+
+source "lib/Kconfig"
diff --git a/arch/frv/Kconfig.debug b/arch/frv/Kconfig.debug
new file mode 100644
index 0000000..0034b65
--- /dev/null
+++ b/arch/frv/Kconfig.debug
@@ -0,0 +1,74 @@
+menu "Kernel hacking"
+
+source "lib/Kconfig.debug"
+
+config EARLY_PRINTK
+	bool "Early printk"
+	depends on EMBEDDED && DEBUG_KERNEL
+	default n
+	help
+	  Write kernel log output directly into the VGA buffer or to a serial
+	  port.
+
+	  This is useful for kernel debugging when your machine crashes very
+	  early before the console code is initialized. For normal operation
+	  it is not recommended because it looks ugly and doesn't cooperate
+	  with klogd/syslogd or the X server. You should normally N here,
+	  unless you want to debug such a crash.
+
+config DEBUG_STACKOVERFLOW
+	bool "Check for stack overflows"
+	depends on DEBUG_KERNEL
+
+config DEBUG_PAGEALLOC
+	bool "Page alloc debugging"
+	depends on DEBUG_KERNEL
+	help
+	  Unmap pages from the kernel linear mapping after free_pages().
+	  This results in a large slowdown, but helps to find certain types
+	  of memory corruptions.
+
+config GDBSTUB
+	bool "Remote GDB kernel debugging"
+	depends on DEBUG_KERNEL
+	select DEBUG_INFO
+	select FRAME_POINTER
+	help
+	  If you say Y here, it will be possible to remotely debug the kernel
+	  using gdb. This enlarges your kernel ELF image disk size by several
+	  megabytes and requires a machine with more than 16 MB, better 32 MB
+	  RAM to avoid excessive linking time. This is only useful for kernel
+	  hackers. If unsure, say N.
+
+choice
+	prompt "GDB stub port"
+	default GDBSTUB_UART1
+	depends on GDBSTUB
+	help
+	  Select the on-CPU port used for GDB-stub
+
+config GDBSTUB_UART0
+	bool "/dev/ttyS0"
+
+config GDBSTUB_UART1
+	bool "/dev/ttyS1"
+
+endchoice
+
+config GDBSTUB_IMMEDIATE
+	bool "Break into GDB stub immediately"
+	depends on GDBSTUB
+	help
+	  If you say Y here, GDB stub will break into the program as soon as
+	  possible, leaving the program counter at the beginning of
+	  start_kernel() in init/main.c.
+
+config GDB_CONSOLE
+	bool "Console output to GDB"
+	depends on GDBSTUB
+	help
+	  If you are using GDB for remote debugging over a serial port and
+	  would like kernel messages to be formatted into GDB $O packets so
+	  that GDB prints them as program output, say 'Y'.
+
+endmenu
diff --git a/arch/frv/Makefile b/arch/frv/Makefile
new file mode 100644
index 0000000..54046d2
--- /dev/null
+++ b/arch/frv/Makefile
@@ -0,0 +1,118 @@
+#
+# frv/Makefile
+#
+# This file is included by the global makefile so that you can add your own
+# architecture-specific flags and dependencies. Remember to do have actions
+# for "archclean" and "archdep" for cleaning up and making dependencies for
+# this architecture
+#
+# This file is subject to the terms and conditions of the GNU General Public
+# License.  See the file "COPYING" in the main directory of this archive
+# for more details.
+#
+# Copyright (c) 2003, 2004 Red Hat Inc.
+# - Written by David Howells <dhowells@redhat.com>
+# - Derived from arch/m68knommu/Makefile,
+#	Copyright (c) 1999,2001  D. Jeff Dionne <jeff@lineo.ca>,
+#	Rt-Control Inc. / Lineo, Inc.
+#
+# Copyright (C) 1998,1999  D. Jeff Dionne <jeff@uclinux.org>,
+#                          Kenneth Albanowski <kjahds@kjahds.com>,
+#
+# Based on arch/m68k/Makefile:
+# Copyright (C) 1994 by Hamish Macdonald
+#
+
+CCSPECS	:= $(shell $(CC) -v 2>&1 | grep "^Reading specs from " | head -1 | cut -c20-)
+CCDIR	:= $(strip $(patsubst %/specs,%,$(CCSPECS)))
+CPUCLASS := fr400
+
+# test for cross compiling
+COMPILE_ARCH = $(shell uname -m)
+
+ifdef CONFIG_MMU
+UTS_SYSNAME = -DUTS_SYSNAME=\"Linux\"
+else
+UTS_SYSNAME = -DUTS_SYSNAME=\"uClinux\"
+endif
+
+ARCHMODFLAGS	+= -G0 -mlong-calls
+
+ifdef CONFIG_GPREL_DATA_8
+CFLAGS		+= -G8
+else
+ifdef CONFIG_GPREL_DATA_4
+CFLAGS		+= -G4
+else
+ifdef CONFIG_GPREL_DATA_NONE
+CFLAGS		+= -G0
+endif
+endif
+endif
+
+#LDFLAGS_vmlinux	:= -Map linkmap.txt
+
+ifdef CONFIG_GC_SECTIONS
+CFLAGS		+= -ffunction-sections -fdata-sections
+LINKFLAGS	+= --gc-sections
+endif
+
+ifndef CONFIG_FRAME_POINTER
+CFLAGS		+= -mno-linked-fp
+endif
+
+ifdef CONFIG_CPU_FR451_COMPILE
+CFLAGS		+= -mcpu=fr450
+AFLAGS		+= -mcpu=fr450
+ASFLAGS		+= -mcpu=fr450
+else
+ifdef CONFIG_CPU_FR551_COMPILE
+CFLAGS		+= -mcpu=fr550
+AFLAGS		+= -mcpu=fr550
+ASFLAGS		+= -mcpu=fr550
+else
+CFLAGS		+= -mcpu=fr400
+AFLAGS		+= -mcpu=fr400
+ASFLAGS		+= -mcpu=fr400
+endif
+endif
+
+# pretend the kernel is going to run on an FR400 with no media-fp unit
+# - reserve CC3 for use with atomic ops
+# - all the extra registers are dealt with only at context switch time
+CFLAGS		+= -mno-fdpic -mgpr-32 -msoft-float -mno-media
+CFLAGS		+= -ffixed-fcc3 -ffixed-cc3 -ffixed-gr15
+AFLAGS		+= -mno-fdpic
+ASFLAGS		+= -mno-fdpic
+
+# make sure the .S files get compiled with debug info
+# and disable optimisations that are unhelpful whilst debugging
+ifdef CONFIG_DEBUG_INFO
+CFLAGS		+= -O1
+AFLAGS		+= -Wa,--gdwarf2
+ASFLAGS		+= -Wa,--gdwarf2
+endif
+
+head-y		:= arch/frv/kernel/head.o arch/frv/kernel/init_task.o
+
+core-y		+= arch/frv/kernel/ arch/frv/mm/
+libs-y		+= arch/frv/lib/
+
+core-$(CONFIG_MB93090_MB00)	+= arch/frv/mb93090-mb00/
+
+all: Image
+
+Image: vmlinux
+	$(Q)$(MAKE) $(build)=arch/frv/boot $@
+
+bootstrap:
+	$(Q)$(MAKEBOOT) bootstrap
+
+archmrproper:
+	$(Q)$(MAKE) -C arch/frv/boot mrproper
+
+archclean:
+	$(Q)$(MAKE) -C arch/frv/boot clean
+
+archdep: scripts/mkdep symlinks
+	$(Q)$(MAKE) -C arch/frv/boot dep
diff --git a/arch/frv/boot/Makefile b/arch/frv/boot/Makefile
new file mode 100644
index 0000000..d75e0d7
--- /dev/null
+++ b/arch/frv/boot/Makefile
@@ -0,0 +1,73 @@
+#
+# arch/arm/boot/Makefile
+#
+# This file is subject to the terms and conditions of the GNU General Public
+# License.  See the file "COPYING" in the main directory of this archive
+# for more details.
+#
+# Copyright (C) 1995-2000 Russell King
+#
+
+SYSTEM	=$(TOPDIR)/$(LINUX)
+
+ZTEXTADDR	 = 0x02080000
+PARAMS_PHYS	 = 0x0207c000
+INITRD_PHYS	 = 0x02180000
+INITRD_VIRT	 = 0x02180000
+
+#
+# If you don't define ZRELADDR above,
+# then it defaults to ZTEXTADDR
+#
+ifeq ($(ZRELADDR),)
+ZRELADDR	= $(ZTEXTADDR)
+endif
+
+export	SYSTEM ZTEXTADDR ZBSSADDR ZRELADDR INITRD_PHYS INITRD_VIRT PARAMS_PHYS
+
+Image: $(obj)/Image
+
+targets: $(obj)/Image
+
+$(obj)/Image: vmlinux FORCE
+	$(OBJCOPY) -O binary -R .note -R .comment -S vmlinux $@
+
+#$(obj)/Image:	$(CONFIGURE) $(SYSTEM)
+#	$(OBJCOPY) -O binary -R .note -R .comment -g -S $(SYSTEM) $@
+
+bzImage: zImage
+
+zImage:	$(CONFIGURE) compressed/$(LINUX)
+	$(OBJCOPY) -O binary -R .note -R .comment -S compressed/$(LINUX) $@
+
+bootpImage: bootp/bootp
+	$(OBJCOPY) -O binary -R .note -R .comment -S bootp/bootp $@
+
+compressed/$(LINUX): $(TOPDIR)/$(LINUX) dep
+	@$(MAKE) -C compressed $(LINUX)
+
+bootp/bootp: zImage initrd
+	@$(MAKE) -C bootp bootp
+
+initrd:
+	@test "$(INITRD_VIRT)" != "" || (echo This architecture does not support INITRD; exit -1)
+	@test "$(INITRD)" != "" || (echo You must specify INITRD; exit -1)
+
+#
+# installation
+#
+install: $(CONFIGURE) Image
+	sh ./install.sh $(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION) Image $(TOPDIR)/System.map "$(INSTALL_PATH)"
+
+zinstall: $(CONFIGURE) zImage
+	sh ./install.sh $(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION) zImage $(TOPDIR)/System.map "$(INSTALL_PATH)"
+
+#
+# miscellany
+#
+mrproper clean:
+	$(RM) Image zImage bootpImage
+#	@$(MAKE) -C compressed clean
+#	@$(MAKE) -C bootp clean
+
+dep:
diff --git a/arch/frv/kernel/Makefile b/arch/frv/kernel/Makefile
new file mode 100644
index 0000000..981c2c7
--- /dev/null
+++ b/arch/frv/kernel/Makefile
@@ -0,0 +1,22 @@
+#
+# Makefile for the linux kernel.
+#
+
+heads-y				:= head-uc-fr401.o head-uc-fr451.o head-uc-fr555.o
+heads-$(CONFIG_MMU)		:= head-mmu-fr451.o
+
+extra-y:= head.o init_task.o vmlinux.lds
+
+obj-y := $(heads-y) entry.o entry-table.o break.o switch_to.o kernel_thread.o \
+	 process.o traps.o ptrace.o signal.o dma.o \
+	 sys_frv.o time.o semaphore.o setup.o frv_ksyms.o \
+	 debug-stub.o irq.o irq-routing.o sleep.o uaccess.o
+
+obj-$(CONFIG_GDBSTUB)		+= gdb-stub.o gdb-io.o
+
+obj-$(CONFIG_MB93091_VDK)	+= irq-mb93091.o
+obj-$(CONFIG_MB93093_PDK)	+= irq-mb93093.o
+obj-$(CONFIG_FUJITSU_MB93493)	+= irq-mb93493.o
+obj-$(CONFIG_PM)		+= pm.o cmode.o
+obj-$(CONFIG_MB93093_PDK)	+= pm-mb93093.o
+obj-$(CONFIG_SYSCTL)		+= sysctl.o
diff --git a/arch/frv/kernel/break.S b/arch/frv/kernel/break.S
new file mode 100644
index 0000000..33233dc
--- /dev/null
+++ b/arch/frv/kernel/break.S
@@ -0,0 +1,720 @@
+/* break.S: Break interrupt handling (kept separate from entry.S)
+ *
+ * Copyright (C) 2003 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 License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/sys.h>
+#include <linux/config.h>
+#include <linux/linkage.h>
+#include <asm/setup.h>
+#include <asm/segment.h>
+#include <asm/ptrace.h>
+#include <asm/spr-regs.h>
+
+#include <asm/errno.h>
+
+#
+# the break handler has its own stack
+#
+	.section	.bss.stack
+	.globl		__break_user_context
+	.balign		8192
+__break_stack:
+	.space		(8192 - (USER_CONTEXT_SIZE + REG__DEBUG_XTRA)) & ~7
+__break_stack_tos:
+	.space		REG__DEBUG_XTRA
+__break_user_context:
+	.space		USER_CONTEXT_SIZE
+
+#
+# miscellaneous variables
+#
+	.section	.bss
+#ifdef CONFIG_MMU
+	.globl		__break_tlb_miss_real_return_info
+__break_tlb_miss_real_return_info:
+	.balign		8
+	.space		2*4			/* saved PCSR, PSR for TLB-miss handler fixup */
+#endif
+
+__break_trace_through_exceptions:
+	.space		4
+
+#define CS2_ECS1	0xe1200000
+#define CS2_USERLED	0x4
+
+.macro LEDS val,reg
+#	sethi.p		%hi(CS2_ECS1+CS2_USERLED),gr30
+#	setlo		%lo(CS2_ECS1+CS2_USERLED),gr30
+#	setlos		#~\val,\reg
+#	st		\reg,@(gr30,gr0)
+#	setlos		#0x5555,\reg
+#	sethi.p		%hi(0xffc00100),gr30
+#	setlo		%lo(0xffc00100),gr30
+#	sth		\reg,@(gr30,gr0)
+#	membar
+.endm
+
+###############################################################################
+#
+# entry point for Break Exceptions/Interrupts
+#
+###############################################################################
+	.text
+	.balign		4
+	.globl		__entry_break
+__entry_break:
+#ifdef CONFIG_MMU
+	movgs		gr31,scr3
+#endif
+	LEDS		0x1001,gr31
+
+	sethi.p		%hi(__break_user_context),gr31
+	setlo		%lo(__break_user_context),gr31
+
+	stdi		gr2,@(gr31,#REG_GR(2))
+	movsg		ccr,gr3
+	sti		gr3,@(gr31,#REG_CCR)
+
+	# catch the return from a TLB-miss handler that had single-step disabled
+	# traps will be enabled, so we have to do this now
+#ifdef CONFIG_MMU
+	movsg		bpcsr,gr3
+	sethi.p		%hi(__break_tlb_miss_return_breaks_here),gr2
+	setlo		%lo(__break_tlb_miss_return_breaks_here),gr2
+	subcc		gr2,gr3,gr0,icc0
+	beq		icc0,#2,__break_return_singlestep_tlbmiss
+#endif
+
+	# determine whether we have stepped through into an exception
+	# - we need to take special action to suspend h/w single stepping if we've done
+	#   that, so that the gdbstub doesn't get bogged down endlessly stepping through
+	#   external interrupt handling
+	movsg		bpsr,gr3
+	andicc		gr3,#BPSR_BET,gr0,icc0
+	bne		icc0,#2,__break_maybe_userspace	/* jump if PSR.ET was 1 */
+
+	LEDS		0x1003,gr2
+
+	movsg		brr,gr3
+	andicc		gr3,#BRR_ST,gr0,icc0
+	andicc.p	gr3,#BRR_SB,gr0,icc1
+	bne		icc0,#2,__break_step		/* jump if single-step caused break */
+	beq		icc1,#2,__break_continue	/* jump if BREAK didn't cause break */
+
+	LEDS		0x1007,gr2
+
+	# handle special breaks
+	movsg		bpcsr,gr3
+
+	sethi.p		%hi(__entry_return_singlestep_breaks_here),gr2
+	setlo		%lo(__entry_return_singlestep_breaks_here),gr2
+	subcc		gr2,gr3,gr0,icc0
+	beq		icc0,#2,__break_return_singlestep
+
+	bra		__break_continue
+
+
+###############################################################################
+#
+# handle BREAK instruction in kernel-mode exception epilogue
+#
+###############################################################################
+__break_return_singlestep:
+	LEDS		0x100f,gr2
+
+	# special break insn requests single-stepping to be turned back on
+	#		HERE		RETT
+	# PSR.ET	0		0
+	# PSR.PS	old PSR.S	?
+	# PSR.S		1		1
+	# BPSR.ET	0		1 (can't have caused orig excep otherwise)
+	# BPSR.BS	1		old PSR.S
+	movsg		dcr,gr2
+	sethi.p		%hi(DCR_SE),gr3
+	setlo		%lo(DCR_SE),gr3
+	or		gr2,gr3,gr2
+	movgs		gr2,dcr
+
+	movsg		psr,gr2
+	andi		gr2,#PSR_PS,gr2
+	slli		gr2,#11,gr2			/* PSR.PS -> BPSR.BS */
+	ori		gr2,#BPSR_BET,gr2		/* 1 -> BPSR.BET */
+	movgs		gr2,bpsr
+
+	# return to the invoker of the original kernel exception
+	movsg		pcsr,gr2
+	movgs		gr2,bpcsr
+
+	LEDS		0x101f,gr2
+
+	ldi		@(gr31,#REG_CCR),gr3
+	movgs		gr3,ccr
+	lddi.p		@(gr31,#REG_GR(2)),gr2
+	xor		gr31,gr31,gr31
+	movgs		gr0,brr
+#ifdef CONFIG_MMU
+	movsg		scr3,gr31
+#endif
+	rett		#1
+
+###############################################################################
+#
+# handle BREAK instruction in TLB-miss handler return path
+#
+###############################################################################
+#ifdef CONFIG_MMU
+__break_return_singlestep_tlbmiss:
+	LEDS		0x1100,gr2
+
+	sethi.p		%hi(__break_tlb_miss_real_return_info),gr3
+	setlo		%lo(__break_tlb_miss_real_return_info),gr3
+	lddi		@(gr3,#0),gr2
+	movgs		gr2,pcsr
+	movgs		gr3,psr
+
+	bra		__break_return_singlestep
+#endif
+
+
+###############################################################################
+#
+# handle single stepping into an exception prologue from kernel mode
+# - we try and catch it whilst it is still in the main vector table
+# - if we catch it there, we have to jump to the fixup handler
+#   - there is a fixup table that has a pointer for every 16b slot in the trap
+#     table
+#
+###############################################################################
+__break_step:
+	LEDS		0x2003,gr2
+
+	# external interrupts seem to escape from the trap table before single
+	# step catches up with them
+	movsg		bpcsr,gr2
+	sethi.p		%hi(__entry_kernel_external_interrupt),gr3
+	setlo		%lo(__entry_kernel_external_interrupt),gr3
+	subcc		gr2,gr3,gr0,icc0
+	beq		icc0,#2,__break_step_kernel_external_interrupt
+	sethi.p		%hi(__entry_uspace_external_interrupt),gr3
+	setlo		%lo(__entry_uspace_external_interrupt),gr3
+	subcc		gr2,gr3,gr0,icc0
+	beq		icc0,#2,__break_step_uspace_external_interrupt
+
+	LEDS		0x2007,gr2
+
+	# the two main vector tables are adjacent on one 8Kb slab
+	movsg		bpcsr,gr2
+	setlos		#0xffffe000,gr3
+	and		gr2,gr3,gr2
+	sethi.p		%hi(__trap_tables),gr3
+	setlo		%lo(__trap_tables),gr3
+	subcc		gr2,gr3,gr0,icc0
+	bne		icc0,#2,__break_continue
+
+	LEDS		0x200f,gr2
+
+	# skip workaround if so requested by GDB
+	sethi.p		%hi(__break_trace_through_exceptions),gr3
+	setlo		%lo(__break_trace_through_exceptions),gr3
+	ld		@(gr3,gr0),gr3
+	subcc		gr3,gr0,gr0,icc0
+	bne		icc0,#0,__break_continue
+
+	LEDS		0x201f,gr2
+
+	# access the fixup table - there's a 1:1 mapping between the slots in the trap tables and
+	# the slots in the trap fixup tables allowing us to simply divide the offset into the
+	# former by 4 to access the latter
+	sethi.p		%hi(__trap_tables),gr3
+	setlo		%lo(__trap_tables),gr3
+	movsg		bpcsr,gr2
+	sub		gr2,gr3,gr2
+	srli.p		gr2,#2,gr2
+
+	sethi		%hi(__trap_fixup_tables),gr3
+	setlo.p		%lo(__trap_fixup_tables),gr3
+	andi		gr2,#~3,gr2
+	ld		@(gr2,gr3),gr2
+	jmpil		@(gr2,#0)
+
+# step through an internal exception from kernel mode
+	.globl		__break_step_kernel_softprog_interrupt
+__break_step_kernel_softprog_interrupt:
+	sethi.p		%hi(__entry_kernel_softprog_interrupt_reentry),gr3
+	setlo		%lo(__entry_kernel_softprog_interrupt_reentry),gr3
+	bra		__break_return_as_kernel_prologue
+
+# step through an external interrupt from kernel mode
+	.globl		__break_step_kernel_external_interrupt
+__break_step_kernel_external_interrupt:
+	sethi.p		%hi(__entry_kernel_external_interrupt_reentry),gr3
+	setlo		%lo(__entry_kernel_external_interrupt_reentry),gr3
+
+__break_return_as_kernel_prologue:
+	LEDS		0x203f,gr2
+
+	movgs		gr3,bpcsr
+
+	# do the bit we had to skip
+#ifdef CONFIG_MMU
+	movsg		ear0,gr2		/* EAR0 can get clobbered by gdb-stub (ICI/ICEI) */
+	movgs		gr2,scr2
+#endif
+
+	or.p		sp,gr0,gr2		/* set up the stack pointer */
+	subi		sp,#REG__END,sp
+	sti.p		gr2,@(sp,#REG_SP)
+
+	setlos		#REG__STATUS_STEP,gr2
+	sti		gr2,@(sp,#REG__STATUS)		/* record single step status */
+
+	# cancel single-stepping mode
+	movsg		dcr,gr2
+	sethi.p		%hi(~DCR_SE),gr3
+	setlo		%lo(~DCR_SE),gr3
+	and		gr2,gr3,gr2
+	movgs		gr2,dcr
+
+	LEDS		0x207f,gr2
+
+	ldi		@(gr31,#REG_CCR),gr3
+	movgs		gr3,ccr
+	lddi.p		@(gr31,#REG_GR(2)),gr2
+	xor		gr31,gr31,gr31
+	movgs		gr0,brr
+#ifdef CONFIG_MMU
+	movsg		scr3,gr31
+#endif
+	rett		#1
+
+# step through an internal exception from uspace mode
+	.globl		__break_step_uspace_softprog_interrupt
+__break_step_uspace_softprog_interrupt:
+	sethi.p		%hi(__entry_uspace_softprog_interrupt_reentry),gr3
+	setlo		%lo(__entry_uspace_softprog_interrupt_reentry),gr3
+	bra		__break_return_as_uspace_prologue
+
+# step through an external interrupt from kernel mode
+	.globl		__break_step_uspace_external_interrupt
+__break_step_uspace_external_interrupt:
+	sethi.p		%hi(__entry_uspace_external_interrupt_reentry),gr3
+	setlo		%lo(__entry_uspace_external_interrupt_reentry),gr3
+
+__break_return_as_uspace_prologue:
+	LEDS		0x20ff,gr2
+
+	movgs		gr3,bpcsr
+
+	# do the bit we had to skip
+	sethi.p		%hi(__kernel_frame0_ptr),gr28
+	setlo		%lo(__kernel_frame0_ptr),gr28
+	ldi.p		@(gr28,#0),gr28
+
+	setlos		#REG__STATUS_STEP,gr2
+	sti		gr2,@(gr28,#REG__STATUS)	/* record single step status */
+
+	# cancel single-stepping mode
+	movsg		dcr,gr2
+	sethi.p		%hi(~DCR_SE),gr3
+	setlo		%lo(~DCR_SE),gr3
+	and		gr2,gr3,gr2
+	movgs		gr2,dcr
+
+	LEDS		0x20fe,gr2
+
+	ldi		@(gr31,#REG_CCR),gr3
+	movgs		gr3,ccr
+	lddi.p		@(gr31,#REG_GR(2)),gr2
+	xor		gr31,gr31,gr31
+	movgs		gr0,brr
+#ifdef CONFIG_MMU
+	movsg		scr3,gr31
+#endif
+	rett		#1
+
+#ifdef CONFIG_MMU
+# step through an ITLB-miss handler from user mode
+	.globl		__break_user_insn_tlb_miss
+__break_user_insn_tlb_miss:
+	# we'll want to try the trap stub again
+	sethi.p		%hi(__trap_user_insn_tlb_miss),gr2
+	setlo		%lo(__trap_user_insn_tlb_miss),gr2
+	movgs		gr2,bpcsr
+
+__break_tlb_miss_common:
+	LEDS		0x2101,gr2
+
+	# cancel single-stepping mode
+	movsg		dcr,gr2
+	sethi.p		%hi(~DCR_SE),gr3
+	setlo		%lo(~DCR_SE),gr3
+	and		gr2,gr3,gr2
+	movgs		gr2,dcr
+
+	# we'll swap the real return address for one with a BREAK insn so that we can re-enable
+	# single stepping on return
+	movsg		pcsr,gr2
+	sethi.p		%hi(__break_tlb_miss_real_return_info),gr3
+	setlo		%lo(__break_tlb_miss_real_return_info),gr3
+	sti		gr2,@(gr3,#0)
+
+	sethi.p		%hi(__break_tlb_miss_return_break),gr2
+	setlo		%lo(__break_tlb_miss_return_break),gr2
+	movgs		gr2,pcsr
+
+	# we also have to fudge PSR because the return BREAK is in kernel space and we want
+	# to get a BREAK fault not an access violation should the return be to userspace
+	movsg		psr,gr2
+	sti.p		gr2,@(gr3,#4)
+	ori		gr2,#PSR_PS,gr2
+	movgs		gr2,psr
+
+	LEDS		0x2102,gr2
+
+	ldi		@(gr31,#REG_CCR),gr3
+	movgs		gr3,ccr
+	lddi		@(gr31,#REG_GR(2)),gr2
+	movsg		scr3,gr31
+	movgs		gr0,brr
+	rett		#1
+
+# step through a DTLB-miss handler from user mode
+	.globl		__break_user_data_tlb_miss
+__break_user_data_tlb_miss:
+	# we'll want to try the trap stub again
+	sethi.p		%hi(__trap_user_data_tlb_miss),gr2
+	setlo		%lo(__trap_user_data_tlb_miss),gr2
+	movgs		gr2,bpcsr
+	bra		__break_tlb_miss_common
+
+# step through an ITLB-miss handler from kernel mode
+	.globl		__break_kernel_insn_tlb_miss
+__break_kernel_insn_tlb_miss:
+	# we'll want to try the trap stub again
+	sethi.p		%hi(__trap_kernel_insn_tlb_miss),gr2
+	setlo		%lo(__trap_kernel_insn_tlb_miss),gr2
+	movgs		gr2,bpcsr
+	bra		__break_tlb_miss_common
+
+# step through a DTLB-miss handler from kernel mode
+	.globl		__break_kernel_data_tlb_miss
+__break_kernel_data_tlb_miss:
+	# we'll want to try the trap stub again
+	sethi.p		%hi(__trap_kernel_data_tlb_miss),gr2
+	setlo		%lo(__trap_kernel_data_tlb_miss),gr2
+	movgs		gr2,bpcsr
+	bra		__break_tlb_miss_common
+#endif
+
+###############################################################################
+#
+# handle debug events originating with userspace
+#
+###############################################################################
+__break_maybe_userspace:
+	LEDS		0x3003,gr2
+
+	setlos		#BPSR_BS,gr2
+	andcc		gr3,gr2,gr0,icc0
+	bne		icc0,#0,__break_continue	/* skip if PSR.S was 1 */
+
+	movsg		brr,gr2
+	andicc		gr2,#BRR_ST|BRR_SB,gr0,icc0
+	beq		icc0,#0,__break_continue	/* jump if not BREAK or single-step */
+
+	LEDS		0x3007,gr2
+
+	# do the first part of the exception prologue here
+	sethi.p		%hi(__kernel_frame0_ptr),gr28
+	setlo		%lo(__kernel_frame0_ptr),gr28
+	ldi		@(gr28,#0),gr28
+	andi		gr28,#~7,gr28
+
+	# set up the kernel stack pointer
+	sti		sp  ,@(gr28,#REG_SP)
+	ori		gr28,0,sp
+	sti		gr0 ,@(gr28,#REG_GR(28))
+
+	stdi		gr20,@(gr28,#REG_GR(20))
+	stdi		gr22,@(gr28,#REG_GR(22))
+
+	movsg		tbr,gr20
+	movsg		bpcsr,gr21
+	movsg		psr,gr22
+
+	# determine the exception type and cancel single-stepping mode
+	or		gr0,gr0,gr23
+
+	movsg		dcr,gr2
+	sethi.p		%hi(DCR_SE),gr3
+	setlo		%lo(DCR_SE),gr3
+	andcc		gr2,gr3,gr0,icc0
+	beq		icc0,#0,__break_no_user_sstep	/* must have been a BREAK insn */
+
+	not		gr3,gr3
+	and		gr2,gr3,gr2
+	movgs		gr2,dcr
+	ori		gr23,#REG__STATUS_STEP,gr23
+
+__break_no_user_sstep:
+	LEDS		0x300f,gr2
+
+	movsg		brr,gr2
+	andi		gr2,#BRR_ST|BRR_SB,gr2
+	slli		gr2,#1,gr2
+	or		gr23,gr2,gr23
+	sti.p		gr23,@(gr28,#REG__STATUS)	/* record single step status */
+
+	# adjust the value acquired from TBR - this indicates the exception
+	setlos		#~TBR_TT,gr2
+	and.p		gr20,gr2,gr20
+	setlos		#TBR_TT_BREAK,gr2
+	or.p		gr20,gr2,gr20
+
+	# fudge PSR.PS and BPSR.BS to return to kernel mode through the trap
+	# table as trap 126
+	andi		gr22,#~PSR_PS,gr22		/* PSR.PS should be 0 */
+	movgs		gr22,psr
+
+	setlos		#BPSR_BS,gr2			/* BPSR.BS should be 1 and BPSR.BET 0 */
+	movgs		gr2,bpsr
+
+	# return through remainder of the exception prologue
+	# - need to load gr23 with return handler address
+	sethi.p		%hi(__entry_return_from_user_exception),gr23
+	setlo		%lo(__entry_return_from_user_exception),gr23
+	sethi.p		%hi(__entry_common),gr3
+	setlo		%lo(__entry_common),gr3
+	movgs		gr3,bpcsr
+
+	LEDS		0x301f,gr2
+
+	ldi		@(gr31,#REG_CCR),gr3
+	movgs		gr3,ccr
+	lddi.p		@(gr31,#REG_GR(2)),gr2
+	xor		gr31,gr31,gr31
+	movgs		gr0,brr
+#ifdef CONFIG_MMU
+	movsg		scr3,gr31
+#endif
+	rett		#1
+
+###############################################################################
+#
+# resume normal debug-mode entry
+#
+###############################################################################
+__break_continue:
+	LEDS		0x4003,gr2
+
+	# set up the kernel stack pointer
+	sti		sp,@(gr31,#REG_SP)
+
+	sethi.p		%hi(__break_stack_tos),sp
+	setlo		%lo(__break_stack_tos),sp
+
+	# finish building the exception frame
+	stdi		gr4 ,@(gr31,#REG_GR(4))
+	stdi		gr6 ,@(gr31,#REG_GR(6))
+	stdi		gr8 ,@(gr31,#REG_GR(8))
+	stdi		gr10,@(gr31,#REG_GR(10))
+	stdi		gr12,@(gr31,#REG_GR(12))
+	stdi		gr14,@(gr31,#REG_GR(14))
+	stdi		gr16,@(gr31,#REG_GR(16))
+	stdi		gr18,@(gr31,#REG_GR(18))
+	stdi		gr20,@(gr31,#REG_GR(20))
+	stdi		gr22,@(gr31,#REG_GR(22))
+	stdi		gr24,@(gr31,#REG_GR(24))
+	stdi		gr26,@(gr31,#REG_GR(26))
+	sti		gr0 ,@(gr31,#REG_GR(28))	/* NULL frame pointer */
+	sti		gr29,@(gr31,#REG_GR(29))
+	sti		gr30,@(gr31,#REG_GR(30))
+	sti		gr8 ,@(gr31,#REG_ORIG_GR8)
+
+#ifdef CONFIG_MMU
+	movsg		scr3,gr19
+	sti		gr19,@(gr31,#REG_GR(31))
+#endif
+
+	movsg		bpsr ,gr19
+	movsg		tbr  ,gr20
+	movsg		bpcsr,gr21
+	movsg		psr  ,gr22
+	movsg		isr  ,gr23
+	movsg		cccr ,gr25
+	movsg		lr   ,gr26
+	movsg		lcr  ,gr27
+
+	andi.p		gr22,#~(PSR_S|PSR_ET),gr5	/* rebuild PSR */
+	andi		gr19,#PSR_ET,gr4
+	or.p		gr4,gr5,gr5
+	srli		gr19,#10,gr4
+	andi		gr4,#PSR_S,gr4
+	or.p		gr4,gr5,gr5
+
+	setlos		#-1,gr6
+	sti		gr20,@(gr31,#REG_TBR)
+	sti		gr21,@(gr31,#REG_PC)
+	sti		gr5 ,@(gr31,#REG_PSR)
+	sti		gr23,@(gr31,#REG_ISR)
+	sti		gr25,@(gr31,#REG_CCCR)
+	stdi		gr26,@(gr31,#REG_LR)
+	sti		gr6 ,@(gr31,#REG_SYSCALLNO)
+
+	# store CPU-specific regs
+	movsg		iacc0h,gr4
+	movsg		iacc0l,gr5
+	stdi		gr4,@(gr31,#REG_IACC0)
+
+	movsg		gner0,gr4
+	movsg		gner1,gr5
+	stdi		gr4,@(gr31,#REG_GNER0)
+
+	# build the debug register frame
+	movsg		brr,gr4
+	movgs		gr0,brr
+	movsg		nmar,gr5
+	movsg		dcr,gr6
+
+	stdi		gr4 ,@(gr31,#REG_BRR)
+	sti		gr19,@(gr31,#REG_BPSR)
+	sti.p		gr6 ,@(gr31,#REG_DCR)
+
+	# trap exceptions during break handling and disable h/w breakpoints/watchpoints
+	sethi		%hi(DCR_EBE),gr5
+	setlo.p		%lo(DCR_EBE),gr5
+	sethi		%hi(__entry_breaktrap_table),gr4
+	setlo		%lo(__entry_breaktrap_table),gr4
+	movgs		gr5,dcr
+	movgs		gr4,tbr
+
+	# set up kernel global registers
+	sethi.p		%hi(__kernel_current_task),gr5
+	setlo		%lo(__kernel_current_task),gr5
+	ld		@(gr5,gr0),gr29
+	ldi.p		@(gr29,#4),gr15		; __current_thread_info = current->thread_info
+
+	sethi		%hi(_gp),gr16
+	setlo.p		%lo(_gp),gr16
+
+	# make sure we (the kernel) get div-zero and misalignment exceptions
+	setlos		#ISR_EDE|ISR_DTT_DIVBYZERO|ISR_EMAM_EXCEPTION,gr5
+	movgs		gr5,isr
+
+	# enter the GDB stub
+	LEDS		0x4007,gr2
+
+	or.p		gr0,gr0,fp
+	call		debug_stub
+
+	LEDS		0x403f,gr2
+
+	# return from break
+	lddi		@(gr31,#REG_IACC0),gr4
+	movgs		gr4,iacc0h
+	movgs		gr5,iacc0l
+
+	lddi		@(gr31,#REG_GNER0),gr4
+	movgs		gr4,gner0
+	movgs		gr5,gner1
+
+	lddi		@(gr31,#REG_LR)  ,gr26
+	lddi		@(gr31,#REG_CCR) ,gr24
+	lddi		@(gr31,#REG_PSR) ,gr22
+	ldi		@(gr31,#REG_PC)  ,gr21
+	ldi		@(gr31,#REG_TBR) ,gr20
+	ldi.p		@(gr31,#REG_DCR) ,gr6
+
+	andi		gr22,#PSR_S,gr19		/* rebuild BPSR */
+	andi.p		gr22,#PSR_ET,gr5
+	slli		gr19,#10,gr19
+	or		gr5,gr19,gr19
+
+	movgs		gr6 ,dcr
+	movgs		gr19,bpsr
+	movgs		gr20,tbr
+	movgs		gr21,bpcsr
+	movgs		gr23,isr
+	movgs		gr24,ccr
+	movgs		gr25,cccr
+	movgs		gr26,lr
+	movgs		gr27,lcr
+
+	LEDS		0x407f,gr2
+
+#ifdef CONFIG_MMU
+	ldi		@(gr31,#REG_GR(31)),gr2
+	movgs		gr2,scr3
+#endif
+
+	ldi		@(gr31,#REG_GR(30)),gr30
+	ldi		@(gr31,#REG_GR(29)),gr29
+	lddi		@(gr31,#REG_GR(26)),gr26
+	lddi		@(gr31,#REG_GR(24)),gr24
+	lddi		@(gr31,#REG_GR(22)),gr22
+	lddi		@(gr31,#REG_GR(20)),gr20
+	lddi		@(gr31,#REG_GR(18)),gr18
+	lddi		@(gr31,#REG_GR(16)),gr16
+	lddi		@(gr31,#REG_GR(14)),gr14
+	lddi		@(gr31,#REG_GR(12)),gr12
+	lddi		@(gr31,#REG_GR(10)),gr10
+	lddi		@(gr31,#REG_GR(8)) ,gr8
+	lddi		@(gr31,#REG_GR(6)) ,gr6
+	lddi		@(gr31,#REG_GR(4)) ,gr4
+	lddi		@(gr31,#REG_GR(2)) ,gr2
+	ldi.p		@(gr31,#REG_SP)    ,sp
+
+	xor		gr31,gr31,gr31
+	movgs		gr0,brr
+#ifdef CONFIG_MMU
+	movsg		scr3,gr31
+#endif
+	rett		#1
+
+###################################################################################################
+#
+# GDB stub "system calls"
+#
+###################################################################################################
+
+#ifdef CONFIG_GDBSTUB
+	# void gdbstub_console_write(struct console *con, const char *p, unsigned n)
+	.globl		gdbstub_console_write
+gdbstub_console_write:
+	break
+	bralr
+#endif
+
+	# GDB stub BUG() trap
+	# GR8 is the proposed signal number
+	.globl		__debug_bug_trap
+__debug_bug_trap:
+	break
+	bralr
+
+	# transfer kernel exeception to GDB for handling
+	.globl		__break_hijack_kernel_event
+__break_hijack_kernel_event:
+	break
+	.globl		__break_hijack_kernel_event_breaks_here
+__break_hijack_kernel_event_breaks_here:
+	nop
+
+#ifdef CONFIG_MMU
+	# handle a return from TLB-miss that requires single-step reactivation
+	.globl		__break_tlb_miss_return_break
+__break_tlb_miss_return_break:
+	break
+__break_tlb_miss_return_breaks_here:
+	nop
+#endif
+
+	# guard the first .text label in the next file from confusion
+	nop
diff --git a/arch/frv/kernel/cmode.S b/arch/frv/kernel/cmode.S
new file mode 100644
index 0000000..6591e6a
--- /dev/null
+++ b/arch/frv/kernel/cmode.S
@@ -0,0 +1,190 @@
+/* cmode.S: clock mode management
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Woodhouse (dwmw2@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ */
+
+#include <linux/sys.h>
+#include <linux/config.h>
+#include <linux/linkage.h>
+#include <asm/setup.h>
+#include <asm/segment.h>
+#include <asm/ptrace.h>
+#include <asm/errno.h>
+#include <asm/cache.h>
+#include <asm/spr-regs.h>
+
+#define __addr_MASK	0xfeff9820	/* interrupt controller mask */
+
+#define __addr_SDRAMC	0xfe000400	/* SDRAM controller regs */
+#define SDRAMC_DSTS	0x28		/* SDRAM status */
+#define SDRAMC_DSTS_SSI	0x00000001	/* indicates that the SDRAM is in self-refresh mode */
+#define SDRAMC_DRCN	0x30		/* SDRAM refresh control */
+#define SDRAMC_DRCN_SR	0x00000001	/* transition SDRAM into self-refresh mode */
+#define __addr_CLKC	0xfeff9a00
+#define CLKC_SWCMODE	0x00000008
+#define __addr_LEDS	0xe1200004
+
+.macro li v r
+	sethi.p		%hi(\v),\r
+	setlo		%lo(\v),\r
+.endm
+
+	.text
+	.balign		4
+
+
+###############################################################################
+#
+# Change CMODE
+# - void frv_change_cmode(int cmode)
+#
+###############################################################################
+	.globl		frv_change_cmode
+        .type		frv_change_cmode,@function
+
+.macro	LEDS v
+#ifdef DEBUG_CMODE
+	setlos	#~\v,gr10
+	sti	gr10,@(gr11,#0)
+	membar
+#endif
+.endm
+
+frv_change_cmode:
+	movsg		lr,gr9
+#ifdef DEBUG_CMODE
+	li		__addr_LEDS,gr11
+#endif
+	dcef		@(gr0,gr0),#1
+
+	# Shift argument left by 24 bits to fit in SWCMODE register later.
+	slli		gr8,#24,gr8
+
+	# (1) Set '0' in the PSR.ET bit, and prohibit interrupts.
+	movsg		psr,gr14
+	andi		gr14,#~PSR_ET,gr3
+	movgs		gr3,psr
+
+#if 0 // Fujitsu recommend to skip this and will update docs.
+	# (2) Set '0' to all bits of the MASK register of the interrupt
+	#     controller, and mask interrupts.
+	li		__addr_MASK,gr12
+	ldi		@(gr12,#0),gr13
+	li		0xffff0000,gr4
+	sti		gr4,@(gr12,#0)
+#endif
+
+	# (3) Stop the transfer function of DMAC. Stop all the bus masters
+	#     to access SDRAM and the internal resources.
+
+	# (already done by caller)
+
+	# (4) Preload a series of following instructions to the instruction
+	#     cache.
+	li		#__cmode_icache_lock_start,gr3
+	li		#__cmode_icache_lock_end,gr4
+
+1:	icpl		gr3,gr0,#1
+	addi		gr3,#L1_CACHE_BYTES,gr3
+	cmp		gr4,gr3,icc0
+	bhi		icc0,#0,1b
+
+	# Set up addresses in regs for later steps.
+	setlos		SDRAMC_DRCN_SR,gr3
+	li		__addr_SDRAMC,gr4
+	li		__addr_CLKC,gr5
+	ldi		@(gr5,#0),gr6
+	li		#0x80000000,gr7
+	or		gr6,gr7,gr6
+
+	bra		__cmode_icache_lock_start
+
+	.balign	L1_CACHE_BYTES
+__cmode_icache_lock_start:
+
+	# (5) Flush the content of all caches by the DCEF instruction.
+	dcef		@(gr0,gr0),#1
+
+	# (6) Execute loading the dummy for SDRAM.
+	ldi		@(gr9,#0),gr0
+
+	# (7) Set '1' to the DRCN.SR bit, and change SDRAM to the
+	#     self-refresh mode. Execute the dummy load to all memory
+	#     devices set to cacheable on the external bus side in parallel
+	#     with this.
+	sti		gr3,@(gr4,#SDRAMC_DRCN)
+
+	# (8) Execute memory barrier instruction (MEMBAR).
+	membar
+
+	# (9) Read the DSTS register repeatedly until '1' stands in the
+	#     DSTS.SSI field.
+1:	ldi		@(gr4,#SDRAMC_DSTS),gr3
+	andicc		gr3,#SDRAMC_DSTS_SSI,gr3,icc0
+	beq		icc0,#0,1b
+
+	# (10) Execute memory barrier instruction (MEMBAR).
+	membar
+
+#if 1
+	# (11) Set the value of CMODE that you want to change to
+	#      SWCMODE.SWCM[3:0].
+	sti		gr8,@(gr5,#CLKC_SWCMODE)
+
+	# (12) Set '1' to the CLKC.SWEN bit. In that case, do not change
+	#      fields other than SWEN of the CLKC register.
+	sti		gr6,@(gr5,#0)
+#endif
+	# (13) Execute the instruction just after the memory barrier
+	# instruction that executes the self-loop 256 times. (Meanwhile,
+	# the CMODE switch is done.)
+	membar
+	setlos		#256,gr7
+2:	subicc		gr7,#1,gr7,icc0
+	bne		icc0,#2,2b
+
+	LEDS	0x36
+
+	# (14) Release the self-refresh of SDRAM.
+	sti		gr0,@(gr4,#SDRAMC_DRCN)
+
+	# Wait for it...
+3:	ldi		@(gr4,#SDRAMC_DSTS),gr3
+	andicc		gr3,#SDRAMC_DSTS_SSI,gr3,icc0
+	bne		icc0,#2,3b
+
+#if 0
+	li		0x0100000,gr10
+4:	subicc		gr10,#1,gr10,icc0
+
+	bne		icc0,#0,4b
+#endif
+
+__cmode_icache_lock_end:
+
+	li		#__cmode_icache_lock_start,gr3
+	li		#__cmode_icache_lock_end,gr4
+
+4:	icul		gr3
+	addi		gr3,#L1_CACHE_BYTES,gr3
+	cmp		gr4,gr3,icc0
+	bhi		icc0,#0,4b
+
+#if 0 // Fujitsu recommend to skip this and will update docs.
+	# (15) Release the interrupt mask setting of the MASK register of
+	# the interrupt controller if necessary.
+	sti		gr13,@(gr12,#0)
+#endif
+	# (16) Set  1' in the PSR.ET bit, and permit interrupt.
+	movgs		gr14,psr
+
+	bralr
+
+	.size		frv_change_cmode, .-frv_change_cmode
diff --git a/arch/frv/kernel/debug-stub.c b/arch/frv/kernel/debug-stub.c
new file mode 100644
index 0000000..4761cc4
--- /dev/null
+++ b/arch/frv/kernel/debug-stub.c
@@ -0,0 +1,259 @@
+/* debug-stub.c: debug-mode stub
+ *
+ * Copyright (C) 2004 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 License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/serial_reg.h>
+
+#include <asm/system.h>
+#include <asm/serial-regs.h>
+#include <asm/timer-regs.h>
+#include <asm/irc-regs.h>
+#include <asm/gdb-stub.h>
+#include "gdb-io.h"
+
+/* CPU board CON5 */
+#define __UART0(X) (*(volatile uint8_t *)(UART0_BASE + (UART_##X)))
+
+#define LSR_WAIT_FOR0(STATE)			\
+do {						\
+} while (!(__UART0(LSR) & UART_LSR_##STATE))
+
+#define FLOWCTL_QUERY0(LINE)	({ __UART0(MSR) & UART_MSR_##LINE; })
+#define FLOWCTL_CLEAR0(LINE)	do { __UART0(MCR) &= ~UART_MCR_##LINE; } while (0)
+#define FLOWCTL_SET0(LINE)	do { __UART0(MCR) |= UART_MCR_##LINE; } while (0)
+
+#define FLOWCTL_WAIT_FOR0(LINE)			\
+do {						\
+	gdbstub_do_rx();			\
+} while(!FLOWCTL_QUERY(LINE))
+
+static void __init debug_stub_init(void);
+
+extern asmlinkage void __break_hijack_kernel_event(void);
+extern asmlinkage void __break_hijack_kernel_event_breaks_here(void);
+
+/*****************************************************************************/
+/*
+ * debug mode handler stub
+ * - we come here with the CPU in debug mode and with exceptions disabled
+ * - handle debugging services for userspace
+ */
+asmlinkage void debug_stub(void)
+{
+	unsigned long hsr0;
+	int type = 0;
+
+	static u8 inited = 0;
+	if (!inited) {
+		debug_stub_init();
+		type = -1;
+		inited = 1;
+	}
+
+	hsr0 = __get_HSR(0);
+	if (hsr0 & HSR0_ETMD)
+		__set_HSR(0, hsr0 & ~HSR0_ETMD);
+
+	/* disable single stepping */
+	__debug_regs->dcr &= ~DCR_SE;
+
+	/* kernel mode can propose an exception be handled in debug mode by jumping to a special
+	 * location */
+	if (__debug_frame->pc == (unsigned long) __break_hijack_kernel_event_breaks_here) {
+		/* replace the debug frame with the kernel frame and discard
+		 * the top kernel context */
+		*__debug_frame = *__frame;
+		__frame = __debug_frame->next_frame;
+		__debug_regs->brr = (__debug_frame->tbr & TBR_TT) << 12;
+		__debug_regs->brr |= BRR_EB;
+	}
+
+	if (__debug_frame->pc == (unsigned long) __debug_bug_trap + 4) {
+		__debug_frame->pc = __debug_frame->lr;
+		type = __debug_frame->gr8;
+	}
+
+#ifdef CONFIG_GDBSTUB
+	gdbstub(type);
+#endif
+
+	if (hsr0 & HSR0_ETMD)
+		__set_HSR(0, __get_HSR(0) | HSR0_ETMD);
+
+} /* end debug_stub() */
+
+/*****************************************************************************/
+/*
+ * debug stub initialisation
+ */
+static void __init debug_stub_init(void)
+{
+	__set_IRR(6, 0xff000000);	/* map ERRs to NMI */
+	__set_IITMR(1, 0x20000000);	/* ERR0/1, UART0/1 IRQ detect levels */
+
+	asm volatile("	movgs	gr0,ibar0	\n"
+		     "	movgs	gr0,ibar1	\n"
+		     "	movgs	gr0,ibar2	\n"
+		     "	movgs	gr0,ibar3	\n"
+		     "	movgs	gr0,dbar0	\n"
+		     "	movgs	gr0,dbmr00	\n"
+		     "	movgs	gr0,dbmr01	\n"
+		     "	movgs	gr0,dbdr00	\n"
+		     "	movgs	gr0,dbdr01	\n"
+		     "	movgs	gr0,dbar1	\n"
+		     "	movgs	gr0,dbmr10	\n"
+		     "	movgs	gr0,dbmr11	\n"
+		     "	movgs	gr0,dbdr10	\n"
+		     "	movgs	gr0,dbdr11	\n"
+		     );
+
+	/* deal with debugging stub initialisation and initial pause */
+	if (__debug_frame->pc == (unsigned long) __debug_stub_init_break)
+		__debug_frame->pc = (unsigned long) start_kernel;
+
+	/* enable the debug events we want to trap */
+	__debug_regs->dcr = DCR_EBE;
+
+#ifdef CONFIG_GDBSTUB
+	gdbstub_init();
+#endif
+
+	__clr_MASK_all();
+	__clr_MASK(15);
+	__clr_RC(15);
+
+} /* end debug_stub_init() */
+
+/*****************************************************************************/
+/*
+ * kernel "exit" trap for gdb stub
+ */
+void debug_stub_exit(int status)
+{
+
+#ifdef CONFIG_GDBSTUB
+	gdbstub_exit(status);
+#endif
+
+} /* end debug_stub_exit() */
+
+/*****************************************************************************/
+/*
+ * send string to serial port
+ */
+void debug_to_serial(const char *p, int n)
+{
+	char ch;
+
+	for (; n > 0; n--) {
+		ch = *p++;
+		FLOWCTL_SET0(DTR);
+		LSR_WAIT_FOR0(THRE);
+		// FLOWCTL_WAIT_FOR(CTS);
+
+		if (ch == 0x0a) {
+			__UART0(TX) = 0x0d;
+			mb();
+			LSR_WAIT_FOR0(THRE);
+			// FLOWCTL_WAIT_FOR(CTS);
+		}
+		__UART0(TX) = ch;
+		mb();
+
+		FLOWCTL_CLEAR0(DTR);
+	}
+
+} /* end debug_to_serial() */
+
+/*****************************************************************************/
+/*
+ * send string to serial port
+ */
+void debug_to_serial2(const char *fmt, ...)
+{
+	va_list va;
+	char buf[64];
+	int n;
+
+	va_start(va, fmt);
+	n = vsprintf(buf, fmt, va);
+	va_end(va);
+
+	debug_to_serial(buf, n);
+
+} /* end debug_to_serial2() */
+
+/*****************************************************************************/
+/*
+ * set up the ttyS0 serial port baud rate timers
+ */
+void __init console_set_baud(unsigned baud)
+{
+	unsigned value, high, low;
+	u8 lcr;
+
+	/* work out the divisor to give us the nearest higher baud rate */
+	value = __serial_clock_speed_HZ / 16 / baud;
+
+	/* determine the baud rate range */
+	high = __serial_clock_speed_HZ / 16 / value;
+	low = __serial_clock_speed_HZ / 16 / (value + 1);
+
+	/* pick the nearest bound */
+	if (low + (high - low) / 2 > baud)
+		value++;
+
+	lcr = __UART0(LCR);
+	__UART0(LCR) |= UART_LCR_DLAB;
+	mb();
+	__UART0(DLL) = value & 0xff;
+	__UART0(DLM) = (value >> 8) & 0xff;
+	mb();
+	__UART0(LCR) = lcr;
+	mb();
+
+} /* end console_set_baud() */
+
+/*****************************************************************************/
+/*
+ *
+ */
+int __init console_get_baud(void)
+{
+	unsigned value;
+	u8 lcr;
+
+	lcr = __UART0(LCR);
+	__UART0(LCR) |= UART_LCR_DLAB;
+	mb();
+	value =  __UART0(DLM) << 8;
+	value |= __UART0(DLL);
+	__UART0(LCR) = lcr;
+	mb();
+
+	return value;
+} /* end console_get_baud() */
+
+/*****************************************************************************/
+/*
+ * display BUG() info
+ */
+#ifndef CONFIG_NO_KERNEL_MSG
+void __debug_bug_printk(const char *file, unsigned line)
+{
+	printk("kernel BUG at %s:%d!\n", file, line);
+
+} /* end __debug_bug_printk() */
+#endif
diff --git a/arch/frv/kernel/dma.c b/arch/frv/kernel/dma.c
new file mode 100644
index 0000000..f5de6cf
--- /dev/null
+++ b/arch/frv/kernel/dma.c
@@ -0,0 +1,464 @@
+/* dma.c: DMA controller management on FR401 and the like
+ *
+ * Copyright (C) 2004 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 License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <asm/dma.h>
+#include <asm/gpio-regs.h>
+#include <asm/irc-regs.h>
+#include <asm/cpu-irqs.h>
+
+struct frv_dma_channel {
+	uint8_t			flags;
+#define FRV_DMA_FLAGS_RESERVED	0x01
+#define FRV_DMA_FLAGS_INUSE	0x02
+#define FRV_DMA_FLAGS_PAUSED	0x04
+	uint8_t			cap;		/* capabilities available */
+	int			irq;		/* completion IRQ */
+	uint32_t		dreqbit;
+	uint32_t		dackbit;
+	uint32_t		donebit;
+	const unsigned long	ioaddr;		/* DMA controller regs addr */
+	const char		*devname;
+	dma_irq_handler_t	handler;
+	void			*data;
+};
+
+
+#define __get_DMAC(IO,X)	({ *(volatile unsigned long *)((IO) + DMAC_##X##x); })
+
+#define __set_DMAC(IO,X,V)					\
+do {								\
+	*(volatile unsigned long *)((IO) + DMAC_##X##x) = (V);	\
+	mb();							\
+} while(0)
+
+#define ___set_DMAC(IO,X,V)					\
+do {								\
+	*(volatile unsigned long *)((IO) + DMAC_##X##x) = (V);	\
+} while(0)
+
+
+static struct frv_dma_channel frv_dma_channels[FRV_DMA_NCHANS] = {
+	[0] = {
+		.cap		= FRV_DMA_CAP_DREQ | FRV_DMA_CAP_DACK | FRV_DMA_CAP_DONE,
+		.irq		= IRQ_CPU_DMA0,
+		.dreqbit	= SIR_DREQ0_INPUT,
+		.dackbit	= SOR_DACK0_OUTPUT,
+		.donebit	= SOR_DONE0_OUTPUT,
+		.ioaddr		= 0xfe000900,
+	},
+	[1] = {
+		.cap		= FRV_DMA_CAP_DREQ | FRV_DMA_CAP_DACK | FRV_DMA_CAP_DONE,
+		.irq		= IRQ_CPU_DMA1,
+		.dreqbit	= SIR_DREQ1_INPUT,
+		.dackbit	= SOR_DACK1_OUTPUT,
+		.donebit	= SOR_DONE1_OUTPUT,
+		.ioaddr		= 0xfe000980,
+	},
+	[2] = {
+		.cap		= FRV_DMA_CAP_DREQ | FRV_DMA_CAP_DACK,
+		.irq		= IRQ_CPU_DMA2,
+		.dreqbit	= SIR_DREQ2_INPUT,
+		.dackbit	= SOR_DACK2_OUTPUT,
+		.ioaddr		= 0xfe000a00,
+	},
+	[3] = {
+		.cap		= FRV_DMA_CAP_DREQ | FRV_DMA_CAP_DACK,
+		.irq		= IRQ_CPU_DMA3,
+		.dreqbit	= SIR_DREQ3_INPUT,
+		.dackbit	= SOR_DACK3_OUTPUT,
+		.ioaddr		= 0xfe000a80,
+	},
+	[4] = {
+		.cap		= FRV_DMA_CAP_DREQ,
+		.irq		= IRQ_CPU_DMA4,
+		.dreqbit	= SIR_DREQ4_INPUT,
+		.ioaddr		= 0xfe001000,
+	},
+	[5] = {
+		.cap		= FRV_DMA_CAP_DREQ,
+		.irq		= IRQ_CPU_DMA5,
+		.dreqbit	= SIR_DREQ5_INPUT,
+		.ioaddr		= 0xfe001080,
+	},
+	[6] = {
+		.cap		= FRV_DMA_CAP_DREQ,
+		.irq		= IRQ_CPU_DMA6,
+		.dreqbit	= SIR_DREQ6_INPUT,
+		.ioaddr		= 0xfe001100,
+	},
+	[7] = {
+		.cap		= FRV_DMA_CAP_DREQ,
+		.irq		= IRQ_CPU_DMA7,
+		.dreqbit	= SIR_DREQ7_INPUT,
+		.ioaddr		= 0xfe001180,
+	},
+};
+
+static DEFINE_RWLOCK(frv_dma_channels_lock);
+
+unsigned long frv_dma_inprogress;
+
+#define frv_clear_dma_inprogress(channel) \
+	atomic_clear_mask(1 << (channel), &frv_dma_inprogress);
+
+#define frv_set_dma_inprogress(channel) \
+	atomic_set_mask(1 << (channel), &frv_dma_inprogress);
+
+/*****************************************************************************/
+/*
+ * DMA irq handler - determine channel involved, grab status and call real handler
+ */
+static irqreturn_t dma_irq_handler(int irq, void *_channel, struct pt_regs *regs)
+{
+	struct frv_dma_channel *channel = _channel;
+
+	frv_clear_dma_inprogress(channel - frv_dma_channels);
+	return channel->handler(channel - frv_dma_channels,
+				__get_DMAC(channel->ioaddr, CSTR),
+				channel->data,
+				regs);
+
+} /* end dma_irq_handler() */
+
+/*****************************************************************************/
+/*
+ * Determine which DMA controllers are present on this CPU
+ */
+void __init frv_dma_init(void)
+{
+	unsigned long psr = __get_PSR();
+	int num_dma, i;
+
+	/* First, determine how many DMA channels are available */
+	switch (PSR_IMPLE(psr)) {
+	case PSR_IMPLE_FR405:
+	case PSR_IMPLE_FR451:
+	case PSR_IMPLE_FR501:
+	case PSR_IMPLE_FR551:
+		num_dma = FRV_DMA_8CHANS;
+		break;
+
+	case PSR_IMPLE_FR401:
+	default:
+		num_dma = FRV_DMA_4CHANS;
+		break;
+	}
+
+	/* Now mark all of the non-existent channels as reserved */
+	for(i = num_dma; i < FRV_DMA_NCHANS; i++)
+		frv_dma_channels[i].flags = FRV_DMA_FLAGS_RESERVED;
+
+} /* end frv_dma_init() */
+
+/*****************************************************************************/
+/*
+ * allocate a DMA controller channel and the IRQ associated with it
+ */
+int frv_dma_open(const char *devname,
+		 unsigned long dmamask,
+		 int dmacap,
+		 dma_irq_handler_t handler,
+		 unsigned long irq_flags,
+		 void *data)
+{
+	struct frv_dma_channel *channel;
+	int dma, ret;
+	uint32_t val;
+
+	write_lock(&frv_dma_channels_lock);
+
+	ret = -ENOSPC;
+
+	for (dma = FRV_DMA_NCHANS - 1; dma >= 0; dma--) {
+		channel = &frv_dma_channels[dma];
+
+		if (!test_bit(dma, &dmamask))
+			continue;
+
+		if ((channel->cap & dmacap) != dmacap)
+			continue;
+
+		if (!frv_dma_channels[dma].flags)
+			goto found;
+	}
+
+	goto out;
+
+ found:
+	ret = request_irq(channel->irq, dma_irq_handler, irq_flags, devname, channel);
+	if (ret < 0)
+		goto out;
+
+	/* okay, we've allocated all the resources */
+	channel = &frv_dma_channels[dma];
+
+	channel->flags		|= FRV_DMA_FLAGS_INUSE;
+	channel->devname	= devname;
+	channel->handler	= handler;
+	channel->data		= data;
+
+	/* Now make sure we are set up for DMA and not GPIO */
+	/* SIR bit must be set for DMA to work */
+	__set_SIR(channel->dreqbit | __get_SIR());
+	/* SOR bits depend on what the caller requests */
+	val = __get_SOR();
+	if(dmacap & FRV_DMA_CAP_DACK)
+		val |= channel->dackbit;
+	else
+		val &= ~channel->dackbit;
+	if(dmacap & FRV_DMA_CAP_DONE)
+		val |= channel->donebit;
+	else
+		val &= ~channel->donebit;
+	__set_SOR(val);
+
+	ret = dma;
+ out:
+	write_unlock(&frv_dma_channels_lock);
+	return ret;
+} /* end frv_dma_open() */
+
+EXPORT_SYMBOL(frv_dma_open);
+
+/*****************************************************************************/
+/*
+ * close a DMA channel and its associated interrupt
+ */
+void frv_dma_close(int dma)
+{
+	struct frv_dma_channel *channel = &frv_dma_channels[dma];
+	unsigned long flags;
+
+	write_lock_irqsave(&frv_dma_channels_lock, flags);
+
+	free_irq(channel->irq, channel);
+	frv_dma_stop(dma);
+
+	channel->flags &= ~FRV_DMA_FLAGS_INUSE;
+
+	write_unlock_irqrestore(&frv_dma_channels_lock, flags);
+} /* end frv_dma_close() */
+
+EXPORT_SYMBOL(frv_dma_close);
+
+/*****************************************************************************/
+/*
+ * set static configuration on a DMA channel
+ */
+void frv_dma_config(int dma, unsigned long ccfr, unsigned long cctr, unsigned long apr)
+{
+	unsigned long ioaddr = frv_dma_channels[dma].ioaddr;
+
+	___set_DMAC(ioaddr, CCFR, ccfr);
+	___set_DMAC(ioaddr, CCTR, cctr);
+	___set_DMAC(ioaddr, APR,  apr);
+	mb();
+
+} /* end frv_dma_config() */
+
+EXPORT_SYMBOL(frv_dma_config);
+
+/*****************************************************************************/
+/*
+ * start a DMA channel
+ */
+void frv_dma_start(int dma,
+		   unsigned long sba, unsigned long dba,
+		   unsigned long pix, unsigned long six, unsigned long bcl)
+{
+	unsigned long ioaddr = frv_dma_channels[dma].ioaddr;
+
+	___set_DMAC(ioaddr, SBA,  sba);
+	___set_DMAC(ioaddr, DBA,  dba);
+	___set_DMAC(ioaddr, PIX,  pix);
+	___set_DMAC(ioaddr, SIX,  six);
+	___set_DMAC(ioaddr, BCL,  bcl);
+	___set_DMAC(ioaddr, CSTR, 0);
+	mb();
+
+	__set_DMAC(ioaddr, CCTR, __get_DMAC(ioaddr, CCTR) | DMAC_CCTRx_ACT);
+	frv_set_dma_inprogress(dma);
+
+} /* end frv_dma_start() */
+
+EXPORT_SYMBOL(frv_dma_start);
+
+/*****************************************************************************/
+/*
+ * restart a DMA channel that's been stopped in circular addressing mode by comparison-end
+ */
+void frv_dma_restart_circular(int dma, unsigned long six)
+{
+	unsigned long ioaddr = frv_dma_channels[dma].ioaddr;
+
+	___set_DMAC(ioaddr, SIX,  six);
+	___set_DMAC(ioaddr, CSTR, __get_DMAC(ioaddr, CSTR) & ~DMAC_CSTRx_CE);
+	mb();
+
+	__set_DMAC(ioaddr, CCTR, __get_DMAC(ioaddr, CCTR) | DMAC_CCTRx_ACT);
+	frv_set_dma_inprogress(dma);
+
+} /* end frv_dma_restart_circular() */
+
+EXPORT_SYMBOL(frv_dma_restart_circular);
+
+/*****************************************************************************/
+/*
+ * stop a DMA channel
+ */
+void frv_dma_stop(int dma)
+{
+	unsigned long ioaddr = frv_dma_channels[dma].ioaddr;
+	uint32_t cctr;
+
+	___set_DMAC(ioaddr, CSTR, 0);
+	cctr = __get_DMAC(ioaddr, CCTR);
+	cctr &= ~(DMAC_CCTRx_IE | DMAC_CCTRx_ACT);
+	cctr |= DMAC_CCTRx_FC; 	/* fifo clear */
+	__set_DMAC(ioaddr, CCTR, cctr);
+	__set_DMAC(ioaddr, BCL,  0);
+	frv_clear_dma_inprogress(dma);
+} /* end frv_dma_stop() */
+
+EXPORT_SYMBOL(frv_dma_stop);
+
+/*****************************************************************************/
+/*
+ * test interrupt status of DMA channel
+ */
+int is_frv_dma_interrupting(int dma)
+{
+	unsigned long ioaddr = frv_dma_channels[dma].ioaddr;
+
+	return __get_DMAC(ioaddr, CSTR) & (1 << 23);
+
+} /* end is_frv_dma_interrupting() */
+
+EXPORT_SYMBOL(is_frv_dma_interrupting);
+
+/*****************************************************************************/
+/*
+ * dump data about a DMA channel
+ */
+void frv_dma_dump(int dma)
+{
+	unsigned long ioaddr = frv_dma_channels[dma].ioaddr;
+	unsigned long cstr, pix, six, bcl;
+
+	cstr = __get_DMAC(ioaddr, CSTR);
+	pix  = __get_DMAC(ioaddr, PIX);
+	six  = __get_DMAC(ioaddr, SIX);
+	bcl  = __get_DMAC(ioaddr, BCL);
+
+	printk("DMA[%d] cstr=%lx pix=%lx six=%lx bcl=%lx\n", dma, cstr, pix, six, bcl);
+
+} /* end frv_dma_dump() */
+
+EXPORT_SYMBOL(frv_dma_dump);
+
+/*****************************************************************************/
+/*
+ * pause all DMA controllers
+ * - called by clock mangling routines
+ * - caller must be holding interrupts disabled
+ */
+void frv_dma_pause_all(void)
+{
+	struct frv_dma_channel *channel;
+	unsigned long ioaddr;
+	unsigned long cstr, cctr;
+	int dma;
+
+	write_lock(&frv_dma_channels_lock);
+
+	for (dma = FRV_DMA_NCHANS - 1; dma >= 0; dma--) {
+		channel = &frv_dma_channels[dma];
+
+		if (!(channel->flags & FRV_DMA_FLAGS_INUSE))
+			continue;
+
+		ioaddr = channel->ioaddr;
+		cctr = __get_DMAC(ioaddr, CCTR);
+		if (cctr & DMAC_CCTRx_ACT) {
+			cctr &= ~DMAC_CCTRx_ACT;
+			__set_DMAC(ioaddr, CCTR, cctr);
+
+			do {
+				cstr = __get_DMAC(ioaddr, CSTR);
+			} while (cstr & DMAC_CSTRx_BUSY);
+
+			if (cstr & DMAC_CSTRx_FED)
+				channel->flags |= FRV_DMA_FLAGS_PAUSED;
+			frv_clear_dma_inprogress(dma);
+		}
+	}
+
+} /* end frv_dma_pause_all() */
+
+EXPORT_SYMBOL(frv_dma_pause_all);
+
+/*****************************************************************************/
+/*
+ * resume paused DMA controllers
+ * - called by clock mangling routines
+ * - caller must be holding interrupts disabled
+ */
+void frv_dma_resume_all(void)
+{
+	struct frv_dma_channel *channel;
+	unsigned long ioaddr;
+	unsigned long cstr, cctr;
+	int dma;
+
+	for (dma = FRV_DMA_NCHANS - 1; dma >= 0; dma--) {
+		channel = &frv_dma_channels[dma];
+
+		if (!(channel->flags & FRV_DMA_FLAGS_PAUSED))
+			continue;
+
+		ioaddr = channel->ioaddr;
+		cstr = __get_DMAC(ioaddr, CSTR);
+		cstr &= ~(DMAC_CSTRx_FED | DMAC_CSTRx_INT);
+		__set_DMAC(ioaddr, CSTR, cstr);
+
+		cctr = __get_DMAC(ioaddr, CCTR);
+		cctr |= DMAC_CCTRx_ACT;
+		__set_DMAC(ioaddr, CCTR, cctr);
+
+		channel->flags &= ~FRV_DMA_FLAGS_PAUSED;
+		frv_set_dma_inprogress(dma);
+	}
+
+	write_unlock(&frv_dma_channels_lock);
+
+} /* end frv_dma_resume_all() */
+
+EXPORT_SYMBOL(frv_dma_resume_all);
+
+/*****************************************************************************/
+/*
+ * dma status clear
+ */
+void frv_dma_status_clear(int dma)
+{
+	unsigned long ioaddr = frv_dma_channels[dma].ioaddr;
+	uint32_t cctr;
+	___set_DMAC(ioaddr, CSTR, 0);
+
+	cctr = __get_DMAC(ioaddr, CCTR);
+} /* end frv_dma_status_clear() */
+
+EXPORT_SYMBOL(frv_dma_status_clear);
diff --git a/arch/frv/kernel/entry-table.S b/arch/frv/kernel/entry-table.S
new file mode 100644
index 0000000..9b9243e
--- /dev/null
+++ b/arch/frv/kernel/entry-table.S
@@ -0,0 +1,295 @@
+/* entry-table.S: main trap vector tables and exception jump table
+ *
+ * Copyright (C) 2003 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 License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ */
+
+#include <linux/sys.h>
+#include <linux/config.h>
+#include <linux/linkage.h>
+#include <asm/spr-regs.h>
+
+###############################################################################
+#
+# Declare the main trap and vector tables
+#
+# There are six tables:
+#
+# (1) The trap table for debug mode
+# (2) The trap table for kernel mode
+# (3) The trap table for user mode
+#
+#     The CPU jumps to an appropriate slot in the appropriate table to perform
+#     exception processing. We have three different tables for the three
+#     different CPU modes because there is no hardware differentiation between
+#     stack pointers for these three modes, and so we have to invent one when
+#     crossing mode boundaries.
+#
+# (4) The exception handler vector table
+#
+#     The user and kernel trap tables use the same prologue for normal
+#     exception processing. The prologue then jumps to the handler in this
+#     table, as indexed by the exception ID from the TBR.
+#
+# (5) The fixup table for kernel-trap single-step
+# (6) The fixup table for user-trap single-step
+#
+#     Due to the way single-stepping works on this CPU (single-step is not
+#     disabled when crossing exception boundaries, only when in debug mode),
+#     we have to catch the single-step event in break.S and jump to the fixup
+#     routine pointed to by this table.
+#
+# The linker script places the user mode and kernel mode trap tables on to
+# the same 8Kb page, so that break.S can be more efficient when performing
+# single-step bypass management
+#
+###############################################################################
+
+	# trap table for entry from debug mode
+	.section	.trap.break,"ax"
+	.balign		256*16
+	.globl		__entry_breaktrap_table
+__entry_breaktrap_table:
+
+	# trap table for entry from user mode
+	.section	.trap.user,"ax"
+	.balign		256*16
+	.globl		__entry_usertrap_table
+__entry_usertrap_table:
+
+	# trap table for entry from kernel mode
+	.section	.trap.kernel,"ax"
+	.balign		256*16
+	.globl		__entry_kerneltrap_table
+__entry_kerneltrap_table:
+
+	# exception handler jump table
+	.section	.trap.vector,"ax"
+	.balign		256*4
+	.globl		__entry_vector_table
+__entry_vector_table:
+
+	# trap fixup table for single-stepping in user mode
+	.section	.trap.fixup.user,"a"
+	.balign		256*4
+	.globl		__break_usertrap_fixup_table
+__break_usertrap_fixup_table:
+
+	# trap fixup table for single-stepping in user mode
+	.section	.trap.fixup.kernel,"a"
+	.balign		256*4
+	.globl		__break_kerneltrap_fixup_table
+__break_kerneltrap_fixup_table:
+
+	# handler declaration for a sofware or program interrupt
+.macro VECTOR_SOFTPROG tbr_tt, vec
+	.section .trap.user
+	.org		\tbr_tt
+	bra		__entry_uspace_softprog_interrupt
+	.section .trap.fixup.user
+	.org		\tbr_tt >> 2
+	.long		__break_step_uspace_softprog_interrupt
+	.section .trap.kernel
+	.org		\tbr_tt
+	bra		__entry_kernel_softprog_interrupt
+	.section .trap.fixup.kernel
+	.org		\tbr_tt >> 2
+	.long		__break_step_kernel_softprog_interrupt
+	.section .trap.vector
+	.org		\tbr_tt >> 2
+	.long		\vec
+.endm
+
+	# handler declaration for a maskable external interrupt
+.macro VECTOR_IRQ tbr_tt, vec
+	.section .trap.user
+	.org		\tbr_tt
+	bra		__entry_uspace_external_interrupt
+	.section .trap.fixup.user
+	.org		\tbr_tt >> 2
+	.long		__break_step_uspace_external_interrupt
+	.section .trap.kernel
+	.org		\tbr_tt
+	bra		__entry_kernel_external_interrupt
+	.section .trap.fixup.kernel
+	.org		\tbr_tt >> 2
+	.long		__break_step_kernel_external_interrupt
+	.section .trap.vector
+	.org		\tbr_tt >> 2
+	.long		\vec
+.endm
+
+	# handler declaration for an NMI external interrupt
+.macro VECTOR_NMI tbr_tt, vec
+	.section .trap.user
+	.org		\tbr_tt
+	break
+	break
+	break
+	break
+	.section .trap.kernel
+	.org		\tbr_tt
+	break
+	break
+	break
+	break
+	.section .trap.vector
+	.org		\tbr_tt >> 2
+	.long		\vec
+.endm
+
+	# handler declaration for an MMU only sofware or program interrupt
+.macro VECTOR_SP_MMU tbr_tt, vec
+#ifdef CONFIG_MMU
+ 	VECTOR_SOFTPROG	\tbr_tt, \vec
+#else
+	VECTOR_NMI	\tbr_tt, 0
+#endif
+.endm
+
+
+###############################################################################
+#
+# specification of the vectors
+# - note: each macro inserts code into multiple sections
+#
+###############################################################################
+	VECTOR_SP_MMU	TBR_TT_INSTR_MMU_MISS,	__entry_insn_mmu_miss
+	VECTOR_SOFTPROG	TBR_TT_INSTR_ACC_ERROR,	__entry_insn_access_error
+	VECTOR_SOFTPROG	TBR_TT_INSTR_ACC_EXCEP,	__entry_insn_access_exception
+	VECTOR_SOFTPROG	TBR_TT_PRIV_INSTR,	__entry_privileged_instruction
+	VECTOR_SOFTPROG	TBR_TT_ILLEGAL_INSTR,	__entry_illegal_instruction
+	VECTOR_SOFTPROG	TBR_TT_FP_EXCEPTION,	__entry_media_exception
+	VECTOR_SOFTPROG	TBR_TT_MP_EXCEPTION,	__entry_media_exception
+	VECTOR_SOFTPROG	TBR_TT_DATA_ACC_ERROR,	__entry_data_access_error
+	VECTOR_SP_MMU	TBR_TT_DATA_MMU_MISS,	__entry_data_mmu_miss
+	VECTOR_SOFTPROG	TBR_TT_DATA_ACC_EXCEP,	__entry_data_access_exception
+	VECTOR_SOFTPROG	TBR_TT_DATA_STR_ERROR,	__entry_data_store_error
+	VECTOR_SOFTPROG	TBR_TT_DIVISION_EXCEP,	__entry_division_exception
+
+#ifdef CONFIG_MMU
+	.section .trap.user
+	.org		TBR_TT_INSTR_TLB_MISS
+	.globl		__trap_user_insn_tlb_miss
+__trap_user_insn_tlb_miss:
+	movsg		ear0,gr28			/* faulting address */
+	movsg		scr0,gr31			/* get mapped PTD coverage start address */
+	xor.p		gr28,gr31,gr31			/* compare addresses */
+	bra		__entry_user_insn_tlb_miss
+
+	.org		TBR_TT_DATA_TLB_MISS
+	.globl		__trap_user_data_tlb_miss
+__trap_user_data_tlb_miss:
+	movsg		ear0,gr28			/* faulting address */
+	movsg		scr1,gr31			/* get mapped PTD coverage start address */
+	xor.p		gr28,gr31,gr31			/* compare addresses */
+	bra		__entry_user_data_tlb_miss
+
+	.section .trap.kernel
+	.org		TBR_TT_INSTR_TLB_MISS
+	.globl		__trap_kernel_insn_tlb_miss
+__trap_kernel_insn_tlb_miss:
+	movsg		ear0,gr29			/* faulting address */
+	movsg		scr0,gr31			/* get mapped PTD coverage start address */
+	xor.p		gr29,gr31,gr31			/* compare addresses */
+	bra		__entry_kernel_insn_tlb_miss
+
+	.org		TBR_TT_DATA_TLB_MISS
+	.globl		__trap_kernel_data_tlb_miss
+__trap_kernel_data_tlb_miss:
+	movsg		ear0,gr29			/* faulting address */
+	movsg		scr1,gr31			/* get mapped PTD coverage start address */
+	xor.p		gr29,gr31,gr31			/* compare addresses */
+	bra		__entry_kernel_data_tlb_miss
+
+	.section .trap.fixup.user
+	.org		TBR_TT_INSTR_TLB_MISS >> 2
+	.globl		__trap_fixup_user_insn_tlb_miss
+__trap_fixup_user_insn_tlb_miss:
+	.long		__break_user_insn_tlb_miss
+	.org		TBR_TT_DATA_TLB_MISS >> 2
+	.globl		__trap_fixup_user_data_tlb_miss
+__trap_fixup_user_data_tlb_miss:
+	.long		__break_user_data_tlb_miss
+
+	.section .trap.fixup.kernel
+	.org		TBR_TT_INSTR_TLB_MISS >> 2
+	.globl		__trap_fixup_kernel_insn_tlb_miss
+__trap_fixup_kernel_insn_tlb_miss:
+	.long		__break_kernel_insn_tlb_miss
+	.org		TBR_TT_DATA_TLB_MISS >> 2
+	.globl		__trap_fixup_kernel_data_tlb_miss
+__trap_fixup_kernel_data_tlb_miss:
+	.long		__break_kernel_data_tlb_miss
+
+	.section .trap.vector
+	.org		TBR_TT_INSTR_TLB_MISS >> 2
+	.long		__entry_insn_mmu_fault
+	.org		TBR_TT_DATA_TLB_MISS >> 2
+	.long		__entry_data_mmu_fault
+#endif
+
+	VECTOR_SP_MMU	TBR_TT_DATA_DAT_EXCEP,	__entry_data_dat_fault
+	VECTOR_NMI	TBR_TT_DECREMENT_TIMER,	__entry_do_NMI
+	VECTOR_SOFTPROG	TBR_TT_COMPOUND_EXCEP,	__entry_compound_exception
+	VECTOR_IRQ	TBR_TT_INTERRUPT_1,	__entry_do_IRQ
+	VECTOR_IRQ	TBR_TT_INTERRUPT_2,	__entry_do_IRQ
+	VECTOR_IRQ	TBR_TT_INTERRUPT_3,	__entry_do_IRQ
+	VECTOR_IRQ	TBR_TT_INTERRUPT_4,	__entry_do_IRQ
+	VECTOR_IRQ	TBR_TT_INTERRUPT_5,	__entry_do_IRQ
+	VECTOR_IRQ	TBR_TT_INTERRUPT_6,	__entry_do_IRQ
+	VECTOR_IRQ	TBR_TT_INTERRUPT_7,	__entry_do_IRQ
+	VECTOR_IRQ	TBR_TT_INTERRUPT_8,	__entry_do_IRQ
+	VECTOR_IRQ	TBR_TT_INTERRUPT_9,	__entry_do_IRQ
+	VECTOR_IRQ	TBR_TT_INTERRUPT_10,	__entry_do_IRQ
+	VECTOR_IRQ	TBR_TT_INTERRUPT_11,	__entry_do_IRQ
+	VECTOR_IRQ	TBR_TT_INTERRUPT_12,	__entry_do_IRQ
+	VECTOR_IRQ	TBR_TT_INTERRUPT_13,	__entry_do_IRQ
+	VECTOR_IRQ	TBR_TT_INTERRUPT_14,	__entry_do_IRQ
+	VECTOR_NMI	TBR_TT_INTERRUPT_15,	__entry_do_NMI
+
+	# miscellaneous user mode entry points
+	.section	.trap.user
+	.org		TBR_TT_TRAP0
+	.rept		127
+	bra		__entry_uspace_softprog_interrupt
+	bra		__break_step_uspace_softprog_interrupt
+	.long		0,0
+	.endr
+	.org		TBR_TT_BREAK
+	bra		__entry_break
+	.long		0,0,0
+
+	# miscellaneous kernel mode entry points
+	.section	.trap.kernel
+	.org		TBR_TT_TRAP0
+	.rept		127
+	bra		__entry_kernel_softprog_interrupt
+	bra		__break_step_kernel_softprog_interrupt
+	.long		0,0
+	.endr
+	.org		TBR_TT_BREAK
+	bra		__entry_break
+	.long		0,0,0
+
+	# miscellaneous debug mode entry points
+	.section	.trap.break
+	.org		TBR_TT_BREAK
+	movsg		bpcsr,gr30
+	jmpl		@(gr30,gr0)
+
+	# miscellaneous vectors
+	.section	.trap.vector
+	.org		TBR_TT_TRAP0 >> 2
+	.long		system_call
+	.rept		126
+	.long		__entry_unsupported_trap
+	.endr
+	.org		TBR_TT_BREAK >> 2
+	.long		__entry_debug_exception
diff --git a/arch/frv/kernel/entry.S b/arch/frv/kernel/entry.S
new file mode 100644
index 0000000..ad10ea5
--- /dev/null
+++ b/arch/frv/kernel/entry.S
@@ -0,0 +1,1428 @@
+/* entry.S: FR-V entry
+ *
+ * Copyright (C) 2003 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 License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ *
+ * Entry to the kernel is "interesting":
+ *  (1) There are no stack pointers, not even for the kernel
+ *  (2) General Registers should not be clobbered
+ *  (3) There are no kernel-only data registers
+ *  (4) Since all addressing modes are wrt to a General Register, no global
+ *      variables can be reached
+ *
+ * We deal with this by declaring that we shall kill GR28 on entering the
+ * kernel from userspace
+ *
+ * However, since break interrupts can interrupt the CPU even when PSR.ET==0,
+ * they can't rely on GR28 to be anything useful, and so need to clobber a
+ * separate register (GR31). Break interrupts are managed in break.S
+ *
+ * GR29 _is_ saved, and holds the current task pointer globally
+ *
+ */
+
+#include <linux/sys.h>
+#include <linux/config.h>
+#include <linux/linkage.h>
+#include <asm/thread_info.h>
+#include <asm/setup.h>
+#include <asm/segment.h>
+#include <asm/ptrace.h>
+#include <asm/errno.h>
+#include <asm/cache.h>
+#include <asm/spr-regs.h>
+
+#define nr_syscalls ((syscall_table_size)/4)
+
+	.text
+	.balign		4
+
+.macro LEDS val
+#	sethi.p		%hi(0xe1200004),gr30
+#	setlo		%lo(0xe1200004),gr30
+#	setlos		#~\val,gr31
+#	st		gr31,@(gr30,gr0)
+#	sethi.p		%hi(0xffc00100),gr30
+#	setlo		%lo(0xffc00100),gr30
+#	sth		gr0,@(gr30,gr0)
+#	membar
+.endm
+
+.macro LEDS32
+#	not		gr31,gr31
+#	sethi.p		%hi(0xe1200004),gr30
+#	setlo		%lo(0xe1200004),gr30
+#	st.p		gr31,@(gr30,gr0)
+#	srli		gr31,#16,gr31
+#	sethi.p		%hi(0xffc00100),gr30
+#	setlo		%lo(0xffc00100),gr30
+#	sth		gr31,@(gr30,gr0)
+#	membar
+.endm
+
+###############################################################################
+#
+# entry point for External interrupts received whilst executing userspace code
+#
+###############################################################################
+	.globl		__entry_uspace_external_interrupt
+        .type		__entry_uspace_external_interrupt,@function
+__entry_uspace_external_interrupt:
+	LEDS		0x6200
+	sethi.p		%hi(__kernel_frame0_ptr),gr28
+	setlo		%lo(__kernel_frame0_ptr),gr28
+	ldi		@(gr28,#0),gr28
+
+	# handle h/w single-step through exceptions
+	sti		gr0,@(gr28,#REG__STATUS)
+
+	.globl		__entry_uspace_external_interrupt_reentry
+__entry_uspace_external_interrupt_reentry:
+	LEDS		0x6201
+
+	setlos		#REG__END,gr30
+	dcpl		gr28,gr30,#0
+
+	# finish building the exception frame
+	sti		sp,  @(gr28,#REG_SP)
+	stdi		gr2, @(gr28,#REG_GR(2))
+	stdi		gr4, @(gr28,#REG_GR(4))
+	stdi		gr6, @(gr28,#REG_GR(6))
+	stdi		gr8, @(gr28,#REG_GR(8))
+	stdi		gr10,@(gr28,#REG_GR(10))
+	stdi		gr12,@(gr28,#REG_GR(12))
+	stdi		gr14,@(gr28,#REG_GR(14))
+	stdi		gr16,@(gr28,#REG_GR(16))
+	stdi		gr18,@(gr28,#REG_GR(18))
+	stdi		gr20,@(gr28,#REG_GR(20))
+	stdi		gr22,@(gr28,#REG_GR(22))
+	stdi		gr24,@(gr28,#REG_GR(24))
+	stdi		gr26,@(gr28,#REG_GR(26))
+	sti		gr0, @(gr28,#REG_GR(28))
+	sti		gr29,@(gr28,#REG_GR(29))
+	stdi.p		gr30,@(gr28,#REG_GR(30))
+
+	# set up the kernel stack pointer
+	ori		gr28,0,sp
+
+	movsg		tbr ,gr20
+	movsg		psr ,gr22
+	movsg		pcsr,gr21
+	movsg		isr ,gr23
+	movsg		ccr ,gr24
+	movsg		cccr,gr25
+	movsg		lr  ,gr26
+	movsg		lcr ,gr27
+
+	setlos.p	#-1,gr4
+	andi		gr22,#PSR_PS,gr5		/* try to rebuild original PSR value */
+	andi.p		gr22,#~(PSR_PS|PSR_S),gr6
+	slli		gr5,#1,gr5
+	or		gr6,gr5,gr5
+	andi		gr5,#~PSR_ET,gr5
+
+	sti		gr20,@(gr28,#REG_TBR)
+	sti		gr21,@(gr28,#REG_PC)
+	sti		gr5 ,@(gr28,#REG_PSR)
+	sti		gr23,@(gr28,#REG_ISR)
+	stdi		gr24,@(gr28,#REG_CCR)
+	stdi		gr26,@(gr28,#REG_LR)
+	sti		gr4 ,@(gr28,#REG_SYSCALLNO)
+
+	movsg		iacc0h,gr4
+	movsg		iacc0l,gr5
+	stdi		gr4,@(gr28,#REG_IACC0)
+
+	movsg		gner0,gr4
+	movsg		gner1,gr5
+	stdi		gr4,@(gr28,#REG_GNER0)
+
+	# set up kernel global registers
+	sethi.p		%hi(__kernel_current_task),gr5
+	setlo		%lo(__kernel_current_task),gr5
+	sethi.p		%hi(_gp),gr16
+	setlo		%lo(_gp),gr16
+	ldi		@(gr5,#0),gr29
+	ldi.p		@(gr29,#4),gr15		; __current_thread_info = current->thread_info
+
+	# make sure we (the kernel) get div-zero and misalignment exceptions
+	setlos		#ISR_EDE|ISR_DTT_DIVBYZERO|ISR_EMAM_EXCEPTION,gr5
+	movgs		gr5,isr
+
+	# switch to the kernel trap table
+	sethi.p		%hi(__entry_kerneltrap_table),gr6
+	setlo		%lo(__entry_kerneltrap_table),gr6
+	movgs		gr6,tbr
+
+	# set the return address
+	sethi.p		%hi(__entry_return_from_user_interrupt),gr4
+	setlo		%lo(__entry_return_from_user_interrupt),gr4
+	movgs		gr4,lr
+
+	# raise the minimum interrupt priority to 15 (NMI only) and enable exceptions
+	movsg		psr,gr4
+
+	ori		gr4,#PSR_PIL_14,gr4
+	movgs		gr4,psr
+	ori		gr4,#PSR_PIL_14|PSR_ET,gr4
+	movgs		gr4,psr
+
+	LEDS		0x6202
+	bra		do_IRQ
+
+	.size		__entry_uspace_external_interrupt,.-__entry_uspace_external_interrupt
+
+###############################################################################
+#
+# entry point for External interrupts received whilst executing kernel code
+# - on arriving here, the following registers should already be set up:
+#	GR15	- current thread_info struct pointer
+#	GR16	- kernel GP-REL pointer
+#	GR29	- current task struct pointer
+#	TBR	- kernel trap vector table
+#	ISR	- kernel's preferred integer controls
+#
+###############################################################################
+	.globl		__entry_kernel_external_interrupt
+        .type		__entry_kernel_external_interrupt,@function
+__entry_kernel_external_interrupt:
+	LEDS		0x6210
+
+	sub		sp,gr15,gr31
+	LEDS32
+
+	# set up the stack pointer
+	or.p		sp,gr0,gr30
+	subi		sp,#REG__END,sp
+	sti		gr30,@(sp,#REG_SP)
+
+	# handle h/w single-step through exceptions
+	sti		gr0,@(sp,#REG__STATUS)
+
+	.globl		__entry_kernel_external_interrupt_reentry
+__entry_kernel_external_interrupt_reentry:
+	LEDS		0x6211
+
+	# set up the exception frame
+	setlos		#REG__END,gr30
+	dcpl		sp,gr30,#0
+
+	sti.p		gr28,@(sp,#REG_GR(28))
+	ori		sp,0,gr28
+
+	# finish building the exception frame
+	stdi		gr2,@(gr28,#REG_GR(2))
+	stdi		gr4,@(gr28,#REG_GR(4))
+	stdi		gr6,@(gr28,#REG_GR(6))
+	stdi		gr8,@(gr28,#REG_GR(8))
+	stdi		gr10,@(gr28,#REG_GR(10))
+	stdi		gr12,@(gr28,#REG_GR(12))
+	stdi		gr14,@(gr28,#REG_GR(14))
+	stdi		gr16,@(gr28,#REG_GR(16))
+	stdi		gr18,@(gr28,#REG_GR(18))
+	stdi		gr20,@(gr28,#REG_GR(20))
+	stdi		gr22,@(gr28,#REG_GR(22))
+	stdi		gr24,@(gr28,#REG_GR(24))
+	stdi		gr26,@(gr28,#REG_GR(26))
+	sti		gr29,@(gr28,#REG_GR(29))
+	stdi		gr30,@(gr28,#REG_GR(30))
+
+	movsg		tbr ,gr20
+	movsg		psr ,gr22
+	movsg		pcsr,gr21
+	movsg		isr ,gr23
+	movsg		ccr ,gr24
+	movsg		cccr,gr25
+	movsg		lr  ,gr26
+	movsg		lcr ,gr27
+
+	setlos.p	#-1,gr4
+	andi		gr22,#PSR_PS,gr5		/* try to rebuild original PSR value */
+	andi.p		gr22,#~(PSR_PS|PSR_S),gr6
+	slli		gr5,#1,gr5
+	or		gr6,gr5,gr5
+	andi.p		gr5,#~PSR_ET,gr5
+
+	# set CCCR.CC3 to Undefined to abort atomic-modify completion inside the kernel
+	# - for an explanation of how it works, see: Documentation/fujitsu/frv/atomic-ops.txt
+	andi		gr25,#~0xc0,gr25
+
+	sti		gr20,@(gr28,#REG_TBR)
+	sti		gr21,@(gr28,#REG_PC)
+	sti		gr5 ,@(gr28,#REG_PSR)
+	sti		gr23,@(gr28,#REG_ISR)
+	stdi		gr24,@(gr28,#REG_CCR)
+	stdi		gr26,@(gr28,#REG_LR)
+	sti		gr4 ,@(gr28,#REG_SYSCALLNO)
+
+	movsg		iacc0h,gr4
+	movsg		iacc0l,gr5
+	stdi		gr4,@(gr28,#REG_IACC0)
+
+	movsg		gner0,gr4
+	movsg		gner1,gr5
+	stdi		gr4,@(gr28,#REG_GNER0)
+
+	# set the return address
+	sethi.p		%hi(__entry_return_from_kernel_interrupt),gr4
+	setlo		%lo(__entry_return_from_kernel_interrupt),gr4
+	movgs		gr4,lr
+
+	# clear power-saving mode flags
+	movsg		hsr0,gr4
+	andi		gr4,#~HSR0_PDM,gr4
+	movgs		gr4,hsr0
+
+	# raise the minimum interrupt priority to 15 (NMI only) and enable exceptions
+	movsg		psr,gr4
+	ori		gr4,#PSR_PIL_14,gr4
+	movgs		gr4,psr
+	ori		gr4,#PSR_ET,gr4
+	movgs		gr4,psr
+
+	LEDS		0x6212
+	bra		do_IRQ
+
+	.size		__entry_kernel_external_interrupt,.-__entry_kernel_external_interrupt
+
+
+###############################################################################
+#
+# entry point for Software and Progam interrupts generated whilst executing userspace code
+#
+###############################################################################
+	.globl		__entry_uspace_softprog_interrupt
+        .type		__entry_uspace_softprog_interrupt,@function
+	.globl		__entry_uspace_handle_mmu_fault
+__entry_uspace_softprog_interrupt:
+	LEDS		0x6000
+#ifdef CONFIG_MMU
+	movsg		ear0,gr28
+__entry_uspace_handle_mmu_fault:
+	movgs		gr28,scr2
+#endif
+	sethi.p		%hi(__kernel_frame0_ptr),gr28
+	setlo		%lo(__kernel_frame0_ptr),gr28
+	ldi		@(gr28,#0),gr28
+
+	# handle h/w single-step through exceptions
+	sti		gr0,@(gr28,#REG__STATUS)
+
+	.globl		__entry_uspace_softprog_interrupt_reentry
+__entry_uspace_softprog_interrupt_reentry:
+	LEDS		0x6001
+
+	setlos		#REG__END,gr30
+	dcpl		gr28,gr30,#0
+
+	# set up the kernel stack pointer
+	sti.p		sp,@(gr28,#REG_SP)
+	ori		gr28,0,sp
+	sti		gr0,@(gr28,#REG_GR(28))
+
+	stdi		gr20,@(gr28,#REG_GR(20))
+	stdi		gr22,@(gr28,#REG_GR(22))
+
+	movsg		tbr,gr20
+	movsg		pcsr,gr21
+	movsg		psr,gr22
+
+	sethi.p		%hi(__entry_return_from_user_exception),gr23
+	setlo		%lo(__entry_return_from_user_exception),gr23
+	bra		__entry_common
+
+	.size		__entry_uspace_softprog_interrupt,.-__entry_uspace_softprog_interrupt
+
+	# single-stepping was disabled on entry to a TLB handler that then faulted
+#ifdef CONFIG_MMU
+	.globl		__entry_uspace_handle_mmu_fault_sstep
+__entry_uspace_handle_mmu_fault_sstep:
+	movgs		gr28,scr2
+	sethi.p		%hi(__kernel_frame0_ptr),gr28
+	setlo		%lo(__kernel_frame0_ptr),gr28
+	ldi		@(gr28,#0),gr28
+
+	# flag single-step re-enablement
+	sti		gr0,@(gr28,#REG__STATUS)
+	bra		__entry_uspace_softprog_interrupt_reentry
+#endif
+
+
+###############################################################################
+#
+# entry point for Software and Progam interrupts generated whilst executing kernel code
+#
+###############################################################################
+	.globl		__entry_kernel_softprog_interrupt
+        .type		__entry_kernel_softprog_interrupt,@function
+__entry_kernel_softprog_interrupt:
+	LEDS		0x6004
+
+#ifdef CONFIG_MMU
+	movsg		ear0,gr30
+	movgs		gr30,scr2
+#endif
+
+	.globl		__entry_kernel_handle_mmu_fault
+__entry_kernel_handle_mmu_fault:
+	# set up the stack pointer
+	subi		sp,#REG__END,sp
+	sti		sp,@(sp,#REG_SP)
+	sti		sp,@(sp,#REG_SP-4)
+	andi		sp,#~7,sp
+
+	# handle h/w single-step through exceptions
+	sti		gr0,@(sp,#REG__STATUS)
+
+	.globl		__entry_kernel_softprog_interrupt_reentry
+__entry_kernel_softprog_interrupt_reentry:
+	LEDS		0x6005
+
+	setlos		#REG__END,gr30
+	dcpl		sp,gr30,#0
+
+	# set up the exception frame
+	sti.p		gr28,@(sp,#REG_GR(28))
+	ori		sp,0,gr28
+
+	stdi		gr20,@(gr28,#REG_GR(20))
+	stdi		gr22,@(gr28,#REG_GR(22))
+
+	ldi		@(sp,#REG_SP),gr22		/* reconstruct the old SP */
+	addi		gr22,#REG__END,gr22
+	sti		gr22,@(sp,#REG_SP)
+
+	# set CCCR.CC3 to Undefined to abort atomic-modify completion inside the kernel
+	# - for an explanation of how it works, see: Documentation/fujitsu/frv/atomic-ops.txt
+	movsg		cccr,gr20
+	andi		gr20,#~0xc0,gr20
+	movgs		gr20,cccr
+
+	movsg		tbr,gr20
+	movsg		pcsr,gr21
+	movsg		psr,gr22
+
+	sethi.p		%hi(__entry_return_from_kernel_exception),gr23
+	setlo		%lo(__entry_return_from_kernel_exception),gr23
+	bra		__entry_common
+
+	.size		__entry_kernel_softprog_interrupt,.-__entry_kernel_softprog_interrupt
+
+	# single-stepping was disabled on entry to a TLB handler that then faulted
+#ifdef CONFIG_MMU
+	.globl		__entry_kernel_handle_mmu_fault_sstep
+__entry_kernel_handle_mmu_fault_sstep:
+	# set up the stack pointer
+	subi		sp,#REG__END,sp
+	sti		sp,@(sp,#REG_SP)
+	sti		sp,@(sp,#REG_SP-4)
+	andi		sp,#~7,sp
+
+	# flag single-step re-enablement
+	sethi		#REG__STATUS_STEP,gr30
+	sti		gr30,@(sp,#REG__STATUS)
+	bra		__entry_kernel_softprog_interrupt_reentry
+#endif
+
+
+###############################################################################
+#
+# the rest of the kernel entry point code
+# - on arriving here, the following registers should be set up:
+#	GR1	- kernel stack pointer
+#	GR7	- syscall number (trap 0 only)
+#	GR8-13	- syscall args (trap 0 only)
+#	GR20	- saved TBR
+#	GR21	- saved PC
+#	GR22	- saved PSR
+#	GR23	- return handler address
+#	GR28	- exception frame on stack
+#	SCR2	- saved EAR0 where applicable (clobbered by ICI & ICEF insns on FR451)
+#	PSR	- PSR.S 1, PSR.ET 0
+#
+###############################################################################
+	.globl		__entry_common
+        .type		__entry_common,@function
+__entry_common:
+	LEDS		0x6008
+
+	# finish building the exception frame
+	stdi		gr2,@(gr28,#REG_GR(2))
+	stdi		gr4,@(gr28,#REG_GR(4))
+	stdi		gr6,@(gr28,#REG_GR(6))
+	stdi		gr8,@(gr28,#REG_GR(8))
+	stdi		gr10,@(gr28,#REG_GR(10))
+	stdi		gr12,@(gr28,#REG_GR(12))
+	stdi		gr14,@(gr28,#REG_GR(14))
+	stdi		gr16,@(gr28,#REG_GR(16))
+	stdi		gr18,@(gr28,#REG_GR(18))
+	stdi		gr24,@(gr28,#REG_GR(24))
+	stdi		gr26,@(gr28,#REG_GR(26))
+	sti		gr29,@(gr28,#REG_GR(29))
+	stdi		gr30,@(gr28,#REG_GR(30))
+
+	movsg		lcr ,gr27
+	movsg		lr  ,gr26
+	movgs		gr23,lr
+	movsg		cccr,gr25
+	movsg		ccr ,gr24
+	movsg		isr ,gr23
+
+	setlos.p	#-1,gr4
+	andi		gr22,#PSR_PS,gr5		/* try to rebuild original PSR value */
+	andi.p		gr22,#~(PSR_PS|PSR_S),gr6
+	slli		gr5,#1,gr5
+	or		gr6,gr5,gr5
+	andi		gr5,#~PSR_ET,gr5
+
+	sti		gr20,@(gr28,#REG_TBR)
+	sti		gr21,@(gr28,#REG_PC)
+	sti		gr5 ,@(gr28,#REG_PSR)
+	sti		gr23,@(gr28,#REG_ISR)
+	stdi		gr24,@(gr28,#REG_CCR)
+	stdi		gr26,@(gr28,#REG_LR)
+	sti		gr4 ,@(gr28,#REG_SYSCALLNO)
+
+	movsg		iacc0h,gr4
+	movsg		iacc0l,gr5
+	stdi		gr4,@(gr28,#REG_IACC0)
+
+	movsg		gner0,gr4
+	movsg		gner1,gr5
+	stdi		gr4,@(gr28,#REG_GNER0)
+
+	# set up kernel global registers
+	sethi.p		%hi(__kernel_current_task),gr5
+	setlo		%lo(__kernel_current_task),gr5
+	sethi.p		%hi(_gp),gr16
+	setlo		%lo(_gp),gr16
+	ldi		@(gr5,#0),gr29
+	ldi		@(gr29,#4),gr15		; __current_thread_info = current->thread_info
+
+	# switch to the kernel trap table
+	sethi.p		%hi(__entry_kerneltrap_table),gr6
+	setlo		%lo(__entry_kerneltrap_table),gr6
+	movgs		gr6,tbr
+
+	# make sure we (the kernel) get div-zero and misalignment exceptions
+	setlos		#ISR_EDE|ISR_DTT_DIVBYZERO|ISR_EMAM_EXCEPTION,gr5
+	movgs		gr5,isr
+
+	# clear power-saving mode flags
+	movsg		hsr0,gr4
+	andi		gr4,#~HSR0_PDM,gr4
+	movgs		gr4,hsr0
+
+	# multiplex again using old TBR as a guide
+	setlos.p	#TBR_TT,gr3
+	sethi		%hi(__entry_vector_table),gr6
+	and.p		gr20,gr3,gr5
+	setlo		%lo(__entry_vector_table),gr6
+	srli		gr5,#2,gr5
+	ld		@(gr5,gr6),gr5
+
+	LEDS		0x6009
+	jmpl		@(gr5,gr0)
+
+
+	.size		__entry_common,.-__entry_common
+
+###############################################################################
+#
+# handle instruction MMU fault
+#
+###############################################################################
+#ifdef CONFIG_MMU
+	.globl		__entry_insn_mmu_fault
+__entry_insn_mmu_fault:
+	LEDS		0x6010
+	setlos		#0,gr8
+	movsg		esr0,gr9
+	movsg		scr2,gr10
+
+	# now that we've accessed the exception regs, we can enable exceptions
+	movsg		psr,gr4
+	ori		gr4,#PSR_ET,gr4
+	movgs		gr4,psr
+
+	sethi.p		%hi(do_page_fault),gr5
+	setlo		%lo(do_page_fault),gr5
+	jmpl		@(gr5,gr0)	; call do_page_fault(0,esr0,ear0)
+#endif
+
+
+###############################################################################
+#
+# handle instruction access error
+#
+###############################################################################
+	.globl		__entry_insn_access_error
+__entry_insn_access_error:
+	LEDS		0x6011
+	sethi.p		%hi(insn_access_error),gr5
+	setlo		%lo(insn_access_error),gr5
+	movsg		esfr1,gr8
+	movsg		epcr0,gr9
+	movsg		esr0,gr10
+
+	# now that we've accessed the exception regs, we can enable exceptions
+	movsg		psr,gr4
+	ori		gr4,#PSR_ET,gr4
+	movgs		gr4,psr
+	jmpl		@(gr5,gr0)	; call insn_access_error(esfr1,epcr0,esr0)
+
+###############################################################################
+#
+# handle various instructions of dubious legality
+#
+###############################################################################
+	.globl		__entry_unsupported_trap
+	.globl		__entry_illegal_instruction
+	.globl		__entry_privileged_instruction
+	.globl		__entry_debug_exception
+__entry_unsupported_trap:
+	subi		gr21,#4,gr21
+	sti		gr21,@(gr28,#REG_PC)
+__entry_illegal_instruction:
+__entry_privileged_instruction:
+__entry_debug_exception:
+	LEDS		0x6012
+	sethi.p		%hi(illegal_instruction),gr5
+	setlo		%lo(illegal_instruction),gr5
+	movsg		esfr1,gr8
+	movsg		epcr0,gr9
+	movsg		esr0,gr10
+
+	# now that we've accessed the exception regs, we can enable exceptions
+	movsg		psr,gr4
+	ori		gr4,#PSR_ET,gr4
+	movgs		gr4,psr
+	jmpl		@(gr5,gr0)	; call ill_insn(esfr1,epcr0,esr0)
+
+###############################################################################
+#
+# handle media exception
+#
+###############################################################################
+	.globl		__entry_media_exception
+__entry_media_exception:
+	LEDS		0x6013
+	sethi.p		%hi(media_exception),gr5
+	setlo		%lo(media_exception),gr5
+	movsg		msr0,gr8
+	movsg		msr1,gr9
+
+	# now that we've accessed the exception regs, we can enable exceptions
+	movsg		psr,gr4
+	ori		gr4,#PSR_ET,gr4
+	movgs		gr4,psr
+	jmpl		@(gr5,gr0)	; call media_excep(msr0,msr1)
+
+###############################################################################
+#
+# handle data MMU fault
+# handle data DAT fault (write-protect exception)
+#
+###############################################################################
+#ifdef CONFIG_MMU
+	.globl		__entry_data_mmu_fault
+__entry_data_mmu_fault:
+	.globl		__entry_data_dat_fault
+__entry_data_dat_fault:
+	LEDS		0x6014
+	setlos		#1,gr8
+	movsg		esr0,gr9
+	movsg		scr2,gr10	; saved EAR0
+
+	# now that we've accessed the exception regs, we can enable exceptions
+	movsg		psr,gr4
+	ori		gr4,#PSR_ET,gr4
+	movgs		gr4,psr
+
+	sethi.p		%hi(do_page_fault),gr5
+	setlo		%lo(do_page_fault),gr5
+	jmpl		@(gr5,gr0)	; call do_page_fault(1,esr0,ear0)
+#endif
+
+###############################################################################
+#
+# handle data and instruction access exceptions
+#
+###############################################################################
+	.globl		__entry_insn_access_exception
+	.globl		__entry_data_access_exception
+__entry_insn_access_exception:
+__entry_data_access_exception:
+	LEDS		0x6016
+	sethi.p		%hi(memory_access_exception),gr5
+	setlo		%lo(memory_access_exception),gr5
+	movsg		esr0,gr8
+	movsg		scr2,gr9	; saved EAR0
+	movsg		epcr0,gr10
+
+	# now that we've accessed the exception regs, we can enable exceptions
+	movsg		psr,gr4
+	ori		gr4,#PSR_ET,gr4
+	movgs		gr4,psr
+	jmpl		@(gr5,gr0)	; call memory_access_error(esr0,ear0,epcr0)
+
+###############################################################################
+#
+# handle data access error
+#
+###############################################################################
+	.globl		__entry_data_access_error
+__entry_data_access_error:
+	LEDS		0x6016
+	sethi.p		%hi(data_access_error),gr5
+	setlo		%lo(data_access_error),gr5
+	movsg		esfr1,gr8
+	movsg		esr15,gr9
+	movsg		ear15,gr10
+
+	# now that we've accessed the exception regs, we can enable exceptions
+	movsg		psr,gr4
+	ori		gr4,#PSR_ET,gr4
+	movgs		gr4,psr
+	jmpl		@(gr5,gr0)	; call data_access_error(esfr1,esr15,ear15)
+
+###############################################################################
+#
+# handle data store error
+#
+###############################################################################
+	.globl		__entry_data_store_error
+__entry_data_store_error:
+	LEDS		0x6017
+	sethi.p		%hi(data_store_error),gr5
+	setlo		%lo(data_store_error),gr5
+	movsg		esfr1,gr8
+	movsg		esr14,gr9
+
+	# now that we've accessed the exception regs, we can enable exceptions
+	movsg		psr,gr4
+	ori		gr4,#PSR_ET,gr4
+	movgs		gr4,psr
+	jmpl		@(gr5,gr0)	; call data_store_error(esfr1,esr14)
+
+###############################################################################
+#
+# handle division exception
+#
+###############################################################################
+	.globl		__entry_division_exception
+__entry_division_exception:
+	LEDS		0x6018
+	sethi.p		%hi(division_exception),gr5
+	setlo		%lo(division_exception),gr5
+	movsg		esfr1,gr8
+	movsg		esr0,gr9
+	movsg		isr,gr10
+
+	# now that we've accessed the exception regs, we can enable exceptions
+	movsg		psr,gr4
+	ori		gr4,#PSR_ET,gr4
+	movgs		gr4,psr
+	jmpl		@(gr5,gr0)	; call div_excep(esfr1,esr0,isr)
+
+###############################################################################
+#
+# handle compound exception
+#
+###############################################################################
+	.globl		__entry_compound_exception
+__entry_compound_exception:
+	LEDS		0x6019
+	sethi.p		%hi(compound_exception),gr5
+	setlo		%lo(compound_exception),gr5
+	movsg		esfr1,gr8
+	movsg		esr0,gr9
+	movsg		esr14,gr10
+	movsg		esr15,gr11
+	movsg		msr0,gr12
+	movsg		msr1,gr13
+
+	# now that we've accessed the exception regs, we can enable exceptions
+	movsg		psr,gr4
+	ori		gr4,#PSR_ET,gr4
+	movgs		gr4,psr
+	jmpl		@(gr5,gr0)	; call comp_excep(esfr1,esr0,esr14,esr15,msr0,msr1)
+
+###############################################################################
+#
+# handle interrupts and NMIs
+#
+###############################################################################
+	.globl		__entry_do_IRQ
+__entry_do_IRQ:
+	LEDS		0x6020
+
+	# we can enable exceptions
+	movsg		psr,gr4
+	ori		gr4,#PSR_ET,gr4
+	movgs		gr4,psr
+	bra		do_IRQ
+
+	.globl		__entry_do_NMI
+__entry_do_NMI:
+	LEDS		0x6021
+
+	# we can enable exceptions
+	movsg		psr,gr4
+	ori		gr4,#PSR_ET,gr4
+	movgs		gr4,psr
+	bra		do_NMI
+
+###############################################################################
+#
+# the return path for a newly forked child process
+# - __switch_to() saved the old current pointer in GR8 for us
+#
+###############################################################################
+	.globl		ret_from_fork
+ret_from_fork:
+	LEDS		0x6100
+	call		schedule_tail
+
+	# fork & co. return 0 to child
+	setlos.p	#0,gr8
+	bra		__syscall_exit
+
+###################################################################################################
+#
+# Return to user mode is not as complex as all this looks,
+# but we want the default path for a system call return to
+# go as quickly as possible which is why some of this is
+# less clear than it otherwise should be.
+#
+###################################################################################################
+	.balign		L1_CACHE_BYTES
+	.globl		system_call
+system_call:
+	LEDS		0x6101
+	movsg		psr,gr4			; enable exceptions
+	ori		gr4,#PSR_ET,gr4
+	movgs		gr4,psr
+
+	sti		gr7,@(gr28,#REG_SYSCALLNO)
+	sti.p		gr8,@(gr28,#REG_ORIG_GR8)
+
+	subicc		gr7,#nr_syscalls,gr0,icc0
+	bnc		icc0,#0,__syscall_badsys
+
+	ldi		@(gr15,#TI_FLAGS),gr4
+	ori		gr4,#_TIF_SYSCALL_TRACE,gr4
+	andicc		gr4,#_TIF_SYSCALL_TRACE,gr0,icc0
+	bne		icc0,#0,__syscall_trace_entry
+
+__syscall_call:
+	slli.p		gr7,#2,gr7
+	sethi		%hi(sys_call_table),gr5
+	setlo		%lo(sys_call_table),gr5
+	ld		@(gr5,gr7),gr4
+	calll		@(gr4,gr0)
+
+
+###############################################################################
+#
+# return to interrupted process
+#
+###############################################################################
+__syscall_exit:
+	LEDS		0x6300
+
+	sti		gr8,@(gr28,#REG_GR(8))	; save return value
+
+	# rebuild saved psr - execve will change it for init/main.c
+	ldi		@(gr28,#REG_PSR),gr22
+	srli		gr22,#1,gr5
+	andi.p		gr22,#~PSR_PS,gr22
+	andi		gr5,#PSR_PS,gr5
+	or		gr5,gr22,gr22
+	ori		gr22,#PSR_S,gr22
+
+	# keep current PSR in GR23
+	movsg		psr,gr23
+
+	# make sure we don't miss an interrupt setting need_resched or sigpending between
+	# sampling and the RETT
+	ori		gr23,#PSR_PIL_14,gr23
+	movgs		gr23,psr
+
+	ldi		@(gr15,#TI_FLAGS),gr4
+	sethi.p		%hi(_TIF_ALLWORK_MASK),gr5
+	setlo		%lo(_TIF_ALLWORK_MASK),gr5
+	andcc		gr4,gr5,gr0,icc0
+	bne		icc0,#0,__syscall_exit_work
+
+	# restore all registers and return
+__entry_return_direct:
+	LEDS		0x6301
+
+	andi		gr22,#~PSR_ET,gr22
+	movgs		gr22,psr
+
+	ldi		@(gr28,#REG_ISR),gr23
+	lddi		@(gr28,#REG_CCR),gr24
+	lddi		@(gr28,#REG_LR) ,gr26
+	ldi		@(gr28,#REG_PC) ,gr21
+	ldi		@(gr28,#REG_TBR),gr20
+
+	movgs		gr20,tbr
+	movgs		gr21,pcsr
+	movgs		gr23,isr
+	movgs		gr24,ccr
+	movgs		gr25,cccr
+	movgs		gr26,lr
+	movgs		gr27,lcr
+
+	lddi		@(gr28,#REG_GNER0),gr4
+	movgs		gr4,gner0
+	movgs		gr5,gner1
+
+	lddi		@(gr28,#REG_IACC0),gr4
+	movgs		gr4,iacc0h
+	movgs		gr5,iacc0l
+
+	lddi		@(gr28,#REG_GR(4)) ,gr4
+	lddi		@(gr28,#REG_GR(6)) ,gr6
+	lddi		@(gr28,#REG_GR(8)) ,gr8
+	lddi		@(gr28,#REG_GR(10)),gr10
+	lddi		@(gr28,#REG_GR(12)),gr12
+	lddi		@(gr28,#REG_GR(14)),gr14
+	lddi		@(gr28,#REG_GR(16)),gr16
+	lddi		@(gr28,#REG_GR(18)),gr18
+	lddi		@(gr28,#REG_GR(20)),gr20
+	lddi		@(gr28,#REG_GR(22)),gr22
+	lddi		@(gr28,#REG_GR(24)),gr24
+	lddi		@(gr28,#REG_GR(26)),gr26
+	ldi		@(gr28,#REG_GR(29)),gr29
+	lddi		@(gr28,#REG_GR(30)),gr30
+
+	# check to see if a debugging return is required
+	LEDS		0x67f0
+	movsg		ccr,gr2
+	ldi		@(gr28,#REG__STATUS),gr3
+	andicc		gr3,#REG__STATUS_STEP,gr0,icc0
+	bne		icc0,#0,__entry_return_singlestep
+	movgs		gr2,ccr
+
+	ldi		@(gr28,#REG_SP)    ,sp
+	lddi		@(gr28,#REG_GR(2)) ,gr2
+	ldi		@(gr28,#REG_GR(28)),gr28
+
+	LEDS		0x67fe
+//	movsg		pcsr,gr31
+//	LEDS32
+
+#if 0
+	# store the current frame in the workram on the FR451
+	movgs		gr28,scr2
+	sethi.p		%hi(0xfe800000),gr28
+	setlo		%lo(0xfe800000),gr28
+
+	stdi		gr2,@(gr28,#REG_GR(2))
+	stdi		gr4,@(gr28,#REG_GR(4))
+	stdi		gr6,@(gr28,#REG_GR(6))
+	stdi		gr8,@(gr28,#REG_GR(8))
+	stdi		gr10,@(gr28,#REG_GR(10))
+	stdi		gr12,@(gr28,#REG_GR(12))
+	stdi		gr14,@(gr28,#REG_GR(14))
+	stdi		gr16,@(gr28,#REG_GR(16))
+	stdi		gr18,@(gr28,#REG_GR(18))
+	stdi		gr24,@(gr28,#REG_GR(24))
+	stdi		gr26,@(gr28,#REG_GR(26))
+	sti		gr29,@(gr28,#REG_GR(29))
+	stdi		gr30,@(gr28,#REG_GR(30))
+
+	movsg		tbr ,gr30
+	sti		gr30,@(gr28,#REG_TBR)
+	movsg		pcsr,gr30
+	sti		gr30,@(gr28,#REG_PC)
+	movsg		psr ,gr30
+	sti		gr30,@(gr28,#REG_PSR)
+	movsg		isr ,gr30
+	sti		gr30,@(gr28,#REG_ISR)
+	movsg		ccr ,gr30
+	movsg		cccr,gr31
+	stdi		gr30,@(gr28,#REG_CCR)
+	movsg		lr  ,gr30
+	movsg		lcr ,gr31
+	stdi		gr30,@(gr28,#REG_LR)
+	sti		gr0 ,@(gr28,#REG_SYSCALLNO)
+	movsg		scr2,gr28
+#endif
+
+	rett		#0
+
+	# return via break.S
+__entry_return_singlestep:
+	movgs		gr2,ccr
+	lddi		@(gr28,#REG_GR(2)) ,gr2
+	ldi		@(gr28,#REG_SP)    ,sp
+	ldi		@(gr28,#REG_GR(28)),gr28
+	LEDS		0x67ff
+	break
+	.globl		__entry_return_singlestep_breaks_here
+__entry_return_singlestep_breaks_here:
+	nop
+
+
+###############################################################################
+#
+# return to a process interrupted in kernel space
+# - we need to consider preemption if that is enabled
+#
+###############################################################################
+	.balign		L1_CACHE_BYTES
+__entry_return_from_kernel_exception:
+	LEDS		0x6302
+	movsg		psr,gr23
+	ori		gr23,#PSR_PIL_14,gr23
+	movgs		gr23,psr
+	bra		__entry_return_direct
+
+	.balign		L1_CACHE_BYTES
+__entry_return_from_kernel_interrupt:
+	LEDS		0x6303
+	movsg		psr,gr23
+	ori		gr23,#PSR_PIL_14,gr23
+	movgs		gr23,psr
+
+#ifdef CONFIG_PREEMPT
+	ldi		@(gr15,#TI_PRE_COUNT),gr5
+	subicc		gr5,#0,gr0,icc0
+	beq		icc0,#0,__entry_return_direct
+
+__entry_preempt_need_resched:
+	ldi		@(gr15,#TI_FLAGS),gr4
+	andicc		gr4,#_TIF_NEED_RESCHED,gr0,icc0
+	beq		icc0,#1,__entry_return_direct
+
+	setlos		#PREEMPT_ACTIVE,gr5
+	sti		gr5,@(gr15,#TI_FLAGS)
+
+	andi		gr23,#~PSR_PIL,gr23
+	movgs		gr23,psr
+
+	call		schedule
+	sti		gr0,@(gr15,#TI_PRE_COUNT)
+
+	movsg		psr,gr23
+	ori		gr23,#PSR_PIL_14,gr23
+	movgs		gr23,psr
+	bra		__entry_preempt_need_resched
+#else
+	bra		__entry_return_direct
+#endif
+
+
+###############################################################################
+#
+# perform work that needs to be done immediately before resumption
+#
+###############################################################################
+	.globl		__entry_return_from_user_exception
+	.balign		L1_CACHE_BYTES
+__entry_return_from_user_exception:
+	LEDS		0x6501
+
+__entry_resume_userspace:
+	# make sure we don't miss an interrupt setting need_resched or sigpending between
+	# sampling and the RETT
+	movsg		psr,gr23
+	ori		gr23,#PSR_PIL_14,gr23
+	movgs		gr23,psr
+
+__entry_return_from_user_interrupt:
+	LEDS		0x6402
+	ldi		@(gr15,#TI_FLAGS),gr4
+	sethi.p		%hi(_TIF_WORK_MASK),gr5
+	setlo		%lo(_TIF_WORK_MASK),gr5
+	andcc		gr4,gr5,gr0,icc0
+	beq		icc0,#1,__entry_return_direct
+
+__entry_work_pending:
+	LEDS		0x6404
+	andicc		gr4,#_TIF_NEED_RESCHED,gr0,icc0
+	beq		icc0,#1,__entry_work_notifysig
+
+__entry_work_resched:
+	LEDS		0x6408
+	movsg		psr,gr23
+	andi		gr23,#~PSR_PIL,gr23
+	movgs		gr23,psr
+	call		schedule
+	movsg		psr,gr23
+	ori		gr23,#PSR_PIL_14,gr23
+	movgs		gr23,psr
+
+	LEDS		0x6401
+	ldi		@(gr15,#TI_FLAGS),gr4
+	sethi.p		%hi(_TIF_WORK_MASK),gr5
+	setlo		%lo(_TIF_WORK_MASK),gr5
+	andcc		gr4,gr5,gr0,icc0
+	beq		icc0,#1,__entry_return_direct
+	andicc		gr4,#_TIF_NEED_RESCHED,gr0,icc0
+	bne		icc0,#1,__entry_work_resched
+
+__entry_work_notifysig:
+	LEDS		0x6410
+	ori.p		gr4,#0,gr8
+	call		do_notify_resume
+	bra		__entry_return_direct
+
+	# perform syscall entry tracing
+__syscall_trace_entry:
+	LEDS		0x6320
+	setlos.p	#0,gr8
+	call		do_syscall_trace
+
+	ldi		@(gr28,#REG_SYSCALLNO),gr7
+	lddi		@(gr28,#REG_GR(8)) ,gr8
+	lddi		@(gr28,#REG_GR(10)),gr10
+	lddi.p		@(gr28,#REG_GR(12)),gr12
+
+	subicc		gr7,#nr_syscalls,gr0,icc0
+	bnc		icc0,#0,__syscall_badsys
+	bra		__syscall_call
+
+	# perform syscall exit tracing
+__syscall_exit_work:
+	LEDS		0x6340
+	andicc		gr4,#_TIF_SYSCALL_TRACE,gr0,icc0
+	beq		icc0,#1,__entry_work_pending
+
+	movsg		psr,gr23
+	andi		gr23,#~PSR_PIL,gr23	; could let do_syscall_trace() call schedule()
+	movgs		gr23,psr
+
+	setlos.p	#1,gr8
+	call		do_syscall_trace
+	bra		__entry_resume_userspace
+
+__syscall_badsys:
+	LEDS		0x6380
+	setlos		#-ENOSYS,gr8
+	sti		gr8,@(gr28,#REG_GR(8))	; save return value
+	bra		__entry_resume_userspace
+
+
+###############################################################################
+#
+# syscall vector table
+#
+###############################################################################
+#ifdef CONFIG_MMU
+#define __MMU(X) X
+#else
+#define __MMU(X) sys_ni_syscall
+#endif
+
+	.section .rodata
+ALIGN
+	.globl		sys_call_table
+sys_call_table:
+	.long sys_restart_syscall	/* 0 - old "setup()" system call, used for restarting */
+	.long sys_exit
+	.long sys_fork
+	.long sys_read
+	.long sys_write
+	.long sys_open		/* 5 */
+	.long sys_close
+	.long sys_waitpid
+	.long sys_creat
+	.long sys_link
+	.long sys_unlink		/* 10 */
+	.long sys_execve
+	.long sys_chdir
+	.long sys_time
+	.long sys_mknod
+	.long sys_chmod		/* 15 */
+	.long sys_lchown16
+	.long sys_ni_syscall			/* old break syscall holder */
+	.long sys_stat
+	.long sys_lseek
+	.long sys_getpid		/* 20 */
+	.long sys_mount
+	.long sys_oldumount
+	.long sys_setuid16
+	.long sys_getuid16
+	.long sys_ni_syscall // sys_stime		/* 25 */
+	.long sys_ptrace
+	.long sys_alarm
+	.long sys_fstat
+	.long sys_pause
+	.long sys_utime		/* 30 */
+	.long sys_ni_syscall			/* old stty syscall holder */
+	.long sys_ni_syscall			/* old gtty syscall holder */
+	.long sys_access
+	.long sys_nice
+	.long sys_ni_syscall	/* 35 */	/* old ftime syscall holder */
+	.long sys_sync
+	.long sys_kill
+	.long sys_rename
+	.long sys_mkdir
+	.long sys_rmdir		/* 40 */
+	.long sys_dup
+	.long sys_pipe
+	.long sys_times
+	.long sys_ni_syscall			/* old prof syscall holder */
+	.long sys_brk		/* 45 */
+	.long sys_setgid16
+	.long sys_getgid16
+	.long sys_ni_syscall // sys_signal
+	.long sys_geteuid16
+	.long sys_getegid16	/* 50 */
+	.long sys_acct
+	.long sys_umount				/* recycled never used phys( */
+	.long sys_ni_syscall			/* old lock syscall holder */
+	.long sys_ioctl
+	.long sys_fcntl		/* 55 */
+	.long sys_ni_syscall			/* old mpx syscall holder */
+	.long sys_setpgid
+	.long sys_ni_syscall			/* old ulimit syscall holder */
+	.long sys_ni_syscall			/* old old uname syscall */
+	.long sys_umask		/* 60 */
+	.long sys_chroot
+	.long sys_ustat
+	.long sys_dup2
+	.long sys_getppid
+	.long sys_getpgrp	/* 65 */
+	.long sys_setsid
+	.long sys_sigaction
+	.long sys_ni_syscall // sys_sgetmask
+	.long sys_ni_syscall // sys_ssetmask
+	.long sys_setreuid16	/* 70 */
+	.long sys_setregid16
+	.long sys_sigsuspend
+	.long sys_ni_syscall // sys_sigpending
+	.long sys_sethostname
+	.long sys_setrlimit	/* 75 */
+	.long sys_ni_syscall // sys_old_getrlimit
+	.long sys_getrusage
+	.long sys_gettimeofday
+	.long sys_settimeofday
+	.long sys_getgroups16	/* 80 */
+	.long sys_setgroups16
+	.long sys_ni_syscall			/* old_select slot */
+	.long sys_symlink
+	.long sys_lstat
+	.long sys_readlink		/* 85 */
+	.long sys_uselib
+	.long sys_swapon
+	.long sys_reboot
+	.long sys_ni_syscall // old_readdir
+	.long sys_ni_syscall	/* 90 */	/* old_mmap slot */
+	.long sys_munmap
+	.long sys_truncate
+	.long sys_ftruncate
+	.long sys_fchmod
+	.long sys_fchown16		/* 95 */
+	.long sys_getpriority
+	.long sys_setpriority
+	.long sys_ni_syscall			/* old profil syscall holder */
+	.long sys_statfs
+	.long sys_fstatfs		/* 100 */
+	.long sys_ni_syscall			/* ioperm for i386 */
+	.long sys_socketcall
+	.long sys_syslog
+	.long sys_setitimer
+	.long sys_getitimer	/* 105 */
+	.long sys_newstat
+	.long sys_newlstat
+	.long sys_newfstat
+	.long sys_ni_syscall	/* obsolete olduname( syscall */
+	.long sys_ni_syscall	/* iopl for i386 */ /* 110 */
+	.long sys_vhangup
+	.long sys_ni_syscall	/* obsolete idle( syscall */
+	.long sys_ni_syscall	/* vm86old for i386 */
+	.long sys_wait4
+	.long sys_swapoff		/* 115 */
+	.long sys_sysinfo
+	.long sys_ipc
+	.long sys_fsync
+	.long sys_sigreturn
+	.long sys_clone		/* 120 */
+	.long sys_setdomainname
+	.long sys_newuname
+	.long sys_ni_syscall	/* old "cacheflush" */
+	.long sys_adjtimex
+	.long __MMU(sys_mprotect) /* 125 */
+	.long sys_sigprocmask
+	.long sys_ni_syscall	/* old "create_module" */
+	.long sys_init_module
+	.long sys_delete_module
+	.long sys_ni_syscall	/* old "get_kernel_syms" */
+	.long sys_quotactl
+	.long sys_getpgid
+	.long sys_fchdir
+	.long sys_bdflush
+	.long sys_sysfs		/* 135 */
+	.long sys_personality
+	.long sys_ni_syscall	/* for afs_syscall */
+	.long sys_setfsuid16
+	.long sys_setfsgid16
+	.long sys_llseek		/* 140 */
+	.long sys_getdents
+	.long sys_select
+	.long sys_flock
+	.long __MMU(sys_msync)
+	.long sys_readv		/* 145 */
+	.long sys_writev
+	.long sys_getsid
+	.long sys_fdatasync
+	.long sys_sysctl
+	.long __MMU(sys_mlock)		/* 150 */
+	.long __MMU(sys_munlock)
+	.long __MMU(sys_mlockall)
+	.long __MMU(sys_munlockall)
+	.long sys_sched_setparam
+	.long sys_sched_getparam   /* 155 */
+	.long sys_sched_setscheduler
+	.long sys_sched_getscheduler
+	.long sys_sched_yield
+	.long sys_sched_get_priority_max
+	.long sys_sched_get_priority_min  /* 160 */
+	.long sys_sched_rr_get_interval
+	.long sys_nanosleep
+	.long __MMU(sys_mremap)
+	.long sys_setresuid16
+	.long sys_getresuid16	/* 165 */
+	.long sys_ni_syscall	/* for vm86 */
+	.long sys_ni_syscall	/* Old sys_query_module */
+	.long sys_poll
+	.long sys_nfsservctl
+	.long sys_setresgid16	/* 170 */
+	.long sys_getresgid16
+	.long sys_prctl
+	.long sys_rt_sigreturn
+	.long sys_rt_sigaction
+	.long sys_rt_sigprocmask	/* 175 */
+	.long sys_rt_sigpending
+	.long sys_rt_sigtimedwait
+	.long sys_rt_sigqueueinfo
+	.long sys_rt_sigsuspend
+	.long sys_pread64		/* 180 */
+	.long sys_pwrite64
+	.long sys_chown16
+	.long sys_getcwd
+	.long sys_capget
+	.long sys_capset           /* 185 */
+	.long sys_sigaltstack
+	.long sys_sendfile
+	.long sys_ni_syscall		/* streams1 */
+	.long sys_ni_syscall		/* streams2 */
+	.long sys_vfork            /* 190 */
+	.long sys_getrlimit
+	.long sys_mmap2
+	.long sys_truncate64
+	.long sys_ftruncate64
+	.long sys_stat64		/* 195 */
+	.long sys_lstat64
+	.long sys_fstat64
+	.long sys_lchown
+	.long sys_getuid
+	.long sys_getgid		/* 200 */
+	.long sys_geteuid
+	.long sys_getegid
+	.long sys_setreuid
+	.long sys_setregid
+	.long sys_getgroups	/* 205 */
+	.long sys_setgroups
+	.long sys_fchown
+	.long sys_setresuid
+	.long sys_getresuid
+	.long sys_setresgid	/* 210 */
+	.long sys_getresgid
+	.long sys_chown
+	.long sys_setuid
+	.long sys_setgid
+	.long sys_setfsuid		/* 215 */
+	.long sys_setfsgid
+	.long sys_pivot_root
+	.long __MMU(sys_mincore)
+	.long __MMU(sys_madvise)
+	.long sys_getdents64	/* 220 */
+	.long sys_fcntl64
+	.long sys_ni_syscall	/* reserved for TUX */
+	.long sys_ni_syscall	/* Reserved for Security */
+	.long sys_gettid
+	.long sys_readahead	/* 225 */
+	.long sys_setxattr
+	.long sys_lsetxattr
+	.long sys_fsetxattr
+	.long sys_getxattr
+	.long sys_lgetxattr	/* 230 */
+	.long sys_fgetxattr
+	.long sys_listxattr
+	.long sys_llistxattr
+	.long sys_flistxattr
+	.long sys_removexattr	/* 235 */
+	.long sys_lremovexattr
+	.long sys_fremovexattr
+ 	.long sys_tkill
+	.long sys_sendfile64
+	.long sys_futex		/* 240 */
+	.long sys_sched_setaffinity
+	.long sys_sched_getaffinity
+	.long sys_ni_syscall	//sys_set_thread_area
+	.long sys_ni_syscall	//sys_get_thread_area
+	.long sys_io_setup	/* 245 */
+	.long sys_io_destroy
+	.long sys_io_getevents
+	.long sys_io_submit
+	.long sys_io_cancel
+	.long sys_fadvise64	/* 250 */
+	.long sys_ni_syscall
+	.long sys_exit_group
+	.long sys_lookup_dcookie
+	.long sys_epoll_create
+	.long sys_epoll_ctl	/* 255 */
+	.long sys_epoll_wait
+ 	.long __MMU(sys_remap_file_pages)
+ 	.long sys_set_tid_address
+ 	.long sys_timer_create
+ 	.long sys_timer_settime		/* 260 */
+ 	.long sys_timer_gettime
+ 	.long sys_timer_getoverrun
+ 	.long sys_timer_delete
+ 	.long sys_clock_settime
+ 	.long sys_clock_gettime		/* 265 */
+ 	.long sys_clock_getres
+ 	.long sys_clock_nanosleep
+	.long sys_statfs64
+	.long sys_fstatfs64
+	.long sys_tgkill	/* 270 */
+	.long sys_utimes
+ 	.long sys_fadvise64_64
+	.long sys_ni_syscall	/* sys_vserver */
+	.long sys_mbind
+	.long sys_get_mempolicy
+	.long sys_set_mempolicy
+	.long sys_mq_open
+	.long sys_mq_unlink
+	.long sys_mq_timedsend
+	.long sys_mq_timedreceive	/* 280 */
+	.long sys_mq_notify
+	.long sys_mq_getsetattr
+	.long sys_ni_syscall		/* reserved for kexec */
+	.long sys_waitid
+	.long sys_ni_syscall		/* 285 */ /* available */
+	.long sys_add_key
+	.long sys_request_key
+	.long sys_keyctl
+	.long sys_ni_syscall // sys_vperfctr_open
+	.long sys_ni_syscall // sys_vperfctr_control	/* 290 */
+	.long sys_ni_syscall // sys_vperfctr_unlink
+	.long sys_ni_syscall // sys_vperfctr_iresume
+	.long sys_ni_syscall // sys_vperfctr_read
+
+
+syscall_table_size = (. - sys_call_table)
diff --git a/arch/frv/kernel/frv_ksyms.c b/arch/frv/kernel/frv_ksyms.c
new file mode 100644
index 0000000..62cfbd9
--- /dev/null
+++ b/arch/frv/kernel/frv_ksyms.c
@@ -0,0 +1,124 @@
+#include <linux/module.h>
+#include <linux/linkage.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/user.h>
+#include <linux/elfcore.h>
+#include <linux/in6.h>
+#include <linux/interrupt.h>
+#include <linux/config.h>
+
+#include <asm/setup.h>
+#include <asm/pgalloc.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+#include <asm/semaphore.h>
+#include <asm/checksum.h>
+#include <asm/hardirq.h>
+#include <asm/current.h>
+
+extern void dump_thread(struct pt_regs *, struct user *);
+extern long __memcpy_user(void *dst, const void *src, size_t count);
+
+/* platform dependent support */
+
+EXPORT_SYMBOL(__ioremap);
+EXPORT_SYMBOL(iounmap);
+
+EXPORT_SYMBOL(dump_thread);
+EXPORT_SYMBOL(strnlen);
+EXPORT_SYMBOL(strrchr);
+EXPORT_SYMBOL(strstr);
+EXPORT_SYMBOL(strchr);
+EXPORT_SYMBOL(strcat);
+EXPORT_SYMBOL(strlen);
+EXPORT_SYMBOL(strcmp);
+EXPORT_SYMBOL(strncmp);
+EXPORT_SYMBOL(strncpy);
+
+EXPORT_SYMBOL(ip_fast_csum);
+
+#if 0
+EXPORT_SYMBOL(local_irq_count);
+EXPORT_SYMBOL(local_bh_count);
+#endif
+EXPORT_SYMBOL(kernel_thread);
+
+EXPORT_SYMBOL(enable_irq);
+EXPORT_SYMBOL(disable_irq);
+EXPORT_SYMBOL(__res_bus_clock_speed_HZ);
+EXPORT_SYMBOL(__page_offset);
+EXPORT_SYMBOL(__memcpy_user);
+EXPORT_SYMBOL(flush_dcache_page);
+
+#ifndef CONFIG_MMU
+EXPORT_SYMBOL(memory_start);
+EXPORT_SYMBOL(memory_end);
+#endif
+
+EXPORT_SYMBOL(__debug_bug_trap);
+
+/* Networking helper routines. */
+EXPORT_SYMBOL(csum_partial_copy);
+
+/* The following are special because they're not called
+   explicitly (the C compiler generates them).  Fortunately,
+   their interface isn't gonna change any time soon now, so
+   it's OK to leave it out of version control.  */
+EXPORT_SYMBOL(memcpy);
+EXPORT_SYMBOL(memset);
+EXPORT_SYMBOL(memcmp);
+EXPORT_SYMBOL(memscan);
+EXPORT_SYMBOL(memmove);
+EXPORT_SYMBOL(strtok);
+
+EXPORT_SYMBOL(get_wchan);
+
+#ifdef CONFIG_FRV_OUTOFLINE_ATOMIC_OPS
+EXPORT_SYMBOL(atomic_test_and_ANDNOT_mask);
+EXPORT_SYMBOL(atomic_test_and_OR_mask);
+EXPORT_SYMBOL(atomic_test_and_XOR_mask);
+EXPORT_SYMBOL(atomic_add_return);
+EXPORT_SYMBOL(atomic_sub_return);
+EXPORT_SYMBOL(__xchg_8);
+EXPORT_SYMBOL(__xchg_16);
+EXPORT_SYMBOL(__xchg_32);
+EXPORT_SYMBOL(__cmpxchg_8);
+EXPORT_SYMBOL(__cmpxchg_16);
+EXPORT_SYMBOL(__cmpxchg_32);
+#endif
+
+/*
+ * libgcc functions - functions that are used internally by the
+ * compiler...  (prototypes are not correct though, but that
+ * doesn't really matter since they're not versioned).
+ */
+extern void __gcc_bcmp(void);
+extern void __ashldi3(void);
+extern void __ashrdi3(void);
+extern void __cmpdi2(void);
+extern void __divdi3(void);
+extern void __lshrdi3(void);
+extern void __moddi3(void);
+extern void __muldi3(void);
+extern void __negdi2(void);
+extern void __ucmpdi2(void);
+extern void __udivdi3(void);
+extern void __udivmoddi4(void);
+extern void __umoddi3(void);
+
+        /* gcc lib functions */
+//EXPORT_SYMBOL(__gcc_bcmp);
+EXPORT_SYMBOL(__ashldi3);
+EXPORT_SYMBOL(__ashrdi3);
+//EXPORT_SYMBOL(__cmpdi2);
+//EXPORT_SYMBOL(__divdi3);
+EXPORT_SYMBOL(__lshrdi3);
+//EXPORT_SYMBOL(__moddi3);
+EXPORT_SYMBOL(__muldi3);
+EXPORT_SYMBOL(__negdi2);
+//EXPORT_SYMBOL(__ucmpdi2);
+//EXPORT_SYMBOL(__udivdi3);
+//EXPORT_SYMBOL(__udivmoddi4);
+//EXPORT_SYMBOL(__umoddi3);
diff --git a/arch/frv/kernel/gdb-io.c b/arch/frv/kernel/gdb-io.c
new file mode 100644
index 0000000..c997bcc
--- /dev/null
+++ b/arch/frv/kernel/gdb-io.c
@@ -0,0 +1,216 @@
+/* gdb-io.c: FR403 GDB stub I/O
+ *
+ * Copyright (C) 2003 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 License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/console.h>
+#include <linux/init.h>
+#include <linux/serial_reg.h>
+
+#include <asm/pgtable.h>
+#include <asm/system.h>
+#include <asm/irc-regs.h>
+#include <asm/timer-regs.h>
+#include <asm/gdb-stub.h>
+#include "gdb-io.h"
+
+#ifdef CONFIG_GDBSTUB_UART0
+#define __UART(X) (*(volatile uint8_t *)(UART0_BASE + (UART_##X)))
+#define __UART_IRR_NMI 0xff0f0000
+#else /* CONFIG_GDBSTUB_UART1 */
+#define __UART(X) (*(volatile uint8_t *)(UART1_BASE + (UART_##X)))
+#define __UART_IRR_NMI 0xfff00000
+#endif
+
+#define LSR_WAIT_FOR(STATE)			\
+do {						\
+	gdbstub_do_rx();			\
+} while (!(__UART(LSR) & UART_LSR_##STATE))
+
+#define FLOWCTL_QUERY(LINE)	({ __UART(MSR) & UART_MSR_##LINE; })
+#define FLOWCTL_CLEAR(LINE)	do { __UART(MCR) &= ~UART_MCR_##LINE; mb(); } while (0)
+#define FLOWCTL_SET(LINE)	do { __UART(MCR) |= UART_MCR_##LINE;  mb(); } while (0)
+
+#define FLOWCTL_WAIT_FOR(LINE)			\
+do {						\
+	gdbstub_do_rx();			\
+} while(!FLOWCTL_QUERY(LINE))
+
+/*****************************************************************************/
+/*
+ * initialise the GDB stub
+ * - called with PSR.ET==0, so can't incur external interrupts
+ */
+void gdbstub_io_init(void)
+{
+	/* set up the serial port */
+	__UART(LCR) = UART_LCR_WLEN8; /* 1N8 */
+	__UART(FCR) =
+		UART_FCR_ENABLE_FIFO |
+		UART_FCR_CLEAR_RCVR |
+		UART_FCR_CLEAR_XMIT |
+		UART_FCR_TRIGGER_1;
+
+	FLOWCTL_CLEAR(DTR);
+	FLOWCTL_SET(RTS);
+
+//	gdbstub_set_baud(115200);
+
+	/* we want to get serial receive interrupts */
+	__UART(IER) = UART_IER_RDI | UART_IER_RLSI;
+	mb();
+
+	__set_IRR(6, __UART_IRR_NMI);	/* map ERRs and UARTx to NMI */
+
+} /* end gdbstub_io_init() */
+
+/*****************************************************************************/
+/*
+ * set up the GDB stub serial port baud rate timers
+ */
+void gdbstub_set_baud(unsigned baud)
+{
+	unsigned value, high, low;
+	u8 lcr;
+
+	/* work out the divisor to give us the nearest higher baud rate */
+	value = __serial_clock_speed_HZ / 16 / baud;
+
+	/* determine the baud rate range */
+	high = __serial_clock_speed_HZ / 16 / value;
+	low = __serial_clock_speed_HZ / 16 / (value + 1);
+
+	/* pick the nearest bound */
+	if (low + (high - low) / 2 > baud)
+		value++;
+
+	lcr = __UART(LCR);
+	__UART(LCR) |= UART_LCR_DLAB;
+	mb();
+	__UART(DLL) = value & 0xff;
+	__UART(DLM) = (value >> 8) & 0xff;
+	mb();
+	__UART(LCR) = lcr;
+	mb();
+
+} /* end gdbstub_set_baud() */
+
+/*****************************************************************************/
+/*
+ * receive characters into the receive FIFO
+ */
+void gdbstub_do_rx(void)
+{
+	unsigned ix, nix;
+
+	ix = gdbstub_rx_inp;
+
+	while (__UART(LSR) & UART_LSR_DR) {
+		nix = (ix + 2) & 0xfff;
+		if (nix == gdbstub_rx_outp)
+			break;
+
+		gdbstub_rx_buffer[ix++] = __UART(LSR);
+		gdbstub_rx_buffer[ix++] = __UART(RX);
+		ix = nix;
+	}
+
+	gdbstub_rx_inp = ix;
+
+	__clr_RC(15);
+	__clr_IRL();
+
+} /* end gdbstub_do_rx() */
+
+/*****************************************************************************/
+/*
+ * wait for a character to come from the debugger
+ */
+int gdbstub_rx_char(unsigned char *_ch, int nonblock)
+{
+	unsigned ix;
+	u8 ch, st;
+
+	*_ch = 0xff;
+
+	if (gdbstub_rx_unget) {
+		*_ch = gdbstub_rx_unget;
+		gdbstub_rx_unget = 0;
+		return 0;
+	}
+
+ try_again:
+	gdbstub_do_rx();
+
+	/* pull chars out of the buffer */
+	ix = gdbstub_rx_outp;
+	if (ix == gdbstub_rx_inp) {
+		if (nonblock)
+			return -EAGAIN;
+		//watchdog_alert_counter = 0;
+		goto try_again;
+	}
+
+	st = gdbstub_rx_buffer[ix++];
+	ch = gdbstub_rx_buffer[ix++];
+	gdbstub_rx_outp = ix & 0x00000fff;
+
+	if (st & UART_LSR_BI) {
+		gdbstub_proto("### GDB Rx Break Detected ###\n");
+		return -EINTR;
+	}
+	else if (st & (UART_LSR_FE|UART_LSR_OE|UART_LSR_PE)) {
+		gdbstub_proto("### GDB Rx Error (st=%02x) ###\n",st);
+		return -EIO;
+	}
+	else {
+		gdbstub_proto("### GDB Rx %02x (st=%02x) ###\n",ch,st);
+		*_ch = ch & 0x7f;
+		return 0;
+	}
+
+} /* end gdbstub_rx_char() */
+
+/*****************************************************************************/
+/*
+ * send a character to the debugger
+ */
+void gdbstub_tx_char(unsigned char ch)
+{
+	FLOWCTL_SET(DTR);
+	LSR_WAIT_FOR(THRE);
+//	FLOWCTL_WAIT_FOR(CTS);
+
+	if (ch == 0x0a) {
+		__UART(TX) = 0x0d;
+		mb();
+		LSR_WAIT_FOR(THRE);
+//		FLOWCTL_WAIT_FOR(CTS);
+	}
+	__UART(TX) = ch;
+	mb();
+
+	FLOWCTL_CLEAR(DTR);
+} /* end gdbstub_tx_char() */
+
+/*****************************************************************************/
+/*
+ * send a character to the debugger
+ */
+void gdbstub_tx_flush(void)
+{
+	LSR_WAIT_FOR(TEMT);
+	LSR_WAIT_FOR(THRE);
+	FLOWCTL_CLEAR(DTR);
+} /* end gdbstub_tx_flush() */
diff --git a/arch/frv/kernel/gdb-io.h b/arch/frv/kernel/gdb-io.h
new file mode 100644
index 0000000..138714b
--- /dev/null
+++ b/arch/frv/kernel/gdb-io.h
@@ -0,0 +1,55 @@
+/* gdb-io.h: FR403 GDB I/O port defs
+ *
+ * Copyright (C) 2003 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 License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _GDB_IO_H
+#define _GDB_IO_H
+
+#include <asm/serial-regs.h>
+
+#undef UART_RX
+#undef UART_TX
+#undef UART_DLL
+#undef UART_DLM
+#undef UART_IER
+#undef UART_IIR
+#undef UART_FCR
+#undef UART_LCR
+#undef UART_MCR
+#undef UART_LSR
+#undef UART_MSR
+#undef UART_SCR
+
+#define UART_RX		0*8	/* In:  Receive buffer (DLAB=0) */
+#define UART_TX		0*8	/* Out: Transmit buffer (DLAB=0) */
+#define UART_DLL	0*8	/* Out: Divisor Latch Low (DLAB=1) */
+#define UART_DLM	1*8	/* Out: Divisor Latch High (DLAB=1) */
+#define UART_IER	1*8	/* Out: Interrupt Enable Register */
+#define UART_IIR	2*8	/* In:  Interrupt ID Register */
+#define UART_FCR	2*8	/* Out: FIFO Control Register */
+#define UART_LCR	3*8	/* Out: Line Control Register */
+#define UART_MCR	4*8	/* Out: Modem Control Register */
+#define UART_LSR	5*8	/* In:  Line Status Register */
+#define UART_MSR	6*8	/* In:  Modem Status Register */
+#define UART_SCR	7*8	/* I/O: Scratch Register */
+
+#define UART_LCR_DLAB	0x80	/* Divisor latch access bit */
+#define UART_LCR_SBC	0x40	/* Set break control */
+#define UART_LCR_SPAR	0x20	/* Stick parity (?) */
+#define UART_LCR_EPAR	0x10	/* Even parity select */
+#define UART_LCR_PARITY	0x08	/* Parity Enable */
+#define UART_LCR_STOP	0x04	/* Stop bits: 0=1 stop bit, 1= 2 stop bits */
+#define UART_LCR_WLEN5  0x00	/* Wordlength: 5 bits */
+#define UART_LCR_WLEN6  0x01	/* Wordlength: 6 bits */
+#define UART_LCR_WLEN7  0x02	/* Wordlength: 7 bits */
+#define UART_LCR_WLEN8  0x03	/* Wordlength: 8 bits */
+
+
+#endif /* _GDB_IO_H */
diff --git a/arch/frv/kernel/gdb-stub.c b/arch/frv/kernel/gdb-stub.c
new file mode 100644
index 0000000..8f860d9
--- /dev/null
+++ b/arch/frv/kernel/gdb-stub.c
@@ -0,0 +1,2084 @@
+/* gdb-stub.c: FRV GDB stub
+ *
+ * Copyright (C) 2003,4 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ * - Derived from Linux/MIPS version, Copyright (C) 1995 Andreas Busse
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+/*
+ *  To enable debugger support, two things need to happen.  One, a
+ *  call to set_debug_traps() is necessary in order to allow any breakpoints
+ *  or error conditions to be properly intercepted and reported to gdb.
+ *  Two, a breakpoint needs to be generated to begin communication.  This
+ *  is most easily accomplished by a call to breakpoint().  Breakpoint()
+ *  simulates a breakpoint by executing a BREAK instruction.
+ *
+ *
+ *    The following gdb commands are supported:
+ *
+ * command          function                               Return value
+ *
+ *    g             return the value of the CPU registers  hex data or ENN
+ *    G             set the value of the CPU registers     OK or ENN
+ *
+ *    mAA..AA,LLLL  Read LLLL bytes at address AA..AA      hex data or ENN
+ *    MAA..AA,LLLL: Write LLLL bytes at address AA.AA      OK or ENN
+ *
+ *    c             Resume at current address              SNN   ( signal NN)
+ *    cAA..AA       Continue at address AA..AA             SNN
+ *
+ *    s             Step one instruction                   SNN
+ *    sAA..AA       Step one instruction from AA..AA       SNN
+ *
+ *    k             kill
+ *
+ *    ?             What was the last sigval ?             SNN   (signal NN)
+ *
+ *    bBB..BB	    Set baud rate to BB..BB		   OK or BNN, then sets
+ *							   baud rate
+ *
+ * All commands and responses are sent with a packet which includes a
+ * checksum.  A packet consists of
+ *
+ * $<packet info>#<checksum>.
+ *
+ * where
+ * <packet info> :: <characters representing the command or response>
+ * <checksum>    :: < two hex digits computed as modulo 256 sum of <packetinfo>>
+ *
+ * When a packet is received, it is first acknowledged with either '+' or '-'.
+ * '+' indicates a successful transfer.  '-' indicates a failed transfer.
+ *
+ * Example:
+ *
+ * Host:                  Reply:
+ * $m0,10#2a               +$00010203040506070809101112131415#42
+ *
+ *
+ *  ==============
+ *  MORE EXAMPLES:
+ *  ==============
+ *
+ *  For reference -- the following are the steps that one
+ *  company took (RidgeRun Inc) to get remote gdb debugging
+ *  going. In this scenario the host machine was a PC and the
+ *  target platform was a Galileo EVB64120A MIPS evaluation
+ *  board.
+ *
+ *  Step 1:
+ *  First download gdb-5.0.tar.gz from the internet.
+ *  and then build/install the package.
+ *
+ *  Example:
+ *    $ tar zxf gdb-5.0.tar.gz
+ *    $ cd gdb-5.0
+ *    $ ./configure --target=frv-elf-gdb
+ *    $ make
+ *    $ frv-elf-gdb
+ *
+ *  Step 2:
+ *  Configure linux for remote debugging and build it.
+ *
+ *  Example:
+ *    $ cd ~/linux
+ *    $ make menuconfig <go to "Kernel Hacking" and turn on remote debugging>
+ *    $ make dep; make vmlinux
+ *
+ *  Step 3:
+ *  Download the kernel to the remote target and start
+ *  the kernel running. It will promptly halt and wait
+ *  for the host gdb session to connect. It does this
+ *  since the "Kernel Hacking" option has defined
+ *  CONFIG_REMOTE_DEBUG which in turn enables your calls
+ *  to:
+ *     set_debug_traps();
+ *     breakpoint();
+ *
+ *  Step 4:
+ *  Start the gdb session on the host.
+ *
+ *  Example:
+ *    $ frv-elf-gdb vmlinux
+ *    (gdb) set remotebaud 115200
+ *    (gdb) target remote /dev/ttyS1
+ *    ...at this point you are connected to
+ *       the remote target and can use gdb
+ *       in the normal fasion. Setting
+ *       breakpoints, single stepping,
+ *       printing variables, etc.
+ *
+ */
+
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/console.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/nmi.h>
+
+#include <asm/pgtable.h>
+#include <asm/system.h>
+#include <asm/gdb-stub.h>
+
+#define LEDS(x) do { /* *(u32*)0xe1200004 = ~(x); mb(); */ } while(0)
+
+#undef GDBSTUB_DEBUG_PROTOCOL
+
+extern void debug_to_serial(const char *p, int n);
+extern void gdbstub_console_write(struct console *co, const char *p, unsigned n);
+
+extern volatile uint32_t __break_error_detect[3]; /* ESFR1, ESR15, EAR15 */
+extern struct user_context __break_user_context;
+
+struct __debug_amr {
+	unsigned long L, P;
+} __attribute__((aligned(8)));
+
+struct __debug_mmu {
+	struct {
+		unsigned long	hsr0, pcsr, esr0, ear0, epcr0;
+#ifdef CONFIG_MMU
+		unsigned long	tplr, tppr, tpxr, cxnr;
+#endif
+	} regs;
+
+	struct __debug_amr	iamr[16];
+	struct __debug_amr	damr[16];
+
+#ifdef CONFIG_MMU
+	struct __debug_amr	tlb[64*2];
+#endif
+};
+
+static struct __debug_mmu __debug_mmu;
+
+/*
+ * BUFMAX defines the maximum number of characters in inbound/outbound buffers
+ * at least NUMREGBYTES*2 are needed for register packets
+ */
+#define BUFMAX 2048
+
+#define BREAK_INSN	0x801000c0	/* use "break" as bkpt */
+
+static const char gdbstub_banner[] = "Linux/FR-V GDB Stub (c) RedHat 2003\n";
+
+volatile u8	gdbstub_rx_buffer[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE)));
+volatile u32	gdbstub_rx_inp = 0;
+volatile u32	gdbstub_rx_outp = 0;
+volatile u8	gdbstub_rx_overflow = 0;
+u8		gdbstub_rx_unget = 0;
+
+/* set with GDB whilst running to permit step through exceptions */
+extern volatile u32 __attribute__((section(".bss"))) gdbstub_trace_through_exceptions;
+
+static char	input_buffer[BUFMAX];
+static char	output_buffer[BUFMAX];
+
+static const char hexchars[] = "0123456789abcdef";
+
+static const char *regnames[] = {
+	"PSR ", "ISR ", "CCR ", "CCCR",
+	"LR  ", "LCR ", "PC  ", "_stt",
+	"sys ", "GR8*", "GNE0", "GNE1",
+	"IACH", "IACL",
+	"TBR ", "SP  ", "FP  ", "GR3 ",
+	"GR4 ", "GR5 ", "GR6 ", "GR7 ",
+	"GR8 ", "GR9 ", "GR10", "GR11",
+	"GR12", "GR13", "GR14", "GR15",
+	"GR16", "GR17", "GR18", "GR19",
+	"GR20", "GR21", "GR22", "GR23",
+	"GR24", "GR25", "GR26", "GR27",
+	"EFRM", "CURR", "GR30", "BFRM"
+};
+
+struct gdbstub_bkpt {
+	unsigned long	addr;		/* address of breakpoint */
+	unsigned	len;		/* size of breakpoint */
+	uint32_t	originsns[7];	/* original instructions */
+};
+
+static struct gdbstub_bkpt gdbstub_bkpts[256];
+
+/*
+ * local prototypes
+ */
+
+static void gdbstub_recv_packet(char *buffer);
+static int gdbstub_send_packet(char *buffer);
+static int gdbstub_compute_signal(unsigned long tbr);
+static int hex(unsigned char ch);
+static int hexToInt(char **ptr, unsigned long *intValue);
+static unsigned char *mem2hex(const void *mem, char *buf, int count, int may_fault);
+static char *hex2mem(const char *buf, void *_mem, int count);
+
+/*
+ * Convert ch from a hex digit to an int
+ */
+static int hex(unsigned char ch)
+{
+	if (ch >= 'a' && ch <= 'f')
+		return ch-'a'+10;
+	if (ch >= '0' && ch <= '9')
+		return ch-'0';
+	if (ch >= 'A' && ch <= 'F')
+		return ch-'A'+10;
+	return -1;
+}
+
+void gdbstub_printk(const char *fmt, ...)
+{
+	static char buf[1024];
+	va_list args;
+	int len;
+
+	/* Emit the output into the temporary buffer */
+	va_start(args, fmt);
+	len = vsnprintf(buf, sizeof(buf), fmt, args);
+	va_end(args);
+	debug_to_serial(buf, len);
+}
+
+static inline char *gdbstub_strcpy(char *dst, const char *src)
+{
+	int loop = 0;
+	while ((dst[loop] = src[loop]))
+	       loop++;
+	return dst;
+}
+
+static void gdbstub_purge_cache(void)
+{
+	asm volatile("	dcef	@(gr0,gr0),#1	\n"
+		     "	icei	@(gr0,gr0),#1	\n"
+		     "	membar			\n"
+		     "	bar			\n"
+		     );
+}
+
+/*****************************************************************************/
+/*
+ * scan for the sequence $<data>#<checksum>
+ */
+static void gdbstub_recv_packet(char *buffer)
+{
+	unsigned char checksum;
+	unsigned char xmitcsum;
+	unsigned char ch;
+	int count, i, ret, error;
+
+	for (;;) {
+		/* wait around for the start character, ignore all other characters */
+		do {
+			gdbstub_rx_char(&ch, 0);
+		} while (ch != '$');
+
+		checksum = 0;
+		xmitcsum = -1;
+		count = 0;
+		error = 0;
+
+		/* now, read until a # or end of buffer is found */
+		while (count < BUFMAX) {
+			ret = gdbstub_rx_char(&ch, 0);
+			if (ret < 0)
+				error = ret;
+
+			if (ch == '#')
+				break;
+			checksum += ch;
+			buffer[count] = ch;
+			count++;
+		}
+
+		if (error == -EIO) {
+			gdbstub_proto("### GDB Rx Error - Skipping packet ###\n");
+			gdbstub_proto("### GDB Tx NAK\n");
+			gdbstub_tx_char('-');
+			continue;
+		}
+
+		if (count >= BUFMAX || error)
+			continue;
+
+		buffer[count] = 0;
+
+		/* read the checksum */
+		ret = gdbstub_rx_char(&ch, 0);
+		if (ret < 0)
+			error = ret;
+		xmitcsum = hex(ch) << 4;
+
+		ret = gdbstub_rx_char(&ch, 0);
+		if (ret < 0)
+			error = ret;
+		xmitcsum |= hex(ch);
+
+		if (error) {
+			if (error == -EIO)
+				gdbstub_proto("### GDB Rx Error - Skipping packet\n");
+			gdbstub_proto("### GDB Tx NAK\n");
+			gdbstub_tx_char('-');
+			continue;
+		}
+
+		/* check the checksum */
+		if (checksum != xmitcsum) {
+			gdbstub_proto("### GDB Tx NAK\n");
+			gdbstub_tx_char('-');	/* failed checksum */
+			continue;
+		}
+
+		gdbstub_proto("### GDB Rx '$%s#%02x' ###\n", buffer, checksum);
+		gdbstub_proto("### GDB Tx ACK\n");
+		gdbstub_tx_char('+'); /* successful transfer */
+
+		/* if a sequence char is present, reply the sequence ID */
+		if (buffer[2] == ':') {
+			gdbstub_tx_char(buffer[0]);
+			gdbstub_tx_char(buffer[1]);
+
+			/* remove sequence chars from buffer */
+			count = 0;
+			while (buffer[count]) count++;
+			for (i=3; i <= count; i++)
+				buffer[i - 3] = buffer[i];
+		}
+
+		break;
+	}
+} /* end gdbstub_recv_packet() */
+
+/*****************************************************************************/
+/*
+ * send the packet in buffer.
+ * - return 0 if successfully ACK'd
+ * - return 1 if abandoned due to new incoming packet
+ */
+static int gdbstub_send_packet(char *buffer)
+{
+	unsigned char checksum;
+	int count;
+	unsigned char ch;
+
+	/* $<packet info>#<checksum> */
+	gdbstub_proto("### GDB Tx '%s' ###\n", buffer);
+
+	do {
+		gdbstub_tx_char('$');
+		checksum = 0;
+		count = 0;
+
+		while ((ch = buffer[count]) != 0) {
+			gdbstub_tx_char(ch);
+			checksum += ch;
+			count += 1;
+		}
+
+		gdbstub_tx_char('#');
+		gdbstub_tx_char(hexchars[checksum >> 4]);
+		gdbstub_tx_char(hexchars[checksum & 0xf]);
+
+	} while (gdbstub_rx_char(&ch,0),
+#ifdef GDBSTUB_DEBUG_PROTOCOL
+		 ch=='-' && (gdbstub_proto("### GDB Rx NAK\n"),0),
+		 ch!='-' && ch!='+' && (gdbstub_proto("### GDB Rx ??? %02x\n",ch),0),
+#endif
+		 ch!='+' && ch!='$');
+
+	if (ch=='+') {
+		gdbstub_proto("### GDB Rx ACK\n");
+		return 0;
+	}
+
+	gdbstub_proto("### GDB Tx Abandoned\n");
+	gdbstub_rx_unget = ch;
+	return 1;
+} /* end gdbstub_send_packet() */
+
+/*
+ * While we find nice hex chars, build an int.
+ * Return number of chars processed.
+ */
+static int hexToInt(char **ptr, unsigned long *_value)
+{
+	int count = 0, ch;
+
+	*_value = 0;
+	while (**ptr) {
+		ch = hex(**ptr);
+		if (ch < 0)
+			break;
+
+		*_value = (*_value << 4) | ((uint8_t) ch & 0xf);
+		count++;
+
+		(*ptr)++;
+	}
+
+	return count;
+}
+
+/*****************************************************************************/
+/*
+ * probe an address to see whether it maps to anything
+ */
+static inline int gdbstub_addr_probe(const void *vaddr)
+{
+#ifdef CONFIG_MMU
+	unsigned long paddr;
+
+	asm("lrad %1,%0,#1,#0,#0" : "=r"(paddr) : "r"(vaddr));
+	if (!(paddr & xAMPRx_V))
+		return 0;
+#endif
+
+	return 1;
+} /* end gdbstub_addr_probe() */
+
+#ifdef CONFIG_MMU
+static unsigned long __saved_dampr, __saved_damlr;
+
+static inline unsigned long gdbstub_virt_to_pte(unsigned long vaddr)
+{
+	pgd_t *pgd;
+	pud_t *pud;
+	pmd_t *pmd;
+	pte_t *pte;
+	unsigned long val, dampr5;
+
+	pgd = (pgd_t *) __get_DAMLR(3) + pgd_index(vaddr);
+	pud = pud_offset(pgd, vaddr);
+	pmd = pmd_offset(pud, vaddr);
+
+	if (pmd_bad(*pmd) || !pmd_present(*pmd))
+		return 0;
+
+	/* make sure dampr5 maps to the correct pmd */
+	dampr5 = __get_DAMPR(5);
+	val = pmd_val(*pmd);
+	__set_DAMPR(5, val | xAMPRx_L | xAMPRx_SS_16Kb | xAMPRx_S | xAMPRx_C | xAMPRx_V);
+
+	/* now its safe to access pmd */
+	pte = (pte_t *)__get_DAMLR(5) + __pte_index(vaddr);
+	if (pte_present(*pte))
+		val = pte_val(*pte);
+	else
+		val = 0;
+
+	/* restore original dampr5 */
+	__set_DAMPR(5, dampr5);
+
+	return val;
+}
+#endif
+
+static inline int gdbstub_addr_map(const void *vaddr)
+{
+#ifdef CONFIG_MMU
+	unsigned long pte;
+
+	__saved_dampr = __get_DAMPR(2);
+	__saved_damlr = __get_DAMLR(2);
+#endif
+	if (gdbstub_addr_probe(vaddr))
+		return 1;
+#ifdef CONFIG_MMU
+	pte = gdbstub_virt_to_pte((unsigned long) vaddr);
+	if (pte) {
+		__set_DAMPR(2, pte);
+		__set_DAMLR(2, (unsigned long) vaddr & PAGE_MASK);
+		return 1;
+	}
+#endif
+	return 0;
+}
+
+static inline void gdbstub_addr_unmap(void)
+{
+#ifdef CONFIG_MMU
+	__set_DAMPR(2, __saved_dampr);
+	__set_DAMLR(2, __saved_damlr);
+#endif
+}
+
+/*
+ * access potentially dodgy memory through a potentially dodgy pointer
+ */
+static inline int gdbstub_read_dword(const void *addr, uint32_t *_res)
+{
+	unsigned long brr;
+	uint32_t res;
+
+	if (!gdbstub_addr_map(addr))
+		return 0;
+
+	asm volatile("	movgs	gr0,brr	\n"
+		     "	ld%I2	%M2,%0	\n"
+		     "	movsg	brr,%1	\n"
+		     : "=r"(res), "=r"(brr)
+		     : "m"(*(uint32_t *) addr));
+	*_res = res;
+	gdbstub_addr_unmap();
+	return likely(!brr);
+}
+
+static inline int gdbstub_write_dword(void *addr, uint32_t val)
+{
+	unsigned long brr;
+
+	if (!gdbstub_addr_map(addr))
+		return 0;
+
+	asm volatile("	movgs	gr0,brr	\n"
+		     "	st%I2	%1,%M2	\n"
+		     "	movsg	brr,%0	\n"
+		     : "=r"(brr)
+		     : "r"(val), "m"(*(uint32_t *) addr));
+	gdbstub_addr_unmap();
+	return likely(!brr);
+}
+
+static inline int gdbstub_read_word(const void *addr, uint16_t *_res)
+{
+	unsigned long brr;
+	uint16_t res;
+
+	if (!gdbstub_addr_map(addr))
+		return 0;
+
+	asm volatile("	movgs	gr0,brr	\n"
+		     "	lduh%I2	%M2,%0	\n"
+		     "	movsg	brr,%1	\n"
+		     : "=r"(res), "=r"(brr)
+		     : "m"(*(uint16_t *) addr));
+	*_res = res;
+	gdbstub_addr_unmap();
+	return likely(!brr);
+}
+
+static inline int gdbstub_write_word(void *addr, uint16_t val)
+{
+	unsigned long brr;
+
+	if (!gdbstub_addr_map(addr))
+		return 0;
+
+	asm volatile("	movgs	gr0,brr	\n"
+		     "	sth%I2	%1,%M2	\n"
+		     "	movsg	brr,%0	\n"
+		     : "=r"(brr)
+		     : "r"(val), "m"(*(uint16_t *) addr));
+	gdbstub_addr_unmap();
+	return likely(!brr);
+}
+
+static inline int gdbstub_read_byte(const void *addr, uint8_t *_res)
+{
+	unsigned long brr;
+	uint8_t res;
+
+	if (!gdbstub_addr_map(addr))
+		return 0;
+
+	asm volatile("	movgs	gr0,brr	\n"
+		     "	ldub%I2	%M2,%0	\n"
+		     "	movsg	brr,%1	\n"
+		     : "=r"(res), "=r"(brr)
+		     : "m"(*(uint8_t *) addr));
+	*_res = res;
+	gdbstub_addr_unmap();
+	return likely(!brr);
+}
+
+static inline int gdbstub_write_byte(void *addr, uint8_t val)
+{
+	unsigned long brr;
+
+	if (!gdbstub_addr_map(addr))
+		return 0;
+
+	asm volatile("	movgs	gr0,brr	\n"
+		     "	stb%I2	%1,%M2	\n"
+		     "	movsg	brr,%0	\n"
+		     : "=r"(brr)
+		     : "r"(val), "m"(*(uint8_t *) addr));
+	gdbstub_addr_unmap();
+	return likely(!brr);
+}
+
+static void __gdbstub_console_write(struct console *co, const char *p, unsigned n)
+{
+	char outbuf[26];
+	int qty;
+
+	outbuf[0] = 'O';
+
+	while (n > 0) {
+		qty = 1;
+
+		while (n > 0 && qty < 20) {
+			mem2hex(p, outbuf + qty, 2, 0);
+			qty += 2;
+			if (*p == 0x0a) {
+				outbuf[qty++] = '0';
+				outbuf[qty++] = 'd';
+			}
+			p++;
+			n--;
+		}
+
+		outbuf[qty] = 0;
+		gdbstub_send_packet(outbuf);
+	}
+}
+
+#if 0
+void debug_to_serial(const char *p, int n)
+{
+	gdbstub_console_write(NULL,p,n);
+}
+#endif
+
+#ifdef CONFIG_GDBSTUB_CONSOLE
+
+static kdev_t gdbstub_console_dev(struct console *con)
+{
+	return MKDEV(1,3); /* /dev/null */
+}
+
+static struct console gdbstub_console = {
+	.name	= "gdb",
+	.write	= gdbstub_console_write,	/* in break.S */
+	.device	= gdbstub_console_dev,
+	.flags	= CON_PRINTBUFFER,
+	.index	= -1,
+};
+
+#endif
+
+/*****************************************************************************/
+/*
+ * Convert the memory pointed to by mem into hex, placing result in buf.
+ * - if successful, return a pointer to the last char put in buf (NUL)
+ * - in case of mem fault, return NULL
+ * may_fault is non-zero if we are reading from arbitrary memory, but is currently
+ * not used.
+ */
+static unsigned char *mem2hex(const void *_mem, char *buf, int count, int may_fault)
+{
+	const uint8_t *mem = _mem;
+	uint8_t ch[4] __attribute__((aligned(4)));
+
+	if ((uint32_t)mem&1 && count>=1) {
+		if (!gdbstub_read_byte(mem,ch))
+			return NULL;
+		*buf++ = hexchars[ch[0] >> 4];
+		*buf++ = hexchars[ch[0] & 0xf];
+		mem++;
+		count--;
+	}
+
+	if ((uint32_t)mem&3 && count>=2) {
+		if (!gdbstub_read_word(mem,(uint16_t *)ch))
+			return NULL;
+		*buf++ = hexchars[ch[0] >> 4];
+		*buf++ = hexchars[ch[0] & 0xf];
+		*buf++ = hexchars[ch[1] >> 4];
+		*buf++ = hexchars[ch[1] & 0xf];
+		mem += 2;
+		count -= 2;
+	}
+
+	while (count>=4) {
+		if (!gdbstub_read_dword(mem,(uint32_t *)ch))
+			return NULL;
+		*buf++ = hexchars[ch[0] >> 4];
+		*buf++ = hexchars[ch[0] & 0xf];
+		*buf++ = hexchars[ch[1] >> 4];
+		*buf++ = hexchars[ch[1] & 0xf];
+		*buf++ = hexchars[ch[2] >> 4];
+		*buf++ = hexchars[ch[2] & 0xf];
+		*buf++ = hexchars[ch[3] >> 4];
+		*buf++ = hexchars[ch[3] & 0xf];
+		mem += 4;
+		count -= 4;
+	}
+
+	if (count>=2) {
+		if (!gdbstub_read_word(mem,(uint16_t *)ch))
+			return NULL;
+		*buf++ = hexchars[ch[0] >> 4];
+		*buf++ = hexchars[ch[0] & 0xf];
+		*buf++ = hexchars[ch[1] >> 4];
+		*buf++ = hexchars[ch[1] & 0xf];
+		mem += 2;
+		count -= 2;
+	}
+
+	if (count>=1) {
+		if (!gdbstub_read_byte(mem,ch))
+			return NULL;
+		*buf++ = hexchars[ch[0] >> 4];
+		*buf++ = hexchars[ch[0] & 0xf];
+	}
+
+	*buf = 0;
+
+	return buf;
+} /* end mem2hex() */
+
+/*****************************************************************************/
+/*
+ * convert the hex array pointed to by buf into binary to be placed in mem
+ * return a pointer to the character AFTER the last byte of buffer consumed
+ */
+static char *hex2mem(const char *buf, void *_mem, int count)
+{
+	uint8_t *mem = _mem;
+	union {
+		uint32_t l;
+		uint16_t w;
+		uint8_t  b[4];
+	} ch;
+
+	if ((u32)mem&1 && count>=1) {
+		ch.b[0]  = hex(*buf++) << 4;
+		ch.b[0] |= hex(*buf++);
+		if (!gdbstub_write_byte(mem,ch.b[0]))
+			return NULL;
+		mem++;
+		count--;
+	}
+
+	if ((u32)mem&3 && count>=2) {
+		ch.b[0]  = hex(*buf++) << 4;
+		ch.b[0] |= hex(*buf++);
+		ch.b[1]  = hex(*buf++) << 4;
+		ch.b[1] |= hex(*buf++);
+		if (!gdbstub_write_word(mem,ch.w))
+			return NULL;
+		mem += 2;
+		count -= 2;
+	}
+
+	while (count>=4) {
+		ch.b[0]  = hex(*buf++) << 4;
+		ch.b[0] |= hex(*buf++);
+		ch.b[1]  = hex(*buf++) << 4;
+		ch.b[1] |= hex(*buf++);
+		ch.b[2]  = hex(*buf++) << 4;
+		ch.b[2] |= hex(*buf++);
+		ch.b[3]  = hex(*buf++) << 4;
+		ch.b[3] |= hex(*buf++);
+		if (!gdbstub_write_dword(mem,ch.l))
+			return NULL;
+		mem += 4;
+		count -= 4;
+	}
+
+	if (count>=2) {
+		ch.b[0]  = hex(*buf++) << 4;
+		ch.b[0] |= hex(*buf++);
+		ch.b[1]  = hex(*buf++) << 4;
+		ch.b[1] |= hex(*buf++);
+		if (!gdbstub_write_word(mem,ch.w))
+			return NULL;
+		mem += 2;
+		count -= 2;
+	}
+
+	if (count>=1) {
+		ch.b[0]  = hex(*buf++) << 4;
+		ch.b[0] |= hex(*buf++);
+		if (!gdbstub_write_byte(mem,ch.b[0]))
+			return NULL;
+	}
+
+	return (char *) buf;
+} /* end hex2mem() */
+
+/*****************************************************************************/
+/*
+ * This table contains the mapping between FRV TBR.TT exception codes,
+ * and signals, which are primarily what GDB understands.  It also
+ * indicates which hardware traps we need to commandeer when
+ * initializing the stub.
+ */
+static const struct brr_to_sig_map {
+	unsigned long	brr_mask;	/* BRR bitmask */
+	unsigned long	tbr_tt;		/* TBR.TT code (in BRR.EBTT) */
+	unsigned int	signo;		/* Signal that we map this into */
+} brr_to_sig_map[] = {
+	{ BRR_EB,	TBR_TT_INSTR_ACC_ERROR,	SIGSEGV		},
+	{ BRR_EB,	TBR_TT_ILLEGAL_INSTR,	SIGILL		},
+	{ BRR_EB,	TBR_TT_PRIV_INSTR,	SIGILL		},
+	{ BRR_EB,	TBR_TT_MP_EXCEPTION,	SIGFPE		},
+	{ BRR_EB,	TBR_TT_DATA_ACC_ERROR,	SIGSEGV		},
+	{ BRR_EB,	TBR_TT_DATA_STR_ERROR,	SIGSEGV		},
+	{ BRR_EB,	TBR_TT_DIVISION_EXCEP,	SIGFPE		},
+	{ BRR_EB,	TBR_TT_COMPOUND_EXCEP,	SIGSEGV		},
+	{ BRR_EB,	TBR_TT_INTERRUPT_13,	SIGALRM		},	/* watchdog */
+	{ BRR_EB,	TBR_TT_INTERRUPT_14,	SIGINT		},	/* GDB serial */
+	{ BRR_EB,	TBR_TT_INTERRUPT_15,	SIGQUIT		},	/* NMI */
+	{ BRR_CB,	0,			SIGUSR1		},
+	{ BRR_TB,	0,			SIGUSR2		},
+	{ BRR_DBNEx,	0,			SIGTRAP		},
+	{ BRR_DBx,	0,			SIGTRAP		},	/* h/w watchpoint */
+	{ BRR_IBx,	0,			SIGTRAP		},	/* h/w breakpoint */
+	{ BRR_CBB,	0,			SIGTRAP		},
+	{ BRR_SB,	0,			SIGTRAP		},
+	{ BRR_ST,	0,			SIGTRAP		},	/* single step */
+	{ 0,		0,			SIGHUP		}	/* default */
+};
+
+/*****************************************************************************/
+/*
+ * convert the FRV BRR register contents into a UNIX signal number
+ */
+static inline int gdbstub_compute_signal(unsigned long brr)
+{
+	const struct brr_to_sig_map *map;
+	unsigned long tbr = (brr & BRR_EBTT) >> 12;
+
+	for (map = brr_to_sig_map; map->brr_mask; map++)
+		if (map->brr_mask & brr)
+			if (!map->tbr_tt || map->tbr_tt == tbr)
+				break;
+
+	return map->signo;
+} /* end gdbstub_compute_signal() */
+
+/*****************************************************************************/
+/*
+ * set a software breakpoint or a hardware breakpoint or watchpoint
+ */
+static int gdbstub_set_breakpoint(unsigned long type, unsigned long addr, unsigned long len)
+{
+	unsigned long tmp;
+	int bkpt, loop, xloop;
+
+	union {
+		struct {
+			unsigned long mask0, mask1;
+		};
+		uint8_t bytes[8];
+	} dbmr;
+
+	//gdbstub_printk("setbkpt(%ld,%08lx,%ld)\n", type, addr, len);
+
+	switch (type) {
+		/* set software breakpoint */
+	case 0:
+		if (addr & 3 || len > 7*4)
+			return -EINVAL;
+
+		for (bkpt = 255; bkpt >= 0; bkpt--)
+			if (!gdbstub_bkpts[bkpt].addr)
+				break;
+		if (bkpt < 0)
+			return -ENOSPC;
+
+		for (loop = 0; loop < len/4; loop++)
+			if (!gdbstub_read_dword(&((uint32_t *) addr)[loop],
+						&gdbstub_bkpts[bkpt].originsns[loop]))
+				return -EFAULT;
+
+		for (loop = 0; loop < len/4; loop++)
+			if (!gdbstub_write_dword(&((uint32_t *) addr)[loop],
+						 BREAK_INSN)
+			    ) {
+				/* need to undo the changes if possible */
+				for (xloop = 0; xloop < loop; xloop++)
+					gdbstub_write_dword(&((uint32_t *) addr)[xloop],
+							    gdbstub_bkpts[bkpt].originsns[xloop]);
+				return -EFAULT;
+			}
+
+		gdbstub_bkpts[bkpt].addr = addr;
+		gdbstub_bkpts[bkpt].len = len;
+
+#if 0
+		gdbstub_printk("Set BKPT[%02x]: %08lx #%d {%04x, %04x} -> { %04x, %04x }\n",
+			       bkpt,
+			       gdbstub_bkpts[bkpt].addr,
+			       gdbstub_bkpts[bkpt].len,
+			       gdbstub_bkpts[bkpt].originsns[0],
+			       gdbstub_bkpts[bkpt].originsns[1],
+			       ((uint32_t *) addr)[0],
+			       ((uint32_t *) addr)[1]
+			       );
+#endif
+		return 0;
+
+		/* set hardware breakpoint */
+	case 1:
+		if (addr & 3 || len != 4)
+			return -EINVAL;
+
+		if (!(__debug_regs->dcr & DCR_IBE0)) {
+			//gdbstub_printk("set h/w break 0: %08lx\n", addr);
+			__debug_regs->dcr |= DCR_IBE0;
+			asm volatile("movgs %0,ibar0" : : "r"(addr));
+			return 0;
+		}
+
+		if (!(__debug_regs->dcr & DCR_IBE1)) {
+			//gdbstub_printk("set h/w break 1: %08lx\n", addr);
+			__debug_regs->dcr |= DCR_IBE1;
+			asm volatile("movgs %0,ibar1" : : "r"(addr));
+			return 0;
+		}
+
+		if (!(__debug_regs->dcr & DCR_IBE2)) {
+			//gdbstub_printk("set h/w break 2: %08lx\n", addr);
+			__debug_regs->dcr |= DCR_IBE2;
+			asm volatile("movgs %0,ibar2" : : "r"(addr));
+			return 0;
+		}
+
+		if (!(__debug_regs->dcr & DCR_IBE3)) {
+			//gdbstub_printk("set h/w break 3: %08lx\n", addr);
+			__debug_regs->dcr |= DCR_IBE3;
+			asm volatile("movgs %0,ibar3" : : "r"(addr));
+			return 0;
+		}
+
+		return -ENOSPC;
+
+		/* set data read/write/access watchpoint */
+	case 2:
+	case 3:
+	case 4:
+		if ((addr & ~7) != ((addr + len - 1) & ~7))
+			return -EINVAL;
+
+		tmp = addr & 7;
+
+		memset(dbmr.bytes, 0xff, sizeof(dbmr.bytes));
+		for (loop = 0; loop < len; loop++)
+			dbmr.bytes[tmp + loop] = 0;
+
+		addr &= ~7;
+
+		if (!(__debug_regs->dcr & (DCR_DRBE0|DCR_DWBE0))) {
+			//gdbstub_printk("set h/w watchpoint 0 type %ld: %08lx\n", type, addr);
+			tmp = type==2 ? DCR_DWBE0 : type==3 ? DCR_DRBE0 : DCR_DRBE0|DCR_DWBE0;
+			__debug_regs->dcr |= tmp;
+			asm volatile("	movgs	%0,dbar0	\n"
+				     "	movgs	%1,dbmr00	\n"
+				     "	movgs	%2,dbmr01	\n"
+				     "	movgs	gr0,dbdr00	\n"
+				     "	movgs	gr0,dbdr01	\n"
+				     : : "r"(addr), "r"(dbmr.mask0), "r"(dbmr.mask1));
+			return 0;
+		}
+
+		if (!(__debug_regs->dcr & (DCR_DRBE1|DCR_DWBE1))) {
+			//gdbstub_printk("set h/w watchpoint 1 type %ld: %08lx\n", type, addr);
+			tmp = type==2 ? DCR_DWBE1 : type==3 ? DCR_DRBE1 : DCR_DRBE1|DCR_DWBE1;
+			__debug_regs->dcr |= tmp;
+			asm volatile("	movgs	%0,dbar1	\n"
+				     "	movgs	%1,dbmr10	\n"
+				     "	movgs	%2,dbmr11	\n"
+				     "	movgs	gr0,dbdr10	\n"
+				     "	movgs	gr0,dbdr11	\n"
+				     : : "r"(addr), "r"(dbmr.mask0), "r"(dbmr.mask1));
+			return 0;
+		}
+
+		return -ENOSPC;
+
+	default:
+		return -EINVAL;
+	}
+
+} /* end gdbstub_set_breakpoint() */
+
+/*****************************************************************************/
+/*
+ * clear a breakpoint or watchpoint
+ */
+int gdbstub_clear_breakpoint(unsigned long type, unsigned long addr, unsigned long len)
+{
+	unsigned long tmp;
+	int bkpt, loop;
+
+	union {
+		struct {
+			unsigned long mask0, mask1;
+		};
+		uint8_t bytes[8];
+	} dbmr;
+
+	//gdbstub_printk("clearbkpt(%ld,%08lx,%ld)\n", type, addr, len);
+
+	switch (type) {
+		/* clear software breakpoint */
+	case 0:
+		for (bkpt = 255; bkpt >= 0; bkpt--)
+			if (gdbstub_bkpts[bkpt].addr == addr && gdbstub_bkpts[bkpt].len == len)
+				break;
+		if (bkpt < 0)
+			return -ENOENT;
+
+		gdbstub_bkpts[bkpt].addr = 0;
+
+		for (loop = 0; loop < len/4; loop++)
+			if (!gdbstub_write_dword(&((uint32_t *) addr)[loop],
+						 gdbstub_bkpts[bkpt].originsns[loop]))
+				return -EFAULT;
+		return 0;
+
+		/* clear hardware breakpoint */
+	case 1:
+		if (addr & 3 || len != 4)
+			return -EINVAL;
+
+#define __get_ibar(X) ({ unsigned long x; asm volatile("movsg ibar"#X",%0" : "=r"(x)); x; })
+
+		if (__debug_regs->dcr & DCR_IBE0 && __get_ibar(0) == addr) {
+			//gdbstub_printk("clear h/w break 0: %08lx\n", addr);
+			__debug_regs->dcr &= ~DCR_IBE0;
+			asm volatile("movgs gr0,ibar0");
+			return 0;
+		}
+
+		if (__debug_regs->dcr & DCR_IBE1 && __get_ibar(1) == addr) {
+			//gdbstub_printk("clear h/w break 1: %08lx\n", addr);
+			__debug_regs->dcr &= ~DCR_IBE1;
+			asm volatile("movgs gr0,ibar1");
+			return 0;
+		}
+
+		if (__debug_regs->dcr & DCR_IBE2 && __get_ibar(2) == addr) {
+			//gdbstub_printk("clear h/w break 2: %08lx\n", addr);
+			__debug_regs->dcr &= ~DCR_IBE2;
+			asm volatile("movgs gr0,ibar2");
+			return 0;
+		}
+
+		if (__debug_regs->dcr & DCR_IBE3 && __get_ibar(3) == addr) {
+			//gdbstub_printk("clear h/w break 3: %08lx\n", addr);
+			__debug_regs->dcr &= ~DCR_IBE3;
+			asm volatile("movgs gr0,ibar3");
+			return 0;
+		}
+
+		return -EINVAL;
+
+		/* clear data read/write/access watchpoint */
+	case 2:
+	case 3:
+	case 4:
+		if ((addr & ~7) != ((addr + len - 1) & ~7))
+			return -EINVAL;
+
+		tmp = addr & 7;
+
+		memset(dbmr.bytes, 0xff, sizeof(dbmr.bytes));
+		for (loop = 0; loop < len; loop++)
+			dbmr.bytes[tmp + loop] = 0;
+
+		addr &= ~7;
+
+#define __get_dbar(X) ({ unsigned long x; asm volatile("movsg dbar"#X",%0" : "=r"(x)); x; })
+#define __get_dbmr0(X) ({ unsigned long x; asm volatile("movsg dbmr"#X"0,%0" : "=r"(x)); x; })
+#define __get_dbmr1(X) ({ unsigned long x; asm volatile("movsg dbmr"#X"1,%0" : "=r"(x)); x; })
+
+		/* consider DBAR 0 */
+		tmp = type==2 ? DCR_DWBE0 : type==3 ? DCR_DRBE0 : DCR_DRBE0|DCR_DWBE0;
+
+		if ((__debug_regs->dcr & (DCR_DRBE0|DCR_DWBE0)) != tmp ||
+		    __get_dbar(0) != addr ||
+		    __get_dbmr0(0) != dbmr.mask0 ||
+		    __get_dbmr1(0) != dbmr.mask1)
+			goto skip_dbar0;
+
+		//gdbstub_printk("clear h/w watchpoint 0 type %ld: %08lx\n", type, addr);
+		__debug_regs->dcr &= ~(DCR_DRBE0|DCR_DWBE0);
+		asm volatile("	movgs	gr0,dbar0	\n"
+			     "	movgs	gr0,dbmr00	\n"
+			     "	movgs	gr0,dbmr01	\n"
+			     "	movgs	gr0,dbdr00	\n"
+			     "	movgs	gr0,dbdr01	\n");
+		return 0;
+
+	skip_dbar0:
+		/* consider DBAR 0 */
+		tmp = type==2 ? DCR_DWBE1 : type==3 ? DCR_DRBE1 : DCR_DRBE1|DCR_DWBE1;
+
+		if ((__debug_regs->dcr & (DCR_DRBE1|DCR_DWBE1)) != tmp ||
+		    __get_dbar(1) != addr ||
+		    __get_dbmr0(1) != dbmr.mask0 ||
+		    __get_dbmr1(1) != dbmr.mask1)
+			goto skip_dbar1;
+
+		//gdbstub_printk("clear h/w watchpoint 1 type %ld: %08lx\n", type, addr);
+		__debug_regs->dcr &= ~(DCR_DRBE1|DCR_DWBE1);
+		asm volatile("	movgs	gr0,dbar1	\n"
+			     "	movgs	gr0,dbmr10	\n"
+			     "	movgs	gr0,dbmr11	\n"
+			     "	movgs	gr0,dbdr10	\n"
+			     "	movgs	gr0,dbdr11	\n");
+		return 0;
+
+	skip_dbar1:
+		return -ENOSPC;
+
+	default:
+		return -EINVAL;
+	}
+} /* end gdbstub_clear_breakpoint() */
+
+/*****************************************************************************/
+/*
+ * check a for an internal software breakpoint, and wind the PC back if necessary
+ */
+static void gdbstub_check_breakpoint(void)
+{
+	unsigned long addr = __debug_frame->pc - 4;
+	int bkpt;
+
+	for (bkpt = 255; bkpt >= 0; bkpt--)
+		if (gdbstub_bkpts[bkpt].addr == addr)
+			break;
+	if (bkpt >= 0)
+		__debug_frame->pc = addr;
+
+	//gdbstub_printk("alter pc [%d] %08lx\n", bkpt, __debug_frame->pc);
+
+} /* end gdbstub_check_breakpoint() */
+
+/*****************************************************************************/
+/*
+ *
+ */
+static void __attribute__((unused)) gdbstub_show_regs(void)
+{
+	uint32_t *reg;
+	int loop;
+
+	gdbstub_printk("\n");
+
+	gdbstub_printk("Frame: @%p [%s]\n",
+		       __debug_frame,
+		       __debug_frame->psr & PSR_S ? "kernel" : "user");
+
+	reg = (uint32_t *) __debug_frame;
+	for (loop = 0; loop < REG__END; loop++) {
+		printk("%s %08x", regnames[loop + 0], reg[loop + 0]);
+
+		if (loop == REG__END - 1 || loop % 5 == 4)
+			printk("\n");
+		else
+			printk(" | ");
+	}
+
+	gdbstub_printk("Process %s (pid: %d)\n", current->comm, current->pid);
+} /* end gdbstub_show_regs() */
+
+/*****************************************************************************/
+/*
+ * dump debugging regs
+ */
+static void __attribute__((unused)) gdbstub_dump_debugregs(void)
+{
+	unsigned long x;
+
+	x = __debug_regs->dcr;
+	gdbstub_printk("DCR    %08lx  ", x);
+
+	x = __debug_regs->brr;
+	gdbstub_printk("BRR %08lx\n", x);
+
+	gdbstub_printk("IBAR0  %08lx  ", __get_ibar(0));
+	gdbstub_printk("IBAR1  %08lx  ", __get_ibar(1));
+	gdbstub_printk("IBAR2  %08lx  ", __get_ibar(2));
+	gdbstub_printk("IBAR3  %08lx\n", __get_ibar(3));
+
+	gdbstub_printk("DBAR0  %08lx  ", __get_dbar(0));
+	gdbstub_printk("DBMR00 %08lx  ", __get_dbmr0(0));
+	gdbstub_printk("DBMR01 %08lx\n", __get_dbmr1(0));
+
+	gdbstub_printk("DBAR1  %08lx  ", __get_dbar(1));
+	gdbstub_printk("DBMR10 %08lx  ", __get_dbmr0(1));
+	gdbstub_printk("DBMR11 %08lx\n", __get_dbmr1(1));
+
+	gdbstub_printk("\n");
+} /* end gdbstub_dump_debugregs() */
+
+/*****************************************************************************/
+/*
+ * dump the MMU state into a structure so that it can be accessed with GDB
+ */
+void gdbstub_get_mmu_state(void)
+{
+	asm volatile("movsg hsr0,%0" : "=r"(__debug_mmu.regs.hsr0));
+	asm volatile("movsg pcsr,%0" : "=r"(__debug_mmu.regs.pcsr));
+	asm volatile("movsg esr0,%0" : "=r"(__debug_mmu.regs.esr0));
+	asm volatile("movsg ear0,%0" : "=r"(__debug_mmu.regs.ear0));
+	asm volatile("movsg epcr0,%0" : "=r"(__debug_mmu.regs.epcr0));
+
+	/* read the protection / SAT registers */
+	__debug_mmu.iamr[0].L  = __get_IAMLR(0);
+	__debug_mmu.iamr[0].P  = __get_IAMPR(0);
+	__debug_mmu.iamr[1].L  = __get_IAMLR(1);
+	__debug_mmu.iamr[1].P  = __get_IAMPR(1);
+	__debug_mmu.iamr[2].L  = __get_IAMLR(2);
+	__debug_mmu.iamr[2].P  = __get_IAMPR(2);
+	__debug_mmu.iamr[3].L  = __get_IAMLR(3);
+	__debug_mmu.iamr[3].P  = __get_IAMPR(3);
+	__debug_mmu.iamr[4].L  = __get_IAMLR(4);
+	__debug_mmu.iamr[4].P  = __get_IAMPR(4);
+	__debug_mmu.iamr[5].L  = __get_IAMLR(5);
+	__debug_mmu.iamr[5].P  = __get_IAMPR(5);
+	__debug_mmu.iamr[6].L  = __get_IAMLR(6);
+	__debug_mmu.iamr[6].P  = __get_IAMPR(6);
+	__debug_mmu.iamr[7].L  = __get_IAMLR(7);
+	__debug_mmu.iamr[7].P  = __get_IAMPR(7);
+	__debug_mmu.iamr[8].L  = __get_IAMLR(8);
+	__debug_mmu.iamr[8].P  = __get_IAMPR(8);
+	__debug_mmu.iamr[9].L  = __get_IAMLR(9);
+	__debug_mmu.iamr[9].P  = __get_IAMPR(9);
+	__debug_mmu.iamr[10].L = __get_IAMLR(10);
+	__debug_mmu.iamr[10].P = __get_IAMPR(10);
+	__debug_mmu.iamr[11].L = __get_IAMLR(11);
+	__debug_mmu.iamr[11].P = __get_IAMPR(11);
+	__debug_mmu.iamr[12].L = __get_IAMLR(12);
+	__debug_mmu.iamr[12].P = __get_IAMPR(12);
+	__debug_mmu.iamr[13].L = __get_IAMLR(13);
+	__debug_mmu.iamr[13].P = __get_IAMPR(13);
+	__debug_mmu.iamr[14].L = __get_IAMLR(14);
+	__debug_mmu.iamr[14].P = __get_IAMPR(14);
+	__debug_mmu.iamr[15].L = __get_IAMLR(15);
+	__debug_mmu.iamr[15].P = __get_IAMPR(15);
+
+	__debug_mmu.damr[0].L  = __get_DAMLR(0);
+	__debug_mmu.damr[0].P  = __get_DAMPR(0);
+	__debug_mmu.damr[1].L  = __get_DAMLR(1);
+	__debug_mmu.damr[1].P  = __get_DAMPR(1);
+	__debug_mmu.damr[2].L  = __get_DAMLR(2);
+	__debug_mmu.damr[2].P  = __get_DAMPR(2);
+	__debug_mmu.damr[3].L  = __get_DAMLR(3);
+	__debug_mmu.damr[3].P  = __get_DAMPR(3);
+	__debug_mmu.damr[4].L  = __get_DAMLR(4);
+	__debug_mmu.damr[4].P  = __get_DAMPR(4);
+	__debug_mmu.damr[5].L  = __get_DAMLR(5);
+	__debug_mmu.damr[5].P  = __get_DAMPR(5);
+	__debug_mmu.damr[6].L  = __get_DAMLR(6);
+	__debug_mmu.damr[6].P  = __get_DAMPR(6);
+	__debug_mmu.damr[7].L  = __get_DAMLR(7);
+	__debug_mmu.damr[7].P  = __get_DAMPR(7);
+	__debug_mmu.damr[8].L  = __get_DAMLR(8);
+	__debug_mmu.damr[8].P  = __get_DAMPR(8);
+	__debug_mmu.damr[9].L  = __get_DAMLR(9);
+	__debug_mmu.damr[9].P  = __get_DAMPR(9);
+	__debug_mmu.damr[10].L = __get_DAMLR(10);
+	__debug_mmu.damr[10].P = __get_DAMPR(10);
+	__debug_mmu.damr[11].L = __get_DAMLR(11);
+	__debug_mmu.damr[11].P = __get_DAMPR(11);
+	__debug_mmu.damr[12].L = __get_DAMLR(12);
+	__debug_mmu.damr[12].P = __get_DAMPR(12);
+	__debug_mmu.damr[13].L = __get_DAMLR(13);
+	__debug_mmu.damr[13].P = __get_DAMPR(13);
+	__debug_mmu.damr[14].L = __get_DAMLR(14);
+	__debug_mmu.damr[14].P = __get_DAMPR(14);
+	__debug_mmu.damr[15].L = __get_DAMLR(15);
+	__debug_mmu.damr[15].P = __get_DAMPR(15);
+
+#ifdef CONFIG_MMU
+	do {
+		/* read the DAT entries from the TLB */
+		struct __debug_amr *p;
+		int loop;
+
+		asm volatile("movsg tplr,%0" : "=r"(__debug_mmu.regs.tplr));
+		asm volatile("movsg tppr,%0" : "=r"(__debug_mmu.regs.tppr));
+		asm volatile("movsg tpxr,%0" : "=r"(__debug_mmu.regs.tpxr));
+		asm volatile("movsg cxnr,%0" : "=r"(__debug_mmu.regs.cxnr));
+
+		p = __debug_mmu.tlb;
+
+		/* way 0 */
+		asm volatile("movgs %0,tpxr" :: "r"(0 << TPXR_WAY_SHIFT));
+		for (loop = 0; loop < 64; loop++) {
+			asm volatile("tlbpr %0,gr0,#1,#0" :: "r"(loop << PAGE_SHIFT));
+			asm volatile("movsg tplr,%0" : "=r"(p->L));
+			asm volatile("movsg tppr,%0" : "=r"(p->P));
+			p++;
+		}
+
+		/* way 1 */
+		asm volatile("movgs %0,tpxr" :: "r"(1 << TPXR_WAY_SHIFT));
+		for (loop = 0; loop < 64; loop++) {
+			asm volatile("tlbpr %0,gr0,#1,#0" :: "r"(loop << PAGE_SHIFT));
+			asm volatile("movsg tplr,%0" : "=r"(p->L));
+			asm volatile("movsg tppr,%0" : "=r"(p->P));
+			p++;
+		}
+
+		asm volatile("movgs %0,tplr" :: "r"(__debug_mmu.regs.tplr));
+		asm volatile("movgs %0,tppr" :: "r"(__debug_mmu.regs.tppr));
+		asm volatile("movgs %0,tpxr" :: "r"(__debug_mmu.regs.tpxr));
+	} while(0);
+#endif
+
+} /* end gdbstub_get_mmu_state() */
+
+/*****************************************************************************/
+/*
+ * handle event interception and GDB remote protocol processing
+ * - on entry:
+ *	PSR.ET==0, PSR.S==1 and the CPU is in debug mode
+ *	__debug_frame points to the saved registers
+ *	__frame points to the kernel mode exception frame, if it was in kernel
+ *      mode when the break happened
+ */
+void gdbstub(int sigval)
+{
+	unsigned long addr, length, loop, dbar, temp, temp2, temp3;
+	uint32_t zero;
+	char *ptr;
+	int flush_cache = 0;
+
+	LEDS(0x5000);
+
+	if (sigval < 0) {
+#ifndef CONFIG_GDBSTUB_IMMEDIATE
+		/* return immediately if GDB immediate activation option not set */
+		return;
+#else
+		sigval = SIGINT;
+#endif
+	}
+
+	save_user_regs(&__break_user_context);
+
+#if 0
+	gdbstub_printk("--> gdbstub() %08x %p %08x %08x\n",
+		       __debug_frame->pc,
+		       __debug_frame,
+		       __debug_regs->brr,
+		       __debug_regs->bpsr);
+//	gdbstub_show_regs();
+#endif
+
+	LEDS(0x5001);
+
+	/* if we were interrupted by input on the serial gdbstub serial port,
+	 * restore the context prior to the interrupt so that we return to that
+	 * directly
+	 */
+	temp = (unsigned long) __entry_kerneltrap_table;
+	temp2 = (unsigned long) __entry_usertrap_table;
+	temp3 = __debug_frame->pc & ~15;
+
+	if (temp3 == temp + TBR_TT_INTERRUPT_15 ||
+	    temp3 == temp2 + TBR_TT_INTERRUPT_15
+	    ) {
+		asm volatile("movsg pcsr,%0" : "=r"(__debug_frame->pc));
+		__debug_frame->psr |= PSR_ET;
+		__debug_frame->psr &= ~PSR_S;
+		if (__debug_frame->psr & PSR_PS)
+			__debug_frame->psr |= PSR_S;
+		__debug_regs->brr = (__debug_frame->tbr & TBR_TT) << 12;
+		__debug_regs->brr |= BRR_EB;
+		sigval = SIGINT;
+	}
+
+	/* handle the decrement timer going off (FR451 only) */
+	if (temp3 == temp + TBR_TT_DECREMENT_TIMER ||
+	    temp3 == temp2 + TBR_TT_DECREMENT_TIMER
+	    ) {
+		asm volatile("movgs %0,timerd" :: "r"(10000000));
+		asm volatile("movsg pcsr,%0" : "=r"(__debug_frame->pc));
+		__debug_frame->psr |= PSR_ET;
+		__debug_frame->psr &= ~PSR_S;
+		if (__debug_frame->psr & PSR_PS)
+			__debug_frame->psr |= PSR_S;
+		__debug_regs->brr = (__debug_frame->tbr & TBR_TT) << 12;
+		__debug_regs->brr |= BRR_EB;
+		sigval = SIGXCPU;;
+	}
+
+	LEDS(0x5002);
+
+	/* after a BREAK insn, the PC lands on the far side of it */
+	if (__debug_regs->brr & BRR_SB)
+		gdbstub_check_breakpoint();
+
+	LEDS(0x5003);
+
+	/* handle attempts to write console data via GDB "O" commands */
+	if (__debug_frame->pc == (unsigned long) gdbstub_console_write + 4) {
+		__gdbstub_console_write((struct console *) __debug_frame->gr8,
+					(const char *) __debug_frame->gr9,
+					(unsigned) __debug_frame->gr10);
+		goto done;
+	}
+
+	if (gdbstub_rx_unget) {
+		sigval = SIGINT;
+		goto packet_waiting;
+	}
+
+	if (!sigval)
+		sigval = gdbstub_compute_signal(__debug_regs->brr);
+
+	LEDS(0x5004);
+
+	/* send a message to the debugger's user saying what happened if it may
+	 * not be clear cut (we can't map exceptions onto signals properly)
+	 */
+	if (sigval != SIGINT && sigval != SIGTRAP && sigval != SIGILL) {
+		static const char title[] = "Break ";
+		static const char crlf[] = "\r\n";
+		unsigned long brr = __debug_regs->brr;
+		char hx;
+
+		ptr = output_buffer;
+		*ptr++ = 'O';
+		ptr = mem2hex(title, ptr, sizeof(title) - 1,0);
+
+		hx = hexchars[(brr & 0xf0000000) >> 28];
+		*ptr++ = hexchars[hx >> 4];	*ptr++ = hexchars[hx & 0xf];
+		hx = hexchars[(brr & 0x0f000000) >> 24];
+		*ptr++ = hexchars[hx >> 4];	*ptr++ = hexchars[hx & 0xf];
+		hx = hexchars[(brr & 0x00f00000) >> 20];
+		*ptr++ = hexchars[hx >> 4];	*ptr++ = hexchars[hx & 0xf];
+		hx = hexchars[(brr & 0x000f0000) >> 16];
+		*ptr++ = hexchars[hx >> 4];	*ptr++ = hexchars[hx & 0xf];
+		hx = hexchars[(brr & 0x0000f000) >> 12];
+		*ptr++ = hexchars[hx >> 4];	*ptr++ = hexchars[hx & 0xf];
+		hx = hexchars[(brr & 0x00000f00) >> 8];
+		*ptr++ = hexchars[hx >> 4];	*ptr++ = hexchars[hx & 0xf];
+		hx = hexchars[(brr & 0x000000f0) >> 4];
+		*ptr++ = hexchars[hx >> 4];	*ptr++ = hexchars[hx & 0xf];
+		hx = hexchars[(brr & 0x0000000f)];
+		*ptr++ = hexchars[hx >> 4];	*ptr++ = hexchars[hx & 0xf];
+
+		ptr = mem2hex(crlf, ptr, sizeof(crlf) - 1, 0);
+		*ptr = 0;
+		gdbstub_send_packet(output_buffer);	/* send it off... */
+	}
+
+	LEDS(0x5005);
+
+	/* tell the debugger that an exception has occurred */
+	ptr = output_buffer;
+
+	/* Send trap type (converted to signal) */
+	*ptr++ = 'T';
+	*ptr++ = hexchars[sigval >> 4];
+	*ptr++ = hexchars[sigval & 0xf];
+
+	/* Send Error PC */
+	*ptr++ = hexchars[GDB_REG_PC >> 4];
+	*ptr++ = hexchars[GDB_REG_PC & 0xf];
+	*ptr++ = ':';
+	ptr = mem2hex(&__debug_frame->pc, ptr, 4, 0);
+	*ptr++ = ';';
+
+	/*
+	 * Send frame pointer
+	 */
+	*ptr++ = hexchars[GDB_REG_FP >> 4];
+	*ptr++ = hexchars[GDB_REG_FP & 0xf];
+	*ptr++ = ':';
+	ptr = mem2hex(&__debug_frame->fp, ptr, 4, 0);
+	*ptr++ = ';';
+
+	/*
+	 * Send stack pointer
+	 */
+	*ptr++ = hexchars[GDB_REG_SP >> 4];
+	*ptr++ = hexchars[GDB_REG_SP & 0xf];
+	*ptr++ = ':';
+	ptr = mem2hex(&__debug_frame->sp, ptr, 4, 0);
+	*ptr++ = ';';
+
+	*ptr++ = 0;
+	gdbstub_send_packet(output_buffer);	/* send it off... */
+
+	LEDS(0x5006);
+
+ packet_waiting:
+	gdbstub_get_mmu_state();
+
+	/* wait for input from remote GDB */
+	while (1) {
+		output_buffer[0] = 0;
+
+		LEDS(0x5007);
+		gdbstub_recv_packet(input_buffer);
+		LEDS(0x5600 | input_buffer[0]);
+
+		switch (input_buffer[0]) {
+			/* request repeat of last signal number */
+		case '?':
+			output_buffer[0] = 'S';
+			output_buffer[1] = hexchars[sigval >> 4];
+			output_buffer[2] = hexchars[sigval & 0xf];
+			output_buffer[3] = 0;
+			break;
+
+		case 'd':
+			/* toggle debug flag */
+			break;
+
+			/* return the value of the CPU registers
+			 * - GR0,  GR1,  GR2,  GR3,  GR4,  GR5,  GR6,  GR7,
+			 * - GR8,  GR9,  GR10, GR11, GR12, GR13, GR14, GR15,
+			 * - GR16, GR17, GR18, GR19, GR20, GR21, GR22, GR23,
+			 * - GR24, GR25, GR26, GR27, GR28, GR29, GR30, GR31,
+			 * - GR32, GR33, GR34, GR35, GR36, GR37, GR38, GR39,
+			 * - GR40, GR41, GR42, GR43, GR44, GR45, GR46, GR47,
+			 * - GR48, GR49, GR50, GR51, GR52, GR53, GR54, GR55,
+			 * - GR56, GR57, GR58, GR59, GR60, GR61, GR62, GR63,
+			 * - FP0,  FP1,  FP2,  FP3,  FP4,  FP5,  FP6,  FP7,
+			 * - FP8,  FP9,  FP10, FP11, FP12, FP13, FP14, FP15,
+			 * - FP16, FP17, FP18, FP19, FP20, FP21, FP22, FP23,
+			 * - FP24, FP25, FP26, FP27, FP28, FP29, FP30, FP31,
+			 * - FP32, FP33, FP34, FP35, FP36, FP37, FP38, FP39,
+			 * - FP40, FP41, FP42, FP43, FP44, FP45, FP46, FP47,
+			 * - FP48, FP49, FP50, FP51, FP52, FP53, FP54, FP55,
+			 * - FP56, FP57, FP58, FP59, FP60, FP61, FP62, FP63,
+			 * - PC, PSR, CCR, CCCR,
+			 * - _X132, _X133, _X134
+			 * - TBR, BRR, DBAR0, DBAR1, DBAR2, DBAR3,
+			 * - _X141, _X142, _X143, _X144,
+			 * - LR, LCR
+			 */
+		case 'g':
+			zero = 0;
+			ptr = output_buffer;
+
+			/* deal with GR0, GR1-GR27, GR28-GR31, GR32-GR63 */
+			ptr = mem2hex(&zero, ptr, 4, 0);
+
+			for (loop = 1; loop <= 27; loop++)
+				ptr = mem2hex((unsigned long *)__debug_frame + REG_GR(loop),
+					      ptr, 4, 0);
+			temp = (unsigned long) __frame;
+			ptr = mem2hex(&temp, ptr, 4, 0);
+			ptr = mem2hex((unsigned long *)__debug_frame + REG_GR(29), ptr, 4, 0);
+			ptr = mem2hex((unsigned long *)__debug_frame + REG_GR(30), ptr, 4, 0);
+#ifdef CONFIG_MMU
+			ptr = mem2hex((unsigned long *)__debug_frame + REG_GR(31), ptr, 4, 0);
+#else
+			temp = (unsigned long) __debug_frame;
+			ptr = mem2hex(&temp, ptr, 4, 0);
+#endif
+
+			for (loop = 32; loop <= 63; loop++)
+				ptr = mem2hex((unsigned long *)__debug_frame + REG_GR(loop),
+					      ptr, 4, 0);
+
+			/* deal with FR0-FR63 */
+			for (loop = 0; loop <= 63; loop++)
+				ptr = mem2hex((unsigned long *)&__break_user_context +
+					      __FPMEDIA_FR(loop),
+					      ptr, 4, 0);
+
+			/* deal with special registers */
+			ptr = mem2hex(&__debug_frame->pc,    ptr, 4, 0);
+			ptr = mem2hex(&__debug_frame->psr,   ptr, 4, 0);
+			ptr = mem2hex(&__debug_frame->ccr,   ptr, 4, 0);
+			ptr = mem2hex(&__debug_frame->cccr,  ptr, 4, 0);
+			ptr = mem2hex(&zero, ptr, 4, 0);
+			ptr = mem2hex(&zero, ptr, 4, 0);
+			ptr = mem2hex(&zero, ptr, 4, 0);
+			ptr = mem2hex(&__debug_frame->tbr,   ptr, 4, 0);
+			ptr = mem2hex(&__debug_regs->brr ,   ptr, 4, 0);
+
+			asm volatile("movsg dbar0,%0" : "=r"(dbar));
+			ptr = mem2hex(&dbar, ptr, 4, 0);
+			asm volatile("movsg dbar1,%0" : "=r"(dbar));
+			ptr = mem2hex(&dbar, ptr, 4, 0);
+			asm volatile("movsg dbar2,%0" : "=r"(dbar));
+			ptr = mem2hex(&dbar, ptr, 4, 0);
+			asm volatile("movsg dbar3,%0" : "=r"(dbar));
+			ptr = mem2hex(&dbar, ptr, 4, 0);
+
+			asm volatile("movsg scr0,%0" : "=r"(dbar));
+			ptr = mem2hex(&dbar, ptr, 4, 0);
+			asm volatile("movsg scr1,%0" : "=r"(dbar));
+			ptr = mem2hex(&dbar, ptr, 4, 0);
+			asm volatile("movsg scr2,%0" : "=r"(dbar));
+			ptr = mem2hex(&dbar, ptr, 4, 0);
+			asm volatile("movsg scr3,%0" : "=r"(dbar));
+			ptr = mem2hex(&dbar, ptr, 4, 0);
+
+			ptr = mem2hex(&__debug_frame->lr, ptr, 4, 0);
+			ptr = mem2hex(&__debug_frame->lcr, ptr, 4, 0);
+
+			ptr = mem2hex(&__debug_frame->iacc0, ptr, 8, 0);
+
+			ptr = mem2hex(&__break_user_context.f.fsr[0], ptr, 4, 0);
+
+			for (loop = 0; loop <= 7; loop++)
+				ptr = mem2hex(&__break_user_context.f.acc[loop], ptr, 4, 0);
+
+			ptr = mem2hex(&__break_user_context.f.accg, ptr, 8, 0);
+
+			for (loop = 0; loop <= 1; loop++)
+				ptr = mem2hex(&__break_user_context.f.msr[loop], ptr, 4, 0);
+
+			ptr = mem2hex(&__debug_frame->gner0, ptr, 4, 0);
+			ptr = mem2hex(&__debug_frame->gner1, ptr, 4, 0);
+
+			ptr = mem2hex(&__break_user_context.f.fner[0], ptr, 4, 0);
+			ptr = mem2hex(&__break_user_context.f.fner[1], ptr, 4, 0);
+
+			break;
+
+			/* set the values of the CPU registers */
+		case 'G':
+			ptr = &input_buffer[1];
+
+			/* deal with GR0, GR1-GR27, GR28-GR31, GR32-GR63 */
+			ptr = hex2mem(ptr, &temp, 4);
+
+			for (loop = 1; loop <= 27; loop++)
+				ptr = hex2mem(ptr, (unsigned long *)__debug_frame + REG_GR(loop),
+					      4);
+
+			ptr = hex2mem(ptr, &temp, 4);
+			__frame = (struct pt_regs *) temp;
+			ptr = hex2mem(ptr, &__debug_frame->gr29, 4);
+			ptr = hex2mem(ptr, &__debug_frame->gr30, 4);
+#ifdef CONFIG_MMU
+			ptr = hex2mem(ptr, &__debug_frame->gr31, 4);
+#else
+			ptr = hex2mem(ptr, &temp, 4);
+#endif
+
+			for (loop = 32; loop <= 63; loop++)
+				ptr = hex2mem(ptr, (unsigned long *)__debug_frame + REG_GR(loop),
+					      4);
+
+			/* deal with FR0-FR63 */
+			for (loop = 0; loop <= 63; loop++)
+				ptr = mem2hex((unsigned long *)&__break_user_context +
+					      __FPMEDIA_FR(loop),
+					      ptr, 4, 0);
+
+			/* deal with special registers */
+			ptr = hex2mem(ptr, &__debug_frame->pc,  4);
+			ptr = hex2mem(ptr, &__debug_frame->psr, 4);
+			ptr = hex2mem(ptr, &__debug_frame->ccr, 4);
+			ptr = hex2mem(ptr, &__debug_frame->cccr,4);
+
+			for (loop = 132; loop <= 140; loop++)
+				ptr = hex2mem(ptr, &temp, 4);
+
+			ptr = hex2mem(ptr, &temp, 4);
+			asm volatile("movgs %0,scr0" :: "r"(temp));
+			ptr = hex2mem(ptr, &temp, 4);
+			asm volatile("movgs %0,scr1" :: "r"(temp));
+			ptr = hex2mem(ptr, &temp, 4);
+			asm volatile("movgs %0,scr2" :: "r"(temp));
+			ptr = hex2mem(ptr, &temp, 4);
+			asm volatile("movgs %0,scr3" :: "r"(temp));
+
+			ptr = hex2mem(ptr, &__debug_frame->lr,  4);
+			ptr = hex2mem(ptr, &__debug_frame->lcr, 4);
+
+			ptr = hex2mem(ptr, &__debug_frame->iacc0, 8);
+
+			ptr = hex2mem(ptr, &__break_user_context.f.fsr[0], 4);
+
+			for (loop = 0; loop <= 7; loop++)
+				ptr = hex2mem(ptr, &__break_user_context.f.acc[loop], 4);
+
+			ptr = hex2mem(ptr, &__break_user_context.f.accg, 8);
+
+			for (loop = 0; loop <= 1; loop++)
+				ptr = hex2mem(ptr, &__break_user_context.f.msr[loop], 4);
+
+			ptr = hex2mem(ptr, &__debug_frame->gner0, 4);
+			ptr = hex2mem(ptr, &__debug_frame->gner1, 4);
+
+			ptr = hex2mem(ptr, &__break_user_context.f.fner[0], 4);
+			ptr = hex2mem(ptr, &__break_user_context.f.fner[1], 4);
+
+			gdbstub_strcpy(output_buffer,"OK");
+			break;
+
+			/* mAA..AA,LLLL  Read LLLL bytes at address AA..AA */
+		case 'm':
+			ptr = &input_buffer[1];
+
+			if (hexToInt(&ptr, &addr) &&
+			    *ptr++ == ',' &&
+			    hexToInt(&ptr, &length)
+			    ) {
+				if (mem2hex((char *)addr, output_buffer, length, 1))
+					break;
+				gdbstub_strcpy (output_buffer, "E03");
+			}
+			else {
+				gdbstub_strcpy(output_buffer,"E01");
+			}
+			break;
+
+			/* MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK */
+		case 'M':
+			ptr = &input_buffer[1];
+
+			if (hexToInt(&ptr, &addr) &&
+			    *ptr++ == ',' &&
+			    hexToInt(&ptr, &length) &&
+			    *ptr++ == ':'
+			    ) {
+				if (hex2mem(ptr, (char *)addr, length)) {
+					gdbstub_strcpy(output_buffer, "OK");
+				}
+				else {
+					gdbstub_strcpy(output_buffer, "E03");
+				}
+			}
+			else
+				gdbstub_strcpy(output_buffer, "E02");
+
+			flush_cache = 1;
+			break;
+
+			/* PNN,=RRRRRRRR: Write value R to reg N return OK */
+		case 'P':
+			ptr = &input_buffer[1];
+
+			if (!hexToInt(&ptr, &addr) ||
+			    *ptr++ != '=' ||
+			    !hexToInt(&ptr, &temp)
+			    ) {
+				gdbstub_strcpy(output_buffer, "E01");
+				break;
+			}
+
+			temp2 = 1;
+			switch (addr) {
+			case GDB_REG_GR(0):
+				break;
+			case GDB_REG_GR(1) ... GDB_REG_GR(63):
+				__break_user_context.i.gr[addr - GDB_REG_GR(0)] = temp;
+				break;
+			case GDB_REG_FR(0) ... GDB_REG_FR(63):
+				__break_user_context.f.fr[addr - GDB_REG_FR(0)] = temp;
+				break;
+			case GDB_REG_PC:
+				__break_user_context.i.pc = temp;
+				break;
+			case GDB_REG_PSR:
+				__break_user_context.i.psr = temp;
+				break;
+			case GDB_REG_CCR:
+				__break_user_context.i.ccr = temp;
+				break;
+			case GDB_REG_CCCR:
+				__break_user_context.i.cccr = temp;
+				break;
+			case GDB_REG_BRR:
+				__debug_regs->brr = temp;
+				break;
+			case GDB_REG_LR:
+				__break_user_context.i.lr = temp;
+				break;
+			case GDB_REG_LCR:
+				__break_user_context.i.lcr = temp;
+				break;
+			case GDB_REG_FSR0:
+				__break_user_context.f.fsr[0] = temp;
+				break;
+			case GDB_REG_ACC(0) ... GDB_REG_ACC(7):
+				__break_user_context.f.acc[addr - GDB_REG_ACC(0)] = temp;
+				break;
+			case GDB_REG_ACCG(0):
+				*(uint32_t *) &__break_user_context.f.accg[0] = temp;
+				break;
+			case GDB_REG_ACCG(4):
+				*(uint32_t *) &__break_user_context.f.accg[4] = temp;
+				break;
+			case GDB_REG_MSR(0) ... GDB_REG_MSR(1):
+				__break_user_context.f.msr[addr - GDB_REG_MSR(0)] = temp;
+				break;
+			case GDB_REG_GNER(0) ... GDB_REG_GNER(1):
+				__break_user_context.i.gner[addr - GDB_REG_GNER(0)] = temp;
+				break;
+			case GDB_REG_FNER(0) ... GDB_REG_FNER(1):
+				__break_user_context.f.fner[addr - GDB_REG_FNER(0)] = temp;
+				break;
+			default:
+				temp2 = 0;
+				break;
+			}
+
+			if (temp2) {
+				gdbstub_strcpy(output_buffer, "OK");
+			}
+			else {
+				gdbstub_strcpy(output_buffer, "E02");
+			}
+			break;
+
+			/* cAA..AA    Continue at address AA..AA(optional) */
+		case 'c':
+			/* try to read optional parameter, pc unchanged if no parm */
+			ptr = &input_buffer[1];
+			if (hexToInt(&ptr, &addr))
+				__debug_frame->pc = addr;
+			goto done;
+
+			/* kill the program */
+		case 'k' :
+			goto done;	/* just continue */
+
+
+			/* reset the whole machine (FIXME: system dependent) */
+		case 'r':
+			break;
+
+
+			/* step to next instruction */
+		case 's':
+			__debug_regs->dcr |= DCR_SE;
+			goto done;
+
+			/* set baud rate (bBB) */
+		case 'b':
+			ptr = &input_buffer[1];
+			if (!hexToInt(&ptr, &temp)) {
+				gdbstub_strcpy(output_buffer,"B01");
+				break;
+			}
+
+			if (temp) {
+				/* ack before changing speed */
+				gdbstub_send_packet("OK");
+				gdbstub_set_baud(temp);
+			}
+			break;
+
+			/* set breakpoint */
+		case 'Z':
+			ptr = &input_buffer[1];
+
+			if (!hexToInt(&ptr,&temp) || *ptr++ != ',' ||
+			    !hexToInt(&ptr,&addr) || *ptr++ != ',' ||
+			    !hexToInt(&ptr,&length)
+			    ) {
+				gdbstub_strcpy(output_buffer,"E01");
+				break;
+			}
+
+			if (temp >= 5) {
+				gdbstub_strcpy(output_buffer,"E03");
+				break;
+			}
+
+			if (gdbstub_set_breakpoint(temp, addr, length) < 0) {
+				gdbstub_strcpy(output_buffer,"E03");
+				break;
+			}
+
+			if (temp == 0)
+				flush_cache = 1; /* soft bkpt by modified memory */
+
+			gdbstub_strcpy(output_buffer,"OK");
+			break;
+
+			/* clear breakpoint */
+		case 'z':
+			ptr = &input_buffer[1];
+
+			if (!hexToInt(&ptr,&temp) || *ptr++ != ',' ||
+			    !hexToInt(&ptr,&addr) || *ptr++ != ',' ||
+			    !hexToInt(&ptr,&length)
+			    ) {
+				gdbstub_strcpy(output_buffer,"E01");
+				break;
+			}
+
+			if (temp >= 5) {
+				gdbstub_strcpy(output_buffer,"E03");
+				break;
+			}
+
+			if (gdbstub_clear_breakpoint(temp, addr, length) < 0) {
+				gdbstub_strcpy(output_buffer,"E03");
+				break;
+			}
+
+			if (temp == 0)
+				flush_cache = 1; /* soft bkpt by modified memory */
+
+			gdbstub_strcpy(output_buffer,"OK");
+			break;
+
+		default:
+			gdbstub_proto("### GDB Unsupported Cmd '%s'\n",input_buffer);
+			break;
+		}
+
+		/* reply to the request */
+		LEDS(0x5009);
+		gdbstub_send_packet(output_buffer);
+	}
+
+ done:
+	restore_user_regs(&__break_user_context);
+
+	//gdbstub_dump_debugregs();
+	//gdbstub_printk("<-- gdbstub() %08x\n", __debug_frame->pc);
+
+	/* need to flush the instruction cache before resuming, as we may have
+	 * deposited a breakpoint, and the icache probably has no way of
+	 * knowing that a data ref to some location may have changed something
+	 * that is in the instruction cache.  NB: We flush both caches, just to
+	 * be sure...
+	 */
+
+	/* note: flushing the icache will clobber EAR0 on the FR451 */
+	if (flush_cache)
+		gdbstub_purge_cache();
+
+	LEDS(0x5666);
+
+} /* end gdbstub() */
+
+/*****************************************************************************/
+/*
+ * initialise the GDB stub
+ */
+void __init gdbstub_init(void)
+{
+#ifdef CONFIG_GDBSTUB_IMMEDIATE
+	unsigned char ch;
+	int ret;
+#endif
+
+	gdbstub_printk("%s", gdbstub_banner);
+	gdbstub_printk("DCR: %x\n", __debug_regs->dcr);
+
+	gdbstub_io_init();
+
+	/* try to talk to GDB (or anyone insane enough to want to type GDB protocol by hand) */
+	gdbstub_proto("### GDB Tx ACK\n");
+	gdbstub_tx_char('+'); /* 'hello world' */
+
+#ifdef CONFIG_GDBSTUB_IMMEDIATE
+	gdbstub_printk("GDB Stub waiting for packet\n");
+
+	/*
+	 * In case GDB is started before us, ack any packets
+	 * (presumably "$?#xx") sitting there.
+	 */
+	do { gdbstub_rx_char(&ch, 0); } while (ch != '$');
+	do { gdbstub_rx_char(&ch, 0); } while (ch != '#');
+	do { ret = gdbstub_rx_char(&ch, 0); } while (ret != 0); /* eat first csum byte */
+	do { ret = gdbstub_rx_char(&ch, 0); } while (ret != 0); /* eat second csum byte */
+
+	gdbstub_proto("### GDB Tx NAK\n");
+	gdbstub_tx_char('-'); /* nak it */
+
+#else
+	gdbstub_printk("GDB Stub set\n");
+#endif
+
+#if 0
+	/* send banner */
+	ptr = output_buffer;
+	*ptr++ = 'O';
+	ptr = mem2hex(gdbstub_banner, ptr, sizeof(gdbstub_banner) - 1, 0);
+	gdbstub_send_packet(output_buffer);
+#endif
+#if defined(CONFIG_GDBSTUB_CONSOLE) && defined(CONFIG_GDBSTUB_IMMEDIATE)
+	register_console(&gdbstub_console);
+#endif
+
+} /* end gdbstub_init() */
+
+/*****************************************************************************/
+/*
+ * register the console at a more appropriate time
+ */
+#if defined (CONFIG_GDBSTUB_CONSOLE) && !defined(CONFIG_GDBSTUB_IMMEDIATE)
+static int __init gdbstub_postinit(void)
+{
+	printk("registering console\n");
+	register_console(&gdbstub_console);
+	return 0;
+} /* end gdbstub_postinit() */
+
+__initcall(gdbstub_postinit);
+#endif
+
+/*****************************************************************************/
+/*
+ * send an exit message to GDB
+ */
+void gdbstub_exit(int status)
+{
+	unsigned char checksum;
+	int count;
+	unsigned char ch;
+
+	sprintf(output_buffer,"W%02x",status&0xff);
+
+	gdbstub_tx_char('$');
+	checksum = 0;
+	count = 0;
+
+	while ((ch = output_buffer[count]) != 0) {
+		gdbstub_tx_char(ch);
+		checksum += ch;
+		count += 1;
+	}
+
+	gdbstub_tx_char('#');
+	gdbstub_tx_char(hexchars[checksum >> 4]);
+	gdbstub_tx_char(hexchars[checksum & 0xf]);
+
+	/* make sure the output is flushed, or else RedBoot might clobber it */
+	gdbstub_tx_char('-');
+	gdbstub_tx_flush();
+
+} /* end gdbstub_exit() */
+
+/*****************************************************************************/
+/*
+ * GDB wants to call malloc() and free() to allocate memory for calling kernel
+ * functions directly from its command line
+ */
+static void *malloc(size_t size) __attribute__((unused));
+static void *malloc(size_t size)
+{
+	return kmalloc(size, GFP_ATOMIC);
+}
+
+static void free(void *p) __attribute__((unused));
+static void free(void *p)
+{
+	kfree(p);
+}
+
+static uint32_t ___get_HSR0(void) __attribute__((unused));
+static uint32_t ___get_HSR0(void)
+{
+	return __get_HSR(0);
+}
+
+static uint32_t ___set_HSR0(uint32_t x) __attribute__((unused));
+static uint32_t ___set_HSR0(uint32_t x)
+{
+	__set_HSR(0, x);
+	return __get_HSR(0);
+}
diff --git a/arch/frv/kernel/head-mmu-fr451.S b/arch/frv/kernel/head-mmu-fr451.S
new file mode 100644
index 0000000..a143c2f
--- /dev/null
+++ b/arch/frv/kernel/head-mmu-fr451.S
@@ -0,0 +1,374 @@
+/* head-mmu-fr451.S: FR451 mmu-linux specific bits of initialisation
+ *
+ * Copyright (C) 2004 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 License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/config.h>
+#include <linux/threads.h>
+#include <linux/linkage.h>
+#include <asm/ptrace.h>
+#include <asm/page.h>
+#include <asm/mem-layout.h>
+#include <asm/spr-regs.h>
+#include <asm/mb86943a.h>
+#include "head.inc"
+
+
+#define __400_DBR0	0xfe000e00
+#define __400_DBR1	0xfe000e08
+#define __400_DBR2	0xfe000e10
+#define __400_DBR3	0xfe000e18
+#define __400_DAM0	0xfe000f00
+#define __400_DAM1	0xfe000f08
+#define __400_DAM2	0xfe000f10
+#define __400_DAM3	0xfe000f18
+#define __400_LGCR	0xfe000010
+#define __400_LCR	0xfe000100
+#define __400_LSBR	0xfe000c00
+
+	.section	.text.init,"ax"
+	.balign		4
+
+###############################################################################
+#
+# describe the position and layout of the SDRAM controller registers
+#
+#	ENTRY:			EXIT:
+# GR5	-			cacheline size
+# GR11	-			displacement of 2nd SDRAM addr reg from GR14
+# GR12	-			displacement of 3rd SDRAM addr reg from GR14
+# GR13	-			displacement of 4th SDRAM addr reg from GR14
+# GR14	-			address of 1st SDRAM addr reg
+# GR15	-			amount to shift address by to match SDRAM addr reg
+# GR26	&__head_reference	[saved]
+# GR30	LED address		[saved]
+# CC0	-			T if DBR0 is present
+# CC1	-			T if DBR1 is present
+# CC2	-			T if DBR2 is present
+# CC3	-			T if DBR3 is present
+#
+###############################################################################
+	.globl		__head_fr451_describe_sdram
+__head_fr451_describe_sdram:
+	sethi.p		%hi(__400_DBR0),gr14
+	setlo		%lo(__400_DBR0),gr14
+	setlos.p	#__400_DBR1-__400_DBR0,gr11
+	setlos		#__400_DBR2-__400_DBR0,gr12
+	setlos.p	#__400_DBR3-__400_DBR0,gr13
+	setlos		#32,gr5			; cacheline size
+	setlos.p	#0,gr15			; amount to shift addr reg by
+	setlos		#0x00ff,gr4
+	movgs		gr4,cccr		; extant DARS/DAMK regs
+	bralr
+
+###############################################################################
+#
+# rearrange the bus controller registers
+#
+#	ENTRY:			EXIT:
+# GR26	&__head_reference	[saved]
+# GR30	LED address		revised LED address
+#
+###############################################################################
+	.globl		__head_fr451_set_busctl
+__head_fr451_set_busctl:
+	sethi.p		%hi(__400_LGCR),gr4
+	setlo		%lo(__400_LGCR),gr4
+	sethi.p		%hi(__400_LSBR),gr10
+	setlo		%lo(__400_LSBR),gr10
+	sethi.p		%hi(__400_LCR),gr11
+	setlo		%lo(__400_LCR),gr11
+
+	# set the bus controller
+	ldi		@(gr4,#0),gr5
+	ori		gr5,#0xff,gr5		; make sure all chip-selects are enabled
+	sti		gr5,@(gr4,#0)
+
+	sethi.p		%hi(__region_CS1),gr4
+	setlo		%lo(__region_CS1),gr4
+	sethi.p		%hi(__region_CS1_M),gr5
+	setlo		%lo(__region_CS1_M),gr5
+	sethi.p		%hi(__region_CS1_C),gr6
+	setlo		%lo(__region_CS1_C),gr6
+	sti		gr4,@(gr10,#1*0x08)
+	sti		gr5,@(gr10,#1*0x08+0x100)
+	sti		gr6,@(gr11,#1*0x08)
+	sethi.p		%hi(__region_CS2),gr4
+	setlo		%lo(__region_CS2),gr4
+	sethi.p		%hi(__region_CS2_M),gr5
+	setlo		%lo(__region_CS2_M),gr5
+	sethi.p		%hi(__region_CS2_C),gr6
+	setlo		%lo(__region_CS2_C),gr6
+	sti		gr4,@(gr10,#2*0x08)
+	sti		gr5,@(gr10,#2*0x08+0x100)
+	sti		gr6,@(gr11,#2*0x08)
+	sethi.p		%hi(__region_CS3),gr4
+	setlo		%lo(__region_CS3),gr4
+	sethi.p		%hi(__region_CS3_M),gr5
+	setlo		%lo(__region_CS3_M),gr5
+	sethi.p		%hi(__region_CS3_C),gr6
+	setlo		%lo(__region_CS3_C),gr6
+	sti		gr4,@(gr10,#3*0x08)
+	sti		gr5,@(gr10,#3*0x08+0x100)
+	sti		gr6,@(gr11,#3*0x08)
+	sethi.p		%hi(__region_CS4),gr4
+	setlo		%lo(__region_CS4),gr4
+	sethi.p		%hi(__region_CS4_M),gr5
+	setlo		%lo(__region_CS4_M),gr5
+	sethi.p		%hi(__region_CS4_C),gr6
+	setlo		%lo(__region_CS4_C),gr6
+	sti		gr4,@(gr10,#4*0x08)
+	sti		gr5,@(gr10,#4*0x08+0x100)
+	sti		gr6,@(gr11,#4*0x08)
+	sethi.p		%hi(__region_CS5),gr4
+	setlo		%lo(__region_CS5),gr4
+	sethi.p		%hi(__region_CS5_M),gr5
+	setlo		%lo(__region_CS5_M),gr5
+	sethi.p		%hi(__region_CS5_C),gr6
+	setlo		%lo(__region_CS5_C),gr6
+	sti		gr4,@(gr10,#5*0x08)
+	sti		gr5,@(gr10,#5*0x08+0x100)
+	sti		gr6,@(gr11,#5*0x08)
+	sethi.p		%hi(__region_CS6),gr4
+	setlo		%lo(__region_CS6),gr4
+	sethi.p		%hi(__region_CS6_M),gr5
+	setlo		%lo(__region_CS6_M),gr5
+	sethi.p		%hi(__region_CS6_C),gr6
+	setlo		%lo(__region_CS6_C),gr6
+	sti		gr4,@(gr10,#6*0x08)
+	sti		gr5,@(gr10,#6*0x08+0x100)
+	sti		gr6,@(gr11,#6*0x08)
+	sethi.p		%hi(__region_CS7),gr4
+	setlo		%lo(__region_CS7),gr4
+	sethi.p		%hi(__region_CS7_M),gr5
+	setlo		%lo(__region_CS7_M),gr5
+	sethi.p		%hi(__region_CS7_C),gr6
+	setlo		%lo(__region_CS7_C),gr6
+	sti		gr4,@(gr10,#7*0x08)
+	sti		gr5,@(gr10,#7*0x08+0x100)
+	sti		gr6,@(gr11,#7*0x08)
+	membar
+	bar
+
+	# adjust LED bank address
+#ifdef CONFIG_MB93091_VDK
+	sethi.p		%hi(__region_CS2 + 0x01200004),gr30
+	setlo		%lo(__region_CS2 + 0x01200004),gr30
+#endif
+	bralr
+
+###############################################################################
+#
+# determine the total SDRAM size
+#
+#	ENTRY:			EXIT:
+# GR25	-			SDRAM size
+# GR26	&__head_reference	[saved]
+# GR30	LED address		[saved]
+#
+###############################################################################
+	.globl		__head_fr451_survey_sdram
+__head_fr451_survey_sdram:
+	sethi.p		%hi(__400_DAM0),gr11
+	setlo		%lo(__400_DAM0),gr11
+	sethi.p		%hi(__400_DBR0),gr12
+	setlo		%lo(__400_DBR0),gr12
+
+	sethi.p		%hi(0xfe000000),gr17		; unused SDRAM DBR value
+	setlo		%lo(0xfe000000),gr17
+	setlos		#0,gr25
+
+	ldi		@(gr12,#0x00),gr4		; DAR0
+	subcc		gr4,gr17,gr0,icc0
+	beq		icc0,#0,__head_no_DCS0
+	ldi		@(gr11,#0x00),gr6		; DAM0: bits 31:20 match addr 31:20
+	add		gr25,gr6,gr25
+	addi		gr25,#1,gr25
+__head_no_DCS0:
+
+	ldi		@(gr12,#0x08),gr4		; DAR1
+	subcc		gr4,gr17,gr0,icc0
+	beq		icc0,#0,__head_no_DCS1
+	ldi		@(gr11,#0x08),gr6		; DAM1: bits 31:20 match addr 31:20
+	add		gr25,gr6,gr25
+	addi		gr25,#1,gr25
+__head_no_DCS1:
+
+	ldi		@(gr12,#0x10),gr4		; DAR2
+	subcc		gr4,gr17,gr0,icc0
+	beq		icc0,#0,__head_no_DCS2
+	ldi		@(gr11,#0x10),gr6		; DAM2: bits 31:20 match addr 31:20
+	add		gr25,gr6,gr25
+	addi		gr25,#1,gr25
+__head_no_DCS2:
+
+	ldi		@(gr12,#0x18),gr4		; DAR3
+	subcc		gr4,gr17,gr0,icc0
+	beq		icc0,#0,__head_no_DCS3
+	ldi		@(gr11,#0x18),gr6		; DAM3: bits 31:20 match addr 31:20
+	add		gr25,gr6,gr25
+	addi		gr25,#1,gr25
+__head_no_DCS3:
+	bralr
+
+###############################################################################
+#
+# set the protection map with the I/DAMPR registers
+#
+#	ENTRY:			EXIT:
+# GR25	SDRAM size		[saved]
+# GR26	&__head_reference	[saved]
+# GR30	LED address		[saved]
+#
+#
+# Using this map:
+#	REGISTERS	ADDRESS RANGE		VIEW
+#	===============	======================	===============================
+#	IAMPR0/DAMPR0	0xC0000000-0xCFFFFFFF	Cached kernel RAM Window
+#	DAMPR11		0xE0000000-0xFFFFFFFF	Uncached I/O
+#
+###############################################################################
+	.globl		__head_fr451_set_protection
+__head_fr451_set_protection:
+	movsg		lr,gr27
+
+	# set the I/O region protection registers for FR451 in MMU mode
+#define PGPROT_IO	xAMPRx_L|xAMPRx_M|xAMPRx_S_KERNEL|xAMPRx_C|xAMPRx_V
+
+	sethi.p		%hi(__region_IO),gr5
+	setlo		%lo(__region_IO),gr5
+	setlos		#PGPROT_IO|xAMPRx_SS_512Mb,gr4
+	or		gr4,gr5,gr4
+	movgs		gr5,damlr11			; General I/O tile
+	movgs		gr4,dampr11
+
+	# need to open a window onto at least part of the RAM for the kernel's use
+	sethi.p		%hi(__sdram_base),gr8
+	setlo		%lo(__sdram_base),gr8		; physical address
+	sethi.p		%hi(__page_offset),gr9
+	setlo		%lo(__page_offset),gr9		; virtual address
+
+	setlos		#xAMPRx_L|xAMPRx_M|xAMPRx_SS_256Mb|xAMPRx_S_KERNEL|xAMPRx_V,gr11
+	or		gr8,gr11,gr8
+
+	movgs		gr9,iamlr0			; mapped from real address 0
+	movgs		gr8,iampr0			; cached kernel memory at 0xC0000000
+	movgs		gr9,damlr0
+	movgs		gr8,dampr0
+
+	# set a temporary mapping for the kernel running at address 0 until we've turned on the MMU
+	sethi.p		%hi(__sdram_base),gr9
+	setlo		%lo(__sdram_base),gr9		; virtual address
+
+	and.p		gr4,gr11,gr4
+	and		gr5,gr11,gr5
+	or.p		gr4,gr11,gr4
+	or		gr5,gr11,gr5
+
+	movgs		gr9,iamlr1			; mapped from real address 0
+	movgs		gr8,iampr1			; cached kernel memory at 0x00000000
+	movgs		gr9,damlr1
+	movgs		gr8,dampr1
+
+	# we use DAMR2-10 for kmap_atomic(), cache flush and TLB management
+	# since the DAMLR regs are not going to change, we can set them now
+	# also set up IAMLR2 to the same as DAMLR5
+	sethi.p		%hi(KMAP_ATOMIC_PRIMARY_FRAME),gr4
+	setlo		%lo(KMAP_ATOMIC_PRIMARY_FRAME),gr4
+	sethi.p		%hi(PAGE_SIZE),gr5
+	setlo		%lo(PAGE_SIZE),gr5
+
+	movgs		gr4,damlr2
+	movgs		gr4,iamlr2
+	add		gr4,gr5,gr4
+	movgs		gr4,damlr3
+	add		gr4,gr5,gr4
+	movgs		gr4,damlr4
+	add		gr4,gr5,gr4
+	movgs		gr4,damlr5
+	add		gr4,gr5,gr4
+	movgs		gr4,damlr6
+	add		gr4,gr5,gr4
+	movgs		gr4,damlr7
+	add		gr4,gr5,gr4
+	movgs		gr4,damlr8
+	add		gr4,gr5,gr4
+	movgs		gr4,damlr9
+	add		gr4,gr5,gr4
+	movgs		gr4,damlr10
+
+	movgs		gr0,dampr2
+	movgs		gr0,dampr4
+	movgs		gr0,dampr5
+	movgs		gr0,dampr6
+	movgs		gr0,dampr7
+	movgs		gr0,dampr8
+	movgs		gr0,dampr9
+	movgs		gr0,dampr10
+
+	movgs		gr0,iamlr3
+	movgs		gr0,iamlr4
+	movgs		gr0,iamlr5
+	movgs		gr0,iamlr6
+	movgs		gr0,iamlr7
+
+	movgs		gr0,iampr2
+	movgs		gr0,iampr3
+	movgs		gr0,iampr4
+	movgs		gr0,iampr5
+	movgs		gr0,iampr6
+	movgs		gr0,iampr7
+
+	# start in TLB context 0 with the swapper's page tables
+	movgs		gr0,cxnr
+
+	sethi.p		%hi(swapper_pg_dir),gr4
+	setlo		%lo(swapper_pg_dir),gr4
+	sethi.p		%hi(__page_offset),gr5
+	setlo		%lo(__page_offset),gr5
+	sub		gr4,gr5,gr4
+	movgs		gr4,ttbr
+	setlos		#xAMPRx_L|xAMPRx_M|xAMPRx_SS_16Kb|xAMPRx_S|xAMPRx_C|xAMPRx_V,gr5
+	or		gr4,gr5,gr4
+	movgs		gr4,dampr3
+
+	# the FR451 also has an extra trap base register
+	movsg		tbr,gr4
+	movgs		gr4,btbr
+
+	LEDS		0x3300
+	jmpl		@(gr27,gr0)
+
+###############################################################################
+#
+# finish setting up the protection registers
+#
+###############################################################################
+	.globl		__head_fr451_finalise_protection
+__head_fr451_finalise_protection:
+	# turn on the timers as appropriate
+	movgs		gr0,timerh
+	movgs		gr0,timerl
+	movgs		gr0,timerd
+	movsg		hsr0,gr4
+	sethi.p		%hi(HSR0_ETMI),gr5
+	setlo		%lo(HSR0_ETMI),gr5
+	or		gr4,gr5,gr4
+	movgs		gr4,hsr0
+
+	# clear the TLB entry cache
+	movgs		gr0,iamlr1
+	movgs		gr0,iampr1
+	movgs		gr0,damlr1
+	movgs		gr0,dampr1
+
+	# clear the PGE cache
+	sethi.p		%hi(__flush_tlb_all),gr4
+	setlo		%lo(__flush_tlb_all),gr4
+	jmpl		@(gr4,gr0)
diff --git a/arch/frv/kernel/head-uc-fr401.S b/arch/frv/kernel/head-uc-fr401.S
new file mode 100644
index 0000000..4ccf841
--- /dev/null
+++ b/arch/frv/kernel/head-uc-fr401.S
@@ -0,0 +1,311 @@
+/* head-uc-fr401.S: FR401/3/5 uc-linux specific bits of initialisation
+ *
+ * Copyright (C) 2004 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 License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/config.h>
+#include <linux/threads.h>
+#include <linux/linkage.h>
+#include <asm/ptrace.h>
+#include <asm/page.h>
+#include <asm/spr-regs.h>
+#include <asm/mb86943a.h>
+#include "head.inc"
+
+
+#define __400_DBR0	0xfe000e00
+#define __400_DBR1	0xfe000e08
+#define __400_DBR2	0xfe000e10	/* not on FR401 */
+#define __400_DBR3	0xfe000e18	/* not on FR401 */
+#define __400_DAM0	0xfe000f00
+#define __400_DAM1	0xfe000f08
+#define __400_DAM2	0xfe000f10	/* not on FR401 */
+#define __400_DAM3	0xfe000f18	/* not on FR401 */
+#define __400_LGCR	0xfe000010
+#define __400_LCR	0xfe000100
+#define __400_LSBR	0xfe000c00
+
+	.section	.text.init,"ax"
+	.balign		4
+
+###############################################################################
+#
+# describe the position and layout of the SDRAM controller registers
+#
+#	ENTRY:			EXIT:
+# GR5	-			cacheline size
+# GR11	-			displacement of 2nd SDRAM addr reg from GR14
+# GR12	-			displacement of 3rd SDRAM addr reg from GR14
+# GR13	-			displacement of 4th SDRAM addr reg from GR14
+# GR14	-			address of 1st SDRAM addr reg
+# GR15	-			amount to shift address by to match SDRAM addr reg
+# GR26	&__head_reference	[saved]
+# GR30	LED address		[saved]
+# CC0	-			T if DBR0 is present
+# CC1	-			T if DBR1 is present
+# CC2	-			T if DBR2 is present (not FR401/FR401A)
+# CC3	-			T if DBR3 is present (not FR401/FR401A)
+#
+###############################################################################
+	.globl		__head_fr401_describe_sdram
+__head_fr401_describe_sdram:
+	sethi.p		%hi(__400_DBR0),gr14
+	setlo		%lo(__400_DBR0),gr14
+	setlos.p	#__400_DBR1-__400_DBR0,gr11
+	setlos		#__400_DBR2-__400_DBR0,gr12
+	setlos.p	#__400_DBR3-__400_DBR0,gr13
+	setlos		#32,gr5			; cacheline size
+	setlos.p	#0,gr15			; amount to shift addr reg by
+
+	# specify which DBR regs are present
+	setlos		#0x00ff,gr4
+	movgs		gr4,cccr
+	movsg		psr,gr3			; check for FR401/FR401A
+	srli		gr3,#25,gr3
+	subicc		gr3,#0x20>>1,gr0,icc0
+	bnelr		icc0,#1
+	setlos		#0x000f,gr4
+	movgs		gr4,cccr
+	bralr
+
+###############################################################################
+#
+# rearrange the bus controller registers
+#
+#	ENTRY:			EXIT:
+# GR26	&__head_reference	[saved]
+# GR30	LED address		revised LED address
+#
+###############################################################################
+	.globl		__head_fr401_set_busctl
+__head_fr401_set_busctl:
+	sethi.p		%hi(__400_LGCR),gr4
+	setlo		%lo(__400_LGCR),gr4
+	sethi.p		%hi(__400_LSBR),gr10
+	setlo		%lo(__400_LSBR),gr10
+	sethi.p		%hi(__400_LCR),gr11
+	setlo		%lo(__400_LCR),gr11
+
+	# set the bus controller
+	ldi		@(gr4,#0),gr5
+	ori		gr5,#0xff,gr5		; make sure all chip-selects are enabled
+	sti		gr5,@(gr4,#0)
+
+	sethi.p		%hi(__region_CS1),gr4
+	setlo		%lo(__region_CS1),gr4
+	sethi.p		%hi(__region_CS1_M),gr5
+	setlo		%lo(__region_CS1_M),gr5
+	sethi.p		%hi(__region_CS1_C),gr6
+	setlo		%lo(__region_CS1_C),gr6
+	sti		gr4,@(gr10,#1*0x08)
+	sti		gr5,@(gr10,#1*0x08+0x100)
+	sti		gr6,@(gr11,#1*0x08)
+	sethi.p		%hi(__region_CS2),gr4
+	setlo		%lo(__region_CS2),gr4
+	sethi.p		%hi(__region_CS2_M),gr5
+	setlo		%lo(__region_CS2_M),gr5
+	sethi.p		%hi(__region_CS2_C),gr6
+	setlo		%lo(__region_CS2_C),gr6
+	sti		gr4,@(gr10,#2*0x08)
+	sti		gr5,@(gr10,#2*0x08+0x100)
+	sti		gr6,@(gr11,#2*0x08)
+	sethi.p		%hi(__region_CS3),gr4
+	setlo		%lo(__region_CS3),gr4
+	sethi.p		%hi(__region_CS3_M),gr5
+	setlo		%lo(__region_CS3_M),gr5
+	sethi.p		%hi(__region_CS3_C),gr6
+	setlo		%lo(__region_CS3_C),gr6
+	sti		gr4,@(gr10,#3*0x08)
+	sti		gr5,@(gr10,#3*0x08+0x100)
+	sti		gr6,@(gr11,#3*0x08)
+	sethi.p		%hi(__region_CS4),gr4
+	setlo		%lo(__region_CS4),gr4
+	sethi.p		%hi(__region_CS4_M),gr5
+	setlo		%lo(__region_CS4_M),gr5
+	sethi.p		%hi(__region_CS4_C),gr6
+	setlo		%lo(__region_CS4_C),gr6
+	sti		gr4,@(gr10,#4*0x08)
+	sti		gr5,@(gr10,#4*0x08+0x100)
+	sti		gr6,@(gr11,#4*0x08)
+	sethi.p		%hi(__region_CS5),gr4
+	setlo		%lo(__region_CS5),gr4
+	sethi.p		%hi(__region_CS5_M),gr5
+	setlo		%lo(__region_CS5_M),gr5
+	sethi.p		%hi(__region_CS5_C),gr6
+	setlo		%lo(__region_CS5_C),gr6
+	sti		gr4,@(gr10,#5*0x08)
+	sti		gr5,@(gr10,#5*0x08+0x100)
+	sti		gr6,@(gr11,#5*0x08)
+	sethi.p		%hi(__region_CS6),gr4
+	setlo		%lo(__region_CS6),gr4
+	sethi.p		%hi(__region_CS6_M),gr5
+	setlo		%lo(__region_CS6_M),gr5
+	sethi.p		%hi(__region_CS6_C),gr6
+	setlo		%lo(__region_CS6_C),gr6
+	sti		gr4,@(gr10,#6*0x08)
+	sti		gr5,@(gr10,#6*0x08+0x100)
+	sti		gr6,@(gr11,#6*0x08)
+	sethi.p		%hi(__region_CS7),gr4
+	setlo		%lo(__region_CS7),gr4
+	sethi.p		%hi(__region_CS7_M),gr5
+	setlo		%lo(__region_CS7_M),gr5
+	sethi.p		%hi(__region_CS7_C),gr6
+	setlo		%lo(__region_CS7_C),gr6
+	sti		gr4,@(gr10,#7*0x08)
+	sti		gr5,@(gr10,#7*0x08+0x100)
+	sti		gr6,@(gr11,#7*0x08)
+	membar
+	bar
+
+	# adjust LED bank address
+	sethi.p		%hi(LED_ADDR - 0x20000000 +__region_CS2),gr30
+	setlo		%lo(LED_ADDR - 0x20000000 +__region_CS2),gr30
+	bralr
+
+###############################################################################
+#
+# determine the total SDRAM size
+#
+#	ENTRY:			EXIT:
+# GR25	-			SDRAM size
+# GR26	&__head_reference	[saved]
+# GR30	LED address		[saved]
+#
+###############################################################################
+	.globl		__head_fr401_survey_sdram
+__head_fr401_survey_sdram:
+	sethi.p		%hi(__400_DAM0),gr11
+	setlo		%lo(__400_DAM0),gr11
+	sethi.p		%hi(__400_DBR0),gr12
+	setlo		%lo(__400_DBR0),gr12
+
+	sethi.p		%hi(0xfe000000),gr17		; unused SDRAM DBR value
+	setlo		%lo(0xfe000000),gr17
+	setlos		#0,gr25
+
+	ldi		@(gr12,#0x00),gr4		; DAR0
+	subcc		gr4,gr17,gr0,icc0
+	beq		icc0,#0,__head_no_DCS0
+	ldi		@(gr11,#0x00),gr6		; DAM0: bits 31:20 match addr 31:20
+	add		gr25,gr6,gr25
+	addi		gr25,#1,gr25
+__head_no_DCS0:
+
+	ldi		@(gr12,#0x08),gr4		; DAR1
+	subcc		gr4,gr17,gr0,icc0
+	beq		icc0,#0,__head_no_DCS1
+	ldi		@(gr11,#0x08),gr6		; DAM1: bits 31:20 match addr 31:20
+	add		gr25,gr6,gr25
+	addi		gr25,#1,gr25
+__head_no_DCS1:
+
+	# FR401/FR401A does not have DCS2/3
+	movsg		psr,gr3
+	srli		gr3,#25,gr3
+	subicc		gr3,#0x20>>1,gr0,icc0
+	beq		icc0,#0,__head_no_DCS3
+
+	ldi		@(gr12,#0x10),gr4		; DAR2
+	subcc		gr4,gr17,gr0,icc0
+	beq		icc0,#0,__head_no_DCS2
+	ldi		@(gr11,#0x10),gr6		; DAM2: bits 31:20 match addr 31:20
+	add		gr25,gr6,gr25
+	addi		gr25,#1,gr25
+__head_no_DCS2:
+
+	ldi		@(gr12,#0x18),gr4		; DAR3
+	subcc		gr4,gr17,gr0,icc0
+	beq		icc0,#0,__head_no_DCS3
+	ldi		@(gr11,#0x18),gr6		; DAM3: bits 31:20 match addr 31:20
+	add		gr25,gr6,gr25
+	addi		gr25,#1,gr25
+__head_no_DCS3:
+	bralr
+
+###############################################################################
+#
+# set the protection map with the I/DAMPR registers
+#
+#	ENTRY:			EXIT:
+# GR25	SDRAM size		[saved]
+# GR26	&__head_reference	[saved]
+# GR30	LED address		[saved]
+#
+###############################################################################
+	.globl		__head_fr401_set_protection
+__head_fr401_set_protection:
+	movsg		lr,gr27
+
+	# set the I/O region protection registers for FR401/3/5
+	sethi.p		%hi(__region_IO),gr5
+	setlo		%lo(__region_IO),gr5
+	ori		gr5,#xAMPRx_SS_512Mb|xAMPRx_S_KERNEL|xAMPRx_C|xAMPRx_V,gr5
+	movgs		gr0,iampr7
+	movgs		gr5,dampr7			; General I/O tile
+
+	# need to tile the remaining IAMPR/DAMPR registers to cover as much of the RAM as possible
+	# - start with the highest numbered registers
+	sethi.p		%hi(__kernel_image_end),gr8
+	setlo		%lo(__kernel_image_end),gr8
+	sethi.p		%hi(32768),gr4			; allow for a maximal allocator bitmap
+	setlo		%lo(32768),gr4
+	add		gr8,gr4,gr8
+	sethi.p		%hi(1024*2048-1),gr4		; round up to nearest 2MiB
+	setlo		%lo(1024*2048-1),gr4
+	add.p		gr8,gr4,gr8
+	not		gr4,gr4
+	and		gr8,gr4,gr8
+
+	sethi.p		%hi(__page_offset),gr9
+	setlo		%lo(__page_offset),gr9
+	add		gr9,gr25,gr9
+
+	# GR8 = base of uncovered RAM
+	# GR9 = top of uncovered RAM
+
+#ifdef CONFIG_MB93093_PDK
+	sethi.p		%hi(__region_CS2),gr4
+	setlo		%lo(__region_CS2),gr4
+	ori		gr4,#xAMPRx_SS_1Mb|xAMPRx_S_KERNEL|xAMPRx_C|xAMPRx_V,gr4
+	movgs		gr4,dampr6
+	movgs		gr0,iampr6
+#else
+	call		__head_split_region
+	movgs		gr4,iampr6
+	movgs		gr5,dampr6
+#endif
+	call		__head_split_region
+	movgs		gr4,iampr5
+	movgs		gr5,dampr5
+	call		__head_split_region
+	movgs		gr4,iampr4
+	movgs		gr5,dampr4
+	call		__head_split_region
+	movgs		gr4,iampr3
+	movgs		gr5,dampr3
+	call		__head_split_region
+	movgs		gr4,iampr2
+	movgs		gr5,dampr2
+	call		__head_split_region
+	movgs		gr4,iampr1
+	movgs		gr5,dampr1
+
+	# cover kernel core image with kernel-only segment
+	sethi.p		%hi(__page_offset),gr8
+	setlo		%lo(__page_offset),gr8
+	call		__head_split_region
+
+#ifdef CONFIG_PROTECT_KERNEL
+	ori.p		gr4,#xAMPRx_S_KERNEL,gr4
+	ori		gr5,#xAMPRx_S_KERNEL,gr5
+#endif
+
+	movgs		gr4,iampr0
+	movgs		gr5,dampr0
+	jmpl		@(gr27,gr0)
diff --git a/arch/frv/kernel/head-uc-fr451.S b/arch/frv/kernel/head-uc-fr451.S
new file mode 100644
index 0000000..31cb54a
--- /dev/null
+++ b/arch/frv/kernel/head-uc-fr451.S
@@ -0,0 +1,174 @@
+/* head-uc-fr451.S: FR451 uc-linux specific bits of initialisation
+ *
+ * Copyright (C) 2004 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 License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/config.h>
+#include <linux/threads.h>
+#include <linux/linkage.h>
+#include <asm/ptrace.h>
+#include <asm/page.h>
+#include <asm/spr-regs.h>
+#include <asm/mb86943a.h>
+#include "head.inc"
+
+
+#define __400_DBR0	0xfe000e00
+#define __400_DBR1	0xfe000e08
+#define __400_DBR2	0xfe000e10
+#define __400_DBR3	0xfe000e18
+#define __400_DAM0	0xfe000f00
+#define __400_DAM1	0xfe000f08
+#define __400_DAM2	0xfe000f10
+#define __400_DAM3	0xfe000f18
+#define __400_LGCR	0xfe000010
+#define __400_LCR	0xfe000100
+#define __400_LSBR	0xfe000c00
+
+	.section	.text.init,"ax"
+	.balign		4
+
+###############################################################################
+#
+# set the protection map with the I/DAMPR registers
+#
+#	ENTRY:			EXIT:
+# GR25	SDRAM size		[saved]
+# GR26	&__head_reference	[saved]
+# GR30	LED address		[saved]
+#
+###############################################################################
+	.globl		__head_fr451_set_protection
+__head_fr451_set_protection:
+	movsg		lr,gr27
+
+	movgs		gr0,dampr10
+	movgs		gr0,damlr10
+	movgs		gr0,dampr9
+	movgs		gr0,damlr9
+	movgs		gr0,dampr8
+	movgs		gr0,damlr8
+
+	# set the I/O region protection registers for FR401/3/5
+	sethi.p		%hi(__region_IO),gr5
+	setlo		%lo(__region_IO),gr5
+	sethi.p		%hi(0x1fffffff),gr7
+	setlo		%lo(0x1fffffff),gr7
+	ori		gr5,#xAMPRx_SS_512Mb|xAMPRx_S_KERNEL|xAMPRx_C|xAMPRx_V,gr5
+	movgs		gr5,dampr11			; General I/O tile
+	movgs		gr7,damlr11
+
+	# need to tile the remaining IAMPR/DAMPR registers to cover as much of the RAM as possible
+	# - start with the highest numbered registers
+	sethi.p		%hi(__kernel_image_end),gr8
+	setlo		%lo(__kernel_image_end),gr8
+	sethi.p		%hi(32768),gr4			; allow for a maximal allocator bitmap
+	setlo		%lo(32768),gr4
+	add		gr8,gr4,gr8
+	sethi.p		%hi(1024*2048-1),gr4		; round up to nearest 2MiB
+	setlo		%lo(1024*2048-1),gr4
+	add.p		gr8,gr4,gr8
+	not		gr4,gr4
+	and		gr8,gr4,gr8
+
+	sethi.p		%hi(__page_offset),gr9
+	setlo		%lo(__page_offset),gr9
+	add		gr9,gr25,gr9
+
+	sethi.p		%hi(0xffffc000),gr11
+	setlo		%lo(0xffffc000),gr11
+
+	# GR8 = base of uncovered RAM
+	# GR9 = top of uncovered RAM
+	# GR11 = xAMLR mask
+	LEDS		0x3317
+	call		__head_split_region
+	movgs		gr4,iampr7
+	movgs		gr6,iamlr7
+	movgs		gr5,dampr7
+	movgs		gr7,damlr7
+
+	LEDS		0x3316
+	call		__head_split_region
+	movgs		gr4,iampr6
+	movgs		gr6,iamlr6
+	movgs		gr5,dampr6
+	movgs		gr7,damlr6
+
+	LEDS		0x3315
+	call		__head_split_region
+	movgs		gr4,iampr5
+	movgs		gr6,iamlr5
+	movgs		gr5,dampr5
+	movgs		gr7,damlr5
+
+	LEDS		0x3314
+	call		__head_split_region
+	movgs		gr4,iampr4
+	movgs		gr6,iamlr4
+	movgs		gr5,dampr4
+	movgs		gr7,damlr4
+
+	LEDS		0x3313
+	call		__head_split_region
+	movgs		gr4,iampr3
+	movgs		gr6,iamlr3
+	movgs		gr5,dampr3
+	movgs		gr7,damlr3
+
+	LEDS		0x3312
+	call		__head_split_region
+	movgs		gr4,iampr2
+	movgs		gr6,iamlr2
+	movgs		gr5,dampr2
+	movgs		gr7,damlr2
+
+	LEDS		0x3311
+	call		__head_split_region
+	movgs		gr4,iampr1
+	movgs		gr6,iamlr1
+	movgs		gr5,dampr1
+	movgs		gr7,damlr1
+
+	# cover kernel core image with kernel-only segment
+	LEDS		0x3310
+	sethi.p		%hi(__page_offset),gr8
+	setlo		%lo(__page_offset),gr8
+	call		__head_split_region
+
+#ifdef CONFIG_PROTECT_KERNEL
+	ori.p		gr4,#xAMPRx_S_KERNEL,gr4
+	ori		gr5,#xAMPRx_S_KERNEL,gr5
+#endif
+
+	movgs		gr4,iampr0
+	movgs		gr6,iamlr0
+	movgs		gr5,dampr0
+	movgs		gr7,damlr0
+
+	# start in TLB context 0 with no page tables
+	movgs		gr0,cxnr
+	movgs		gr0,ttbr
+
+	# the FR451 also has an extra trap base register
+	movsg		tbr,gr4
+	movgs		gr4,btbr
+
+	# turn on the timers as appropriate
+	movgs		gr0,timerh
+	movgs		gr0,timerl
+	movgs		gr0,timerd
+	movsg		hsr0,gr4
+	sethi.p		%hi(HSR0_ETMI),gr5
+	setlo		%lo(HSR0_ETMI),gr5
+	or		gr4,gr5,gr4
+	movgs		gr4,hsr0
+
+	LEDS		0x3300
+	jmpl		@(gr27,gr0)
diff --git a/arch/frv/kernel/head-uc-fr555.S b/arch/frv/kernel/head-uc-fr555.S
new file mode 100644
index 0000000..d088db2
--- /dev/null
+++ b/arch/frv/kernel/head-uc-fr555.S
@@ -0,0 +1,347 @@
+/* head-uc-fr555.S: FR555 uc-linux specific bits of initialisation
+ *
+ * Copyright (C) 2004 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 License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/config.h>
+#include <linux/threads.h>
+#include <linux/linkage.h>
+#include <asm/ptrace.h>
+#include <asm/page.h>
+#include <asm/spr-regs.h>
+#include <asm/mb86943a.h>
+#include "head.inc"
+
+
+#define __551_DARS0	0xfeff0100
+#define __551_DARS1	0xfeff0104
+#define __551_DARS2	0xfeff0108
+#define __551_DARS3	0xfeff010c
+#define __551_DAMK0	0xfeff0110
+#define __551_DAMK1	0xfeff0114
+#define __551_DAMK2	0xfeff0118
+#define __551_DAMK3	0xfeff011c
+#define __551_LCR	0xfeff1100
+#define __551_LSBR	0xfeff1c00
+
+	.section	.text.init,"ax"
+	.balign		4
+
+###############################################################################
+#
+# describe the position and layout of the SDRAM controller registers
+#
+#	ENTRY:			EXIT:
+# GR5	-			cacheline size
+# GR11	-			displacement of 2nd SDRAM addr reg from GR14
+# GR12	-			displacement of 3rd SDRAM addr reg from GR14
+# GR13	-			displacement of 4th SDRAM addr reg from GR14
+# GR14	-			address of 1st SDRAM addr reg
+# GR15	-			amount to shift address by to match SDRAM addr reg
+# GR26	&__head_reference	[saved]
+# GR30	LED address		[saved]
+# CC0	-			T if DARS0 is present
+# CC1	-			T if DARS1 is present
+# CC2	-			T if DARS2 is present
+# CC3	-			T if DARS3 is present
+#
+###############################################################################
+	.globl		__head_fr555_describe_sdram
+__head_fr555_describe_sdram:
+	sethi.p		%hi(__551_DARS0),gr14
+	setlo		%lo(__551_DARS0),gr14
+	setlos.p	#__551_DARS1-__551_DARS0,gr11
+	setlos		#__551_DARS2-__551_DARS0,gr12
+	setlos.p	#__551_DARS3-__551_DARS0,gr13
+	setlos		#64,gr5			; cacheline size
+	setlos		#20,gr15		; amount to shift addr by
+	setlos		#0x00ff,gr4
+	movgs		gr4,cccr		; extant DARS/DAMK regs
+	bralr
+
+###############################################################################
+#
+# rearrange the bus controller registers
+#
+#	ENTRY:			EXIT:
+# GR26	&__head_reference	[saved]
+# GR30	LED address		revised LED address
+#
+###############################################################################
+	.globl		__head_fr555_set_busctl
+__head_fr555_set_busctl:
+	LEDS		0x100f
+	sethi.p		%hi(__551_LSBR),gr10
+	setlo		%lo(__551_LSBR),gr10
+	sethi.p		%hi(__551_LCR),gr11
+	setlo		%lo(__551_LCR),gr11
+
+	# set the bus controller
+	sethi.p		%hi(__region_CS1),gr4
+	setlo		%lo(__region_CS1),gr4
+	sethi.p		%hi(__region_CS1_M),gr5
+	setlo		%lo(__region_CS1_M),gr5
+	sethi.p		%hi(__region_CS1_C),gr6
+	setlo		%lo(__region_CS1_C),gr6
+	sti		gr4,@(gr10,#1*0x08)
+	sti		gr5,@(gr10,#1*0x08+0x100)
+	sti		gr6,@(gr11,#1*0x08)
+	sethi.p		%hi(__region_CS2),gr4
+	setlo		%lo(__region_CS2),gr4
+	sethi.p		%hi(__region_CS2_M),gr5
+	setlo		%lo(__region_CS2_M),gr5
+	sethi.p		%hi(__region_CS2_C),gr6
+	setlo		%lo(__region_CS2_C),gr6
+	sti		gr4,@(gr10,#2*0x08)
+	sti		gr5,@(gr10,#2*0x08+0x100)
+	sti		gr6,@(gr11,#2*0x08)
+	sethi.p		%hi(__region_CS3),gr4
+	setlo		%lo(__region_CS3),gr4
+	sethi.p		%hi(__region_CS3_M),gr5
+	setlo		%lo(__region_CS3_M),gr5
+	sethi.p		%hi(__region_CS3_C),gr6
+	setlo		%lo(__region_CS3_C),gr6
+	sti		gr4,@(gr10,#3*0x08)
+	sti		gr5,@(gr10,#3*0x08+0x100)
+	sti		gr6,@(gr11,#3*0x08)
+	sethi.p		%hi(__region_CS4),gr4
+	setlo		%lo(__region_CS4),gr4
+	sethi.p		%hi(__region_CS4_M),gr5
+	setlo		%lo(__region_CS4_M),gr5
+	sethi.p		%hi(__region_CS4_C),gr6
+	setlo		%lo(__region_CS4_C),gr6
+	sti		gr4,@(gr10,#4*0x08)
+	sti		gr5,@(gr10,#4*0x08+0x100)
+	sti		gr6,@(gr11,#4*0x08)
+	sethi.p		%hi(__region_CS5),gr4
+	setlo		%lo(__region_CS5),gr4
+	sethi.p		%hi(__region_CS5_M),gr5
+	setlo		%lo(__region_CS5_M),gr5
+	sethi.p		%hi(__region_CS5_C),gr6
+	setlo		%lo(__region_CS5_C),gr6
+	sti		gr4,@(gr10,#5*0x08)
+	sti		gr5,@(gr10,#5*0x08+0x100)
+	sti		gr6,@(gr11,#5*0x08)
+	sethi.p		%hi(__region_CS6),gr4
+	setlo		%lo(__region_CS6),gr4
+	sethi.p		%hi(__region_CS6_M),gr5
+	setlo		%lo(__region_CS6_M),gr5
+	sethi.p		%hi(__region_CS6_C),gr6
+	setlo		%lo(__region_CS6_C),gr6
+	sti		gr4,@(gr10,#6*0x08)
+	sti		gr5,@(gr10,#6*0x08+0x100)
+	sti		gr6,@(gr11,#6*0x08)
+	sethi.p		%hi(__region_CS7),gr4
+	setlo		%lo(__region_CS7),gr4
+	sethi.p		%hi(__region_CS7_M),gr5
+	setlo		%lo(__region_CS7_M),gr5
+	sethi.p		%hi(__region_CS7_C),gr6
+	setlo		%lo(__region_CS7_C),gr6
+	sti		gr4,@(gr10,#7*0x08)
+	sti		gr5,@(gr10,#7*0x08+0x100)
+	sti		gr6,@(gr11,#7*0x08)
+	membar
+	bar
+
+	# adjust LED bank address
+#ifdef CONFIG_MB93091_VDK
+	sethi.p		%hi(LED_ADDR - 0x20000000 +__region_CS2),gr30
+	setlo		%lo(LED_ADDR - 0x20000000 +__region_CS2),gr30
+#endif
+	bralr
+
+###############################################################################
+#
+# determine the total SDRAM size
+#
+#	ENTRY:			EXIT:
+# GR25	-			SDRAM size
+# GR26	&__head_reference	[saved]
+# GR30	LED address		[saved]
+#
+###############################################################################
+	.globl		__head_fr555_survey_sdram
+__head_fr555_survey_sdram:
+	sethi.p		%hi(__551_DAMK0),gr11
+	setlo		%lo(__551_DAMK0),gr11
+	sethi.p		%hi(__551_DARS0),gr12
+	setlo		%lo(__551_DARS0),gr12
+
+	sethi.p		%hi(0xfff),gr17			; unused SDRAM AMK value
+	setlo		%lo(0xfff),gr17
+	setlos		#0,gr25
+
+	ldi		@(gr11,#0x00),gr6		; DAMK0: bits 11:0 match addr 11:0
+	subcc		gr6,gr17,gr0,icc0
+	beq		icc0,#0,__head_no_DCS0
+	ldi		@(gr12,#0x00),gr4		; DARS0
+	add		gr25,gr6,gr25
+	addi		gr25,#1,gr25
+__head_no_DCS0:
+
+	ldi		@(gr11,#0x04),gr6		; DAMK1: bits 11:0 match addr 11:0
+	subcc		gr6,gr17,gr0,icc0
+	beq		icc0,#0,__head_no_DCS1
+	ldi		@(gr12,#0x04),gr4		; DARS1
+	add		gr25,gr6,gr25
+	addi		gr25,#1,gr25
+__head_no_DCS1:
+
+	ldi		@(gr11,#0x8),gr6		; DAMK2: bits 11:0 match addr 11:0
+	subcc		gr6,gr17,gr0,icc0
+	beq		icc0,#0,__head_no_DCS2
+	ldi		@(gr12,#0x8),gr4		; DARS2
+	add		gr25,gr6,gr25
+	addi		gr25,#1,gr25
+__head_no_DCS2:
+
+	ldi		@(gr11,#0xc),gr6		; DAMK3: bits 11:0 match addr 11:0
+	subcc		gr6,gr17,gr0,icc0
+	beq		icc0,#0,__head_no_DCS3
+	ldi		@(gr12,#0xc),gr4		; DARS3
+	add		gr25,gr6,gr25
+	addi		gr25,#1,gr25
+__head_no_DCS3:
+
+	slli		gr25,#20,gr25			; shift [11:0] -> [31:20]
+	bralr
+
+###############################################################################
+#
+# set the protection map with the I/DAMPR registers
+#
+#	ENTRY:			EXIT:
+# GR25	SDRAM size		saved
+# GR30	LED address		saved
+#
+###############################################################################
+	.globl		__head_fr555_set_protection
+__head_fr555_set_protection:
+	movsg		lr,gr27
+
+	sethi.p		%hi(0xfff00000),gr11
+	setlo		%lo(0xfff00000),gr11
+
+	# set the I/O region protection registers for FR555
+	sethi.p		%hi(__region_IO),gr7
+	setlo		%lo(__region_IO),gr7
+	ori		gr7,#xAMPRx_SS_512Mb|xAMPRx_S_KERNEL|xAMPRx_C|xAMPRx_V,gr5
+	movgs		gr0,iampr15
+	movgs		gr0,iamlr15
+	movgs		gr5,dampr15
+	movgs		gr7,damlr15
+
+	# need to tile the remaining IAMPR/DAMPR registers to cover as much of the RAM as possible
+	# - start with the highest numbered registers
+	sethi.p		%hi(__kernel_image_end),gr8
+	setlo		%lo(__kernel_image_end),gr8
+	sethi.p		%hi(32768),gr4			; allow for a maximal allocator bitmap
+	setlo		%lo(32768),gr4
+	add		gr8,gr4,gr8
+	sethi.p		%hi(1024*2048-1),gr4		; round up to nearest 2MiB
+	setlo		%lo(1024*2048-1),gr4
+	add.p		gr8,gr4,gr8
+	not		gr4,gr4
+	and		gr8,gr4,gr8
+
+	sethi.p		%hi(__page_offset),gr9
+	setlo		%lo(__page_offset),gr9
+	add		gr9,gr25,gr9
+
+	# GR8 = base of uncovered RAM
+	# GR9 = top of uncovered RAM
+	# GR11 - mask for DAMLR/IAMLR regs
+	#
+	call		__head_split_region
+	movgs		gr4,iampr14
+	movgs		gr6,iamlr14
+	movgs		gr5,dampr14
+	movgs		gr7,damlr14
+	call		__head_split_region
+	movgs		gr4,iampr13
+	movgs		gr6,iamlr13
+	movgs		gr5,dampr13
+	movgs		gr7,damlr13
+	call		__head_split_region
+	movgs		gr4,iampr12
+	movgs		gr6,iamlr12
+	movgs		gr5,dampr12
+	movgs		gr7,damlr12
+	call		__head_split_region
+	movgs		gr4,iampr11
+	movgs		gr6,iamlr11
+	movgs		gr5,dampr11
+	movgs		gr7,damlr11
+	call		__head_split_region
+	movgs		gr4,iampr10
+	movgs		gr6,iamlr10
+	movgs		gr5,dampr10
+	movgs		gr7,damlr10
+	call		__head_split_region
+	movgs		gr4,iampr9
+	movgs		gr6,iamlr9
+	movgs		gr5,dampr9
+	movgs		gr7,damlr9
+	call		__head_split_region
+	movgs		gr4,iampr8
+	movgs		gr6,iamlr8
+	movgs		gr5,dampr8
+	movgs		gr7,damlr8
+
+	call		__head_split_region
+	movgs		gr4,iampr7
+	movgs		gr6,iamlr7
+	movgs		gr5,dampr7
+	movgs		gr7,damlr7
+	call		__head_split_region
+	movgs		gr4,iampr6
+	movgs		gr6,iamlr6
+	movgs		gr5,dampr6
+	movgs		gr7,damlr6
+	call		__head_split_region
+	movgs		gr4,iampr5
+	movgs		gr6,iamlr5
+	movgs		gr5,dampr5
+	movgs		gr7,damlr5
+	call		__head_split_region
+	movgs		gr4,iampr4
+	movgs		gr6,iamlr4
+	movgs		gr5,dampr4
+	movgs		gr7,damlr4
+	call		__head_split_region
+	movgs		gr4,iampr3
+	movgs		gr6,iamlr3
+	movgs		gr5,dampr3
+	movgs		gr7,damlr3
+	call		__head_split_region
+	movgs		gr4,iampr2
+	movgs		gr6,iamlr2
+	movgs		gr5,dampr2
+	movgs		gr7,damlr2
+	call		__head_split_region
+	movgs		gr4,iampr1
+	movgs		gr6,iamlr1
+	movgs		gr5,dampr1
+	movgs		gr7,damlr1
+
+	# cover kernel core image with kernel-only segment
+	sethi.p		%hi(__page_offset),gr8
+	setlo		%lo(__page_offset),gr8
+	call		__head_split_region
+
+#ifdef CONFIG_PROTECT_KERNEL
+	ori.p		gr4,#xAMPRx_S_KERNEL,gr4
+	ori		gr5,#xAMPRx_S_KERNEL,gr5
+#endif
+
+	movgs		gr4,iampr0
+	movgs		gr6,iamlr0
+	movgs		gr5,dampr0
+	movgs		gr7,damlr0
+	jmpl		@(gr27,gr0)
diff --git a/arch/frv/kernel/head.S b/arch/frv/kernel/head.S
new file mode 100644
index 0000000..c73b4fe
--- /dev/null
+++ b/arch/frv/kernel/head.S
@@ -0,0 +1,639 @@
+/* head.S: kernel entry point for FR-V kernel
+ *
+ * Copyright (C) 2003, 2004 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 License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/config.h>
+#include <linux/threads.h>
+#include <linux/linkage.h>
+#include <asm/ptrace.h>
+#include <asm/page.h>
+#include <asm/spr-regs.h>
+#include <asm/mb86943a.h>
+#include <asm/cache.h>
+#include "head.inc"
+
+###############################################################################
+#
+# void _boot(unsigned long magic, char *command_line) __attribute__((noreturn))
+#
+# - if magic is 0xdead1eaf, then command_line is assumed to point to the kernel
+#   command line string
+#
+###############################################################################
+	.section	.text.head,"ax"
+	.balign		4
+
+	.globl		_boot, __head_reference
+        .type		_boot,@function
+_boot:
+__head_reference:
+	sethi.p		%hi(LED_ADDR),gr30
+	setlo		%lo(LED_ADDR),gr30
+
+	LEDS		0x0000
+
+	# calculate reference address for PC-relative stuff
+	call		0f
+0:	movsg		lr,gr26
+	addi		gr26,#__head_reference-0b,gr26
+
+	# invalidate and disable both of the caches and turn off the memory access checking
+	dcef		@(gr0,gr0),1
+	bar
+
+	sethi.p		%hi(~(HSR0_ICE|HSR0_DCE|HSR0_CBM|HSR0_EIMMU|HSR0_EDMMU)),gr4
+	setlo		%lo(~(HSR0_ICE|HSR0_DCE|HSR0_CBM|HSR0_EIMMU|HSR0_EDMMU)),gr4
+	movsg		hsr0,gr5
+	and		gr4,gr5,gr5
+	movgs		gr5,hsr0
+	movsg		hsr0,gr5
+
+	LEDS		0x0001
+
+	icei		@(gr0,gr0),1
+	dcei		@(gr0,gr0),1
+	bar
+
+	# turn the instruction cache back on
+	sethi.p		%hi(HSR0_ICE),gr4
+	setlo		%lo(HSR0_ICE),gr4
+	movsg		hsr0,gr5
+	or		gr4,gr5,gr5
+	movgs		gr5,hsr0
+	movsg		hsr0,gr5
+
+	bar
+
+	LEDS		0x0002
+
+	# retrieve the parameters (including command line) before we overwrite them
+	sethi.p		%hi(0xdead1eaf),gr7
+	setlo		%lo(0xdead1eaf),gr7
+	subcc		gr7,gr8,gr0,icc0
+	bne		icc0,#0,__head_no_parameters
+
+	sethi.p		%hi(redboot_command_line-1),gr6
+	setlo		%lo(redboot_command_line-1),gr6
+	sethi.p		%hi(__head_reference),gr4
+	setlo		%lo(__head_reference),gr4
+	sub		gr6,gr4,gr6
+	add.p		gr6,gr26,gr6
+	subi		gr9,#1,gr9
+	setlos.p	#511,gr4
+	setlos		#1,gr5
+
+__head_copy_cmdline:
+	ldubu.p		@(gr9,gr5),gr16
+	subicc		gr4,#1,gr4,icc0
+	stbu.p		gr16,@(gr6,gr5)
+	subicc		gr16,#0,gr0,icc1
+	bls		icc0,#0,__head_end_cmdline
+	bne		icc1,#1,__head_copy_cmdline
+__head_end_cmdline:
+	stbu		gr0,@(gr6,gr5)
+__head_no_parameters:
+
+###############################################################################
+#
+# we need to relocate the SDRAM to 0x00000000 (linux) or 0xC0000000 (uClinux)
+# - note that we're going to have to run entirely out of the icache whilst
+#   fiddling with the SDRAM controller registers
+#
+###############################################################################
+#ifdef CONFIG_MMU
+	call		__head_fr451_describe_sdram
+
+#else
+	movsg		psr,gr5
+	srli		gr5,#28,gr5
+	subicc		gr5,#3,gr0,icc0
+	beq		icc0,#0,__head_fr551_sdram
+
+	call		__head_fr401_describe_sdram
+	bra		__head_do_sdram
+
+__head_fr551_sdram:
+	call		__head_fr555_describe_sdram
+	LEDS		0x000d
+
+__head_do_sdram:
+#endif
+
+	# preload the registers with invalid values in case any DBR/DARS are marked not present
+	sethi.p		%hi(0xfe000000),gr17		; unused SDRAM DBR value
+	setlo		%lo(0xfe000000),gr17
+	or.p		gr17,gr0,gr20
+	or		gr17,gr0,gr21
+	or.p		gr17,gr0,gr22
+	or		gr17,gr0,gr23
+
+	# consult the SDRAM controller CS address registers
+	cld		@(gr14,gr0 ),gr20,	cc0,#1	; DBR0 / DARS0
+	cld		@(gr14,gr11),gr21,	cc1,#1	; DBR1 / DARS1
+	cld		@(gr14,gr12),gr22,	cc2,#1	; DBR2 / DARS2
+	cld.p		@(gr14,gr13),gr23,	cc3,#1	; DBR3 / DARS3
+
+	sll		gr20,gr15,gr20			; shift values up for FR551
+	sll		gr21,gr15,gr21
+	sll		gr22,gr15,gr22
+	sll		gr23,gr15,gr23
+
+	LEDS		0x0003
+
+	# assume the lowest valid CS line to be the SDRAM base and get its address
+	subcc		gr20,gr17,gr0,icc0
+	subcc.p		gr21,gr17,gr0,icc1
+	subcc		gr22,gr17,gr0,icc2
+	subcc.p		gr23,gr17,gr0,icc3
+	ckne		icc0,cc4			; T if DBR0 != 0xfe000000
+	ckne		icc1,cc5
+	ckne		icc2,cc6
+	ckne		icc3,cc7
+	cor		gr23,gr0,gr24,		cc7,#1	; GR24 = SDRAM base
+	cor		gr22,gr0,gr24,		cc6,#1
+	cor		gr21,gr0,gr24,		cc5,#1
+	cor		gr20,gr0,gr24,		cc4,#1
+
+	# calculate the displacement required to get the SDRAM into the right place in memory
+	sethi.p		%hi(__sdram_base),gr16
+	setlo		%lo(__sdram_base),gr16
+	sub		gr16,gr24,gr16			; delta = __sdram_base - DBRx
+
+	# calculate the new values to go in the controller regs
+	cadd.p		gr20,gr16,gr20,		cc4,#1	; DCS#0 (new) = DCS#0 (old) + delta
+	cadd		gr21,gr16,gr21,		cc5,#1
+	cadd.p		gr22,gr16,gr22,		cc6,#1
+	cadd		gr23,gr16,gr23,		cc7,#1
+
+	srl		gr20,gr15,gr20			; shift values down for FR551
+	srl		gr21,gr15,gr21
+	srl		gr22,gr15,gr22
+	srl		gr23,gr15,gr23
+
+	# work out the address at which the reg updater resides and lock it into icache
+	# also work out the address the updater will jump to when finished
+	sethi.p		%hi(__head_move_sdram-__head_reference),gr18
+	setlo		%lo(__head_move_sdram-__head_reference),gr18
+	sethi.p		%hi(__head_sdram_moved-__head_reference),gr19
+	setlo		%lo(__head_sdram_moved-__head_reference),gr19
+	add.p		gr18,gr26,gr18
+	add		gr19,gr26,gr19
+	add.p		gr19,gr16,gr19			; moved = addr + (__sdram_base - DBRx)
+	add		gr18,gr5,gr4			; two cachelines probably required
+
+	icpl		gr18,gr0,#1			; load and lock the cachelines
+	icpl		gr4,gr0,#1
+	LEDS		0x0004
+	membar
+	bar
+	jmpl		@(gr18,gr0)
+
+	.balign		L1_CACHE_BYTES
+__head_move_sdram:
+	cst		gr20,@(gr14,gr0 ),	cc4,#1
+	cst		gr21,@(gr14,gr11),	cc5,#1
+	cst		gr22,@(gr14,gr12),	cc6,#1
+	cst		gr23,@(gr14,gr13),	cc7,#1
+	cld		@(gr14,gr0 ),gr20,	cc4,#1
+	cld		@(gr14,gr11),gr21,	cc5,#1
+	cld		@(gr14,gr12),gr22,	cc4,#1
+	cld		@(gr14,gr13),gr23,	cc7,#1
+	bar
+	membar
+	jmpl		@(gr19,gr0)
+
+	.balign		L1_CACHE_BYTES
+__head_sdram_moved:
+	icul		gr18
+	add		gr18,gr5,gr4
+	icul		gr4
+	icei		@(gr0,gr0),1
+	dcei		@(gr0,gr0),1
+
+	LEDS		0x0005
+
+	# recalculate reference address
+	call		0f
+0:	movsg		lr,gr26
+	addi		gr26,#__head_reference-0b,gr26
+
+
+###############################################################################
+#
+# move the kernel image down to the bottom of the SDRAM
+#
+###############################################################################
+	sethi.p		%hi(__kernel_image_size_no_bss+15),gr4
+	setlo		%lo(__kernel_image_size_no_bss+15),gr4
+	srli.p		gr4,#4,gr4			; count
+	or		gr26,gr26,gr16			; source
+
+	sethi.p		%hi(__sdram_base),gr17		; destination
+	setlo		%lo(__sdram_base),gr17
+
+	setlos		#8,gr5
+	sub.p		gr16,gr5,gr16			; adjust src for LDDU
+	sub		gr17,gr5,gr17			; adjust dst for LDDU
+
+	sethi.p		%hi(__head_move_kernel-__head_reference),gr18
+	setlo		%lo(__head_move_kernel-__head_reference),gr18
+	sethi.p		%hi(__head_kernel_moved-__head_reference+__sdram_base),gr19
+	setlo		%lo(__head_kernel_moved-__head_reference+__sdram_base),gr19
+	add		gr18,gr26,gr18
+	icpl		gr18,gr0,#1
+	jmpl		@(gr18,gr0)
+
+	.balign		32
+__head_move_kernel:
+	lddu		@(gr16,gr5),gr10
+	lddu		@(gr16,gr5),gr12
+	stdu.p		gr10,@(gr17,gr5)
+	subicc		gr4,#1,gr4,icc0
+	stdu.p		gr12,@(gr17,gr5)
+	bhi		icc0,#0,__head_move_kernel
+	jmpl		@(gr19,gr0)
+
+	.balign		32
+__head_kernel_moved:
+	icul		gr18
+	icei		@(gr0,gr0),1
+	dcei		@(gr0,gr0),1
+
+	LEDS		0x0006
+
+	# recalculate reference address
+	call		0f
+0:	movsg		lr,gr26
+	addi		gr26,#__head_reference-0b,gr26
+
+
+###############################################################################
+#
+# rearrange the iomem map and set the protection registers
+#
+###############################################################################
+
+#ifdef CONFIG_MMU
+	LEDS		0x3301
+	call		__head_fr451_set_busctl
+	LEDS		0x3303
+	call		__head_fr451_survey_sdram
+	LEDS		0x3305
+	call		__head_fr451_set_protection
+
+#else
+	movsg		psr,gr5
+	srli		gr5,#PSR_IMPLE_SHIFT,gr5
+	subicc		gr5,#PSR_IMPLE_FR551,gr0,icc0
+	beq		icc0,#0,__head_fr555_memmap
+	subicc		gr5,#PSR_IMPLE_FR451,gr0,icc0
+	beq		icc0,#0,__head_fr451_memmap
+
+	LEDS		0x3101
+	call		__head_fr401_set_busctl
+	LEDS		0x3103
+	call		__head_fr401_survey_sdram
+	LEDS		0x3105
+	call		__head_fr401_set_protection
+	bra		__head_done_memmap
+
+__head_fr451_memmap:
+	LEDS		0x3301
+	call		__head_fr401_set_busctl
+	LEDS		0x3303
+	call		__head_fr401_survey_sdram
+	LEDS		0x3305
+	call		__head_fr451_set_protection
+	bra		__head_done_memmap
+
+__head_fr555_memmap:
+	LEDS		0x3501
+	call		__head_fr555_set_busctl
+	LEDS		0x3503
+	call		__head_fr555_survey_sdram
+	LEDS		0x3505
+	call		__head_fr555_set_protection
+
+__head_done_memmap:
+#endif
+	LEDS		0x0007
+
+###############################################################################
+#
+# turn the data cache and MMU on
+# - for the FR451 this'll mean that the window through which the kernel is
+#   viewed will change
+#
+###############################################################################
+
+#ifdef CONFIG_MMU
+#define MMUMODE		HSR0_EIMMU|HSR0_EDMMU|HSR0_EXMMU|HSR0_EDAT|HSR0_XEDAT
+#else
+#define MMUMODE		HSR0_EIMMU|HSR0_EDMMU
+#endif
+
+	movsg		hsr0,gr5
+
+	sethi.p		%hi(MMUMODE),gr4
+	setlo		%lo(MMUMODE),gr4
+	or		gr4,gr5,gr5
+
+#if defined(CONFIG_FRV_DEFL_CACHE_WTHRU)
+	sethi.p		%hi(HSR0_DCE|HSR0_CBM_WRITE_THRU),gr4
+	setlo		%lo(HSR0_DCE|HSR0_CBM_WRITE_THRU),gr4
+#elif defined(CONFIG_FRV_DEFL_CACHE_WBACK)
+	sethi.p		%hi(HSR0_DCE|HSR0_CBM_COPY_BACK),gr4
+	setlo		%lo(HSR0_DCE|HSR0_CBM_COPY_BACK),gr4
+#elif defined(CONFIG_FRV_DEFL_CACHE_WBEHIND)
+	sethi.p		%hi(HSR0_DCE|HSR0_CBM_COPY_BACK),gr4
+	setlo		%lo(HSR0_DCE|HSR0_CBM_COPY_BACK),gr4
+
+	movsg		psr,gr6
+	srli		gr6,#24,gr6
+	cmpi		gr6,#0x50,icc0		// FR451
+	beq		icc0,#0,0f
+	cmpi		gr6,#0x40,icc0		// FR405
+	bne		icc0,#0,1f
+0:
+	# turn off write-allocate
+	sethi.p		%hi(HSR0_NWA),gr6
+	setlo		%lo(HSR0_NWA),gr6
+	or		gr4,gr6,gr4
+1:
+
+#else
+#error No default cache configuration set
+#endif
+
+	or		gr4,gr5,gr5
+	movgs		gr5,hsr0
+	bar
+
+	LEDS		0x0008
+
+	sethi.p		%hi(__head_mmu_enabled),gr19
+	setlo		%lo(__head_mmu_enabled),gr19
+	jmpl		@(gr19,gr0)
+
+__head_mmu_enabled:
+	icei		@(gr0,gr0),#1
+	dcei		@(gr0,gr0),#1
+
+	LEDS		0x0009
+
+#ifdef CONFIG_MMU
+	call		__head_fr451_finalise_protection
+#endif
+
+	LEDS		0x000a
+
+###############################################################################
+#
+# set up the runtime environment
+#
+###############################################################################
+
+	# clear the BSS area
+	sethi.p		%hi(__bss_start),gr4
+	setlo		%lo(__bss_start),gr4
+	sethi.p		%hi(_end),gr5
+	setlo		%lo(_end),gr5
+	or.p		gr0,gr0,gr18
+	or		gr0,gr0,gr19
+
+0:
+	stdi		gr18,@(gr4,#0)
+	stdi		gr18,@(gr4,#8)
+	stdi		gr18,@(gr4,#16)
+	stdi.p		gr18,@(gr4,#24)
+	addi		gr4,#24,gr4
+	subcc		gr5,gr4,gr0,icc0
+	bhi		icc0,#2,0b
+
+	LEDS		0x000b
+
+	# save the SDRAM details
+	sethi.p		%hi(__sdram_old_base),gr4
+	setlo		%lo(__sdram_old_base),gr4
+	st		gr24,@(gr4,gr0)
+
+	sethi.p		%hi(__sdram_base),gr5
+	setlo		%lo(__sdram_base),gr5
+	sethi.p		%hi(memory_start),gr4
+	setlo		%lo(memory_start),gr4
+	st		gr5,@(gr4,gr0)
+
+	add		gr25,gr5,gr25
+	sethi.p		%hi(memory_end),gr4
+	setlo		%lo(memory_end),gr4
+	st		gr25,@(gr4,gr0)
+
+	# point the TBR at the kernel trap table
+	sethi.p		%hi(__entry_kerneltrap_table),gr4
+	setlo		%lo(__entry_kerneltrap_table),gr4
+	movgs		gr4,tbr
+
+	# set up the exception frame for init
+	sethi.p		%hi(__kernel_frame0_ptr),gr28
+	setlo		%lo(__kernel_frame0_ptr),gr28
+	sethi.p		%hi(_gp),gr16
+	setlo		%lo(_gp),gr16
+	sethi.p		%hi(__entry_usertrap_table),gr4
+	setlo		%lo(__entry_usertrap_table),gr4
+
+	lddi		@(gr28,#0),gr28		; load __frame & current
+	ldi.p		@(gr29,#4),gr15		; set current_thread
+
+	or		gr0,gr0,fp
+	or		gr28,gr0,sp
+
+	sti.p		gr4,@(gr28,REG_TBR)
+	setlos		#ISR_EDE|ISR_DTT_DIVBYZERO|ISR_EMAM_EXCEPTION,gr5
+	movgs		gr5,isr
+
+	# turn on and off various CPU services
+	movsg		psr,gr22
+	sethi.p		%hi(#PSR_EM|PSR_EF|PSR_CM|PSR_NEM),gr4
+	setlo		%lo(#PSR_EM|PSR_EF|PSR_CM|PSR_NEM),gr4
+	or		gr22,gr4,gr22
+	movgs		gr22,psr
+
+	andi		gr22,#~(PSR_PIL|PSR_PS|PSR_S),gr22
+	ori		gr22,#PSR_ET,gr22
+	sti		gr22,@(gr28,REG_PSR)
+
+
+###############################################################################
+#
+# set up the registers and jump into the kernel
+#
+###############################################################################
+
+	LEDS		0x000c
+
+	# initialise the processor and the peripherals
+	#call		SYMBOL_NAME(processor_init)
+	#call		SYMBOL_NAME(unit_init)
+	#LEDS		0x0aff
+
+	sethi.p		#0xe5e5,gr3
+	setlo		#0xe5e5,gr3
+	or.p		gr3,gr0,gr4
+	or		gr3,gr0,gr5
+	or.p		gr3,gr0,gr6
+	or		gr3,gr0,gr7
+	or.p		gr3,gr0,gr8
+	or		gr3,gr0,gr9
+	or.p		gr3,gr0,gr10
+	or		gr3,gr0,gr11
+	or.p		gr3,gr0,gr12
+	or		gr3,gr0,gr13
+	or.p		gr3,gr0,gr14
+	or		gr3,gr0,gr17
+	or.p		gr3,gr0,gr18
+	or		gr3,gr0,gr19
+	or.p		gr3,gr0,gr20
+	or		gr3,gr0,gr21
+	or.p		gr3,gr0,gr23
+	or		gr3,gr0,gr24
+	or.p		gr3,gr0,gr25
+	or		gr3,gr0,gr26
+	or.p		gr3,gr0,gr27
+#	or		gr3,gr0,gr30
+	or		gr3,gr0,gr31
+	movgs		gr0,lr
+	movgs		gr0,lcr
+	movgs		gr0,ccr
+	movgs		gr0,cccr
+
+#ifdef CONFIG_MMU
+	movgs		gr3,scr2
+	movgs		gr3,scr3
+#endif
+
+	LEDS		0x0fff
+
+	# invoke the debugging stub if present
+	# - arch/frv/kernel/debug-stub.c will shift control directly to init/main.c
+	#   (it will not return here)
+	break
+	.globl		__debug_stub_init_break
+__debug_stub_init_break:
+
+	# however, if you need to use an ICE, and don't care about using any userspace
+	# debugging tools (such as the ptrace syscall), you can just step over the break
+	# above and get to the kernel this way
+	# look at arch/frv/kernel/debug-stub.c: debug_stub_init() to see what you've missed
+	call		start_kernel
+
+	.globl		__head_end
+__head_end:
+	.size		_boot, .-_boot
+
+	# provide a point for GDB to place a break
+	.section	.text.start,"ax"
+	.globl		_start
+	.balign		4
+_start:
+	call		_boot
+
+	.previous
+###############################################################################
+#
+# split a tile off of the region defined by GR8-GR9
+#
+#	ENTRY:			EXIT:
+# GR4	-			IAMPR value representing tile
+# GR5	-			DAMPR value representing tile
+# GR6	-			IAMLR value representing tile
+# GR7	-			DAMLR value representing tile
+# GR8	region base pointer	[saved]
+# GR9	region top pointer	updated to exclude new tile
+# GR11	xAMLR mask		[saved]
+# GR25	SDRAM size		[saved]
+# GR30	LED address		[saved]
+#
+# - GR8 and GR9 should be rounded up/down to the nearest megabyte before calling
+#
+###############################################################################
+	.globl		__head_split_region
+	.type		__head_split_region,@function
+__head_split_region:
+	subcc.p		gr9,gr8,gr4,icc0
+	setlos		#31,gr5
+	scan.p		gr4,gr0,gr6
+	beq		icc0,#0,__head_region_empty
+	sub.p		gr5,gr6,gr6			; bit number of highest set bit (1MB=>20)
+	setlos		#1,gr4
+	sll.p		gr4,gr6,gr4			; size of region (1 << bitno)
+	subi		gr6,#17,gr6			; 1MB => 0x03
+	slli.p		gr6,#4,gr6			; 1MB => 0x30
+	sub		gr9,gr4,gr9			; move uncovered top down
+
+	or		gr9,gr6,gr4
+	ori		gr4,#xAMPRx_S_USER|xAMPRx_C_CACHED|xAMPRx_V,gr4
+	or.p		gr4,gr0,gr5
+
+	and		gr4,gr11,gr6
+	and.p		gr5,gr11,gr7
+	bralr
+
+__head_region_empty:
+	or.p		gr0,gr0,gr4
+	or		gr0,gr0,gr5
+	or.p		gr0,gr0,gr6
+	or		gr0,gr0,gr7
+	bralr
+	.size		__head_split_region, .-__head_split_region
+
+###############################################################################
+#
+# write the 32-bit hex number in GR8 to ttyS0
+#
+###############################################################################
+#if 0
+	.globl		__head_write_to_ttyS0
+	.type		__head_write_to_ttyS0,@function
+__head_write_to_ttyS0:
+	sethi.p		%hi(0xfeff9c00),gr31
+	setlo		%lo(0xfeff9c00),gr31
+	setlos		#8,gr20
+
+0:	ldubi		@(gr31,#5*8),gr21
+	andi		gr21,#0x60,gr21
+	subicc		gr21,#0x60,gr21,icc0
+	bne		icc0,#0,0b
+
+1:	srli		gr8,#28,gr21
+	slli		gr8,#4,gr8
+
+	addi		gr21,#'0',gr21
+	subicc		gr21,#'9',gr0,icc0
+	bls		icc0,#2,2f
+	addi		gr21,#'A'-'0'-10,gr21
+2:
+	stbi		gr21,@(gr31,#0*8)
+	subicc		gr20,#1,gr20,icc0
+	bhi		icc0,#2,1b
+
+	setlos		#'\r',gr21
+	stbi		gr21,@(gr31,#0*8)
+
+	setlos		#'\n',gr21
+	stbi		gr21,@(gr31,#0*8)
+
+3:	ldubi		@(gr31,#5*8),gr21
+	andi		gr21,#0x60,gr21
+	subicc		gr21,#0x60,gr21,icc0
+	bne		icc0,#0,3b
+	bralr
+
+	.size		__head_write_to_ttyS0, .-__head_write_to_ttyS0
+#endif
diff --git a/arch/frv/kernel/head.inc b/arch/frv/kernel/head.inc
new file mode 100644
index 0000000..d424cd2
--- /dev/null
+++ b/arch/frv/kernel/head.inc
@@ -0,0 +1,50 @@
+/* head.inc: head common definitions -*- asm -*-
+ *
+ * Copyright (C) 2004 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 License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+
+#if defined(CONFIG_MB93090_MB00)
+#define LED_ADDR (0x21200000+4)
+
+.macro LEDS val
+	sethi.p		%hi(0xFFC00030),gr3
+	setlo		%lo(0xFFC00030),gr3
+	lduh		@(gr3,gr0),gr3
+	andicc		gr3,#0x100,gr0,icc0
+	bne		icc0,0,999f
+
+	setlos		#~\val,gr3
+	st		gr3,@(gr30,gr0)
+	membar
+	dcf		@(gr30,gr0)
+    999:
+.endm
+
+#elif defined(CONFIG_MB93093_PDK)
+#define LED_ADDR (0x20000023)
+
+.macro LEDS val
+	setlos		#\val,gr3
+	stb		gr3,@(gr30,gr0)
+	membar
+.endm
+
+#else
+#define LED_ADDR 0
+
+.macro LEDS val
+.endm
+#endif
+
+#ifdef CONFIG_MMU
+__sdram_base = 0x00000000		/* base address to which SDRAM relocated */
+#else
+__sdram_base = 0xc0000000		/* base address to which SDRAM relocated */
+#endif
diff --git a/arch/frv/kernel/init_task.c b/arch/frv/kernel/init_task.c
new file mode 100644
index 0000000..2299393
--- /dev/null
+++ b/arch/frv/kernel/init_task.c
@@ -0,0 +1,39 @@
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/init_task.h>
+#include <linux/fs.h>
+#include <linux/mqueue.h>
+
+#include <asm/uaccess.h>
+#include <asm/pgtable.h>
+
+
+static struct fs_struct init_fs = INIT_FS;
+static struct files_struct init_files = INIT_FILES;
+static struct signal_struct init_signals = INIT_SIGNALS(init_signals);
+static struct sighand_struct init_sighand = INIT_SIGHAND(init_sighand);
+struct mm_struct init_mm = INIT_MM(init_mm);
+
+EXPORT_SYMBOL(init_mm);
+
+/*
+ * Initial thread structure.
+ *
+ * We need to make sure that this is THREAD_SIZE aligned due to the
+ * way process stacks are handled. This is done by having a special
+ * "init_task" linker map entry..
+ */
+union thread_union init_thread_union
+	__attribute__((__section__(".data.init_task"))) =
+		{ INIT_THREAD_INFO(init_task) };
+
+/*
+ * Initial task structure.
+ *
+ * All other task structs will be allocated on slabs in fork.c
+ */
+struct task_struct init_task = INIT_TASK(init_task);
+
+EXPORT_SYMBOL(init_task);
diff --git a/arch/frv/kernel/irq-mb93091.c b/arch/frv/kernel/irq-mb93091.c
new file mode 100644
index 0000000..9778e0f
--- /dev/null
+++ b/arch/frv/kernel/irq-mb93091.c
@@ -0,0 +1,116 @@
+/* irq-mb93091.c: MB93091 FPGA interrupt handling
+ *
+ * Copyright (C) 2003 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 License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/config.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/delay.h>
+#include <asm/irq.h>
+#include <asm/irc-regs.h>
+#include <asm/irq-routing.h>
+
+#define __reg16(ADDR) (*(volatile unsigned short *)(ADDR))
+
+#define __get_IMR()	({ __reg16(0xffc00004); })
+#define __set_IMR(M)	do { __reg16(0xffc00004) = (M); wmb(); } while(0)
+#define __get_IFR()	({ __reg16(0xffc0000c); })
+#define __clr_IFR(M)	do { __reg16(0xffc0000c) = ~(M); wmb(); } while(0)
+
+static void frv_fpga_doirq(struct irq_source *source);
+static void frv_fpga_control(struct irq_group *group, int irq, int on);
+
+/*****************************************************************************/
+/*
+ * FPGA IRQ multiplexor
+ */
+static struct irq_source frv_fpga[4] = {
+#define __FPGA(X, M)					\
+	[X] = {						\
+		.muxname	= "fpga."#X,		\
+		.irqmask	= M,			\
+		.doirq		= frv_fpga_doirq,	\
+	}
+
+	__FPGA(0, 0x0028),
+	__FPGA(1, 0x0050),
+	__FPGA(2, 0x1c00),
+	__FPGA(3, 0x6386),
+};
+
+static struct irq_group frv_fpga_irqs = {
+	.first_irq	= IRQ_BASE_FPGA,
+	.control	= frv_fpga_control,
+	.sources = {
+		[ 1] = &frv_fpga[3],
+		[ 2] = &frv_fpga[3],
+		[ 3] = &frv_fpga[0],
+		[ 4] = &frv_fpga[1],
+		[ 5] = &frv_fpga[0],
+		[ 6] = &frv_fpga[1],
+		[ 7] = &frv_fpga[3],
+		[ 8] = &frv_fpga[3],
+		[ 9] = &frv_fpga[3],
+		[10] = &frv_fpga[2],
+		[11] = &frv_fpga[2],
+		[12] = &frv_fpga[2],
+		[13] = &frv_fpga[3],
+		[14] = &frv_fpga[3],
+	},
+};
+
+
+static void frv_fpga_control(struct irq_group *group, int index, int on)
+{
+	uint16_t imr = __get_IMR();
+
+	if (on)
+		imr &= ~(1 << index);
+	else
+		imr |= 1 << index;
+
+	__set_IMR(imr);
+}
+
+static void frv_fpga_doirq(struct irq_source *source)
+{
+	uint16_t mask, imr;
+
+	imr = __get_IMR();
+	mask = source->irqmask & ~imr & __get_IFR();
+	if (mask) {
+		__set_IMR(imr | mask);
+		__clr_IFR(mask);
+		distribute_irqs(&frv_fpga_irqs, mask);
+		__set_IMR(imr);
+	}
+}
+
+void __init fpga_init(void)
+{
+	__set_IMR(0x7ffe);
+	__clr_IFR(0x0000);
+
+	frv_irq_route_external(&frv_fpga[0], IRQ_CPU_EXTERNAL0);
+	frv_irq_route_external(&frv_fpga[1], IRQ_CPU_EXTERNAL1);
+	frv_irq_route_external(&frv_fpga[2], IRQ_CPU_EXTERNAL2);
+	frv_irq_route_external(&frv_fpga[3], IRQ_CPU_EXTERNAL3);
+	frv_irq_set_group(&frv_fpga_irqs);
+}
diff --git a/arch/frv/kernel/irq-mb93093.c b/arch/frv/kernel/irq-mb93093.c
new file mode 100644
index 0000000..21ca2b2
--- /dev/null
+++ b/arch/frv/kernel/irq-mb93093.c
@@ -0,0 +1,99 @@
+/* irq-mb93093.c: MB93093 FPGA interrupt handling
+ *
+ * Copyright (C) 2004 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 License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/config.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/delay.h>
+#include <asm/irq.h>
+#include <asm/irc-regs.h>
+#include <asm/irq-routing.h>
+
+#define __reg16(ADDR) (*(volatile unsigned short *)(__region_CS2 + (ADDR)))
+
+#define __get_IMR()	({ __reg16(0x0a); })
+#define __set_IMR(M)	do { __reg16(0x0a) = (M);  wmb(); } while(0)
+#define __get_IFR()	({ __reg16(0x02); })
+#define __clr_IFR(M)	do { __reg16(0x02) = ~(M); wmb(); } while(0)
+
+static void frv_fpga_doirq(struct irq_source *source);
+static void frv_fpga_control(struct irq_group *group, int irq, int on);
+
+/*****************************************************************************/
+/*
+ * FPGA IRQ multiplexor
+ */
+static struct irq_source frv_fpga[4] = {
+#define __FPGA(X, M)					\
+	[X] = {						\
+		.muxname	= "fpga."#X,		\
+		.irqmask	= M,			\
+		.doirq		= frv_fpga_doirq,	\
+	}
+
+	__FPGA(0, 0x0700),
+};
+
+static struct irq_group frv_fpga_irqs = {
+	.first_irq	= IRQ_BASE_FPGA,
+	.control	= frv_fpga_control,
+	.sources = {
+		[ 8] = &frv_fpga[0],
+		[ 9] = &frv_fpga[0],
+		[10] = &frv_fpga[0],
+	},
+};
+
+
+static void frv_fpga_control(struct irq_group *group, int index, int on)
+{
+	uint16_t imr = __get_IMR();
+
+	if (on)
+		imr &= ~(1 << index);
+	else
+		imr |= 1 << index;
+
+	__set_IMR(imr);
+}
+
+static void frv_fpga_doirq(struct irq_source *source)
+{
+	uint16_t mask, imr;
+
+	imr = __get_IMR();
+	mask = source->irqmask & ~imr & __get_IFR();
+	if (mask) {
+		__set_IMR(imr | mask);
+		__clr_IFR(mask);
+		distribute_irqs(&frv_fpga_irqs, mask);
+		__set_IMR(imr);
+	}
+}
+
+void __init fpga_init(void)
+{
+	__set_IMR(0x0700);
+	__clr_IFR(0x0000);
+
+	frv_irq_route_external(&frv_fpga[0], IRQ_CPU_EXTERNAL2);
+	frv_irq_set_group(&frv_fpga_irqs);
+}
diff --git a/arch/frv/kernel/irq-mb93493.c b/arch/frv/kernel/irq-mb93493.c
new file mode 100644
index 0000000..c003ae5
--- /dev/null
+++ b/arch/frv/kernel/irq-mb93493.c
@@ -0,0 +1,108 @@
+/* irq-mb93493.c: MB93493 companion chip interrupt handler
+ *
+ * Copyright (C) 2004 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 License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/config.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/delay.h>
+#include <asm/irq.h>
+#include <asm/irc-regs.h>
+#include <asm/irq-routing.h>
+#include <asm/mb93493-irqs.h>
+
+static void frv_mb93493_doirq(struct irq_source *source);
+
+/*****************************************************************************/
+/*
+ * MB93493 companion chip IRQ multiplexor
+ */
+static struct irq_source frv_mb93493[2] = {
+	[0] = {
+		.muxname		= "mb93493.0",
+		.muxdata		= __region_CS3 + 0x3d0,
+		.doirq			= frv_mb93493_doirq,
+		.irqmask		= 0x0000,
+	},
+	[1] = {
+		.muxname		= "mb93493.1",
+		.muxdata		= __region_CS3 + 0x3d4,
+		.doirq			= frv_mb93493_doirq,
+		.irqmask		= 0x0000,
+	},
+};
+
+static void frv_mb93493_control(struct irq_group *group, int index, int on)
+{
+	struct irq_source *source;
+	uint32_t iqsr;
+
+	if ((frv_mb93493[0].irqmask & (1 << index)))
+		source = &frv_mb93493[0];
+	else
+		source = &frv_mb93493[1];
+
+	iqsr = readl(source->muxdata);
+	if (on)
+		iqsr |= 1 << (index + 16);
+	else
+		iqsr &= ~(1 << (index + 16));
+
+	writel(iqsr, source->muxdata);
+}
+
+static struct irq_group frv_mb93493_irqs = {
+	.first_irq	= IRQ_BASE_MB93493,
+	.control	= frv_mb93493_control,
+};
+
+static void frv_mb93493_doirq(struct irq_source *source)
+{
+	uint32_t mask = readl(source->muxdata);
+	mask = mask & (mask >> 16) & 0xffff;
+
+	if (mask)
+		distribute_irqs(&frv_mb93493_irqs, mask);
+}
+
+static void __init mb93493_irq_route(int irq, int source)
+{
+	frv_mb93493[source].irqmask |= 1 << (irq - IRQ_BASE_MB93493);
+	frv_mb93493_irqs.sources[irq - IRQ_BASE_MB93493] = &frv_mb93493[source];
+}
+
+void __init route_mb93493_irqs(void)
+{
+	frv_irq_route_external(&frv_mb93493[0], IRQ_CPU_MB93493_0);
+	frv_irq_route_external(&frv_mb93493[1], IRQ_CPU_MB93493_1);
+
+	frv_irq_set_group(&frv_mb93493_irqs);
+
+	mb93493_irq_route(IRQ_MB93493_VDC,		IRQ_MB93493_VDC_ROUTE);
+	mb93493_irq_route(IRQ_MB93493_VCC,		IRQ_MB93493_VCC_ROUTE);
+	mb93493_irq_route(IRQ_MB93493_AUDIO_IN,		IRQ_MB93493_AUDIO_IN_ROUTE);
+	mb93493_irq_route(IRQ_MB93493_I2C_0,		IRQ_MB93493_I2C_0_ROUTE);
+	mb93493_irq_route(IRQ_MB93493_I2C_1,		IRQ_MB93493_I2C_1_ROUTE);
+	mb93493_irq_route(IRQ_MB93493_USB,		IRQ_MB93493_USB_ROUTE);
+	mb93493_irq_route(IRQ_MB93493_LOCAL_BUS,	IRQ_MB93493_LOCAL_BUS_ROUTE);
+	mb93493_irq_route(IRQ_MB93493_PCMCIA,		IRQ_MB93493_PCMCIA_ROUTE);
+	mb93493_irq_route(IRQ_MB93493_GPIO,		IRQ_MB93493_GPIO_ROUTE);
+	mb93493_irq_route(IRQ_MB93493_AUDIO_OUT,	IRQ_MB93493_AUDIO_OUT_ROUTE);
+}
diff --git a/arch/frv/kernel/irq-routing.c b/arch/frv/kernel/irq-routing.c
new file mode 100644
index 0000000..d4776d1
--- /dev/null
+++ b/arch/frv/kernel/irq-routing.c
@@ -0,0 +1,291 @@
+/* irq-routing.c: IRQ routing
+ *
+ * Copyright (C) 2004 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 License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/sched.h>
+#include <linux/random.h>
+#include <linux/init.h>
+#include <linux/serial_reg.h>
+#include <asm/io.h>
+#include <asm/irq-routing.h>
+#include <asm/irc-regs.h>
+#include <asm/serial-regs.h>
+#include <asm/dma.h>
+
+struct irq_level frv_irq_levels[16] = {
+	[0 ... 15] = {
+		.lock	= SPIN_LOCK_UNLOCKED,
+	}
+};
+
+struct irq_group *irq_groups[NR_IRQ_GROUPS];
+
+extern struct irq_group frv_cpu_irqs;
+
+void __init frv_irq_route(struct irq_source *source, int irqlevel)
+{
+	source->level = &frv_irq_levels[irqlevel];
+	source->next = frv_irq_levels[irqlevel].sources;
+	frv_irq_levels[irqlevel].sources = source;
+}
+
+void __init frv_irq_route_external(struct irq_source *source, int irq)
+{
+	int irqlevel = 0;
+
+	switch (irq) {
+	case IRQ_CPU_EXTERNAL0:	irqlevel = IRQ_XIRQ0_LEVEL; break;
+	case IRQ_CPU_EXTERNAL1:	irqlevel = IRQ_XIRQ1_LEVEL; break;
+	case IRQ_CPU_EXTERNAL2:	irqlevel = IRQ_XIRQ2_LEVEL; break;
+	case IRQ_CPU_EXTERNAL3:	irqlevel = IRQ_XIRQ3_LEVEL; break;
+	case IRQ_CPU_EXTERNAL4:	irqlevel = IRQ_XIRQ4_LEVEL; break;
+	case IRQ_CPU_EXTERNAL5:	irqlevel = IRQ_XIRQ5_LEVEL; break;
+	case IRQ_CPU_EXTERNAL6:	irqlevel = IRQ_XIRQ6_LEVEL; break;
+	case IRQ_CPU_EXTERNAL7:	irqlevel = IRQ_XIRQ7_LEVEL; break;
+	default: BUG();
+	}
+
+	source->level = &frv_irq_levels[irqlevel];
+	source->next = frv_irq_levels[irqlevel].sources;
+	frv_irq_levels[irqlevel].sources = source;
+}
+
+void __init frv_irq_set_group(struct irq_group *group)
+{
+	irq_groups[group->first_irq >> NR_IRQ_LOG2_ACTIONS_PER_GROUP] = group;
+}
+
+void distribute_irqs(struct irq_group *group, unsigned long irqmask)
+{
+	struct irqaction *action;
+	int irq;
+
+	while (irqmask) {
+		asm("scan %1,gr0,%0" : "=r"(irq) : "r"(irqmask));
+		if (irq < 0 || irq > 31)
+			asm volatile("break");
+		irq = 31 - irq;
+
+		irqmask &= ~(1 << irq);
+		action = group->actions[irq];
+
+		irq += group->first_irq;
+
+		if (action) {
+			int status = 0;
+
+//			if (!(action->flags & SA_INTERRUPT))
+//				local_irq_enable();
+
+			do {
+				status |= action->flags;
+				action->handler(irq, action->dev_id, __frame);
+				action = action->next;
+			} while (action);
+
+			if (status & SA_SAMPLE_RANDOM)
+				add_interrupt_randomness(irq);
+			local_irq_disable();
+		}
+	}
+}
+
+/*****************************************************************************/
+/*
+ * CPU UART interrupts
+ */
+static void frv_cpuuart_doirq(struct irq_source *source)
+{
+//	uint8_t iir = readb(source->muxdata + UART_IIR * 8);
+//	if ((iir & 0x0f) != UART_IIR_NO_INT)
+		distribute_irqs(&frv_cpu_irqs, source->irqmask);
+}
+
+struct irq_source frv_cpuuart[2] = {
+#define __CPUUART(X, A)						\
+	[X] = {							\
+		.muxname	= "uart",			\
+		.muxdata	= (volatile void __iomem *) A,	\
+		.irqmask	= 1 << IRQ_CPU_UART##X,		\
+		.doirq		= frv_cpuuart_doirq,		\
+	}
+
+	__CPUUART(0, UART0_BASE),
+	__CPUUART(1, UART1_BASE),
+};
+
+/*****************************************************************************/
+/*
+ * CPU DMA interrupts
+ */
+static void frv_cpudma_doirq(struct irq_source *source)
+{
+	uint32_t cstr = readl(source->muxdata + DMAC_CSTRx);
+	if (cstr & DMAC_CSTRx_INT)
+		distribute_irqs(&frv_cpu_irqs, source->irqmask);
+}
+
+struct irq_source frv_cpudma[8] = {
+#define __CPUDMA(X, A)						\
+	[X] = {							\
+		.muxname	= "dma",			\
+		.muxdata	= (volatile void __iomem *) A,	\
+		.irqmask	= 1 << IRQ_CPU_DMA##X,		\
+		.doirq		= frv_cpudma_doirq,		\
+	}
+
+	__CPUDMA(0, 0xfe000900),
+	__CPUDMA(1, 0xfe000980),
+	__CPUDMA(2, 0xfe000a00),
+	__CPUDMA(3, 0xfe000a80),
+	__CPUDMA(4, 0xfe001000),
+	__CPUDMA(5, 0xfe001080),
+	__CPUDMA(6, 0xfe001100),
+	__CPUDMA(7, 0xfe001180),
+};
+
+/*****************************************************************************/
+/*
+ * CPU timer interrupts - can't tell whether they've generated an interrupt or not
+ */
+static void frv_cputimer_doirq(struct irq_source *source)
+{
+	distribute_irqs(&frv_cpu_irqs, source->irqmask);
+}
+
+struct irq_source frv_cputimer[3] = {
+#define __CPUTIMER(X)						\
+	[X] = {							\
+		.muxname	= "timer",			\
+		.muxdata	= 0,				\
+		.irqmask	= 1 << IRQ_CPU_TIMER##X,	\
+		.doirq		= frv_cputimer_doirq,		\
+	}
+
+	__CPUTIMER(0),
+	__CPUTIMER(1),
+	__CPUTIMER(2),
+};
+
+/*****************************************************************************/
+/*
+ * external CPU interrupts - can't tell directly whether they've generated an interrupt or not
+ */
+static void frv_cpuexternal_doirq(struct irq_source *source)
+{
+	distribute_irqs(&frv_cpu_irqs, source->irqmask);
+}
+
+struct irq_source frv_cpuexternal[8] = {
+#define __CPUEXTERNAL(X)					\
+	[X] = {							\
+		.muxname	= "ext",			\
+		.muxdata	= 0,				\
+		.irqmask	= 1 << IRQ_CPU_EXTERNAL##X,	\
+		.doirq		= frv_cpuexternal_doirq,	\
+	}
+
+	__CPUEXTERNAL(0),
+	__CPUEXTERNAL(1),
+	__CPUEXTERNAL(2),
+	__CPUEXTERNAL(3),
+	__CPUEXTERNAL(4),
+	__CPUEXTERNAL(5),
+	__CPUEXTERNAL(6),
+	__CPUEXTERNAL(7),
+};
+
+#define set_IRR(N,A,B,C,D) __set_IRR(N, (A << 28) | (B << 24) | (C << 20) | (D << 16))
+
+struct irq_group frv_cpu_irqs = {
+	.sources = {
+		[IRQ_CPU_UART0]		= &frv_cpuuart[0],
+		[IRQ_CPU_UART1]		= &frv_cpuuart[1],
+		[IRQ_CPU_TIMER0]	= &frv_cputimer[0],
+		[IRQ_CPU_TIMER1]	= &frv_cputimer[1],
+		[IRQ_CPU_TIMER2]	= &frv_cputimer[2],
+		[IRQ_CPU_DMA0]		= &frv_cpudma[0],
+		[IRQ_CPU_DMA1]		= &frv_cpudma[1],
+		[IRQ_CPU_DMA2]		= &frv_cpudma[2],
+		[IRQ_CPU_DMA3]		= &frv_cpudma[3],
+		[IRQ_CPU_DMA4]		= &frv_cpudma[4],
+		[IRQ_CPU_DMA5]		= &frv_cpudma[5],
+		[IRQ_CPU_DMA6]		= &frv_cpudma[6],
+		[IRQ_CPU_DMA7]		= &frv_cpudma[7],
+		[IRQ_CPU_EXTERNAL0]	= &frv_cpuexternal[0],
+		[IRQ_CPU_EXTERNAL1]	= &frv_cpuexternal[1],
+		[IRQ_CPU_EXTERNAL2]	= &frv_cpuexternal[2],
+		[IRQ_CPU_EXTERNAL3]	= &frv_cpuexternal[3],
+		[IRQ_CPU_EXTERNAL4]	= &frv_cpuexternal[4],
+		[IRQ_CPU_EXTERNAL5]	= &frv_cpuexternal[5],
+		[IRQ_CPU_EXTERNAL6]	= &frv_cpuexternal[6],
+		[IRQ_CPU_EXTERNAL7]	= &frv_cpuexternal[7],
+	},
+};
+
+/*****************************************************************************/
+/*
+ * route the CPU's interrupt sources
+ */
+void __init route_cpu_irqs(void)
+{
+	frv_irq_set_group(&frv_cpu_irqs);
+
+	__set_IITMR(0, 0x003f0000);	/* DMA0-3, TIMER0-2 IRQ detect levels */
+	__set_IITMR(1, 0x20000000);	/* ERR0-1, UART0-1, DMA4-7 IRQ detect levels */
+
+	/* route UART and error interrupts */
+	frv_irq_route(&frv_cpuuart[0],	IRQ_UART0_LEVEL);
+	frv_irq_route(&frv_cpuuart[1],	IRQ_UART1_LEVEL);
+
+	set_IRR(6, IRQ_GDBSTUB_LEVEL, IRQ_GDBSTUB_LEVEL, IRQ_UART1_LEVEL, IRQ_UART0_LEVEL);
+
+	/* route DMA channel interrupts */
+	frv_irq_route(&frv_cpudma[0],	IRQ_DMA0_LEVEL);
+	frv_irq_route(&frv_cpudma[1],	IRQ_DMA1_LEVEL);
+	frv_irq_route(&frv_cpudma[2],	IRQ_DMA2_LEVEL);
+	frv_irq_route(&frv_cpudma[3],	IRQ_DMA3_LEVEL);
+	frv_irq_route(&frv_cpudma[4],	IRQ_DMA4_LEVEL);
+	frv_irq_route(&frv_cpudma[5],	IRQ_DMA5_LEVEL);
+	frv_irq_route(&frv_cpudma[6],	IRQ_DMA6_LEVEL);
+	frv_irq_route(&frv_cpudma[7],	IRQ_DMA7_LEVEL);
+
+	set_IRR(4, IRQ_DMA3_LEVEL, IRQ_DMA2_LEVEL, IRQ_DMA1_LEVEL, IRQ_DMA0_LEVEL);
+	set_IRR(7, IRQ_DMA7_LEVEL, IRQ_DMA6_LEVEL, IRQ_DMA5_LEVEL, IRQ_DMA4_LEVEL);
+
+	/* route timer interrupts */
+	frv_irq_route(&frv_cputimer[0],	IRQ_TIMER0_LEVEL);
+	frv_irq_route(&frv_cputimer[1],	IRQ_TIMER1_LEVEL);
+	frv_irq_route(&frv_cputimer[2],	IRQ_TIMER2_LEVEL);
+
+	set_IRR(5, 0, IRQ_TIMER2_LEVEL, IRQ_TIMER1_LEVEL, IRQ_TIMER0_LEVEL);
+
+	/* route external interrupts */
+	frv_irq_route(&frv_cpuexternal[0], IRQ_XIRQ0_LEVEL);
+	frv_irq_route(&frv_cpuexternal[1], IRQ_XIRQ1_LEVEL);
+	frv_irq_route(&frv_cpuexternal[2], IRQ_XIRQ2_LEVEL);
+	frv_irq_route(&frv_cpuexternal[3], IRQ_XIRQ3_LEVEL);
+	frv_irq_route(&frv_cpuexternal[4], IRQ_XIRQ4_LEVEL);
+	frv_irq_route(&frv_cpuexternal[5], IRQ_XIRQ5_LEVEL);
+	frv_irq_route(&frv_cpuexternal[6], IRQ_XIRQ6_LEVEL);
+	frv_irq_route(&frv_cpuexternal[7], IRQ_XIRQ7_LEVEL);
+
+	set_IRR(2, IRQ_XIRQ7_LEVEL, IRQ_XIRQ6_LEVEL, IRQ_XIRQ5_LEVEL, IRQ_XIRQ4_LEVEL);
+	set_IRR(3, IRQ_XIRQ3_LEVEL, IRQ_XIRQ2_LEVEL, IRQ_XIRQ1_LEVEL, IRQ_XIRQ0_LEVEL);
+
+#if defined(CONFIG_MB93091_VDK)
+	__set_TM1(0x55550000);		/* XIRQ7-0 all active low */
+#elif defined(CONFIG_MB93093_PDK)
+	__set_TM1(0x15550000);		/* XIRQ7 active high, 6-0 all active low */
+#else
+#error dont know external IRQ trigger levels for this setup
+#endif
+
+} /* end route_cpu_irqs() */
diff --git a/arch/frv/kernel/irq.c b/arch/frv/kernel/irq.c
new file mode 100644
index 0000000..8c524cd
--- /dev/null
+++ b/arch/frv/kernel/irq.c
@@ -0,0 +1,764 @@
+/* irq.c: FRV IRQ handling
+ *
+ * Copyright (C) 2003, 2004 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 License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+/*
+ * (mostly architecture independent, will move to kernel/irq.c in 2.5.)
+ *
+ * IRQs are in fact implemented a bit like signal handlers for the kernel.
+ * Naturally it's not a 1:1 relation, but there are similarities.
+ */
+
+#include <linux/config.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/timex.h>
+#include <linux/slab.h>
+#include <linux/random.h>
+#include <linux/smp_lock.h>
+#include <linux/init.h>
+#include <linux/kernel_stat.h>
+#include <linux/irq.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+
+#include <asm/atomic.h>
+#include <asm/io.h>
+#include <asm/smp.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/uaccess.h>
+#include <asm/pgalloc.h>
+#include <asm/delay.h>
+#include <asm/irq.h>
+#include <asm/irc-regs.h>
+#include <asm/irq-routing.h>
+#include <asm/gdb-stub.h>
+
+extern void __init fpga_init(void);
+extern void __init route_mb93493_irqs(void);
+
+static void register_irq_proc (unsigned int irq);
+
+/*
+ * Special irq handlers.
+ */
+
+irqreturn_t no_action(int cpl, void *dev_id, struct pt_regs *regs) { return IRQ_HANDLED; }
+
+atomic_t irq_err_count;
+
+/*
+ * Generic, controller-independent functions:
+ */
+int show_interrupts(struct seq_file *p, void *v)
+{
+	struct irqaction *action;
+	struct irq_group *group;
+	unsigned long flags;
+	int level, grp, ix, i, j;
+
+	i = *(loff_t *) v;
+
+	switch (i) {
+	case 0:
+		seq_printf(p, "           ");
+		for (j = 0; j < NR_CPUS; j++)
+			if (cpu_online(j))
+				seq_printf(p, "CPU%d       ",j);
+
+		seq_putc(p, '\n');
+		break;
+
+	case 1 ... NR_IRQ_GROUPS * NR_IRQ_ACTIONS_PER_GROUP:
+		local_irq_save(flags);
+
+		grp = (i - 1) / NR_IRQ_ACTIONS_PER_GROUP;
+		group = irq_groups[grp];
+		if (!group)
+			goto skip;
+
+		ix = (i - 1) % NR_IRQ_ACTIONS_PER_GROUP;
+		action = group->actions[ix];
+		if (!action)
+			goto skip;
+
+		seq_printf(p, "%3d: ", i - 1);
+
+#ifndef CONFIG_SMP
+		seq_printf(p, "%10u ", kstat_irqs(i));
+#else
+		for (j = 0; j < NR_CPUS; j++)
+			if (cpu_online(j))
+				seq_printf(p, "%10u ", kstat_cpu(j).irqs[i - 1]);
+#endif
+
+		level = group->sources[ix]->level - frv_irq_levels;
+
+		seq_printf(p, " %12s@%x", group->sources[ix]->muxname, level);
+		seq_printf(p, "  %s", action->name);
+
+		for (action = action->next; action; action = action->next)
+			seq_printf(p, ", %s", action->name);
+
+		seq_putc(p, '\n');
+skip:
+		local_irq_restore(flags);
+		break;
+
+	case NR_IRQ_GROUPS * NR_IRQ_ACTIONS_PER_GROUP + 1:
+		seq_printf(p, "ERR: %10u\n", atomic_read(&irq_err_count));
+		break;
+
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+
+/*
+ * Generic enable/disable code: this just calls
+ * down into the PIC-specific version for the actual
+ * hardware disable after having gotten the irq
+ * controller lock.
+ */
+
+/**
+ *	disable_irq_nosync - disable an irq without waiting
+ *	@irq: Interrupt to disable
+ *
+ *	Disable the selected interrupt line.  Disables and Enables are
+ *	nested.
+ *	Unlike disable_irq(), this function does not ensure existing
+ *	instances of the IRQ handler have completed before returning.
+ *
+ *	This function may be called from IRQ context.
+ */
+
+void disable_irq_nosync(unsigned int irq)
+{
+	struct irq_source *source;
+	struct irq_group *group;
+	struct irq_level *level;
+	unsigned long flags;
+	int idx = irq & (NR_IRQ_ACTIONS_PER_GROUP - 1);
+
+	group = irq_groups[irq >> NR_IRQ_LOG2_ACTIONS_PER_GROUP];
+	if (!group)
+		BUG();
+
+	source = group->sources[idx];
+	if (!source)
+		BUG();
+
+	level = source->level;
+
+	spin_lock_irqsave(&level->lock, flags);
+
+	if (group->control) {
+		if (!group->disable_cnt[idx]++)
+			group->control(group, idx, 0);
+	} else if (!level->disable_count++) {
+		__set_MASK(level - frv_irq_levels);
+	}
+
+	spin_unlock_irqrestore(&level->lock, flags);
+}
+
+/**
+ *	disable_irq - disable an irq and wait for completion
+ *	@irq: Interrupt to disable
+ *
+ *	Disable the selected interrupt line.  Enables and Disables are
+ *	nested.
+ *	This function waits for any pending IRQ handlers for this interrupt
+ *	to complete before returning. If you use this function while
+ *	holding a resource the IRQ handler may need you will deadlock.
+ *
+ *	This function may be called - with care - from IRQ context.
+ */
+
+void disable_irq(unsigned int irq)
+{
+	disable_irq_nosync(irq);
+
+#ifdef CONFIG_SMP
+	if (!local_irq_count(smp_processor_id())) {
+		do {
+			barrier();
+		} while (irq_desc[irq].status & IRQ_INPROGRESS);
+	}
+#endif
+}
+
+/**
+ *	enable_irq - enable handling of an irq
+ *	@irq: Interrupt to enable
+ *
+ *	Undoes the effect of one call to disable_irq().  If this
+ *	matches the last disable, processing of interrupts on this
+ *	IRQ line is re-enabled.
+ *
+ *	This function may be called from IRQ context.
+ */
+
+void enable_irq(unsigned int irq)
+{
+	struct irq_source *source;
+	struct irq_group *group;
+	struct irq_level *level;
+	unsigned long flags;
+	int idx = irq & (NR_IRQ_ACTIONS_PER_GROUP - 1);
+	int count;
+
+	group = irq_groups[irq >> NR_IRQ_LOG2_ACTIONS_PER_GROUP];
+	if (!group)
+		BUG();
+
+	source = group->sources[idx];
+	if (!source)
+		BUG();
+
+	level = source->level;
+
+	spin_lock_irqsave(&level->lock, flags);
+
+	if (group->control)
+		count = group->disable_cnt[idx];
+	else
+		count = level->disable_count;
+
+	switch (count) {
+	case 1:
+		if (group->control) {
+			if (group->actions[idx])
+				group->control(group, idx, 1);
+		} else {
+			if (level->usage)
+				__clr_MASK(level - frv_irq_levels);
+		}
+		/* fall-through */
+
+	default:
+		count--;
+		break;
+
+	case 0:
+		printk("enable_irq(%u) unbalanced from %p\n", irq, __builtin_return_address(0));
+	}
+
+	if (group->control)
+		group->disable_cnt[idx] = count;
+	else
+		level->disable_count = count;
+
+	spin_unlock_irqrestore(&level->lock, flags);
+}
+
+/*****************************************************************************/
+/*
+ * handles all normal device IRQ's
+ * - registers are referred to by the __frame variable (GR28)
+ * - IRQ distribution is complicated in this arch because of the many PICs, the
+ *   way they work and the way they cascade
+ */
+asmlinkage void do_IRQ(void)
+{
+	struct irq_source *source;
+	int level, cpu;
+
+	level = (__frame->tbr >> 4) & 0xf;
+	cpu = smp_processor_id();
+
+#if 0
+	{
+		static u32 irqcount;
+		*(volatile u32 *) 0xe1200004 = ~((irqcount++ << 8) | level);
+		*(volatile u16 *) 0xffc00100 = (u16) ~0x9999;
+		mb();
+	}
+#endif
+
+	if ((unsigned long) __frame - (unsigned long) (current + 1) < 512)
+		BUG();
+
+	__set_MASK(level);
+	__clr_RC(level);
+	__clr_IRL();
+
+	kstat_this_cpu.irqs[level]++;
+
+	irq_enter();
+
+	for (source = frv_irq_levels[level].sources; source; source = source->next)
+		source->doirq(source);
+
+	irq_exit();
+
+	__clr_MASK(level);
+
+	/* only process softirqs if we didn't interrupt another interrupt handler */
+	if ((__frame->psr & PSR_PIL) == PSR_PIL_0)
+		if (local_softirq_pending())
+			do_softirq();
+
+#ifdef CONFIG_PREEMPT
+	local_irq_disable();
+	while (--current->preempt_count == 0) {
+		if (!(__frame->psr & PSR_S) ||
+		    current->need_resched == 0 ||
+		    in_interrupt())
+			break;
+		current->preempt_count++;
+		local_irq_enable();
+		preempt_schedule();
+		local_irq_disable();
+	}
+#endif
+
+#if 0
+	{
+		*(volatile u16 *) 0xffc00100 = (u16) ~0x6666;
+		mb();
+	}
+#endif
+
+} /* end do_IRQ() */
+
+/*****************************************************************************/
+/*
+ * handles all NMIs when not co-opted by the debugger
+ * - registers are referred to by the __frame variable (GR28)
+ */
+asmlinkage void do_NMI(void)
+{
+} /* end do_NMI() */
+
+/*****************************************************************************/
+/**
+ *	request_irq - allocate an interrupt line
+ *	@irq: Interrupt line to allocate
+ *	@handler: Function to be called when the IRQ occurs
+ *	@irqflags: Interrupt type flags
+ *	@devname: An ascii name for the claiming device
+ *	@dev_id: A cookie passed back to the handler function
+ *
+ *	This call allocates interrupt resources and enables the
+ *	interrupt line and IRQ handling. From the point this
+ *	call is made your handler function may be invoked. Since
+ *	your handler function must clear any interrupt the board
+ *	raises, you must take care both to initialise your hardware
+ *	and to set up the interrupt handler in the right order.
+ *
+ *	Dev_id must be globally unique. Normally the address of the
+ *	device data structure is used as the cookie. Since the handler
+ *	receives this value it makes sense to use it.
+ *
+ *	If your interrupt is shared you must pass a non NULL dev_id
+ *	as this is required when freeing the interrupt.
+ *
+ *	Flags:
+ *
+ *	SA_SHIRQ		Interrupt is shared
+ *
+ *	SA_INTERRUPT		Disable local interrupts while processing
+ *
+ *	SA_SAMPLE_RANDOM	The interrupt can be used for entropy
+ *
+ */
+
+int request_irq(unsigned int irq,
+		irqreturn_t (*handler)(int, void *, struct pt_regs *),
+		unsigned long irqflags,
+		const char * devname,
+		void *dev_id)
+{
+	int retval;
+	struct irqaction *action;
+
+#if 1
+	/*
+	 * Sanity-check: shared interrupts should REALLY pass in
+	 * a real dev-ID, otherwise we'll have trouble later trying
+	 * to figure out which interrupt is which (messes up the
+	 * interrupt freeing logic etc).
+	 */
+	if (irqflags & SA_SHIRQ) {
+		if (!dev_id)
+			printk("Bad boy: %s (at 0x%x) called us without a dev_id!\n",
+			       devname, (&irq)[-1]);
+	}
+#endif
+
+	if ((irq >> NR_IRQ_LOG2_ACTIONS_PER_GROUP) >= NR_IRQ_GROUPS)
+		return -EINVAL;
+	if (!handler)
+		return -EINVAL;
+
+	action = (struct irqaction *) kmalloc(sizeof(struct irqaction), GFP_KERNEL);
+	if (!action)
+		return -ENOMEM;
+
+	action->handler = handler;
+	action->flags = irqflags;
+	action->mask = CPU_MASK_NONE;
+	action->name = devname;
+	action->next = NULL;
+	action->dev_id = dev_id;
+
+	retval = setup_irq(irq, action);
+	if (retval)
+		kfree(action);
+	return retval;
+}
+
+/**
+ *	free_irq - free an interrupt
+ *	@irq: Interrupt line to free
+ *	@dev_id: Device identity to free
+ *
+ *	Remove an interrupt handler. The handler is removed and if the
+ *	interrupt line is no longer in use by any driver it is disabled.
+ *	On a shared IRQ the caller must ensure the interrupt is disabled
+ *	on the card it drives before calling this function. The function
+ *	does not return until any executing interrupts for this IRQ
+ *	have completed.
+ *
+ *	This function may be called from interrupt context.
+ *
+ *	Bugs: Attempting to free an irq in a handler for the same irq hangs
+ *	      the machine.
+ */
+
+void free_irq(unsigned int irq, void *dev_id)
+{
+	struct irq_source *source;
+	struct irq_group *group;
+	struct irq_level *level;
+	struct irqaction **p, **pp;
+	unsigned long flags;
+
+	if ((irq >> NR_IRQ_LOG2_ACTIONS_PER_GROUP) >= NR_IRQ_GROUPS)
+		return;
+
+	group = irq_groups[irq >> NR_IRQ_LOG2_ACTIONS_PER_GROUP];
+	if (!group)
+		BUG();
+
+	source = group->sources[irq & (NR_IRQ_ACTIONS_PER_GROUP - 1)];
+	if (!source)
+		BUG();
+
+	level = source->level;
+	p = &group->actions[irq & (NR_IRQ_ACTIONS_PER_GROUP - 1)];
+
+	spin_lock_irqsave(&level->lock, flags);
+
+	for (pp = p; *pp; pp = &(*pp)->next) {
+		struct irqaction *action = *pp;
+
+		if (action->dev_id != dev_id)
+			continue;
+
+		/* found it - remove from the list of entries */
+		*pp = action->next;
+
+		level->usage--;
+
+		if (p == pp && group->control)
+			group->control(group, irq & (NR_IRQ_ACTIONS_PER_GROUP - 1), 0);
+
+		if (level->usage == 0)
+			__set_MASK(level - frv_irq_levels);
+
+		spin_unlock_irqrestore(&level->lock,flags);
+
+#ifdef CONFIG_SMP
+		/* Wait to make sure it's not being used on another CPU */
+		while (desc->status & IRQ_INPROGRESS)
+			barrier();
+#endif
+		kfree(action);
+		return;
+	}
+}
+
+/*
+ * IRQ autodetection code..
+ *
+ * This depends on the fact that any interrupt that comes in on to an
+ * unassigned IRQ will cause GxICR_DETECT to be set
+ */
+
+static DECLARE_MUTEX(probe_sem);
+
+/**
+ *	probe_irq_on	- begin an interrupt autodetect
+ *
+ *	Commence probing for an interrupt. The interrupts are scanned
+ *	and a mask of potential interrupt lines is returned.
+ *
+ */
+
+unsigned long probe_irq_on(void)
+{
+	down(&probe_sem);
+	return 0;
+}
+
+/*
+ * Return a mask of triggered interrupts (this
+ * can handle only legacy ISA interrupts).
+ */
+
+/**
+ *	probe_irq_mask - scan a bitmap of interrupt lines
+ *	@val:	mask of interrupts to consider
+ *
+ *	Scan the ISA bus interrupt lines and return a bitmap of
+ *	active interrupts. The interrupt probe logic state is then
+ *	returned to its previous value.
+ *
+ *	Note: we need to scan all the irq's even though we will
+ *	only return ISA irq numbers - just so that we reset them
+ *	all to a known state.
+ */
+unsigned int probe_irq_mask(unsigned long xmask)
+{
+	up(&probe_sem);
+	return 0;
+}
+
+/*
+ * Return the one interrupt that triggered (this can
+ * handle any interrupt source).
+ */
+
+/**
+ *	probe_irq_off	- end an interrupt autodetect
+ *	@xmask: mask of potential interrupts (unused)
+ *
+ *	Scans the unused interrupt lines and returns the line which
+ *	appears to have triggered the interrupt. If no interrupt was
+ *	found then zero is returned. If more than one interrupt is
+ *	found then minus the first candidate is returned to indicate
+ *	their is doubt.
+ *
+ *	The interrupt probe logic state is returned to its previous
+ *	value.
+ *
+ *	BUGS: When used in a module (which arguably shouldnt happen)
+ *	nothing prevents two IRQ probe callers from overlapping. The
+ *	results of this are non-optimal.
+ */
+
+int probe_irq_off(unsigned long xmask)
+{
+	up(&probe_sem);
+	return -1;
+}
+
+/* this was setup_x86_irq but it seems pretty generic */
+int setup_irq(unsigned int irq, struct irqaction *new)
+{
+	struct irq_source *source;
+	struct irq_group *group;
+	struct irq_level *level;
+	struct irqaction **p, **pp;
+	unsigned long flags;
+
+	group = irq_groups[irq >> NR_IRQ_LOG2_ACTIONS_PER_GROUP];
+	if (!group)
+		BUG();
+
+	source = group->sources[irq & (NR_IRQ_ACTIONS_PER_GROUP - 1)];
+	if (!source)
+		BUG();
+
+	level = source->level;
+
+	p = &group->actions[irq & (NR_IRQ_ACTIONS_PER_GROUP - 1)];
+
+	/*
+	 * Some drivers like serial.c use request_irq() heavily,
+	 * so we have to be careful not to interfere with a
+	 * running system.
+	 */
+	if (new->flags & SA_SAMPLE_RANDOM) {
+		/*
+		 * This function might sleep, we want to call it first,
+		 * outside of the atomic block.
+		 * Yes, this might clear the entropy pool if the wrong
+		 * driver is attempted to be loaded, without actually
+		 * installing a new handler, but is this really a problem,
+		 * only the sysadmin is able to do this.
+		 */
+		rand_initialize_irq(irq);
+	}
+
+	/* must juggle the interrupt processing stuff with interrupts disabled */
+	spin_lock_irqsave(&level->lock, flags);
+
+	/* can't share interrupts unless all parties agree to */
+	if (level->usage != 0 && !(level->flags & new->flags & SA_SHIRQ)) {
+		spin_unlock_irqrestore(&level->lock,flags);
+		return -EBUSY;
+	}
+
+	/* add new interrupt at end of irq queue */
+	pp = p;
+	while (*pp)
+		pp = &(*pp)->next;
+
+	*pp = new;
+
+	level->usage++;
+	level->flags = new->flags;
+
+	/* turn the interrupts on */
+	if (level->usage == 1)
+		__clr_MASK(level - frv_irq_levels);
+
+	if (p == pp && group->control)
+		group->control(group, irq & (NR_IRQ_ACTIONS_PER_GROUP - 1), 1);
+
+	spin_unlock_irqrestore(&level->lock, flags);
+	register_irq_proc(irq);
+	return 0;
+}
+
+static struct proc_dir_entry * root_irq_dir;
+static struct proc_dir_entry * irq_dir [NR_IRQS];
+
+#define HEX_DIGITS 8
+
+static unsigned int parse_hex_value (const char *buffer,
+				     unsigned long count, unsigned long *ret)
+{
+	unsigned char hexnum [HEX_DIGITS];
+	unsigned long value;
+	int i;
+
+	if (!count)
+		return -EINVAL;
+	if (count > HEX_DIGITS)
+		count = HEX_DIGITS;
+	if (copy_from_user(hexnum, buffer, count))
+		return -EFAULT;
+
+	/*
+	 * Parse the first 8 characters as a hex string, any non-hex char
+	 * is end-of-string. '00e1', 'e1', '00E1', 'E1' are all the same.
+	 */
+	value = 0;
+
+	for (i = 0; i < count; i++) {
+		unsigned int c = hexnum[i];
+
+		switch (c) {
+			case '0' ... '9': c -= '0'; break;
+			case 'a' ... 'f': c -= 'a'-10; break;
+			case 'A' ... 'F': c -= 'A'-10; break;
+		default:
+			goto out;
+		}
+		value = (value << 4) | c;
+	}
+out:
+	*ret = value;
+	return 0;
+}
+
+
+static int prof_cpu_mask_read_proc (char *page, char **start, off_t off,
+			int count, int *eof, void *data)
+{
+	unsigned long *mask = (unsigned long *) data;
+	if (count < HEX_DIGITS+1)
+		return -EINVAL;
+	return sprintf (page, "%08lx\n", *mask);
+}
+
+static int prof_cpu_mask_write_proc (struct file *file, const char *buffer,
+					unsigned long count, void *data)
+{
+	unsigned long *mask = (unsigned long *) data, full_count = count, err;
+	unsigned long new_value;
+
+	show_state();
+	err = parse_hex_value(buffer, count, &new_value);
+	if (err)
+		return err;
+
+	*mask = new_value;
+	return full_count;
+}
+
+#define MAX_NAMELEN 10
+
+static void register_irq_proc (unsigned int irq)
+{
+	char name [MAX_NAMELEN];
+
+	if (!root_irq_dir || irq_dir[irq])
+		return;
+
+	memset(name, 0, MAX_NAMELEN);
+	sprintf(name, "%d", irq);
+
+	/* create /proc/irq/1234 */
+	irq_dir[irq] = proc_mkdir(name, root_irq_dir);
+}
+
+unsigned long prof_cpu_mask = -1;
+
+void init_irq_proc (void)
+{
+	struct proc_dir_entry *entry;
+	int i;
+
+	/* create /proc/irq */
+	root_irq_dir = proc_mkdir("irq", 0);
+
+	/* create /proc/irq/prof_cpu_mask */
+	entry = create_proc_entry("prof_cpu_mask", 0600, root_irq_dir);
+	if (!entry)
+	    return;
+
+	entry->nlink = 1;
+	entry->data = (void *)&prof_cpu_mask;
+	entry->read_proc = prof_cpu_mask_read_proc;
+	entry->write_proc = prof_cpu_mask_write_proc;
+
+	/*
+	 * Create entries for all existing IRQs.
+	 */
+	for (i = 0; i < NR_IRQS; i++)
+		register_irq_proc(i);
+}
+
+/*****************************************************************************/
+/*
+ * initialise the interrupt system
+ */
+void __init init_IRQ(void)
+{
+	route_cpu_irqs();
+	fpga_init();
+#ifdef CONFIG_FUJITSU_MB93493
+	route_mb93493_irqs();
+#endif
+} /* end init_IRQ() */
diff --git a/arch/frv/kernel/kernel_thread.S b/arch/frv/kernel/kernel_thread.S
new file mode 100644
index 0000000..4531c83
--- /dev/null
+++ b/arch/frv/kernel/kernel_thread.S
@@ -0,0 +1,77 @@
+/* kernel_thread.S: kernel thread creation
+ *
+ * Copyright (C) 2003 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 License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/linkage.h>
+#include <asm/unistd.h>
+
+#define CLONE_VM	0x00000100	/* set if VM shared between processes */
+#define	KERN_ERR	"<3>"
+
+	.section .rodata
+kernel_thread_emsg:
+	.asciz	KERN_ERR "failed to create kernel thread: error=%d\n"
+
+	.text
+	.balign		4
+
+###############################################################################
+#
+# Create a kernel thread
+#
+# int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
+#
+###############################################################################
+	.globl		kernel_thread
+	.type		kernel_thread,@function
+kernel_thread:
+	or.p		gr8,gr0,gr4
+	or		gr9,gr0,gr5
+
+	# start by forking the current process, but with shared VM
+	setlos.p	#__NR_clone,gr7		; syscall number
+	ori		gr10,#CLONE_VM,gr8	; first syscall arg	[clone_flags]
+	sethi.p		#0xe4e4,gr9		; second syscall arg	[newsp]
+	setlo		#0xe4e4,gr9
+	setlos.p	#0,gr10			; third syscall arg	[parent_tidptr]
+	setlos		#0,gr11			; fourth syscall arg	[child_tidptr]
+	tira		gr0,#0
+	setlos.p	#4095,gr7
+	andcc		gr8,gr8,gr0,icc0
+	addcc.p		gr8,gr7,gr0,icc1
+	bnelr		icc0,#2
+	bc		icc1,#0,kernel_thread_error
+
+	# now invoke the work function
+	or		gr5,gr0,gr8
+	calll		@(gr4,gr0)
+
+	# and finally exit the thread
+	setlos		#__NR_exit,gr7		; syscall number
+	tira		gr0,#0
+
+kernel_thread_error:
+	subi		sp,#8,sp
+	movsg		lr,gr4
+	sti		gr8,@(sp,#0)
+	sti.p		gr4,@(sp,#4)
+
+	or		gr8,gr0,gr9
+	sethi.p		%hi(kernel_thread_emsg),gr8
+	setlo		%lo(kernel_thread_emsg),gr8
+
+	call		printk
+
+	ldi		@(sp,#4),gr4
+	ldi		@(sp,#0),gr8
+	subi		sp,#8,sp
+	jmpl		@(gr4,gr0)
+
+	.size		kernel_thread,.-kernel_thread
diff --git a/arch/frv/kernel/local.h b/arch/frv/kernel/local.h
new file mode 100644
index 0000000..e947176
--- /dev/null
+++ b/arch/frv/kernel/local.h
@@ -0,0 +1,56 @@
+/* local.h: local definitions
+ *
+ * Copyright (C) 2004 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 License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _FRV_LOCAL_H
+#define _FRV_LOCAL_H
+
+#include <asm/sections.h>
+
+#ifndef __ASSEMBLY__
+
+/* dma.c */
+extern unsigned long frv_dma_inprogress;
+
+extern void frv_dma_pause_all(void);
+extern void frv_dma_resume_all(void);
+
+/* sleep.S */
+extern asmlinkage void frv_cpu_suspend(unsigned long);
+extern asmlinkage void frv_cpu_core_sleep(void);
+
+/* setup.c */
+extern unsigned long __nongprelbss pdm_suspend_mode;
+extern void determine_clocks(int verbose);
+extern int __nongprelbss clock_p0_current;
+extern int __nongprelbss clock_cm_current;
+extern int __nongprelbss clock_cmode_current;
+
+#ifdef CONFIG_PM
+extern int __nongprelbss clock_cmodes_permitted;
+extern unsigned long __nongprelbss clock_bits_settable;
+#define CLOCK_BIT_CM		0x0000000f
+#define CLOCK_BIT_CM_H		0x00000001	/* CLKC.CM can be set to 0 */
+#define CLOCK_BIT_CM_M		0x00000002	/* CLKC.CM can be set to 1 */
+#define CLOCK_BIT_CM_L		0x00000004	/* CLKC.CM can be set to 2 */
+#define CLOCK_BIT_P0		0x00000010	/* CLKC.P0 can be changed */
+#define CLOCK_BIT_CMODE		0x00000020	/* CLKC.CMODE can be changed */
+
+extern void (*__power_switch_wake_setup)(void);
+extern int  (*__power_switch_wake_check)(void);
+extern void (*__power_switch_wake_cleanup)(void);
+#endif
+
+/* time.c */
+extern void time_divisor_init(void);
+
+
+#endif /* __ASSEMBLY__ */
+#endif /* _FRV_LOCAL_H */
diff --git a/arch/frv/kernel/pm-mb93093.c b/arch/frv/kernel/pm-mb93093.c
new file mode 100644
index 0000000..34d01d7
--- /dev/null
+++ b/arch/frv/kernel/pm-mb93093.c
@@ -0,0 +1,66 @@
+/*
+ * FR-V MB93093 Power Management Routines
+ *
+ * Copyright (c) 2004 Red Hat, Inc.
+ *
+ * Written by: msalter@redhat.com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/pm.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/sysctl.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <asm/uaccess.h>
+
+#include <asm/mb86943a.h>
+
+#include "local.h"
+
+static unsigned long imask;
+/*
+ * Setup interrupt masks, etc to enable wakeup by power switch
+ */
+static void mb93093_power_switch_setup(void)
+{
+	/* mask all but FPGA interrupt sources. */
+	imask = *(volatile unsigned long *)0xfeff9820;
+	*(volatile unsigned long *)0xfeff9820 = ~(1 << (IRQ_XIRQ2_LEVEL + 16)) & 0xfffe0000;
+}
+
+/*
+ * Cleanup interrupt masks, etc after wakeup by power switch
+ */
+static void mb93093_power_switch_cleanup(void)
+{
+	*(volatile unsigned long *)0xfeff9820 = imask;
+}
+
+/*
+ * Return non-zero if wakeup irq was caused by power switch
+ */
+static int mb93093_power_switch_check(void)
+{
+	return 1;
+}
+
+/*
+ * Initialize power interface
+ */
+static int __init mb93093_pm_init(void)
+{
+	__power_switch_wake_setup = mb93093_power_switch_setup;
+	__power_switch_wake_check = mb93093_power_switch_check;
+	__power_switch_wake_cleanup = mb93093_power_switch_cleanup;
+	return 0;
+}
+
+__initcall(mb93093_pm_init);
+
diff --git a/arch/frv/kernel/pm.c b/arch/frv/kernel/pm.c
new file mode 100644
index 0000000..1a1e8a1
--- /dev/null
+++ b/arch/frv/kernel/pm.c
@@ -0,0 +1,432 @@
+/*
+ * FR-V Power Management Routines
+ *
+ * Copyright (c) 2004 Red Hat, Inc.
+ *
+ * Based on SA1100 version:
+ * Copyright (c) 2001 Cliff Brake <cbrake@accelent.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/pm.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/sysctl.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <asm/uaccess.h>
+
+#include <asm/mb86943a.h>
+
+#include "local.h"
+
+void (*pm_power_off)(void);
+
+extern void frv_change_cmode(int);
+
+/*
+ * Debug macros
+ */
+#define DEBUG
+
+int pm_do_suspend(void)
+{
+	local_irq_disable();
+
+	__set_LEDS(0xb1);
+
+	/* go zzz */
+	frv_cpu_suspend(pdm_suspend_mode);
+
+	__set_LEDS(0xb2);
+
+	local_irq_enable();
+
+	return 0;
+}
+
+static unsigned long __irq_mask;
+
+/*
+ * Setup interrupt masks, etc to enable wakeup by power switch
+ */
+static void __default_power_switch_setup(void)
+{
+	/* default is to mask all interrupt sources. */
+	__irq_mask = *(unsigned long *)0xfeff9820;
+	*(unsigned long *)0xfeff9820 = 0xfffe0000;
+}
+
+/*
+ * Cleanup interrupt masks, etc after wakeup by power switch
+ */
+static void __default_power_switch_cleanup(void)
+{
+	*(unsigned long *)0xfeff9820 = __irq_mask;
+}
+
+/*
+ * Return non-zero if wakeup irq was caused by power switch
+ */
+static int __default_power_switch_check(void)
+{
+	return 1;
+}
+
+void (*__power_switch_wake_setup)(void) = __default_power_switch_setup;
+int  (*__power_switch_wake_check)(void) = __default_power_switch_check;
+void (*__power_switch_wake_cleanup)(void) = __default_power_switch_cleanup;
+
+int pm_do_bus_sleep(void)
+{
+	local_irq_disable();
+
+	/*
+         * Here is where we need some platform-dependent setup
+	 * of the interrupt state so that appropriate wakeup
+	 * sources are allowed and all others are masked.
+	 */
+	__power_switch_wake_setup();
+
+	__set_LEDS(0xa1);
+
+	/* go zzz
+	 *
+	 * This is in a loop in case power switch shares an irq with other
+	 * devices. The wake_check() tells us if we need to finish waking
+	 * or go back to sleep.
+	 */
+	do {
+		frv_cpu_suspend(HSR0_PDM_BUS_SLEEP);
+	} while (__power_switch_wake_check && !__power_switch_wake_check());
+
+	__set_LEDS(0xa2);
+
+	/*
+         * Here is where we need some platform-dependent restore
+	 * of the interrupt state prior to being called.
+	 */
+	__power_switch_wake_cleanup();
+
+	local_irq_enable();
+
+	return 0;
+}
+
+unsigned long sleep_phys_sp(void *sp)
+{
+	return virt_to_phys(sp);
+}
+
+#ifdef CONFIG_SYSCTL
+/*
+ * Use a temporary sysctl number. Horrid, but will be cleaned up in 2.6
+ * when all the PM interfaces exist nicely.
+ */
+#define CTL_PM 9899
+#define CTL_PM_SUSPEND 1
+#define CTL_PM_CMODE 2
+#define CTL_PM_P0 4
+#define CTL_PM_CM 5
+
+static int user_atoi(char *ubuf, size_t len)
+{
+	char buf[16];
+	unsigned long ret;
+
+	if (len > 15)
+		return -EINVAL;
+
+	if (copy_from_user(buf, ubuf, len))
+		return -EFAULT;
+
+	buf[len] = 0;
+	ret = simple_strtoul(buf, NULL, 0);
+	if (ret > INT_MAX)
+		return -ERANGE;
+	return ret;
+}
+
+/*
+ * Send us to sleep.
+ */
+static int sysctl_pm_do_suspend(ctl_table *ctl, int write, struct file *filp,
+				void *buffer, size_t *lenp, loff_t *fpos)
+{
+	int retval, mode;
+
+	if (*lenp <= 0)
+		return -EIO;
+
+	mode = user_atoi(buffer, *lenp);
+	if ((mode != 1) && (mode != 5))
+		return -EINVAL;
+
+	retval = pm_send_all(PM_SUSPEND, (void *)3);
+
+	if (retval == 0) {
+		if (mode == 5)
+		    retval = pm_do_bus_sleep();
+		else
+		    retval = pm_do_suspend();
+		pm_send_all(PM_RESUME, (void *)0);
+	}
+
+	return retval;
+}
+
+static int try_set_cmode(int new_cmode)
+{
+	if (new_cmode > 15)
+		return -EINVAL;
+	if (!(clock_cmodes_permitted & (1<<new_cmode)))
+		return -EINVAL;
+
+	/* tell all the drivers we're suspending */
+	pm_send_all(PM_SUSPEND, (void *)3);
+
+	/* now change cmode */
+	local_irq_disable();
+	frv_dma_pause_all();
+
+	frv_change_cmode(new_cmode);
+
+	determine_clocks(0);
+	time_divisor_init();
+
+#ifdef DEBUG
+	determine_clocks(1);
+#endif
+	frv_dma_resume_all();
+	local_irq_enable();
+
+	/* tell all the drivers we're resuming */
+	pm_send_all(PM_RESUME, (void *)0);
+	return 0;
+}
+
+
+static int cmode_procctl(ctl_table *ctl, int write, struct file *filp,
+			 void *buffer, size_t *lenp, loff_t *fpos)
+{
+	int new_cmode;
+
+	if (!write)
+		return proc_dointvec(ctl, write, filp, buffer, lenp, fpos);
+
+	new_cmode = user_atoi(buffer, *lenp);
+
+	return try_set_cmode(new_cmode)?:*lenp;
+}
+
+static int cmode_sysctl(ctl_table *table, int *name, int nlen,
+			void *oldval, size_t *oldlenp,
+			void *newval, size_t newlen, void **context)
+{
+	if (oldval && oldlenp) {
+		size_t oldlen;
+
+		if (get_user(oldlen, oldlenp))
+			return -EFAULT;
+
+		if (oldlen != sizeof(int))
+			return -EINVAL;
+
+		if (put_user(clock_cmode_current, (unsigned int *)oldval) ||
+		    put_user(sizeof(int), oldlenp))
+			return -EFAULT;
+	}
+	if (newval && newlen) {
+		int new_cmode;
+
+		if (newlen != sizeof(int))
+			return -EINVAL;
+
+		if (get_user(new_cmode, (int *)newval))
+			return -EFAULT;
+
+		return try_set_cmode(new_cmode)?:1;
+	}
+	return 1;
+}
+
+static int try_set_p0(int new_p0)
+{
+	unsigned long flags, clkc;
+
+	if (new_p0 < 0 || new_p0 > 1)
+		return -EINVAL;
+
+	local_irq_save(flags);
+	__set_PSR(flags & ~PSR_ET);
+
+	frv_dma_pause_all();
+
+	clkc = __get_CLKC();
+	if (new_p0)
+		clkc |= CLKC_P0;
+	else
+		clkc &= ~CLKC_P0;
+	__set_CLKC(clkc);
+
+	determine_clocks(0);
+	time_divisor_init();
+
+#ifdef DEBUG
+	determine_clocks(1);
+#endif
+	frv_dma_resume_all();
+	local_irq_restore(flags);
+	return 0;
+}
+
+static int try_set_cm(int new_cm)
+{
+	unsigned long flags, clkc;
+
+	if (new_cm < 0 || new_cm > 1)
+		return -EINVAL;
+
+	local_irq_save(flags);
+	__set_PSR(flags & ~PSR_ET);
+
+	frv_dma_pause_all();
+
+	clkc = __get_CLKC();
+	clkc &= ~CLKC_CM;
+	clkc |= new_cm;
+	__set_CLKC(clkc);
+
+	determine_clocks(0);
+	time_divisor_init();
+
+#if 1 //def DEBUG
+	determine_clocks(1);
+#endif
+
+	frv_dma_resume_all();
+	local_irq_restore(flags);
+	return 0;
+}
+
+static int p0_procctl(ctl_table *ctl, int write, struct file *filp,
+		      void *buffer, size_t *lenp, loff_t *fpos)
+{
+	int new_p0;
+
+	if (!write)
+		return proc_dointvec(ctl, write, filp, buffer, lenp, fpos);
+
+	new_p0 = user_atoi(buffer, *lenp);
+
+	return try_set_p0(new_p0)?:*lenp;
+}
+
+static int p0_sysctl(ctl_table *table, int *name, int nlen,
+		     void *oldval, size_t *oldlenp,
+		     void *newval, size_t newlen, void **context)
+{
+	if (oldval && oldlenp) {
+		size_t oldlen;
+
+		if (get_user(oldlen, oldlenp))
+			return -EFAULT;
+
+		if (oldlen != sizeof(int))
+			return -EINVAL;
+
+		if (put_user(clock_p0_current, (unsigned int *)oldval) ||
+		    put_user(sizeof(int), oldlenp))
+			return -EFAULT;
+	}
+	if (newval && newlen) {
+		int new_p0;
+
+		if (newlen != sizeof(int))
+			return -EINVAL;
+
+		if (get_user(new_p0, (int *)newval))
+			return -EFAULT;
+
+		return try_set_p0(new_p0)?:1;
+	}
+	return 1;
+}
+
+static int cm_procctl(ctl_table *ctl, int write, struct file *filp,
+		      void *buffer, size_t *lenp, loff_t *fpos)
+{
+	int new_cm;
+
+	if (!write)
+		return proc_dointvec(ctl, write, filp, buffer, lenp, fpos);
+
+	new_cm = user_atoi(buffer, *lenp);
+
+	return try_set_cm(new_cm)?:*lenp;
+}
+
+static int cm_sysctl(ctl_table *table, int *name, int nlen,
+		     void *oldval, size_t *oldlenp,
+		     void *newval, size_t newlen, void **context)
+{
+	if (oldval && oldlenp) {
+		size_t oldlen;
+
+		if (get_user(oldlen, oldlenp))
+			return -EFAULT;
+
+		if (oldlen != sizeof(int))
+			return -EINVAL;
+
+		if (put_user(clock_cm_current, (unsigned int *)oldval) ||
+		    put_user(sizeof(int), oldlenp))
+			return -EFAULT;
+	}
+	if (newval && newlen) {
+		int new_cm;
+
+		if (newlen != sizeof(int))
+			return -EINVAL;
+
+		if (get_user(new_cm, (int *)newval))
+			return -EFAULT;
+
+		return try_set_cm(new_cm)?:1;
+	}
+	return 1;
+}
+
+
+static struct ctl_table pm_table[] =
+{
+	{CTL_PM_SUSPEND, "suspend", NULL, 0, 0200, NULL, &sysctl_pm_do_suspend},
+	{CTL_PM_CMODE, "cmode", &clock_cmode_current, sizeof(int), 0644, NULL, &cmode_procctl, &cmode_sysctl, NULL},
+	{CTL_PM_P0, "p0", &clock_p0_current, sizeof(int), 0644, NULL, &p0_procctl, &p0_sysctl, NULL},
+	{CTL_PM_CM, "cm", &clock_cm_current, sizeof(int), 0644, NULL, &cm_procctl, &cm_sysctl, NULL},
+	{0}
+};
+
+static struct ctl_table pm_dir_table[] =
+{
+	{CTL_PM, "pm", NULL, 0, 0555, pm_table},
+	{0}
+};
+
+/*
+ * Initialize power interface
+ */
+static int __init pm_init(void)
+{
+	register_sysctl_table(pm_dir_table, 1);
+	return 0;
+}
+
+__initcall(pm_init);
+
+#endif
diff --git a/arch/frv/kernel/process.c b/arch/frv/kernel/process.c
new file mode 100644
index 0000000..3001b82
--- /dev/null
+++ b/arch/frv/kernel/process.c
@@ -0,0 +1,388 @@
+/* process.c: FRV specific parts of process handling
+ *
+ * Copyright (C) 2003-5 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ * - Derived from arch/m68k/kernel/process.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+#include <linux/stddef.h>
+#include <linux/unistd.h>
+#include <linux/ptrace.h>
+#include <linux/slab.h>
+#include <linux/user.h>
+#include <linux/elf.h>
+#include <linux/reboot.h>
+#include <linux/interrupt.h>
+
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <asm/setup.h>
+#include <asm/pgtable.h>
+#include <asm/gdb-stub.h>
+#include <asm/mb-regs.h>
+
+#include "local.h"
+
+asmlinkage void ret_from_fork(void);
+
+#include <asm/pgalloc.h>
+
+struct task_struct *alloc_task_struct(void)
+{
+	struct task_struct *p = kmalloc(THREAD_SIZE, GFP_KERNEL);
+	if (p)
+		atomic_set((atomic_t *)(p+1), 1);
+	return p;
+}
+
+void free_task_struct(struct task_struct *p)
+{
+	if (atomic_dec_and_test((atomic_t *)(p+1)))
+		kfree(p);
+}
+
+static void core_sleep_idle(void)
+{
+#ifdef LED_DEBUG_SLEEP
+	/* Show that we're sleeping... */
+	__set_LEDS(0x55aa);
+#endif
+	frv_cpu_core_sleep();
+#ifdef LED_DEBUG_SLEEP
+	/* ... and that we woke up */
+	__set_LEDS(0);
+#endif
+	mb();
+}
+
+void (*idle)(void) = core_sleep_idle;
+
+/*
+ * The idle thread. There's no useful work to be
+ * done, so just try to conserve power and have a
+ * low exit latency (ie sit in a loop waiting for
+ * somebody to say that they'd like to reschedule)
+ */
+void cpu_idle(void)
+{
+	/* endless idle loop with no priority at all */
+	while (1) {
+		while (!need_resched()) {
+			irq_stat[smp_processor_id()].idle_timestamp = jiffies;
+
+			if (!frv_dma_inprogress && idle)
+				idle();
+		}
+
+		schedule();
+	}
+}
+
+void machine_restart(char * __unused)
+{
+	unsigned long reset_addr;
+#ifdef CONFIG_GDBSTUB
+	gdbstub_exit(0);
+#endif
+
+	if (PSR_IMPLE(__get_PSR()) == PSR_IMPLE_FR551)
+		reset_addr = 0xfefff500;
+	else
+		reset_addr = 0xfeff0500;
+
+	/* Software reset. */
+	asm volatile("      dcef @(gr0,gr0),1 ! membar !"
+		     "      sti     %1,@(%0,0) !"
+		     "      nop ! nop ! nop ! nop ! nop ! "
+		     "      nop ! nop ! nop ! nop ! nop ! "
+		     "      nop ! nop ! nop ! nop ! nop ! "
+		     "      nop ! nop ! nop ! nop ! nop ! "
+		     : : "r" (reset_addr), "r" (1) );
+
+	for (;;)
+		;
+}
+
+void machine_halt(void)
+{
+#ifdef CONFIG_GDBSTUB
+	gdbstub_exit(0);
+#endif
+
+	for (;;);
+}
+
+void machine_power_off(void)
+{
+#ifdef CONFIG_GDBSTUB
+	gdbstub_exit(0);
+#endif
+
+	for (;;);
+}
+
+void flush_thread(void)
+{
+#if 0 //ndef NO_FPU
+	unsigned long zero = 0;
+#endif
+	set_fs(USER_DS);
+}
+
+inline unsigned long user_stack(const struct pt_regs *regs)
+{
+	while (regs->next_frame)
+		regs = regs->next_frame;
+	return user_mode(regs) ? regs->sp : 0;
+}
+
+asmlinkage int sys_fork(void)
+{
+#ifndef CONFIG_MMU
+	/* fork almost works, enough to trick you into looking elsewhere:-( */
+	return -EINVAL;
+#else
+	return do_fork(SIGCHLD, user_stack(__frame), __frame, 0, NULL, NULL);
+#endif
+}
+
+asmlinkage int sys_vfork(void)
+{
+	return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, user_stack(__frame), __frame, 0,
+		       NULL, NULL);
+}
+
+/*****************************************************************************/
+/*
+ * clone a process
+ * - tlsptr is retrieved by copy_thread()
+ */
+asmlinkage int sys_clone(unsigned long clone_flags, unsigned long newsp,
+			 int __user *parent_tidptr, int __user *child_tidptr,
+			 int __user *tlsptr)
+{
+	if (!newsp)
+		newsp = user_stack(__frame);
+	return do_fork(clone_flags, newsp, __frame, 0, parent_tidptr, child_tidptr);
+} /* end sys_clone() */
+
+/*****************************************************************************/
+/*
+ * This gets called before we allocate a new thread and copy
+ * the current task into it.
+ */
+void prepare_to_copy(struct task_struct *tsk)
+{
+	//unlazy_fpu(tsk);
+} /* end prepare_to_copy() */
+
+/*****************************************************************************/
+/*
+ * set up the kernel stack and exception frames for a new process
+ */
+int copy_thread(int nr, unsigned long clone_flags,
+		unsigned long usp, unsigned long topstk,
+		struct task_struct *p, struct pt_regs *regs)
+{
+	struct pt_regs *childregs0, *childregs, *regs0;
+
+	regs0 = __kernel_frame0_ptr;
+	childregs0 = (struct pt_regs *)
+		((unsigned long) p->thread_info + THREAD_SIZE - USER_CONTEXT_SIZE);
+	childregs = childregs0;
+
+	/* set up the userspace frame (the only place that the USP is stored) */
+	*childregs0 = *regs0;
+
+	childregs0->gr8		= 0;
+	childregs0->sp		= usp;
+	childregs0->next_frame	= NULL;
+
+	/* set up the return kernel frame if called from kernel_thread() */
+	if (regs != regs0) {
+		childregs--;
+		*childregs = *regs;
+		childregs->sp = (unsigned long) childregs0;
+		childregs->next_frame = childregs0;
+		childregs->gr15 = (unsigned long) p->thread_info;
+		childregs->gr29 = (unsigned long) p;
+	}
+
+	p->set_child_tid = p->clear_child_tid = NULL;
+
+	p->thread.frame	 = childregs;
+	p->thread.curr	 = p;
+	p->thread.sp	 = (unsigned long) childregs;
+	p->thread.fp	 = 0;
+	p->thread.lr	 = 0;
+	p->thread.pc	 = (unsigned long) ret_from_fork;
+	p->thread.frame0 = childregs0;
+
+	/* the new TLS pointer is passed in as arg #5 to sys_clone() */
+	if (clone_flags & CLONE_SETTLS)
+		childregs->gr29 = childregs->gr12;
+
+	save_user_regs(p->thread.user);
+
+	return 0;
+} /* end copy_thread() */
+
+/*
+ * fill in the user structure for a core dump..
+ */
+void dump_thread(struct pt_regs *regs, struct user *dump)
+{
+#if 0
+	/* changed the size calculations - should hopefully work better. lbt */
+	dump->magic = CMAGIC;
+	dump->start_code = 0;
+	dump->start_stack = user_stack(regs) & ~(PAGE_SIZE - 1);
+	dump->u_tsize = ((unsigned long) current->mm->end_code) >> PAGE_SHIFT;
+	dump->u_dsize = ((unsigned long) (current->mm->brk + (PAGE_SIZE-1))) >> PAGE_SHIFT;
+	dump->u_dsize -= dump->u_tsize;
+	dump->u_ssize = 0;
+
+	if (dump->start_stack < TASK_SIZE)
+		dump->u_ssize = ((unsigned long) (TASK_SIZE - dump->start_stack)) >> PAGE_SHIFT;
+
+	dump->regs = *(struct user_context *) regs;
+#endif
+}
+
+/*
+ * sys_execve() executes a new program.
+ */
+asmlinkage int sys_execve(char *name, char **argv, char **envp)
+{
+	int error;
+	char * filename;
+
+	lock_kernel();
+	filename = getname(name);
+	error = PTR_ERR(filename);
+	if (IS_ERR(filename))
+		goto out;
+	error = do_execve(filename, argv, envp, __frame);
+	putname(filename);
+ out:
+	unlock_kernel();
+	return error;
+}
+
+unsigned long get_wchan(struct task_struct *p)
+{
+	struct pt_regs *regs0;
+	unsigned long fp, pc;
+	unsigned long stack_limit;
+	int count = 0;
+	if (!p || p == current || p->state == TASK_RUNNING)
+		return 0;
+
+	stack_limit = (unsigned long) (p + 1);
+	fp = p->thread.fp;
+	regs0 = p->thread.frame0;
+
+	do {
+		if (fp < stack_limit || fp >= (unsigned long) regs0 || fp & 3)
+			return 0;
+
+		pc = ((unsigned long *) fp)[2];
+
+		/* FIXME: This depends on the order of these functions. */
+		if (!in_sched_functions(pc))
+			return pc;
+
+		fp = *(unsigned long *) fp;
+	} while (count++ < 16);
+
+	return 0;
+}
+
+unsigned long thread_saved_pc(struct task_struct *tsk)
+{
+	/* Check whether the thread is blocked in resume() */
+	if (in_sched_functions(tsk->thread.pc))
+		return ((unsigned long *)tsk->thread.fp)[2];
+	else
+		return tsk->thread.pc;
+}
+
+int elf_check_arch(const struct elf32_hdr *hdr)
+{
+	unsigned long hsr0 = __get_HSR(0);
+	unsigned long psr = __get_PSR();
+
+	if (hdr->e_machine != EM_FRV)
+		return 0;
+
+	switch (hdr->e_flags & EF_FRV_GPR_MASK) {
+	case EF_FRV_GPR64:
+		if ((hsr0 & HSR0_GRN) == HSR0_GRN_32)
+			return 0;
+	case EF_FRV_GPR32:
+	case 0:
+		break;
+	default:
+		return 0;
+	}
+
+	switch (hdr->e_flags & EF_FRV_FPR_MASK) {
+	case EF_FRV_FPR64:
+		if ((hsr0 & HSR0_FRN) == HSR0_FRN_32)
+			return 0;
+	case EF_FRV_FPR32:
+	case EF_FRV_FPR_NONE:
+	case 0:
+		break;
+	default:
+		return 0;
+	}
+
+	if ((hdr->e_flags & EF_FRV_MULADD) == EF_FRV_MULADD)
+		if (PSR_IMPLE(psr) != PSR_IMPLE_FR405 &&
+		    PSR_IMPLE(psr) != PSR_IMPLE_FR451)
+			return 0;
+
+	switch (hdr->e_flags & EF_FRV_CPU_MASK) {
+	case EF_FRV_CPU_GENERIC:
+		break;
+	case EF_FRV_CPU_FR300:
+	case EF_FRV_CPU_SIMPLE:
+	case EF_FRV_CPU_TOMCAT:
+	default:
+		return 0;
+	case EF_FRV_CPU_FR400:
+		if (PSR_IMPLE(psr) != PSR_IMPLE_FR401 &&
+		    PSR_IMPLE(psr) != PSR_IMPLE_FR405 &&
+		    PSR_IMPLE(psr) != PSR_IMPLE_FR451 &&
+		    PSR_IMPLE(psr) != PSR_IMPLE_FR551)
+			return 0;
+		break;
+	case EF_FRV_CPU_FR450:
+		if (PSR_IMPLE(psr) != PSR_IMPLE_FR451)
+			return 0;
+		break;
+	case EF_FRV_CPU_FR500:
+		if (PSR_IMPLE(psr) != PSR_IMPLE_FR501)
+			return 0;
+		break;
+	case EF_FRV_CPU_FR550:
+		if (PSR_IMPLE(psr) != PSR_IMPLE_FR551)
+			return 0;
+		break;
+	}
+
+	return 1;
+}
diff --git a/arch/frv/kernel/ptrace.c b/arch/frv/kernel/ptrace.c
new file mode 100644
index 0000000..2a0efb7
--- /dev/null
+++ b/arch/frv/kernel/ptrace.c
@@ -0,0 +1,764 @@
+/* ptrace.c: FRV specific parts of process tracing
+ *
+ * Copyright (C) 2003-5 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ * - Derived from arch/m68k/kernel/ptrace.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+#include <linux/errno.h>
+#include <linux/ptrace.h>
+#include <linux/user.h>
+#include <linux/config.h>
+#include <linux/security.h>
+
+#include <asm/uaccess.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/system.h>
+#include <asm/processor.h>
+#include <asm/unistd.h>
+
+/*
+ * does not yet catch signals sent when the child dies.
+ * in exit.c or in signal.c.
+ */
+
+/*
+ * Get contents of register REGNO in task TASK.
+ */
+static inline long get_reg(struct task_struct *task, int regno)
+{
+	struct user_context *user = task->thread.user;
+
+	if (regno < 0 || regno >= PT__END)
+		return 0;
+
+	return ((unsigned long *) user)[regno];
+}
+
+/*
+ * Write contents of register REGNO in task TASK.
+ */
+static inline int put_reg(struct task_struct *task, int regno,
+			  unsigned long data)
+{
+	struct user_context *user = task->thread.user;
+
+	if (regno < 0 || regno >= PT__END)
+		return -EIO;
+
+	switch (regno) {
+	case PT_GR(0):
+		return 0;
+	case PT_PSR:
+	case PT__STATUS:
+		return -EIO;
+	default:
+		((unsigned long *) user)[regno] = data;
+		return 0;
+	}
+}
+
+/*
+ * check that an address falls within the bounds of the target process's memory mappings
+ */
+static inline int is_user_addr_valid(struct task_struct *child,
+				     unsigned long start, unsigned long len)
+{
+#ifdef CONFIG_MMU
+	if (start >= PAGE_OFFSET || len > PAGE_OFFSET - start)
+		return -EIO;
+	return 0;
+#else
+	struct vm_list_struct *vml;
+
+	for (vml = child->mm->context.vmlist; vml; vml = vml->next)
+		if (start >= vml->vma->vm_start && start + len <= vml->vma->vm_end)
+			return 0;
+
+	return -EIO;
+#endif
+}
+
+/*
+ * Called by kernel/ptrace.c when detaching..
+ *
+ * Control h/w single stepping
+ */
+void ptrace_disable(struct task_struct *child)
+{
+	child->thread.frame0->__status &= ~REG__STATUS_STEP;
+}
+
+void ptrace_enable(struct task_struct *child)
+{
+	child->thread.frame0->__status |= REG__STATUS_STEP;
+}
+
+asmlinkage int sys_ptrace(long request, long pid, long addr, long data)
+{
+	struct task_struct *child;
+	unsigned long tmp;
+	int ret;
+
+	lock_kernel();
+	ret = -EPERM;
+	if (request == PTRACE_TRACEME) {
+		/* are we already being traced? */
+		if (current->ptrace & PT_PTRACED)
+			goto out;
+		ret = security_ptrace(current->parent, current);
+		if (ret)
+			goto out;
+		/* set the ptrace bit in the process flags. */
+		current->ptrace |= PT_PTRACED;
+		ret = 0;
+		goto out;
+	}
+	ret = -ESRCH;
+	read_lock(&tasklist_lock);
+	child = find_task_by_pid(pid);
+	if (child)
+		get_task_struct(child);
+	read_unlock(&tasklist_lock);
+	if (!child)
+		goto out;
+
+	ret = -EPERM;
+	if (pid == 1)		/* you may not mess with init */
+		goto out_tsk;
+
+	if (request == PTRACE_ATTACH) {
+		ret = ptrace_attach(child);
+		goto out_tsk;
+	}
+
+	ret = ptrace_check_attach(child, request == PTRACE_KILL);
+	if (ret < 0)
+		goto out_tsk;
+
+	switch (request) {
+		/* when I and D space are separate, these will need to be fixed. */
+	case PTRACE_PEEKTEXT: /* read word at location addr. */
+	case PTRACE_PEEKDATA: {
+		int copied;
+
+		ret = -EIO;
+		if (is_user_addr_valid(child, addr, sizeof(tmp)) < 0)
+			break;
+
+		copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
+		if (copied != sizeof(tmp))
+			break;
+
+		ret = put_user(tmp,(unsigned long *) data);
+		break;
+	}
+
+		/* read the word at location addr in the USER area. */
+	case PTRACE_PEEKUSR: {
+		tmp = 0;
+		ret = -EIO;
+		if ((addr & 3) || addr < 0)
+			break;
+
+		ret = 0;
+		switch (addr >> 2) {
+		case 0 ... PT__END - 1:
+			tmp = get_reg(child, addr >> 2);
+			break;
+
+		case PT__END + 0:
+			tmp = child->mm->end_code - child->mm->start_code;
+			break;
+
+		case PT__END + 1:
+			tmp = child->mm->end_data - child->mm->start_data;
+			break;
+
+		case PT__END + 2:
+			tmp = child->mm->start_stack - child->mm->start_brk;
+			break;
+
+		case PT__END + 3:
+			tmp = child->mm->start_code;
+			break;
+
+		case PT__END + 4:
+			tmp = child->mm->start_stack;
+			break;
+
+		default:
+			ret = -EIO;
+			break;
+		}
+
+		if (ret == 0)
+			ret = put_user(tmp, (unsigned long *) data);
+		break;
+	}
+
+		/* when I and D space are separate, this will have to be fixed. */
+	case PTRACE_POKETEXT: /* write the word at location addr. */
+	case PTRACE_POKEDATA:
+		ret = -EIO;
+		if (is_user_addr_valid(child, addr, sizeof(tmp)) < 0)
+			break;
+		if (access_process_vm(child, addr, &data, sizeof(data), 1) != sizeof(data))
+			break;
+		ret = 0;
+		break;
+
+	case PTRACE_POKEUSR: /* write the word at location addr in the USER area */
+		ret = -EIO;
+		if ((addr & 3) || addr < 0)
+			break;
+
+		ret = 0;
+		switch (addr >> 2) {
+		case 0 ... PT__END-1:
+			ret = put_reg(child, addr >> 2, data);
+			break;
+
+		default:
+			ret = -EIO;
+			break;
+		}
+		break;
+
+	case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */
+	case PTRACE_CONT: /* restart after signal. */
+		ret = -EIO;
+		if ((unsigned long) data > _NSIG)
+			break;
+		if (request == PTRACE_SYSCALL)
+			set_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
+		else
+			clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
+		child->exit_code = data;
+		ptrace_disable(child);
+		wake_up_process(child);
+		ret = 0;
+		break;
+
+		/* make the child exit.  Best I can do is send it a sigkill.
+		 * perhaps it should be put in the status that it wants to
+		 * exit.
+		 */
+	case PTRACE_KILL:
+		ret = 0;
+		if (child->exit_state == EXIT_ZOMBIE)	/* already dead */
+			break;
+		child->exit_code = SIGKILL;
+		clear_tsk_thread_flag(child, TIF_SINGLESTEP);
+		ptrace_disable(child);
+		wake_up_process(child);
+		break;
+
+	case PTRACE_SINGLESTEP:  /* set the trap flag. */
+		ret = -EIO;
+		if ((unsigned long) data > _NSIG)
+			break;
+		clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
+		ptrace_enable(child);
+		child->exit_code = data;
+		wake_up_process(child);
+		ret = 0;
+		break;
+
+	case PTRACE_DETACH:	/* detach a process that was attached. */
+		ret = ptrace_detach(child, data);
+		break;
+
+	case PTRACE_GETREGS: { /* Get all integer regs from the child. */
+		int i;
+		for (i = 0; i < PT__GPEND; i++) {
+			tmp = get_reg(child, i);
+			if (put_user(tmp, (unsigned long *) data)) {
+				ret = -EFAULT;
+				break;
+			}
+			data += sizeof(long);
+		}
+		ret = 0;
+		break;
+	}
+
+	case PTRACE_SETREGS: { /* Set all integer regs in the child. */
+		int i;
+		for (i = 0; i < PT__GPEND; i++) {
+			if (get_user(tmp, (unsigned long *) data)) {
+				ret = -EFAULT;
+				break;
+			}
+			put_reg(child, i, tmp);
+			data += sizeof(long);
+		}
+		ret = 0;
+		break;
+	}
+
+	case PTRACE_GETFPREGS: { /* Get the child FP/Media state. */
+		ret = 0;
+		if (copy_to_user((void *) data,
+				 &child->thread.user->f,
+				 sizeof(child->thread.user->f)))
+			ret = -EFAULT;
+		break;
+	}
+
+	case PTRACE_SETFPREGS: { /* Set the child FP/Media state. */
+		ret = 0;
+		if (copy_from_user(&child->thread.user->f,
+				   (void *) data,
+				   sizeof(child->thread.user->f)))
+			ret = -EFAULT;
+		break;
+	}
+
+	case PTRACE_GETFDPIC:
+		tmp = 0;
+		switch (addr) {
+		case PTRACE_GETFDPIC_EXEC:
+			tmp = child->mm->context.exec_fdpic_loadmap;
+			break;
+		case PTRACE_GETFDPIC_INTERP:
+			tmp = child->mm->context.interp_fdpic_loadmap;
+			break;
+		default:
+			break;
+		}
+
+		ret = 0;
+		if (put_user(tmp, (unsigned long *) data)) {
+			ret = -EFAULT;
+			break;
+		}
+		break;
+
+	default:
+		ret = -EIO;
+		break;
+	}
+out_tsk:
+	put_task_struct(child);
+out:
+	unlock_kernel();
+	return ret;
+}
+
+int __nongprelbss kstrace;
+
+static const struct {
+	const char	*name;
+	unsigned	argmask;
+} __syscall_name_table[NR_syscalls] = {
+	[0]	= { "restart_syscall"			},
+	[1]	= { "exit",		0x000001	},
+	[2]	= { "fork",		0xffffff	},
+	[3]	= { "read",		0x000141	},
+	[4]	= { "write",		0x000141	},
+	[5]	= { "open",		0x000235	},
+	[6]	= { "close",		0x000001	},
+	[7]	= { "waitpid",		0x000141	},
+	[8]	= { "creat",		0x000025	},
+	[9]	= { "link",		0x000055	},
+	[10]	= { "unlink",		0x000005	},
+	[11]	= { "execve",		0x000445	},
+	[12]	= { "chdir",		0x000005	},
+	[13]	= { "time",		0x000004	},
+	[14]	= { "mknod",		0x000325	},
+	[15]	= { "chmod",		0x000025	},
+	[16]	= { "lchown",		0x000025	},
+	[17]	= { "break" },
+	[18]	= { "oldstat",		0x000045	},
+	[19]	= { "lseek",		0x000131	},
+	[20]	= { "getpid",		0xffffff	},
+	[21]	= { "mount",		0x043555	},
+	[22]	= { "umount",		0x000005	},
+	[23]	= { "setuid",		0x000001	},
+	[24]	= { "getuid",		0xffffff	},
+	[25]	= { "stime",		0x000004	},
+	[26]	= { "ptrace",		0x004413	},
+	[27]	= { "alarm",		0x000001	},
+	[28]	= { "oldfstat",		0x000041	},
+	[29]	= { "pause",		0xffffff	},
+	[30]	= { "utime",		0x000045	},
+	[31]	= { "stty" },
+	[32]	= { "gtty" },
+	[33]	= { "access",		0x000025	},
+	[34]	= { "nice",		0x000001	},
+	[35]	= { "ftime" },
+	[36]	= { "sync",		0xffffff	},
+	[37]	= { "kill",		0x000011	},
+	[38]	= { "rename",		0x000055	},
+	[39]	= { "mkdir",		0x000025	},
+	[40]	= { "rmdir",		0x000005	},
+	[41]	= { "dup",		0x000001	},
+	[42]	= { "pipe",		0x000004	},
+	[43]	= { "times",		0x000004	},
+	[44]	= { "prof" },
+	[45]	= { "brk",		0x000004	},
+	[46]	= { "setgid",		0x000001	},
+	[47]	= { "getgid",		0xffffff	},
+	[48]	= { "signal",		0x000041	},
+	[49]	= { "geteuid",		0xffffff	},
+	[50]	= { "getegid",		0xffffff	},
+	[51]	= { "acct",		0x000005	},
+	[52]	= { "umount2",		0x000035	},
+	[53]	= { "lock" },
+	[54]	= { "ioctl",		0x000331	},
+	[55]	= { "fcntl",		0x000331	},
+	[56]	= { "mpx" },
+	[57]	= { "setpgid",		0x000011	},
+	[58]	= { "ulimit" },
+	[60]	= { "umask",		0x000002	},
+	[61]	= { "chroot",		0x000005	},
+	[62]	= { "ustat",		0x000043	},
+	[63]	= { "dup2",		0x000011	},
+	[64]	= { "getppid",		0xffffff	},
+	[65]	= { "getpgrp",		0xffffff	},
+	[66]	= { "setsid",		0xffffff	},
+	[67]	= { "sigaction" },
+	[68]	= { "sgetmask" },
+	[69]	= { "ssetmask" },
+	[70]	= { "setreuid" },
+	[71]	= { "setregid" },
+	[72]	= { "sigsuspend" },
+	[73]	= { "sigpending" },
+	[74]	= { "sethostname" },
+	[75]	= { "setrlimit" },
+	[76]	= { "getrlimit" },
+	[77]	= { "getrusage" },
+	[78]	= { "gettimeofday" },
+	[79]	= { "settimeofday" },
+	[80]	= { "getgroups" },
+	[81]	= { "setgroups" },
+	[82]	= { "select" },
+	[83]	= { "symlink" },
+	[84]	= { "oldlstat" },
+	[85]	= { "readlink" },
+	[86]	= { "uselib" },
+	[87]	= { "swapon" },
+	[88]	= { "reboot" },
+	[89]	= { "readdir" },
+	[91]	= { "munmap",		0x000034	},
+	[92]	= { "truncate" },
+	[93]	= { "ftruncate" },
+	[94]	= { "fchmod" },
+	[95]	= { "fchown" },
+	[96]	= { "getpriority" },
+	[97]	= { "setpriority" },
+	[99]	= { "statfs" },
+	[100]	= { "fstatfs" },
+	[102]	= { "socketcall" },
+	[103]	= { "syslog" },
+	[104]	= { "setitimer" },
+	[105]	= { "getitimer" },
+	[106]	= { "stat" },
+	[107]	= { "lstat" },
+	[108]	= { "fstat" },
+	[111]	= { "vhangup" },
+	[114]	= { "wait4" },
+	[115]	= { "swapoff" },
+	[116]	= { "sysinfo" },
+	[117]	= { "ipc" },
+	[118]	= { "fsync" },
+	[119]	= { "sigreturn" },
+	[120]	= { "clone" },
+	[121]	= { "setdomainname" },
+	[122]	= { "uname" },
+	[123]	= { "modify_ldt" },
+	[123]	= { "cacheflush" },
+	[124]	= { "adjtimex" },
+	[125]	= { "mprotect" },
+	[126]	= { "sigprocmask" },
+	[127]	= { "create_module" },
+	[128]	= { "init_module" },
+	[129]	= { "delete_module" },
+	[130]	= { "get_kernel_syms" },
+	[131]	= { "quotactl" },
+	[132]	= { "getpgid" },
+	[133]	= { "fchdir" },
+	[134]	= { "bdflush" },
+	[135]	= { "sysfs" },
+	[136]	= { "personality" },
+	[137]	= { "afs_syscall" },
+	[138]	= { "setfsuid" },
+	[139]	= { "setfsgid" },
+	[140]	= { "_llseek",			0x014331	},
+	[141]	= { "getdents" },
+	[142]	= { "_newselect",		0x000141	},
+	[143]	= { "flock" },
+	[144]	= { "msync" },
+	[145]	= { "readv" },
+	[146]	= { "writev" },
+	[147]	= { "getsid",			0x000001	},
+	[148]	= { "fdatasync",		0x000001	},
+	[149]	= { "_sysctl",			0x000004	},
+	[150]	= { "mlock" },
+	[151]	= { "munlock" },
+	[152]	= { "mlockall" },
+	[153]	= { "munlockall" },
+	[154]	= { "sched_setparam" },
+	[155]	= { "sched_getparam" },
+	[156]	= { "sched_setscheduler" },
+	[157]	= { "sched_getscheduler" },
+	[158]	= { "sched_yield" },
+	[159]	= { "sched_get_priority_max" },
+	[160]	= { "sched_get_priority_min" },
+	[161]	= { "sched_rr_get_interval" },
+	[162]	= { "nanosleep",		0x000044	},
+	[163]	= { "mremap" },
+	[164]	= { "setresuid" },
+	[165]	= { "getresuid" },
+	[166]	= { "vm86" },
+	[167]	= { "query_module" },
+	[168]	= { "poll" },
+	[169]	= { "nfsservctl" },
+	[170]	= { "setresgid" },
+	[171]	= { "getresgid" },
+	[172]	= { "prctl",			0x333331	},
+	[173]	= { "rt_sigreturn",		0xffffff	},
+	[174]	= { "rt_sigaction",		0x001441	},
+	[175]	= { "rt_sigprocmask",		0x001441	},
+	[176]	= { "rt_sigpending",		0x000014	},
+	[177]	= { "rt_sigtimedwait",		0x001444	},
+	[178]	= { "rt_sigqueueinfo",		0x000411	},
+	[179]	= { "rt_sigsuspend",		0x000014	},
+	[180]	= { "pread",			0x003341	},
+	[181]	= { "pwrite",			0x003341	},
+	[182]	= { "chown",			0x000115	},
+	[183]	= { "getcwd" },
+	[184]	= { "capget" },
+	[185]	= { "capset" },
+	[186]	= { "sigaltstack" },
+	[187]	= { "sendfile" },
+	[188]	= { "getpmsg" },
+	[189]	= { "putpmsg" },
+	[190]	= { "vfork",			0xffffff	},
+	[191]	= { "ugetrlimit" },
+	[192]	= { "mmap2",			0x313314	},
+	[193]	= { "truncate64" },
+	[194]	= { "ftruncate64" },
+	[195]	= { "stat64",			0x000045	},
+	[196]	= { "lstat64",			0x000045	},
+	[197]	= { "fstat64",			0x000041	},
+	[198]	= { "lchown32" },
+	[199]	= { "getuid32",			0xffffff	},
+	[200]	= { "getgid32",			0xffffff	},
+	[201]	= { "geteuid32",		0xffffff	},
+	[202]	= { "getegid32",		0xffffff	},
+	[203]	= { "setreuid32" },
+	[204]	= { "setregid32" },
+	[205]	= { "getgroups32" },
+	[206]	= { "setgroups32" },
+	[207]	= { "fchown32" },
+	[208]	= { "setresuid32" },
+	[209]	= { "getresuid32" },
+	[210]	= { "setresgid32" },
+	[211]	= { "getresgid32" },
+	[212]	= { "chown32" },
+	[213]	= { "setuid32" },
+	[214]	= { "setgid32" },
+	[215]	= { "setfsuid32" },
+	[216]	= { "setfsgid32" },
+	[217]	= { "pivot_root" },
+	[218]	= { "mincore" },
+	[219]	= { "madvise" },
+	[220]	= { "getdents64" },
+	[221]	= { "fcntl64" },
+	[223]	= { "security" },
+	[224]	= { "gettid" },
+	[225]	= { "readahead" },
+	[226]	= { "setxattr" },
+	[227]	= { "lsetxattr" },
+	[228]	= { "fsetxattr" },
+	[229]	= { "getxattr" },
+	[230]	= { "lgetxattr" },
+	[231]	= { "fgetxattr" },
+	[232]	= { "listxattr" },
+	[233]	= { "llistxattr" },
+	[234]	= { "flistxattr" },
+	[235]	= { "removexattr" },
+	[236]	= { "lremovexattr" },
+	[237]	= { "fremovexattr" },
+	[238]	= { "tkill" },
+	[239]	= { "sendfile64" },
+	[240]	= { "futex" },
+	[241]	= { "sched_setaffinity" },
+	[242]	= { "sched_getaffinity" },
+	[243]	= { "set_thread_area" },
+	[244]	= { "get_thread_area" },
+	[245]	= { "io_setup" },
+	[246]	= { "io_destroy" },
+	[247]	= { "io_getevents" },
+	[248]	= { "io_submit" },
+	[249]	= { "io_cancel" },
+	[250]	= { "fadvise64" },
+	[252]	= { "exit_group",		0x000001	},
+	[253]	= { "lookup_dcookie" },
+	[254]	= { "epoll_create" },
+	[255]	= { "epoll_ctl" },
+	[256]	= { "epoll_wait" },
+	[257]	= { "remap_file_pages" },
+	[258]	= { "set_tid_address" },
+	[259]	= { "timer_create" },
+	[260]	= { "timer_settime" },
+	[261]	= { "timer_gettime" },
+	[262]	= { "timer_getoverrun" },
+	[263]	= { "timer_delete" },
+	[264]	= { "clock_settime" },
+	[265]	= { "clock_gettime" },
+	[266]	= { "clock_getres" },
+	[267]	= { "clock_nanosleep" },
+	[268]	= { "statfs64" },
+	[269]	= { "fstatfs64" },
+	[270]	= { "tgkill" },
+	[271]	= { "utimes" },
+	[272]	= { "fadvise64_64" },
+	[273]	= { "vserver" },
+	[274]	= { "mbind" },
+	[275]	= { "get_mempolicy" },
+	[276]	= { "set_mempolicy" },
+	[277]	= { "mq_open" },
+	[278]	= { "mq_unlink" },
+	[279]	= { "mq_timedsend" },
+	[280]	= { "mq_timedreceive" },
+	[281]	= { "mq_notify" },
+	[282]	= { "mq_getsetattr" },
+	[283]	= { "sys_kexec_load" },
+};
+
+asmlinkage void do_syscall_trace(int leaving)
+{
+#if 0
+	unsigned long *argp;
+	const char *name;
+	unsigned argmask;
+	char buffer[16];
+
+	if (!kstrace)
+		return;
+
+	if (!current->mm)
+		return;
+
+	if (__frame->gr7 == __NR_close)
+		return;
+
+#if 0
+	if (__frame->gr7 != __NR_mmap2 &&
+	    __frame->gr7 != __NR_vfork &&
+	    __frame->gr7 != __NR_execve &&
+	    __frame->gr7 != __NR_exit)
+		return;
+#endif
+
+	argmask = 0;
+	name = NULL;
+	if (__frame->gr7 < NR_syscalls) {
+		name = __syscall_name_table[__frame->gr7].name;
+		argmask = __syscall_name_table[__frame->gr7].argmask;
+	}
+	if (!name) {
+		sprintf(buffer, "sys_%lx", __frame->gr7);
+		name = buffer;
+	}
+
+	if (!leaving) {
+		if (!argmask) {
+			printk(KERN_CRIT "[%d] %s(%lx,%lx,%lx,%lx,%lx,%lx)\n",
+			       current->pid,
+			       name,
+			       __frame->gr8,
+			       __frame->gr9,
+			       __frame->gr10,
+			       __frame->gr11,
+			       __frame->gr12,
+			       __frame->gr13);
+		}
+		else if (argmask == 0xffffff) {
+			printk(KERN_CRIT "[%d] %s()\n",
+			       current->pid,
+			       name);
+		}
+		else {
+			printk(KERN_CRIT "[%d] %s(",
+			       current->pid,
+			       name);
+
+			argp = &__frame->gr8;
+
+			do {
+				switch (argmask & 0xf) {
+				case 1:
+					printk("%ld", (long) *argp);
+					break;
+				case 2:
+					printk("%lo", *argp);
+					break;
+				case 3:
+					printk("%lx", *argp);
+					break;
+				case 4:
+					printk("%p", (void *) *argp);
+					break;
+				case 5:
+					printk("\"%s\"", (char *) *argp);
+					break;
+				}
+
+				argp++;
+				argmask >>= 4;
+				if (argmask)
+					printk(",");
+
+			} while (argmask);
+
+			printk(")\n");
+		}
+	}
+	else {
+		if ((int)__frame->gr8 > -4096 && (int)__frame->gr8 < 4096)
+			printk(KERN_CRIT "[%d] %s() = %ld\n", current->pid, name, __frame->gr8);
+		else
+			printk(KERN_CRIT "[%d] %s() = %lx\n", current->pid, name, __frame->gr8);
+	}
+	return;
+#endif
+
+	if (!test_thread_flag(TIF_SYSCALL_TRACE))
+		return;
+
+	if (!(current->ptrace & PT_PTRACED))
+		return;
+
+	/* we need to indicate entry or exit to strace */
+	if (leaving)
+		__frame->__status |= REG__STATUS_SYSC_EXIT;
+	else
+		__frame->__status |= REG__STATUS_SYSC_ENTRY;
+
+	ptrace_notify(SIGTRAP);
+
+	/*
+	 * this isn't the same as continuing with a signal, but it will do
+	 * for normal use.  strace only continues with a signal if the
+	 * stopping signal is not SIGTRAP.  -brl
+	 */
+	if (current->exit_code) {
+		send_sig(current->exit_code, current, 1);
+		current->exit_code = 0;
+	}
+}
diff --git a/arch/frv/kernel/semaphore.c b/arch/frv/kernel/semaphore.c
new file mode 100644
index 0000000..5cba9c1
--- /dev/null
+++ b/arch/frv/kernel/semaphore.c
@@ -0,0 +1,156 @@
+/* semaphore.c: FR-V semaphores
+ *
+ * Copyright (C) 2003 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ * - Derived from lib/rwsem-spinlock.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/config.h>
+#include <linux/sched.h>
+#include <linux/module.h>
+#include <asm/semaphore.h>
+
+struct sem_waiter {
+	struct list_head	list;
+	struct task_struct	*task;
+};
+
+#if SEM_DEBUG
+void semtrace(struct semaphore *sem, const char *str)
+{
+	if (sem->debug)
+		printk("[%d] %s({%d,%d})\n",
+		       current->pid,
+		       str,
+		       sem->counter,
+		       list_empty(&sem->wait_list) ? 0 : 1);
+}
+#else
+#define semtrace(SEM,STR) do { } while(0)
+#endif
+
+/*
+ * wait for a token to be granted from a semaphore
+ * - entered with lock held and interrupts disabled
+ */
+void __down(struct semaphore *sem, unsigned long flags)
+{
+	struct task_struct *tsk = current;
+	struct sem_waiter waiter;
+
+	semtrace(sem, "Entering __down");
+
+	/* set up my own style of waitqueue */
+	waiter.task = tsk;
+	get_task_struct(tsk);
+
+	list_add_tail(&waiter.list, &sem->wait_list);
+
+	/* we don't need to touch the semaphore struct anymore */
+	spin_unlock_irqrestore(&sem->wait_lock, flags);
+
+	/* wait to be given the semaphore */
+	set_task_state(tsk, TASK_UNINTERRUPTIBLE);
+
+	for (;;) {
+		if (list_empty(&waiter.list))
+			break;
+		schedule();
+		set_task_state(tsk, TASK_UNINTERRUPTIBLE);
+	}
+
+	tsk->state = TASK_RUNNING;
+	semtrace(sem, "Leaving __down");
+}
+
+EXPORT_SYMBOL(__down);
+
+/*
+ * interruptibly wait for a token to be granted from a semaphore
+ * - entered with lock held and interrupts disabled
+ */
+int __down_interruptible(struct semaphore *sem, unsigned long flags)
+{
+	struct task_struct *tsk = current;
+	struct sem_waiter waiter;
+	int ret;
+
+	semtrace(sem,"Entering __down_interruptible");
+
+	/* set up my own style of waitqueue */
+	waiter.task = tsk;
+	get_task_struct(tsk);
+
+	list_add_tail(&waiter.list, &sem->wait_list);
+
+	/* we don't need to touch the semaphore struct anymore */
+	set_task_state(tsk, TASK_INTERRUPTIBLE);
+
+	spin_unlock_irqrestore(&sem->wait_lock, flags);
+
+	/* wait to be given the semaphore */
+	ret = 0;
+	for (;;) {
+		if (list_empty(&waiter.list))
+			break;
+		if (unlikely(signal_pending(current)))
+			goto interrupted;
+		schedule();
+		set_task_state(tsk, TASK_INTERRUPTIBLE);
+	}
+
+ out:
+	tsk->state = TASK_RUNNING;
+	semtrace(sem, "Leaving __down_interruptible");
+	return ret;
+
+ interrupted:
+	spin_lock_irqsave(&sem->wait_lock, flags);
+
+	if (!list_empty(&waiter.list)) {
+		list_del(&waiter.list);
+		ret = -EINTR;
+	}
+
+	spin_unlock_irqrestore(&sem->wait_lock, flags);
+	if (ret == -EINTR)
+		put_task_struct(current);
+	goto out;
+}
+
+EXPORT_SYMBOL(__down_interruptible);
+
+/*
+ * release a single token back to a semaphore
+ * - entered with lock held and interrupts disabled
+ */
+void __up(struct semaphore *sem)
+{
+	struct task_struct *tsk;
+	struct sem_waiter *waiter;
+
+	semtrace(sem,"Entering __up");
+
+	/* grant the token to the process at the front of the queue */
+	waiter = list_entry(sem->wait_list.next, struct sem_waiter, list);
+
+	/* We must be careful not to touch 'waiter' after we set ->task = NULL.
+	 * It is an allocated on the waiter's stack and may become invalid at
+	 * any time after that point (due to a wakeup from another source).
+	 */
+	list_del_init(&waiter->list);
+	tsk = waiter->task;
+	mb();
+	waiter->task = NULL;
+	wake_up_process(tsk);
+	put_task_struct(tsk);
+
+	semtrace(sem,"Leaving __up");
+}
+
+EXPORT_SYMBOL(__up);
diff --git a/arch/frv/kernel/setup.c b/arch/frv/kernel/setup.c
new file mode 100644
index 0000000..ef6865f
--- /dev/null
+++ b/arch/frv/kernel/setup.c
@@ -0,0 +1,1194 @@
+/* setup.c: FRV specific setup
+ *
+ * Copyright (C) 2003-5 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ * - Derived from arch/m68k/kernel/setup.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/config.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/fb.h>
+#include <linux/console.h>
+#include <linux/genhd.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/major.h>
+#include <linux/bootmem.h>
+#include <linux/highmem.h>
+#include <linux/seq_file.h>
+#include <linux/serial.h>
+#include <linux/serial_core.h>
+#include <linux/serial_reg.h>
+
+#include <asm/setup.h>
+#include <asm/serial.h>
+#include <asm/irq.h>
+#include <asm/sections.h>
+#include <asm/pgalloc.h>
+#include <asm/busctl-regs.h>
+#include <asm/serial-regs.h>
+#include <asm/timer-regs.h>
+#include <asm/irc-regs.h>
+#include <asm/spr-regs.h>
+#include <asm/mb-regs.h>
+#include <asm/mb93493-regs.h>
+#include <asm/gdb-stub.h>
+#include <asm/irq-routing.h>
+#include <asm/io.h>
+
+#ifdef CONFIG_BLK_DEV_INITRD
+#include <linux/blk.h>
+#include <asm/pgtable.h>
+#endif
+
+#include "local.h"
+
+#ifdef CONFIG_MB93090_MB00
+static void __init mb93090_display(void);
+#endif
+#ifdef CONFIG_MMU
+static void __init setup_linux_memory(void);
+#else
+static void __init setup_uclinux_memory(void);
+#endif
+
+#ifdef CONFIG_CONSOLE
+extern struct consw *conswitchp;
+#endif
+
+#ifdef CONFIG_MB93090_MB00
+static char __initdata mb93090_banner[] = "FJ/RH FR-V Linux";
+static char __initdata mb93090_version[] = UTS_RELEASE;
+
+int __nongprelbss mb93090_mb00_detected;
+#endif
+
+const char __frv_unknown_system[] = "unknown";
+const char __frv_mb93091_cb10[] = "mb93091-cb10";
+const char __frv_mb93091_cb11[] = "mb93091-cb11";
+const char __frv_mb93091_cb30[] = "mb93091-cb30";
+const char __frv_mb93091_cb41[] = "mb93091-cb41";
+const char __frv_mb93091_cb60[] = "mb93091-cb60";
+const char __frv_mb93091_cb70[] = "mb93091-cb70";
+const char __frv_mb93091_cb451[] = "mb93091-cb451";
+const char __frv_mb93090_mb00[] = "mb93090-mb00";
+
+const char __frv_mb93493[] = "mb93493";
+
+const char __frv_mb93093[] = "mb93093";
+
+static const char *__nongprelbss cpu_series;
+static const char *__nongprelbss cpu_core;
+static const char *__nongprelbss cpu_silicon;
+static const char *__nongprelbss cpu_mmu;
+static const char *__nongprelbss cpu_system;
+static const char *__nongprelbss cpu_board1;
+static const char *__nongprelbss cpu_board2;
+
+static unsigned long __nongprelbss cpu_psr_all;
+static unsigned long __nongprelbss cpu_hsr0_all;
+
+unsigned long __nongprelbss pdm_suspend_mode;
+
+unsigned long __nongprelbss rom_length;
+unsigned long __nongprelbss memory_start;
+unsigned long __nongprelbss memory_end;
+
+unsigned long __nongprelbss dma_coherent_mem_start;
+unsigned long __nongprelbss dma_coherent_mem_end;
+
+unsigned long __initdata __sdram_old_base;
+unsigned long __initdata num_mappedpages;
+
+struct cpuinfo_frv __nongprelbss boot_cpu_data;
+
+char command_line[COMMAND_LINE_SIZE];
+char __initdata redboot_command_line[COMMAND_LINE_SIZE];
+
+#ifdef CONFIG_PM
+#define __pminit
+#define __pminitdata
+#else
+#define __pminit __init
+#define __pminitdata __initdata
+#endif
+
+struct clock_cmode {
+	uint8_t	xbus, sdram, corebus, core, dsu;
+};
+
+#define _frac(N,D) ((N)<<4 | (D))
+#define _x0_16	_frac(1,6)
+#define _x0_25	_frac(1,4)
+#define _x0_33	_frac(1,3)
+#define _x0_375	_frac(3,8)
+#define _x0_5	_frac(1,2)
+#define _x0_66	_frac(2,3)
+#define _x0_75	_frac(3,4)
+#define _x1	_frac(1,1)
+#define _x1_5	_frac(3,2)
+#define _x2	_frac(2,1)
+#define _x3	_frac(3,1)
+#define _x4	_frac(4,1)
+#define _x4_5	_frac(9,2)
+#define _x6	_frac(6,1)
+#define _x8	_frac(8,1)
+#define _x9	_frac(9,1)
+
+int __nongprelbss clock_p0_current;
+int __nongprelbss clock_cm_current;
+int __nongprelbss clock_cmode_current;
+#ifdef CONFIG_PM
+int __nongprelbss clock_cmodes_permitted;
+unsigned long __nongprelbss clock_bits_settable;
+#endif
+
+static struct clock_cmode __pminitdata undef_clock_cmode = { _x1, _x1, _x1, _x1, _x1 };
+
+static struct clock_cmode __pminitdata clock_cmodes_fr401_fr403[16] = {
+	[4]	= {	_x1,	_x1,	_x2,	_x2,	_x0_25	},
+	[5]	= { 	_x1,	_x2,	_x4,	_x4,	_x0_5	},
+	[8]	= { 	_x1,	_x1,	_x1,	_x2,	_x0_25	},
+	[9]	= { 	_x1,	_x2,	_x2,	_x4,	_x0_5	},
+	[11]	= { 	_x1,	_x4,	_x4,	_x8,	_x1	},
+	[12]	= { 	_x1,	_x1,	_x2,	_x4,	_x0_5	},
+	[13]	= { 	_x1,	_x2,	_x4,	_x8,	_x1	},
+};
+
+static struct clock_cmode __pminitdata clock_cmodes_fr405[16] = {
+	[0]	= {	_x1,	_x1,	_x1,	_x1,	_x0_5	},
+	[1]	= {	_x1,	_x1,	_x1,	_x3,	_x0_25	},
+	[2]	= {	_x1,	_x1,	_x2,	_x6,	_x0_5	},
+	[3]	= {	_x1,	_x2,	_x2,	_x6,	_x0_5	},
+	[4]	= {	_x1,	_x1,	_x2,	_x2,	_x0_16	},
+	[8]	= { 	_x1,	_x1,	_x1,	_x2,	_x0_16	},
+	[9]	= { 	_x1,	_x2,	_x2,	_x4,	_x0_33	},
+	[12]	= { 	_x1,	_x1,	_x2,	_x4,	_x0_33	},
+	[14]	= { 	_x1,	_x3,	_x3,	_x9,	_x0_75	},
+	[15]	= { 	_x1,	_x1_5,	_x1_5,	_x4_5,	_x0_375	},
+
+#define CLOCK_CMODES_PERMITTED_FR405 0xd31f
+};
+
+static struct clock_cmode __pminitdata clock_cmodes_fr555[16] = {
+	[0]	= {	_x1,	_x2,	_x2,	_x4,	_x0_33	},
+	[1]	= {	_x1,	_x3,	_x3,	_x6,	_x0_5	},
+	[2]	= {	_x1,	_x2,	_x4,	_x8,	_x0_66	},
+	[3]	= {	_x1,	_x1_5,	_x3,	_x6,	_x0_5	},
+	[4]	= {	_x1,	_x3,	_x3,	_x9,	_x0_75	},
+	[5]	= {	_x1,	_x2,	_x2,	_x6,	_x0_5	},
+	[6]	= {	_x1,	_x1_5,	_x1_5,	_x4_5,	_x0_375	},
+};
+
+static const struct clock_cmode __pminitdata *clock_cmodes;
+static int __pminitdata clock_doubled;
+
+static struct uart_port __initdata __frv_uart0 = {
+	.uartclk		= 0,
+	.membase		= (char *) UART0_BASE,
+	.irq			= IRQ_CPU_UART0,
+	.regshift		= 3,
+	.iotype			= UPIO_MEM,
+	.flags			= UPF_BOOT_AUTOCONF | UPF_SKIP_TEST,
+};
+
+static struct uart_port __initdata __frv_uart1 = {
+	.uartclk		= 0,
+	.membase		= (char *) UART1_BASE,
+	.irq			= IRQ_CPU_UART1,
+	.regshift		= 3,
+	.iotype			= UPIO_MEM,
+	.flags			= UPF_BOOT_AUTOCONF | UPF_SKIP_TEST,
+};
+
+#if 0
+static void __init printk_xampr(unsigned long ampr, unsigned long amlr, char i_d, int n)
+{
+	unsigned long phys, virt, cxn, size;
+
+#ifdef CONFIG_MMU
+	virt = amlr & 0xffffc000;
+	cxn = amlr & 0x3fff;
+#else
+	virt = ampr & 0xffffc000;
+	cxn = 0;
+#endif
+	phys = ampr & xAMPRx_PPFN;
+	size = 1 << (((ampr & xAMPRx_SS) >> 4) + 17);
+
+	printk("%cAMPR%d: va %08lx-%08lx [pa %08lx] %c%c%c%c [cxn:%04lx]\n",
+	       i_d, n,
+	       virt, virt + size - 1,
+	       phys,
+	       ampr & xAMPRx_S  ? 'S' : '-',
+	       ampr & xAMPRx_C  ? 'C' : '-',
+	       ampr & DAMPRx_WP ? 'W' : '-',
+	       ampr & xAMPRx_V  ? 'V' : '-',
+	       cxn
+	       );
+}
+#endif
+
+/*****************************************************************************/
+/*
+ * dump the memory map
+ */
+static void __init dump_memory_map(void)
+{
+
+#if 0
+	/* dump the protection map */
+	printk_xampr(__get_IAMPR(0),  __get_IAMLR(0),  'I', 0);
+	printk_xampr(__get_IAMPR(1),  __get_IAMLR(1),  'I', 1);
+	printk_xampr(__get_IAMPR(2),  __get_IAMLR(2),  'I', 2);
+	printk_xampr(__get_IAMPR(3),  __get_IAMLR(3),  'I', 3);
+	printk_xampr(__get_IAMPR(4),  __get_IAMLR(4),  'I', 4);
+	printk_xampr(__get_IAMPR(5),  __get_IAMLR(5),  'I', 5);
+	printk_xampr(__get_IAMPR(6),  __get_IAMLR(6),  'I', 6);
+	printk_xampr(__get_IAMPR(7),  __get_IAMLR(7),  'I', 7);
+	printk_xampr(__get_IAMPR(8),  __get_IAMLR(8),  'I', 8);
+	printk_xampr(__get_IAMPR(9),  __get_IAMLR(9),  'i', 9);
+	printk_xampr(__get_IAMPR(10), __get_IAMLR(10), 'I', 10);
+	printk_xampr(__get_IAMPR(11), __get_IAMLR(11), 'I', 11);
+	printk_xampr(__get_IAMPR(12), __get_IAMLR(12), 'I', 12);
+	printk_xampr(__get_IAMPR(13), __get_IAMLR(13), 'I', 13);
+	printk_xampr(__get_IAMPR(14), __get_IAMLR(14), 'I', 14);
+	printk_xampr(__get_IAMPR(15), __get_IAMLR(15), 'I', 15);
+
+	printk_xampr(__get_DAMPR(0),  __get_DAMLR(0),  'D', 0);
+	printk_xampr(__get_DAMPR(1),  __get_DAMLR(1),  'D', 1);
+	printk_xampr(__get_DAMPR(2),  __get_DAMLR(2),  'D', 2);
+	printk_xampr(__get_DAMPR(3),  __get_DAMLR(3),  'D', 3);
+	printk_xampr(__get_DAMPR(4),  __get_DAMLR(4),  'D', 4);
+	printk_xampr(__get_DAMPR(5),  __get_DAMLR(5),  'D', 5);
+	printk_xampr(__get_DAMPR(6),  __get_DAMLR(6),  'D', 6);
+	printk_xampr(__get_DAMPR(7),  __get_DAMLR(7),  'D', 7);
+	printk_xampr(__get_DAMPR(8),  __get_DAMLR(8),  'D', 8);
+	printk_xampr(__get_DAMPR(9),  __get_DAMLR(9),  'D', 9);
+	printk_xampr(__get_DAMPR(10), __get_DAMLR(10), 'D', 10);
+	printk_xampr(__get_DAMPR(11), __get_DAMLR(11), 'D', 11);
+	printk_xampr(__get_DAMPR(12), __get_DAMLR(12), 'D', 12);
+	printk_xampr(__get_DAMPR(13), __get_DAMLR(13), 'D', 13);
+	printk_xampr(__get_DAMPR(14), __get_DAMLR(14), 'D', 14);
+	printk_xampr(__get_DAMPR(15), __get_DAMLR(15), 'D', 15);
+#endif
+
+#if 0
+	/* dump the bus controller registers */
+	printk("LGCR: %08lx\n", __get_LGCR());
+	printk("Master: %08lx-%08lx CR=%08lx\n",
+	       __get_LEMBR(), __get_LEMBR() + __get_LEMAM(),
+	       __get_LMAICR());
+
+	int loop;
+	for (loop = 1; loop <= 7; loop++) {
+		unsigned long lcr = __get_LCR(loop), lsbr = __get_LSBR(loop);
+		printk("CS#%d: %08lx-%08lx %c%c%c%c%c%c%c%c%c\n",
+		       loop,
+		       lsbr, lsbr + __get_LSAM(loop),
+		       lcr & 0x80000000 ? 'r' : '-',
+		       lcr & 0x40000000 ? 'w' : '-',
+		       lcr & 0x08000000 ? 'b' : '-',
+		       lcr & 0x04000000 ? 'B' : '-',
+		       lcr & 0x02000000 ? 'C' : '-',
+		       lcr & 0x01000000 ? 'D' : '-',
+		       lcr & 0x00800000 ? 'W' : '-',
+		       lcr & 0x00400000 ? 'R' : '-',
+		       (lcr & 0x00030000) == 0x00000000 ? '4' :
+		       (lcr & 0x00030000) == 0x00010000 ? '2' :
+		       (lcr & 0x00030000) == 0x00020000 ? '1' :
+		       '-'
+		       );
+	}
+#endif
+
+#if 0
+	printk("\n");
+#endif
+} /* end dump_memory_map() */
+
+/*****************************************************************************/
+/*
+ * attempt to detect a VDK motherboard and DAV daughter board on an MB93091 system
+ */
+#ifdef CONFIG_MB93091_VDK
+static void __init detect_mb93091(void)
+{
+#ifdef CONFIG_MB93090_MB00
+	/* Detect CB70 without motherboard */
+	if (!(cpu_system == __frv_mb93091_cb70 && ((*(unsigned short *)0xffc00030) & 0x100))) {
+		cpu_board1 = __frv_mb93090_mb00;
+		mb93090_mb00_detected = 1;
+	}
+#endif
+
+#ifdef CONFIG_FUJITSU_MB93493
+	cpu_board2 = __frv_mb93493;
+#endif
+
+} /* end detect_mb93091() */
+#endif
+
+/*****************************************************************************/
+/*
+ * determine the CPU type and set appropriate parameters
+ *
+ * Family     Series      CPU Core    Silicon    Imple  Vers
+ * ----------------------------------------------------------
+ * FR-V --+-> FR400 --+-> FR401 --+-> MB93401     02     00 [1]
+ *        |           |           |
+ *        |           |           +-> MB93401/A   02     01
+ *        |           |           |
+ *        |           |           +-> MB93403     02     02
+ *        |           |
+ *        |           +-> FR405 ----> MB93405     04     00
+ *        |
+ *        +-> FR450 ----> FR451 ----> MB93451     05     00
+ *        |
+ *        +-> FR500 ----> FR501 --+-> MB93501     01     01 [2]
+ *        |                       |
+ *        |                       +-> MB93501/A   01     02
+ *        |
+ *        +-> FR550 --+-> FR551 ----> MB93555     03     01
+ *
+ *  [1] The MB93401 is an obsolete CPU replaced by the MB93401A
+ *  [2] The MB93501 is an obsolete CPU replaced by the MB93501A
+ *
+ * Imple is PSR(Processor Status Register)[31:28].
+ * Vers is PSR(Processor Status Register)[27:24].
+ *
+ * A "Silicon" consists of CPU core and some on-chip peripherals.
+ */
+static void __init determine_cpu(void)
+{
+	unsigned long hsr0 = __get_HSR(0);
+	unsigned long psr = __get_PSR();
+
+	/* work out what selectable services the CPU supports */
+	__set_PSR(psr | PSR_EM | PSR_EF | PSR_CM | PSR_NEM);
+	cpu_psr_all = __get_PSR();
+	__set_PSR(psr);
+
+	__set_HSR(0, hsr0 | HSR0_GRLE | HSR0_GRHE | HSR0_FRLE | HSR0_FRHE);
+	cpu_hsr0_all = __get_HSR(0);
+	__set_HSR(0, hsr0);
+
+	/* derive other service specs from the CPU type */
+	cpu_series		= "unknown";
+	cpu_core		= "unknown";
+	cpu_silicon		= "unknown";
+	cpu_mmu			= "Prot";
+	cpu_system		= __frv_unknown_system;
+	clock_cmodes		= NULL;
+	clock_doubled		= 0;
+#ifdef CONFIG_PM
+	clock_bits_settable	= CLOCK_BIT_CM_H | CLOCK_BIT_CM_M | CLOCK_BIT_P0;
+#endif
+
+	switch (PSR_IMPLE(psr)) {
+	case PSR_IMPLE_FR401:
+		cpu_series	= "fr400";
+		cpu_core	= "fr401";
+		pdm_suspend_mode = HSR0_PDM_PLL_RUN;
+
+		switch (PSR_VERSION(psr)) {
+		case PSR_VERSION_FR401_MB93401:
+			cpu_silicon	= "mb93401";
+			cpu_system	= __frv_mb93091_cb10;
+			clock_cmodes	= clock_cmodes_fr401_fr403;
+			clock_doubled	= 1;
+			break;
+		case PSR_VERSION_FR401_MB93401A:
+			cpu_silicon	= "mb93401/A";
+			cpu_system	= __frv_mb93091_cb11;
+			clock_cmodes	= clock_cmodes_fr401_fr403;
+			break;
+		case PSR_VERSION_FR401_MB93403:
+			cpu_silicon	= "mb93403";
+#ifndef CONFIG_MB93093_PDK
+			cpu_system	= __frv_mb93091_cb30;
+#else
+			cpu_system	= __frv_mb93093;
+#endif
+			clock_cmodes	= clock_cmodes_fr401_fr403;
+			break;
+		default:
+			break;
+		}
+		break;
+
+	case PSR_IMPLE_FR405:
+		cpu_series	= "fr400";
+		cpu_core	= "fr405";
+		pdm_suspend_mode = HSR0_PDM_PLL_STOP;
+
+		switch (PSR_VERSION(psr)) {
+		case PSR_VERSION_FR405_MB93405:
+			cpu_silicon	= "mb93405";
+			cpu_system	= __frv_mb93091_cb60;
+			clock_cmodes	= clock_cmodes_fr405;
+#ifdef CONFIG_PM
+			clock_bits_settable |= CLOCK_BIT_CMODE;
+			clock_cmodes_permitted = CLOCK_CMODES_PERMITTED_FR405;
+#endif
+
+			/* the FPGA on the CB70 has extra registers
+			 * - it has 0x0046 in the VDK_ID FPGA register at 0x1a0, which is
+			 *   how we tell the difference between it and a CB60
+			 */
+			if (*(volatile unsigned short *) 0xffc001a0 == 0x0046)
+				cpu_system = __frv_mb93091_cb70;
+			break;
+		default:
+			break;
+		}
+		break;
+
+	case PSR_IMPLE_FR451:
+		cpu_series	= "fr450";
+		cpu_core	= "fr451";
+		pdm_suspend_mode = HSR0_PDM_PLL_STOP;
+#ifdef CONFIG_PM
+		clock_bits_settable |= CLOCK_BIT_CMODE;
+		clock_cmodes_permitted = CLOCK_CMODES_PERMITTED_FR405;
+#endif
+		switch (PSR_VERSION(psr)) {
+		case PSR_VERSION_FR451_MB93451:
+			cpu_silicon	= "mb93451";
+			cpu_mmu		= "Prot, SAT, xSAT, DAT";
+			cpu_system	= __frv_mb93091_cb451;
+			clock_cmodes	= clock_cmodes_fr405;
+			break;
+		default:
+			break;
+		}
+		break;
+
+	case PSR_IMPLE_FR501:
+		cpu_series	= "fr500";
+		cpu_core	= "fr501";
+		pdm_suspend_mode = HSR0_PDM_PLL_STOP;
+
+		switch (PSR_VERSION(psr)) {
+		case PSR_VERSION_FR501_MB93501:  cpu_silicon = "mb93501";   break;
+		case PSR_VERSION_FR501_MB93501A: cpu_silicon = "mb93501/A"; break;
+		default:
+			break;
+		}
+		break;
+
+	case PSR_IMPLE_FR551:
+		cpu_series	= "fr550";
+		cpu_core	= "fr551";
+		pdm_suspend_mode = HSR0_PDM_PLL_RUN;
+
+		switch (PSR_VERSION(psr)) {
+		case PSR_VERSION_FR551_MB93555:
+			cpu_silicon	= "mb93555";
+			cpu_mmu		= "Prot, SAT";
+			cpu_system	= __frv_mb93091_cb41;
+			clock_cmodes	= clock_cmodes_fr555;
+			clock_doubled	= 1;
+			break;
+		default:
+			break;
+		}
+		break;
+
+	default:
+		break;
+	}
+
+	printk("- Series:%s CPU:%s Silicon:%s\n",
+	       cpu_series, cpu_core, cpu_silicon);
+
+#ifdef CONFIG_MB93091_VDK
+	detect_mb93091();
+#endif
+
+#if defined(CONFIG_MB93093_PDK) && defined(CONFIG_FUJITSU_MB93493)
+	cpu_board2 = __frv_mb93493;
+#endif
+
+} /* end determine_cpu() */
+
+/*****************************************************************************/
+/*
+ * calculate the bus clock speed
+ */
+void __pminit determine_clocks(int verbose)
+{
+	const struct clock_cmode *mode, *tmode;
+	unsigned long clkc, psr, quot;
+
+	clkc = __get_CLKC();
+	psr = __get_PSR();
+
+	clock_p0_current = !!(clkc & CLKC_P0);
+	clock_cm_current = clkc & CLKC_CM;
+	clock_cmode_current = (clkc & CLKC_CMODE) >> CLKC_CMODE_s;
+
+	if (verbose)
+		printk("psr=%08lx hsr0=%08lx clkc=%08lx\n", psr, __get_HSR(0), clkc);
+
+	/* the CB70 has some alternative ways of setting the clock speed through switches accessed
+	 * through the FPGA.  */
+	if (cpu_system == __frv_mb93091_cb70) {
+		unsigned short clkswr = *(volatile unsigned short *) 0xffc00104UL & 0x1fffUL;
+
+		if (clkswr & 0x1000)
+			__clkin_clock_speed_HZ = 60000000UL;
+		else
+			__clkin_clock_speed_HZ =
+				((clkswr >> 8) & 0xf) * 10000000 +
+				((clkswr >> 4) & 0xf) * 1000000 +
+				((clkswr     ) & 0xf) * 100000;
+	}
+	/* the FR451 is currently fixed at 24MHz */
+	else if (cpu_system == __frv_mb93091_cb451) {
+		//__clkin_clock_speed_HZ = 24000000UL; // CB451-FPGA
+		unsigned short clkswr = *(volatile unsigned short *) 0xffc00104UL & 0x1fffUL;
+
+		if (clkswr & 0x1000)
+			__clkin_clock_speed_HZ = 60000000UL;
+		else
+			__clkin_clock_speed_HZ =
+				((clkswr >> 8) & 0xf) * 10000000 +
+				((clkswr >> 4) & 0xf) * 1000000 +
+				((clkswr     ) & 0xf) * 100000;
+	}
+	/* otherwise determine the clockspeed from VDK or other registers */
+	else {
+		__clkin_clock_speed_HZ = __get_CLKIN();
+	}
+
+	/* look up the appropriate clock relationships table entry */
+	mode = &undef_clock_cmode;
+	if (clock_cmodes) {
+		tmode = &clock_cmodes[(clkc & CLKC_CMODE) >> CLKC_CMODE_s];
+		if (tmode->xbus)
+			mode = tmode;
+	}
+
+#define CLOCK(SRC,RATIO) ((SRC) * (((RATIO) >> 4) & 0x0f) / ((RATIO) & 0x0f))
+
+	if (clock_doubled)
+		__clkin_clock_speed_HZ <<= 1;
+
+	__ext_bus_clock_speed_HZ	= CLOCK(__clkin_clock_speed_HZ, mode->xbus);
+	__sdram_clock_speed_HZ		= CLOCK(__clkin_clock_speed_HZ, mode->sdram);
+	__dsu_clock_speed_HZ		= CLOCK(__clkin_clock_speed_HZ, mode->dsu);
+
+	switch (clkc & CLKC_CM) {
+	case 0: /* High */
+		__core_bus_clock_speed_HZ	= CLOCK(__clkin_clock_speed_HZ, mode->corebus);
+		__core_clock_speed_HZ		= CLOCK(__clkin_clock_speed_HZ, mode->core);
+		break;
+	case 1: /* Medium */
+		__core_bus_clock_speed_HZ	= CLOCK(__clkin_clock_speed_HZ, mode->sdram);
+		__core_clock_speed_HZ		= CLOCK(__clkin_clock_speed_HZ, mode->sdram);
+		break;
+	case 2: /* Low; not supported */
+	case 3: /* UNDEF */
+		printk("Unsupported CLKC CM %ld\n", clkc & CLKC_CM);
+		panic("Bye");
+	}
+
+	__res_bus_clock_speed_HZ = __ext_bus_clock_speed_HZ;
+	if (clkc & CLKC_P0)
+		__res_bus_clock_speed_HZ >>= 1;
+
+	if (verbose) {
+		printk("CLKIN: %lu.%3.3luMHz\n",
+		       __clkin_clock_speed_HZ / 1000000,
+		       (__clkin_clock_speed_HZ / 1000) % 1000);
+
+		printk("CLKS:"
+		       " ext=%luMHz res=%luMHz sdram=%luMHz cbus=%luMHz core=%luMHz dsu=%luMHz\n",
+		       __ext_bus_clock_speed_HZ / 1000000,
+		       __res_bus_clock_speed_HZ / 1000000,
+		       __sdram_clock_speed_HZ / 1000000,
+		       __core_bus_clock_speed_HZ / 1000000,
+		       __core_clock_speed_HZ / 1000000,
+		       __dsu_clock_speed_HZ / 1000000
+		       );
+	}
+
+	/* calculate the number of __delay() loop iterations per sec (2 insn loop) */
+	__delay_loops_MHz = __core_clock_speed_HZ / (1000000 * 2);
+
+	/* set the serial prescaler */
+	__serial_clock_speed_HZ = __res_bus_clock_speed_HZ;
+	quot = 1;
+	while (__serial_clock_speed_HZ / quot / 16 / 65536 > 3000)
+		quot += 1;
+
+	/* double the divisor if P0 is clear, so that if/when P0 is set, it's still achievable
+	 * - we have to be careful - dividing too much can mean we can't get 115200 baud
+	 */
+	if (__serial_clock_speed_HZ > 32000000 && !(clkc & CLKC_P0))
+		quot <<= 1;
+
+	__serial_clock_speed_HZ /= quot;
+	__frv_uart0.uartclk = __serial_clock_speed_HZ;
+	__frv_uart1.uartclk = __serial_clock_speed_HZ;
+
+	if (verbose)
+		printk("      uart=%luMHz\n", __serial_clock_speed_HZ / 1000000 * quot);
+
+	while (!(__get_UART0_LSR() & UART_LSR_TEMT))
+		continue;
+
+	while (!(__get_UART1_LSR() & UART_LSR_TEMT))
+		continue;
+
+	__set_UCPVR(quot);
+	__set_UCPSR(0);
+} /* end determine_clocks() */
+
+/*****************************************************************************/
+/*
+ * reserve some DMA consistent memory
+ */
+#ifdef CONFIG_RESERVE_DMA_COHERENT
+static void __init reserve_dma_coherent(void)
+{
+	unsigned long ampr;
+
+	/* find the first non-kernel memory tile and steal it */
+#define __steal_AMPR(r)						\
+	if (__get_DAMPR(r) & xAMPRx_V) {			\
+		ampr = __get_DAMPR(r);				\
+		__set_DAMPR(r, ampr | xAMPRx_S | xAMPRx_C);	\
+		__set_IAMPR(r, 0);				\
+		goto found;					\
+	}
+
+	__steal_AMPR(1);
+	__steal_AMPR(2);
+	__steal_AMPR(3);
+	__steal_AMPR(4);
+	__steal_AMPR(5);
+	__steal_AMPR(6);
+
+	if (PSR_IMPLE(__get_PSR()) == PSR_IMPLE_FR551) {
+		__steal_AMPR(7);
+		__steal_AMPR(8);
+		__steal_AMPR(9);
+		__steal_AMPR(10);
+		__steal_AMPR(11);
+		__steal_AMPR(12);
+		__steal_AMPR(13);
+		__steal_AMPR(14);
+	}
+
+	/* unable to grant any DMA consistent memory */
+	printk("No DMA consistent memory reserved\n");
+	return;
+
+ found:
+	dma_coherent_mem_start = ampr & xAMPRx_PPFN;
+	ampr &= xAMPRx_SS;
+	ampr >>= 4;
+	ampr = 1 << (ampr - 3 + 20);
+	dma_coherent_mem_end = dma_coherent_mem_start + ampr;
+
+	printk("DMA consistent memory reserved %lx-%lx\n",
+	       dma_coherent_mem_start, dma_coherent_mem_end);
+
+} /* end reserve_dma_coherent() */
+#endif
+
+/*****************************************************************************/
+/*
+ * calibrate the delay loop
+ */
+void __init calibrate_delay(void)
+{
+	loops_per_jiffy = __delay_loops_MHz * (1000000 / HZ);
+
+	printk("Calibrating delay loop... %lu.%02lu BogoMIPS\n",
+	       loops_per_jiffy / (500000 / HZ),
+	       (loops_per_jiffy / (5000 / HZ)) % 100);
+
+} /* end calibrate_delay() */
+
+/*****************************************************************************/
+/*
+ * look through the command line for some things we need to know immediately
+ */
+static void __init parse_cmdline_early(char *cmdline)
+{
+	if (!cmdline)
+		return;
+
+	while (*cmdline) {
+		if (*cmdline == ' ')
+			cmdline++;
+
+		/* "mem=XXX[kKmM]" sets SDRAM size to <mem>, overriding the value we worked
+		 * out from the SDRAM controller mask register
+		 */
+		if (!memcmp(cmdline, "mem=", 4)) {
+			unsigned long long mem_size;
+
+			mem_size = memparse(cmdline + 4, &cmdline);
+			memory_end = memory_start + mem_size;
+		}
+
+		while (*cmdline && *cmdline != ' ')
+			cmdline++;
+	}
+
+} /* end parse_cmdline_early() */
+
+/*****************************************************************************/
+/*
+ *
+ */
+void __init setup_arch(char **cmdline_p)
+{
+#ifdef CONFIG_MMU
+	printk("Linux FR-V port done by Red Hat Inc <dhowells@redhat.com>\n");
+#else
+	printk("uClinux FR-V port done by Red Hat Inc <dhowells@redhat.com>\n");
+#endif
+
+	memcpy(saved_command_line, redboot_command_line, COMMAND_LINE_SIZE);
+
+	determine_cpu();
+	determine_clocks(1);
+
+	/* For printk-directly-beats-on-serial-hardware hack */
+	console_set_baud(115200);
+#ifdef CONFIG_GDBSTUB
+	gdbstub_set_baud(115200);
+#endif
+
+#ifdef CONFIG_RESERVE_DMA_COHERENT
+	reserve_dma_coherent();
+#endif
+	dump_memory_map();
+
+#ifdef CONFIG_MB93090_MB00
+	if (mb93090_mb00_detected)
+		mb93090_display();
+#endif
+
+	/* register those serial ports that are available */
+#ifndef CONFIG_GDBSTUB_UART0
+	__reg(UART0_BASE + UART_IER * 8) = 0;
+	early_serial_setup(&__frv_uart0);
+//	register_serial(&__frv_uart0);
+#endif
+#ifndef CONFIG_GDBSTUB_UART1
+	__reg(UART1_BASE + UART_IER * 8) = 0;
+	early_serial_setup(&__frv_uart1);
+//	register_serial(&__frv_uart1);
+#endif
+
+#if defined(CONFIG_CHR_DEV_FLASH) || defined(CONFIG_BLK_DEV_FLASH)
+	/* we need to initialize the Flashrom device here since we might
+	 * do things with flash early on in the boot
+	 */
+	flash_probe();
+#endif
+
+	/* deal with the command line - RedBoot may have passed one to the kernel */
+	memcpy(command_line, saved_command_line, sizeof(command_line));
+	*cmdline_p = &command_line[0];
+	parse_cmdline_early(command_line);
+
+	/* set up the memory description
+	 * - by now the stack is part of the init task */
+	printk("Memory %08lx-%08lx\n", memory_start, memory_end);
+
+	if (memory_start == memory_end) BUG();
+
+	init_mm.start_code = (unsigned long) &_stext;
+	init_mm.end_code = (unsigned long) &_etext;
+	init_mm.end_data = (unsigned long) &_edata;
+#if 0 /* DAVIDM - don't set brk just incase someone decides to use it */
+	init_mm.brk = (unsigned long) &_end;
+#else
+	init_mm.brk = (unsigned long) 0;
+#endif
+
+#ifdef DEBUG
+	printk("KERNEL -> TEXT=0x%06x-0x%06x DATA=0x%06x-0x%06x BSS=0x%06x-0x%06x\n",
+	       (int) &_stext, (int) &_etext,
+	       (int) &_sdata, (int) &_edata,
+	       (int) &_sbss, (int) &_ebss);
+#endif
+
+#ifdef CONFIG_VT
+#if defined(CONFIG_VGA_CONSOLE)
+        conswitchp = &vga_con;
+#elif defined(CONFIG_DUMMY_CONSOLE)
+        conswitchp = &dummy_con;
+#endif
+#endif
+
+#ifdef CONFIG_BLK_DEV_BLKMEM
+	ROOT_DEV = MKDEV(BLKMEM_MAJOR,0);
+#endif
+	/*rom_length = (unsigned long)&_flashend - (unsigned long)&_romvec;*/
+
+#ifdef CONFIG_MMU
+	setup_linux_memory();
+#else
+	setup_uclinux_memory();
+#endif
+
+	/* get kmalloc into gear */
+	paging_init();
+
+	/* init DMA */
+	frv_dma_init();
+#ifdef DEBUG
+	printk("Done setup_arch\n");
+#endif
+
+	/* start the decrement timer running */
+//	asm volatile("movgs %0,timerd" :: "r"(10000000));
+//	__set_HSR(0, __get_HSR(0) | HSR0_ETMD);
+
+} /* end setup_arch() */
+
+#if 0
+/*****************************************************************************/
+/*
+ *
+ */
+static int __devinit setup_arch_serial(void)
+{
+	/* register those serial ports that are available */
+#ifndef CONFIG_GDBSTUB_UART0
+	early_serial_setup(&__frv_uart0);
+#endif
+#ifndef CONFIG_GDBSTUB_UART1
+	early_serial_setup(&__frv_uart1);
+#endif
+
+	return 0;
+} /* end setup_arch_serial() */
+
+late_initcall(setup_arch_serial);
+#endif
+
+/*****************************************************************************/
+/*
+ * set up the memory map for normal MMU linux
+ */
+#ifdef CONFIG_MMU
+static void __init setup_linux_memory(void)
+{
+	unsigned long bootmap_size, low_top_pfn, kstart, kend, high_mem;
+
+	kstart	= (unsigned long) &__kernel_image_start - PAGE_OFFSET;
+	kend	= (unsigned long) &__kernel_image_end - PAGE_OFFSET;
+
+	kstart = kstart & PAGE_MASK;
+	kend = (kend + PAGE_SIZE - 1) & PAGE_MASK;
+
+	/* give all the memory to the bootmap allocator,  tell it to put the
+	 * boot mem_map immediately following the kernel image
+	 */
+	bootmap_size = init_bootmem_node(NODE_DATA(0),
+					 kend >> PAGE_SHIFT,		/* map addr */
+					 memory_start >> PAGE_SHIFT,	/* start of RAM */
+					 memory_end >> PAGE_SHIFT	/* end of RAM */
+					 );
+
+	/* pass the memory that the kernel can immediately use over to the bootmem allocator */
+	max_mapnr = num_physpages = (memory_end - memory_start) >> PAGE_SHIFT;
+	low_top_pfn = (KERNEL_LOWMEM_END - KERNEL_LOWMEM_START) >> PAGE_SHIFT;
+	high_mem = 0;
+
+	if (num_physpages > low_top_pfn) {
+#ifdef CONFIG_HIGHMEM
+		high_mem = num_physpages - low_top_pfn;
+#else
+		max_mapnr = num_physpages = low_top_pfn;
+#endif
+	}
+	else {
+		low_top_pfn = num_physpages;
+	}
+
+	min_low_pfn = memory_start >> PAGE_SHIFT;
+	max_low_pfn = low_top_pfn;
+	max_pfn = memory_end >> PAGE_SHIFT;
+
+	num_mappedpages = low_top_pfn;
+
+	printk(KERN_NOTICE "%ldMB LOWMEM available.\n", low_top_pfn >> (20 - PAGE_SHIFT));
+
+	free_bootmem(memory_start, low_top_pfn << PAGE_SHIFT);
+
+#ifdef CONFIG_HIGHMEM
+	if (high_mem)
+		printk(KERN_NOTICE "%ldMB HIGHMEM available.\n", high_mem >> (20 - PAGE_SHIFT));
+#endif
+
+	/* take back the memory occupied by the kernel image and the bootmem alloc map */
+	reserve_bootmem(kstart, kend - kstart + bootmap_size);
+
+	/* reserve the memory occupied by the initial ramdisk */
+#ifdef CONFIG_BLK_DEV_INITRD
+	if (LOADER_TYPE && INITRD_START) {
+		if (INITRD_START + INITRD_SIZE <= (low_top_pfn << PAGE_SHIFT)) {
+			reserve_bootmem(INITRD_START, INITRD_SIZE);
+			initrd_start = INITRD_START ? INITRD_START + PAGE_OFFSET : 0;
+			initrd_end = initrd_start + INITRD_SIZE;
+		}
+		else {
+			printk(KERN_ERR
+			       "initrd extends beyond end of memory (0x%08lx > 0x%08lx)\n"
+			       "disabling initrd\n",
+			       INITRD_START + INITRD_SIZE,
+			       low_top_pfn << PAGE_SHIFT);
+			initrd_start = 0;
+		}
+	}
+#endif
+
+} /* end setup_linux_memory() */
+#endif
+
+/*****************************************************************************/
+/*
+ * set up the memory map for uClinux
+ */
+#ifndef CONFIG_MMU
+static void __init setup_uclinux_memory(void)
+{
+#ifdef CONFIG_PROTECT_KERNEL
+	unsigned long dampr;
+#endif
+	unsigned long kend;
+	int bootmap_size;
+
+	kend = (unsigned long) &__kernel_image_end;
+	kend = (kend + PAGE_SIZE - 1) & PAGE_MASK;
+
+	/* give all the memory to the bootmap allocator,  tell it to put the
+	 * boot mem_map immediately following the kernel image
+	 */
+	bootmap_size = init_bootmem_node(NODE_DATA(0),
+					 kend >> PAGE_SHIFT,		/* map addr */
+					 memory_start >> PAGE_SHIFT,	/* start of RAM */
+					 memory_end >> PAGE_SHIFT	/* end of RAM */
+					 );
+
+	/* free all the usable memory */
+	free_bootmem(memory_start, memory_end - memory_start);
+
+	high_memory = (void *) (memory_end & PAGE_MASK);
+	max_mapnr = num_physpages = ((unsigned long) high_memory - PAGE_OFFSET) >> PAGE_SHIFT;
+
+	min_low_pfn = memory_start >> PAGE_SHIFT;
+	max_low_pfn = memory_end >> PAGE_SHIFT;
+	max_pfn = max_low_pfn;
+
+	/* now take back the bits the core kernel is occupying */
+#ifndef CONFIG_PROTECT_KERNEL
+	reserve_bootmem(kend, bootmap_size);
+	reserve_bootmem((unsigned long) &__kernel_image_start,
+			kend - (unsigned long) &__kernel_image_start);
+
+#else
+	dampr = __get_DAMPR(0);
+	dampr &= xAMPRx_SS;
+	dampr = (dampr >> 4) + 17;
+	dampr = 1 << dampr;
+
+	reserve_bootmem(__get_DAMPR(0) & xAMPRx_PPFN, dampr);
+#endif
+
+	/* reserve some memory to do uncached DMA through if requested */
+#ifdef CONFIG_RESERVE_DMA_COHERENT
+	if (dma_coherent_mem_start)
+		reserve_bootmem(dma_coherent_mem_start,
+				dma_coherent_mem_end - dma_coherent_mem_start);
+#endif
+
+} /* end setup_uclinux_memory() */
+#endif
+
+/*****************************************************************************/
+/*
+ * get CPU information for use by procfs
+ */
+static int show_cpuinfo(struct seq_file *m, void *v)
+{
+	const char *gr, *fr, *fm, *fp, *cm, *nem, *ble;
+#ifdef CONFIG_PM
+	const char *sep;
+#endif
+
+	gr  = cpu_hsr0_all & HSR0_GRHE	? "gr0-63"	: "gr0-31";
+	fr  = cpu_hsr0_all & HSR0_FRHE	? "fr0-63"	: "fr0-31";
+	fm  = cpu_psr_all  & PSR_EM	? ", Media"	: "";
+	fp  = cpu_psr_all  & PSR_EF	? ", FPU"	: "";
+	cm  = cpu_psr_all  & PSR_CM	? ", CCCR"	: "";
+	nem = cpu_psr_all  & PSR_NEM	? ", NE"	: "";
+	ble = cpu_psr_all  & PSR_BE	? "BE"		: "LE";
+
+	seq_printf(m,
+		   "CPU-Series:\t%s\n"
+		   "CPU-Core:\t%s, %s, %s%s%s\n"
+		   "CPU:\t\t%s\n"
+		   "MMU:\t\t%s\n"
+		   "FP-Media:\t%s%s%s\n"
+		   "System:\t\t%s",
+		   cpu_series,
+		   cpu_core, gr, ble, cm, nem,
+		   cpu_silicon,
+		   cpu_mmu,
+		   fr, fm, fp,
+		   cpu_system);
+
+	if (cpu_board1)
+		seq_printf(m, ", %s", cpu_board1);
+
+	if (cpu_board2)
+		seq_printf(m, ", %s", cpu_board2);
+
+	seq_printf(m, "\n");
+
+#ifdef CONFIG_PM
+	seq_printf(m, "PM-Controls:");
+	sep = "\t";
+
+	if (clock_bits_settable & CLOCK_BIT_CMODE) {
+		seq_printf(m, "%scmode=0x%04hx", sep, clock_cmodes_permitted);
+		sep = ", ";
+	}
+
+	if (clock_bits_settable & CLOCK_BIT_CM) {
+		seq_printf(m, "%scm=0x%lx", sep, clock_bits_settable & CLOCK_BIT_CM);
+		sep = ", ";
+	}
+
+	if (clock_bits_settable & CLOCK_BIT_P0) {
+		seq_printf(m, "%sp0=0x3", sep);
+		sep = ", ";
+	}
+
+	seq_printf(m, "%ssuspend=0x22\n", sep);
+#endif
+
+	seq_printf(m,
+		   "PM-Status:\tcmode=%d, cm=%d, p0=%d\n",
+		   clock_cmode_current, clock_cm_current, clock_p0_current);
+
+#define print_clk(TAG, VAR) \
+	seq_printf(m, "Clock-" TAG ":\t%lu.%2.2lu MHz\n", VAR / 1000000, (VAR / 10000) % 100)
+
+	print_clk("In",    __clkin_clock_speed_HZ);
+	print_clk("Core",  __core_clock_speed_HZ);
+	print_clk("SDRAM", __sdram_clock_speed_HZ);
+	print_clk("CBus",  __core_bus_clock_speed_HZ);
+	print_clk("Res",   __res_bus_clock_speed_HZ);
+	print_clk("Ext",   __ext_bus_clock_speed_HZ);
+	print_clk("DSU",   __dsu_clock_speed_HZ);
+
+	seq_printf(m,
+		   "BogoMips:\t%lu.%02lu\n",
+		   (loops_per_jiffy * HZ) / 500000, ((loops_per_jiffy * HZ) / 5000) % 100);
+
+	return 0;
+} /* end show_cpuinfo() */
+
+static void *c_start(struct seq_file *m, loff_t *pos)
+{
+	return *pos < NR_CPUS ? (void *) 0x12345678 : NULL;
+}
+
+static void *c_next(struct seq_file *m, void *v, loff_t *pos)
+{
+	++*pos;
+	return c_start(m, pos);
+}
+
+static void c_stop(struct seq_file *m, void *v)
+{
+}
+
+struct seq_operations cpuinfo_op = {
+	.start	= c_start,
+	.next	= c_next,
+	.stop	= c_stop,
+	.show	= show_cpuinfo,
+};
+
+void arch_gettod(int *year, int *mon, int *day, int *hour,
+		 int *min, int *sec)
+{
+	*year = *mon = *day = *hour = *min = *sec = 0;
+}
+
+/*****************************************************************************/
+/*
+ *
+ */
+#ifdef CONFIG_MB93090_MB00
+static void __init mb93090_sendlcdcmd(uint32_t cmd)
+{
+	unsigned long base = __addr_LCD();
+	int loop;
+
+	/* request reading of the busy flag */
+	__set_LCD(base, LCD_CMD_READ_BUSY);
+	__set_LCD(base, LCD_CMD_READ_BUSY & ~LCD_E);
+
+	/* wait for the busy flag to become clear */
+	for (loop = 10000; loop > 0; loop--)
+		if (!(__get_LCD(base) & 0x80))
+			break;
+
+	/* send the command */
+	__set_LCD(base, cmd);
+	__set_LCD(base, cmd & ~LCD_E);
+
+} /* end mb93090_sendlcdcmd() */
+
+/*****************************************************************************/
+/*
+ * write to the MB93090 LEDs and LCD
+ */
+static void __init mb93090_display(void)
+{
+	const char *p;
+
+	__set_LEDS(0);
+
+	/* set up the LCD */
+	mb93090_sendlcdcmd(LCD_CMD_CLEAR);
+	mb93090_sendlcdcmd(LCD_CMD_FUNCSET(1,1,0));
+	mb93090_sendlcdcmd(LCD_CMD_ON(0,0));
+	mb93090_sendlcdcmd(LCD_CMD_HOME);
+
+	mb93090_sendlcdcmd(LCD_CMD_SET_DD_ADDR(0));
+	for (p = mb93090_banner; *p; p++)
+		mb93090_sendlcdcmd(LCD_DATA_WRITE(*p));
+
+	mb93090_sendlcdcmd(LCD_CMD_SET_DD_ADDR(64));
+	for (p = mb93090_version; *p; p++)
+		mb93090_sendlcdcmd(LCD_DATA_WRITE(*p));
+
+} /* end mb93090_display() */
+
+#endif // CONFIG_MB93090_MB00
diff --git a/arch/frv/kernel/signal.c b/arch/frv/kernel/signal.c
new file mode 100644
index 0000000..d8d8f3d
--- /dev/null
+++ b/arch/frv/kernel/signal.c
@@ -0,0 +1,588 @@
+/* signal.c: FRV specific bits of signal handling
+ *
+ * Copyright (C) 2003-5 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ * - Derived from arch/m68k/kernel/signal.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/errno.h>
+#include <linux/wait.h>
+#include <linux/ptrace.h>
+#include <linux/unistd.h>
+#include <linux/personality.h>
+#include <linux/suspend.h>
+#include <asm/ucontext.h>
+#include <asm/uaccess.h>
+#include <asm/cacheflush.h>
+
+#define DEBUG_SIG 0
+
+#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
+
+struct fdpic_func_descriptor {
+	unsigned long	text;
+	unsigned long	GOT;
+};
+
+asmlinkage int do_signal(struct pt_regs *regs, sigset_t *oldset);
+
+/*
+ * Atomically swap in the new signal mask, and wait for a signal.
+ */
+asmlinkage int sys_sigsuspend(int history0, int history1, old_sigset_t mask)
+{
+	sigset_t saveset;
+
+	mask &= _BLOCKABLE;
+	spin_lock_irq(&current->sighand->siglock);
+	saveset = current->blocked;
+	siginitset(&current->blocked, mask);
+	recalc_sigpending();
+	spin_unlock_irq(&current->sighand->siglock);
+
+	__frame->gr8 = -EINTR;
+	while (1) {
+		current->state = TASK_INTERRUPTIBLE;
+		schedule();
+		if (do_signal(__frame, &saveset))
+			/* return the signal number as the return value of this function
+			 * - this is an utterly evil hack. syscalls should not invoke do_signal()
+			 *   as entry.S sets regs->gr8 to the return value of the system call
+			 * - we can't just use sigpending() as we'd have to discard SIG_IGN signals
+			 *   and call waitpid() if SIGCHLD needed discarding
+			 * - this only works on the i386 because it passes arguments to the signal
+			 *   handler on the stack, and the return value in EAX is effectively
+			 *   discarded
+			 */
+			return __frame->gr8;
+	}
+}
+
+asmlinkage int sys_rt_sigsuspend(sigset_t __user *unewset, size_t sigsetsize)
+{
+	sigset_t saveset, newset;
+
+	/* XXX: Don't preclude handling different sized sigset_t's.  */
+	if (sigsetsize != sizeof(sigset_t))
+		return -EINVAL;
+
+	if (copy_from_user(&newset, unewset, sizeof(newset)))
+		return -EFAULT;
+	sigdelsetmask(&newset, ~_BLOCKABLE);
+
+	spin_lock_irq(&current->sighand->siglock);
+	saveset = current->blocked;
+	current->blocked = newset;
+	recalc_sigpending();
+	spin_unlock_irq(&current->sighand->siglock);
+
+	__frame->gr8 = -EINTR;
+	while (1) {
+		current->state = TASK_INTERRUPTIBLE;
+		schedule();
+		if (do_signal(__frame, &saveset))
+			/* return the signal number as the return value of this function
+			 * - this is an utterly evil hack. syscalls should not invoke do_signal()
+			 *   as entry.S sets regs->gr8 to the return value of the system call
+			 * - we can't just use sigpending() as we'd have to discard SIG_IGN signals
+			 *   and call waitpid() if SIGCHLD needed discarding
+			 * - this only works on the i386 because it passes arguments to the signal
+			 *   handler on the stack, and the return value in EAX is effectively
+			 *   discarded
+			 */
+			return __frame->gr8;
+	}
+}
+
+asmlinkage int sys_sigaction(int sig,
+			     const struct old_sigaction __user *act,
+			     struct old_sigaction __user *oact)
+{
+	struct k_sigaction new_ka, old_ka;
+	int ret;
+
+	if (act) {
+		old_sigset_t mask;
+		if (!access_ok(VERIFY_READ, act, sizeof(*act)) ||
+		    __get_user(new_ka.sa.sa_handler, &act->sa_handler) ||
+		    __get_user(new_ka.sa.sa_restorer, &act->sa_restorer))
+			return -EFAULT;
+		__get_user(new_ka.sa.sa_flags, &act->sa_flags);
+		__get_user(mask, &act->sa_mask);
+		siginitset(&new_ka.sa.sa_mask, mask);
+	}
+
+	ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL);
+
+	if (!ret && oact) {
+		if (!access_ok(VERIFY_WRITE, oact, sizeof(*oact)) ||
+		    __put_user(old_ka.sa.sa_handler, &oact->sa_handler) ||
+		    __put_user(old_ka.sa.sa_restorer, &oact->sa_restorer))
+			return -EFAULT;
+		__put_user(old_ka.sa.sa_flags, &oact->sa_flags);
+		__put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask);
+	}
+
+	return ret;
+}
+
+asmlinkage
+int sys_sigaltstack(const stack_t __user *uss, stack_t __user *uoss)
+{
+	return do_sigaltstack(uss, uoss, __frame->sp);
+}
+
+
+/*
+ * Do a signal return; undo the signal stack.
+ */
+
+struct sigframe
+{
+	void (*pretcode)(void);
+	int sig;
+	struct sigcontext sc;
+	unsigned long extramask[_NSIG_WORDS-1];
+	uint32_t retcode[2];
+};
+
+struct rt_sigframe
+{
+	void (*pretcode)(void);
+	int sig;
+	struct siginfo *pinfo;
+	void *puc;
+	struct siginfo info;
+	struct ucontext uc;
+	uint32_t retcode[2];
+};
+
+static int restore_sigcontext(struct sigcontext __user *sc, int *_gr8)
+{
+	struct user_context *user = current->thread.user;
+	unsigned long tbr, psr;
+
+	tbr = user->i.tbr;
+	psr = user->i.psr;
+	if (copy_from_user(user, &sc->sc_context, sizeof(sc->sc_context)))
+		goto badframe;
+	user->i.tbr = tbr;
+	user->i.psr = psr;
+
+	restore_user_regs(user);
+
+	user->i.syscallno = -1;		/* disable syscall checks */
+
+	*_gr8 = user->i.gr[8];
+	return 0;
+
+ badframe:
+	return 1;
+}
+
+asmlinkage int sys_sigreturn(void)
+{
+	struct sigframe __user *frame = (struct sigframe __user *) __frame->sp;
+	sigset_t set;
+	int gr8;
+
+	if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
+		goto badframe;
+	if (__get_user(set.sig[0], &frame->sc.sc_oldmask))
+		goto badframe;
+
+	if (_NSIG_WORDS > 1 &&
+	    __copy_from_user(&set.sig[1], &frame->extramask, sizeof(frame->extramask)))
+		goto badframe;
+
+	sigdelsetmask(&set, ~_BLOCKABLE);
+	spin_lock_irq(&current->sighand->siglock);
+	current->blocked = set;
+	recalc_sigpending();
+	spin_unlock_irq(&current->sighand->siglock);
+
+	if (restore_sigcontext(&frame->sc, &gr8))
+		goto badframe;
+	return gr8;
+
+ badframe:
+	force_sig(SIGSEGV, current);
+	return 0;
+}
+
+asmlinkage int sys_rt_sigreturn(void)
+{
+	struct rt_sigframe __user *frame = (struct rt_sigframe __user *) __frame->sp;
+	sigset_t set;
+	int gr8;
+
+	if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
+		goto badframe;
+	if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set)))
+		goto badframe;
+
+	sigdelsetmask(&set, ~_BLOCKABLE);
+	spin_lock_irq(&current->sighand->siglock);
+	current->blocked = set;
+	recalc_sigpending();
+	spin_unlock_irq(&current->sighand->siglock);
+
+	if (restore_sigcontext(&frame->uc.uc_mcontext, &gr8))
+		goto badframe;
+
+	if (do_sigaltstack(&frame->uc.uc_stack, NULL, __frame->sp) == -EFAULT)
+		goto badframe;
+
+	return gr8;
+
+badframe:
+	force_sig(SIGSEGV, current);
+	return 0;
+}
+
+/*
+ * Set up a signal frame
+ */
+static int setup_sigcontext(struct sigcontext __user *sc, unsigned long mask)
+{
+	save_user_regs(current->thread.user);
+
+	if (copy_to_user(&sc->sc_context, current->thread.user, sizeof(sc->sc_context)) != 0)
+		goto badframe;
+
+	/* non-iBCS2 extensions.. */
+	if (__put_user(mask, &sc->sc_oldmask) < 0)
+		goto badframe;
+
+	return 0;
+
+ badframe:
+	return 1;
+}
+
+/*****************************************************************************/
+/*
+ * Determine which stack to use..
+ */
+static inline void __user *get_sigframe(struct k_sigaction *ka,
+					struct pt_regs *regs,
+					size_t frame_size)
+{
+	unsigned long sp;
+
+	/* Default to using normal stack */
+	sp = regs->sp;
+
+	/* This is the X/Open sanctioned signal stack switching.  */
+	if (ka->sa.sa_flags & SA_ONSTACK) {
+		if (! on_sig_stack(sp))
+			sp = current->sas_ss_sp + current->sas_ss_size;
+	}
+
+	return (void __user *) ((sp - frame_size) & ~7UL);
+} /* end get_sigframe() */
+
+/*****************************************************************************/
+/*
+ *
+ */
+static void setup_frame(int sig, struct k_sigaction *ka, sigset_t *set, struct pt_regs * regs)
+{
+	struct sigframe __user *frame;
+	int rsig;
+
+	frame = get_sigframe(ka, regs, sizeof(*frame));
+
+	if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
+		goto give_sigsegv;
+
+	rsig = sig;
+	if (sig < 32 &&
+	    __current_thread_info->exec_domain &&
+	    __current_thread_info->exec_domain->signal_invmap)
+		rsig = __current_thread_info->exec_domain->signal_invmap[sig];
+
+	if (__put_user(rsig, &frame->sig) < 0)
+		goto give_sigsegv;
+
+	if (setup_sigcontext(&frame->sc, set->sig[0]))
+		goto give_sigsegv;
+
+	if (_NSIG_WORDS > 1) {
+		if (__copy_to_user(frame->extramask, &set->sig[1],
+				   sizeof(frame->extramask)))
+			goto give_sigsegv;
+	}
+
+	/* Set up to return from userspace.  If provided, use a stub
+	 * already in userspace.  */
+	if (ka->sa.sa_flags & SA_RESTORER) {
+		if (__put_user(ka->sa.sa_restorer, &frame->pretcode) < 0)
+			goto give_sigsegv;
+	}
+	else {
+		/* Set up the following code on the stack:
+		 *	setlos	#__NR_sigreturn,gr7
+		 *	tira	gr0,0
+		 */
+		if (__put_user((void (*)(void))frame->retcode, &frame->pretcode) ||
+		    __put_user(0x8efc0000|__NR_sigreturn, &frame->retcode[0]) ||
+		    __put_user(0xc0700000, &frame->retcode[1]))
+			goto give_sigsegv;
+
+		flush_icache_range((unsigned long) frame->retcode,
+				   (unsigned long) (frame->retcode + 2));
+	}
+
+	/* set up registers for signal handler */
+	regs->sp   = (unsigned long) frame;
+	regs->lr   = (unsigned long) &frame->retcode;
+	regs->gr8  = sig;
+
+	if (get_personality & FDPIC_FUNCPTRS) {
+		struct fdpic_func_descriptor __user *funcptr =
+			(struct fdpic_func_descriptor *) ka->sa.sa_handler;
+		__get_user(regs->pc, &funcptr->text);
+		__get_user(regs->gr15, &funcptr->GOT);
+	} else {
+		regs->pc   = (unsigned long) ka->sa.sa_handler;
+		regs->gr15 = 0;
+	}
+
+	set_fs(USER_DS);
+
+#if DEBUG_SIG
+	printk("SIG deliver %d (%s:%d): sp=%p pc=%lx ra=%p\n",
+		sig, current->comm, current->pid, frame, regs->pc, frame->pretcode);
+#endif
+
+	return;
+
+give_sigsegv:
+	if (sig == SIGSEGV)
+		ka->sa.sa_handler = SIG_DFL;
+
+	force_sig(SIGSEGV, current);
+} /* end setup_frame() */
+
+/*****************************************************************************/
+/*
+ *
+ */
+static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
+			   sigset_t *set, struct pt_regs * regs)
+{
+	struct rt_sigframe __user *frame;
+	int rsig;
+
+	frame = get_sigframe(ka, regs, sizeof(*frame));
+
+	if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
+		goto give_sigsegv;
+
+	rsig = sig;
+	if (sig < 32 &&
+	    __current_thread_info->exec_domain &&
+	    __current_thread_info->exec_domain->signal_invmap)
+		rsig = __current_thread_info->exec_domain->signal_invmap[sig];
+
+	if (__put_user(rsig,		&frame->sig) ||
+	    __put_user(&frame->info,	&frame->pinfo) ||
+	    __put_user(&frame->uc,	&frame->puc))
+		goto give_sigsegv;
+
+	if (copy_siginfo_to_user(&frame->info, info))
+		goto give_sigsegv;
+
+	/* Create the ucontext.  */
+	if (__put_user(0, &frame->uc.uc_flags) ||
+	    __put_user(0, &frame->uc.uc_link) ||
+	    __put_user((void*)current->sas_ss_sp, &frame->uc.uc_stack.ss_sp) ||
+	    __put_user(sas_ss_flags(regs->sp), &frame->uc.uc_stack.ss_flags) ||
+	    __put_user(current->sas_ss_size, &frame->uc.uc_stack.ss_size))
+		goto give_sigsegv;
+
+	if (setup_sigcontext(&frame->uc.uc_mcontext, set->sig[0]))
+		goto give_sigsegv;
+
+	if (__copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)))
+		goto give_sigsegv;
+
+	/* Set up to return from userspace.  If provided, use a stub
+	 * already in userspace.  */
+	if (ka->sa.sa_flags & SA_RESTORER) {
+		if (__put_user(ka->sa.sa_restorer, &frame->pretcode))
+			goto give_sigsegv;
+	}
+	else {
+		/* Set up the following code on the stack:
+		 *	setlos	#__NR_sigreturn,gr7
+		 *	tira	gr0,0
+		 */
+		if (__put_user((void (*)(void))frame->retcode, &frame->pretcode) ||
+		    __put_user(0x8efc0000|__NR_rt_sigreturn, &frame->retcode[0]) ||
+		    __put_user(0xc0700000, &frame->retcode[1]))
+			goto give_sigsegv;
+
+		flush_icache_range((unsigned long) frame->retcode,
+				   (unsigned long) (frame->retcode + 2));
+	}
+
+	/* Set up registers for signal handler */
+	regs->sp  = (unsigned long) frame;
+	regs->lr   = (unsigned long) &frame->retcode;
+	regs->gr8 = sig;
+	regs->gr9 = (unsigned long) &frame->info;
+
+	if (get_personality & FDPIC_FUNCPTRS) {
+		struct fdpic_func_descriptor *funcptr =
+			(struct fdpic_func_descriptor __user *) ka->sa.sa_handler;
+		__get_user(regs->pc, &funcptr->text);
+		__get_user(regs->gr15, &funcptr->GOT);
+	} else {
+		regs->pc   = (unsigned long) ka->sa.sa_handler;
+		regs->gr15 = 0;
+	}
+
+	set_fs(USER_DS);
+
+#if DEBUG_SIG
+	printk("SIG deliver %d (%s:%d): sp=%p pc=%lx ra=%p\n",
+		sig, current->comm, current->pid, frame, regs->pc, frame->pretcode);
+#endif
+
+	return;
+
+give_sigsegv:
+	if (sig == SIGSEGV)
+		ka->sa.sa_handler = SIG_DFL;
+	force_sig(SIGSEGV, current);
+
+} /* end setup_rt_frame() */
+
+/*****************************************************************************/
+/*
+ * OK, we're invoking a handler
+ */
+static void handle_signal(unsigned long sig, siginfo_t *info,
+			  struct k_sigaction *ka, sigset_t *oldset,
+			  struct pt_regs *regs)
+{
+	/* Are we from a system call? */
+	if (in_syscall(regs)) {
+		/* If so, check system call restarting.. */
+		switch (regs->gr8) {
+		case -ERESTART_RESTARTBLOCK:
+		case -ERESTARTNOHAND:
+			regs->gr8 = -EINTR;
+			break;
+
+		case -ERESTARTSYS:
+			if (!(ka->sa.sa_flags & SA_RESTART)) {
+				regs->gr8 = -EINTR;
+				break;
+			}
+			/* fallthrough */
+		case -ERESTARTNOINTR:
+			regs->gr8 = regs->orig_gr8;
+			regs->pc -= 4;
+		}
+	}
+
+	/* Set up the stack frame */
+	if (ka->sa.sa_flags & SA_SIGINFO)
+		setup_rt_frame(sig, ka, info, oldset, regs);
+	else
+		setup_frame(sig, ka, oldset, regs);
+
+	if (!(ka->sa.sa_flags & SA_NODEFER)) {
+		spin_lock_irq(&current->sighand->siglock);
+		sigorsets(&current->blocked, &current->blocked, &ka->sa.sa_mask);
+		sigaddset(&current->blocked, sig);
+		recalc_sigpending();
+		spin_unlock_irq(&current->sighand->siglock);
+	}
+} /* end handle_signal() */
+
+/*****************************************************************************/
+/*
+ * Note that 'init' is a special process: it doesn't get signals it doesn't
+ * want to handle. Thus you cannot kill init even with a SIGKILL even by
+ * mistake.
+ */
+int do_signal(struct pt_regs *regs, sigset_t *oldset)
+{
+	struct k_sigaction ka;
+	siginfo_t info;
+	int signr;
+
+	/*
+	 * We want the common case to go fast, which
+	 * is why we may in certain cases get here from
+	 * kernel mode. Just return without doing anything
+	 * if so.
+	 */
+	if (!user_mode(regs))
+		return 1;
+
+	if (current->flags & PF_FREEZE) {
+		refrigerator(0);
+		goto no_signal;
+	}
+
+	if (!oldset)
+		oldset = &current->blocked;
+
+	signr = get_signal_to_deliver(&info, &ka, regs, NULL);
+	if (signr > 0) {
+		handle_signal(signr, &info, &ka, oldset, regs);
+		return 1;
+	}
+
+ no_signal:
+	/* Did we come from a system call? */
+	if (regs->syscallno >= 0) {
+		/* Restart the system call - no handlers present */
+		if (regs->gr8 == -ERESTARTNOHAND ||
+		    regs->gr8 == -ERESTARTSYS ||
+		    regs->gr8 == -ERESTARTNOINTR) {
+			regs->gr8 = regs->orig_gr8;
+			regs->pc -= 4;
+		}
+
+		if (regs->gr8 == -ERESTART_RESTARTBLOCK){
+			regs->gr8 = __NR_restart_syscall;
+			regs->pc -= 4;
+		}
+	}
+
+	return 0;
+} /* end do_signal() */
+
+/*****************************************************************************/
+/*
+ * notification of userspace execution resumption
+ * - triggered by current->work.notify_resume
+ */
+asmlinkage void do_notify_resume(__u32 thread_info_flags)
+{
+	/* pending single-step? */
+	if (thread_info_flags & _TIF_SINGLESTEP)
+		clear_thread_flag(TIF_SINGLESTEP);
+
+	/* deal with pending signal delivery */
+	if (thread_info_flags & _TIF_SIGPENDING)
+		do_signal(__frame, NULL);
+
+} /* end do_notify_resume() */
diff --git a/arch/frv/kernel/sleep.S b/arch/frv/kernel/sleep.S
new file mode 100644
index 0000000..e6079b8
--- /dev/null
+++ b/arch/frv/kernel/sleep.S
@@ -0,0 +1,374 @@
+/* sleep.S: power saving mode entry
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Woodhouse (dwmw2@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ */
+
+#include <linux/sys.h>
+#include <linux/config.h>
+#include <linux/linkage.h>
+#include <asm/setup.h>
+#include <asm/segment.h>
+#include <asm/page.h>
+#include <asm/ptrace.h>
+#include <asm/errno.h>
+#include <asm/cache.h>
+#include <asm/spr-regs.h>
+
+#define __addr_MASK	0xfeff9820	/* interrupt controller mask */
+
+#define __addr_FR55X_DRCN	0xfeff0218      /* Address of DRCN register */
+#define FR55X_DSTS_OFFSET	-4		/* Offset from DRCN to DSTS */
+#define FR55X_SDRAMC_DSTS_SSI	0x00000002	/* indicates that the SDRAM is in self-refresh mode */
+
+#define __addr_FR4XX_DRCN	0xfe000430      /* Address of DRCN register */
+#define FR4XX_DSTS_OFFSET	-8		/* Offset from DRCN to DSTS */
+#define FR4XX_SDRAMC_DSTS_SSI	0x00000001	/* indicates that the SDRAM is in self-refresh mode */
+
+#define SDRAMC_DRCN_SR	0x00000001	/* transition SDRAM into self-refresh mode */
+
+	.section	.bss
+	.balign		8
+	.globl		__sleep_save_area
+__sleep_save_area:
+	.space		16
+
+
+	.text
+	.balign		4
+
+.macro li v r
+	sethi.p		%hi(\v),\r
+	setlo		%lo(\v),\r
+.endm
+
+#ifdef CONFIG_PM
+###############################################################################
+#
+# CPU suspension routine
+# - void frv_cpu_suspend(unsigned long pdm_mode)
+#
+###############################################################################
+	.globl		frv_cpu_suspend
+        .type		frv_cpu_suspend,@function
+frv_cpu_suspend:
+
+	#----------------------------------------------------
+	# save hsr0, psr, isr, and lr for resume code
+	#----------------------------------------------------
+	li		__sleep_save_area,gr11
+
+	movsg		hsr0,gr4
+	movsg		psr,gr5
+	movsg		isr,gr6
+	movsg		lr,gr7
+	stdi		gr4,@(gr11,#0)
+	stdi		gr6,@(gr11,#8)
+
+	# store the return address from sleep in GR14, and its complement in GR13 as a check
+	li		__ramboot_resume,gr14
+#ifdef CONFIG_MMU
+	# Resume via RAMBOOT# will turn MMU off, so bootloader needs a physical address.
+	sethi.p		%hi(__page_offset),gr13
+	setlo		%lo(__page_offset),gr13
+	sub		gr14,gr13,gr14
+#endif
+	not		gr14,gr13
+
+	#----------------------------------------------------
+	# preload and lock into icache that code which may have to run
+	# when dram is in self-refresh state.
+	#----------------------------------------------------
+	movsg		hsr0, gr3
+	li		HSR0_ICE,gr4
+	or		gr3,gr4,gr3
+	movgs		gr3,hsr0
+	or		gr3,gr8,gr7	// add the sleep bits for later
+
+	li		#__icache_lock_start,gr3
+	li		#__icache_lock_end,gr4
+1:	icpl		gr3,gr0,#1
+	addi		gr3,#L1_CACHE_BYTES,gr3
+	cmp		gr4,gr3,icc0
+	bhi		icc0,#0,1b
+
+	# disable exceptions
+	movsg		psr,gr8
+	andi.p		gr8,#~PSR_PIL,gr8
+	andi		gr8,~PSR_ET,gr8
+	movgs		gr8,psr
+	ori		gr8,#PSR_ET,gr8
+
+	srli		gr8,#28,gr4
+	subicc		gr4,#3,gr0,icc0
+	beq		icc0,#0,1f
+	# FR4xx
+	li		__addr_FR4XX_DRCN,gr4
+	li		FR4XX_SDRAMC_DSTS_SSI,gr5
+	li		FR4XX_DSTS_OFFSET,gr6
+	bra		__icache_lock_start
+1:
+	# FR5xx
+	li		__addr_FR55X_DRCN,gr4
+	li		FR55X_SDRAMC_DSTS_SSI,gr5
+	li		FR55X_DSTS_OFFSET,gr6
+	bra		__icache_lock_start
+
+	.size		frv_cpu_suspend, .-frv_cpu_suspend
+
+#
+# the final part of the sleep sequence...
+# - we want it to be be cacheline aligned so we can lock it into the icache easily
+#  On entry:	gr7 holds desired hsr0 sleep value
+#               gr8 holds desired psr sleep value
+#
+	.balign		L1_CACHE_BYTES
+        .type		__icache_lock_start,@function
+__icache_lock_start:
+
+	#----------------------------------------------------
+	# put SDRAM in self-refresh mode
+	#----------------------------------------------------
+
+	# Flush all data in the cache using the DCEF instruction.
+	dcef		@(gr0,gr0),#1
+
+	# Stop DMAC transfer
+
+	# Execute dummy load from SDRAM
+	ldi		@(gr11,#0),gr11
+
+	# put the SDRAM into self-refresh mode
+	ld              @(gr4,gr0),gr11
+	ori		gr11,#SDRAMC_DRCN_SR,gr11
+	st		gr11,@(gr4,gr0)
+	membar
+
+	# wait for SDRAM to reach self-refresh mode
+1:	ld		@(gr4,gr6),gr11
+	andcc		gr11,gr5,gr11,icc0
+	beq		icc0,#0,1b
+
+	#  Set the GPIO register so that the IRQ[3:0] pins become valid, as required.
+	#  Set the clock mode (CLKC register) as required.
+	#     - At this time, also set the CLKC register P0 bit.
+
+	# Set the HSR0 register PDM field.
+	movgs		gr7,hsr0
+
+	# Execute NOP 32 times.
+	.rept		32
+	nop
+	.endr
+
+#if 0 // Fujitsu recommend to skip this and will update docs.
+	#      Release the interrupt mask setting of the MASK register of the
+	#      interrupt controller if necessary.
+	sti		gr10,@(gr9,#0)
+	membar
+#endif
+
+	# Set the PSR register ET bit to 1 to enable interrupts.
+	movgs		gr8,psr
+
+	###################################################
+	# this is only reached if waking up via interrupt
+	###################################################
+
+	# Execute NOP 32 times.
+	.rept		32
+	nop
+	.endr
+
+	#----------------------------------------------------
+	# wake SDRAM from self-refresh mode
+	#----------------------------------------------------
+	ld              @(gr4,gr0),gr11
+	andi		gr11,#~SDRAMC_DRCN_SR,gr11
+	st		gr11,@(gr4,gr0)
+	membar
+2:
+	ld		@(gr4,gr6),gr11	// Wait for it to come back...
+	andcc		gr11,gr5,gr0,icc0
+	bne		icc0,0,2b
+
+	# wait for the SDRAM to stabilise
+	li		0x0100000,gr3
+3:	subicc		gr3,#1,gr3,icc0
+	bne		icc0,#0,3b
+
+	# now that DRAM is back, this is the end of the code which gets
+	# locked in icache.
+__icache_lock_end:
+	.size		__icache_lock_start, .-__icache_lock_start
+
+	# Fall-through to the RAMBOOT# wakeup path
+
+###############################################################################
+#
+#  resume from suspend re-entry point reached via RAMBOOT# and bootloader
+#
+###############################################################################
+__ramboot_resume:
+
+	#----------------------------------------------------
+	# restore hsr0, psr, isr, and leave saved lr in gr7
+	#----------------------------------------------------
+	li		__sleep_save_area,gr11
+#ifdef CONFIG_MMU
+	movsg		hsr0,gr4
+	sethi.p		%hi(HSR0_EXMMU),gr3
+	setlo		%lo(HSR0_EXMMU),gr3
+	andcc		gr3,gr4,gr0,icc0
+	bne		icc0,#0,2f
+
+	# need to use physical address
+	sethi.p		%hi(__page_offset),gr3
+	setlo		%lo(__page_offset),gr3
+	sub		gr11,gr3,gr11
+
+	# flush all tlb entries
+	setlos		#64,gr4
+	setlos.p	#PAGE_SIZE,gr5
+	setlos		#0,gr6
+1:
+	tlbpr		gr6,gr0,#6,#0
+	subicc.p	gr4,#1,gr4,icc0
+	add		gr6,gr5,gr6
+	bne		icc0,#2,1b
+
+	# need a temporary mapping for the current physical address we are
+	# using between time MMU is enabled and jump to virtual address is
+	# made.
+	sethi.p		%hi(0x00000000),gr4
+	setlo		%lo(0x00000000),gr4		; physical address
+	setlos		#xAMPRx_L|xAMPRx_M|xAMPRx_SS_256Mb|xAMPRx_S_KERNEL|xAMPRx_V,gr5
+	or		gr4,gr5,gr5
+
+	movsg		cxnr,gr13
+	or		gr4,gr13,gr4
+
+	movgs		gr4,iamlr1			; mapped from real address 0
+	movgs		gr5,iampr1			; cached kernel memory at 0x00000000
+2:
+#endif
+
+	lddi		@(gr11,#0),gr4 ; hsr0, psr
+	lddi		@(gr11,#8),gr6 ; isr, lr
+	movgs		gr4,hsr0
+	bar
+
+#ifdef CONFIG_MMU
+	sethi.p		%hi(1f),gr11
+	setlo		%lo(1f),gr11
+	jmpl		@(gr11,gr0)
+1:
+	movgs		gr0,iampr1 	; get rid of temporary mapping
+#endif
+	movgs		gr5,psr
+	movgs		gr6,isr
+
+	#----------------------------------------------------
+	# unlock the icache which was locked before going to sleep
+	#----------------------------------------------------
+	li		__icache_lock_start,gr3
+	li		__icache_lock_end,gr4
+1:	icul		gr3
+	addi		gr3,#L1_CACHE_BYTES,gr3
+	cmp		gr4,gr3,icc0
+	bhi		icc0,#0,1b
+
+	#----------------------------------------------------
+	# back to business as usual
+	#----------------------------------------------------
+	jmpl		@(gr7,gr0)		;
+
+#endif /* CONFIG_PM */
+
+###############################################################################
+#
+# CPU core sleep mode routine
+#
+###############################################################################
+	.globl		frv_cpu_core_sleep
+        .type		frv_cpu_core_sleep,@function
+frv_cpu_core_sleep:
+
+	# Preload into icache.
+	li		#__core_sleep_icache_lock_start,gr3
+	li		#__core_sleep_icache_lock_end,gr4
+
+1:	icpl		gr3,gr0,#1
+	addi		gr3,#L1_CACHE_BYTES,gr3
+	cmp		gr4,gr3,icc0
+	bhi		icc0,#0,1b
+
+	bra	__core_sleep_icache_lock_start
+
+	.balign L1_CACHE_BYTES
+__core_sleep_icache_lock_start:
+
+	# (1) Set the PSR register ET bit to 0 to disable interrupts.
+	movsg		psr,gr8
+	andi.p		gr8,#~(PSR_PIL),gr8
+	andi		gr8,#~(PSR_ET),gr4
+	movgs		gr4,psr
+
+#if 0 // Fujitsu recommend to skip this and will update docs.
+	# (2) Set '1' to all bits in the MASK register of the interrupt
+	#     controller and mask interrupts.
+	sethi.p		%hi(__addr_MASK),gr9
+	setlo		%lo(__addr_MASK),gr9
+	sethi.p		%hi(0xffff0000),gr4
+	setlo		%lo(0xffff0000),gr4
+	ldi		@(gr9,#0),gr10
+	sti		gr4,@(gr9,#0)
+#endif
+	# (3) Flush all data in the cache using the DCEF instruction.
+	dcef		@(gr0,gr0),#1
+
+	# (4) Execute the memory barrier instruction
+	membar
+
+	# (5) Set the GPIO register so that the IRQ[3:0] pins become valid, as required.
+	# (6) Set the clock mode (CLKC register) as required.
+	#     - At this time, also set the CLKC register P0 bit.
+	# (7) Set the HSR0 register PDM field to  001 .
+	movsg		hsr0,gr4
+	ori		gr4,HSR0_PDM_CORE_SLEEP,gr4
+	movgs		gr4,hsr0
+
+	# (8) Execute NOP 32 times.
+	.rept		32
+	nop
+	.endr
+
+#if 0 // Fujitsu recommend to skip this and will update docs.
+	# (9) Release the interrupt mask setting of the MASK register of the
+	#     interrupt controller if necessary.
+	sti		gr10,@(gr9,#0)
+	membar
+#endif
+
+	# (10) Set the PSR register ET bit to 1 to enable interrupts.
+	movgs		gr8,psr
+
+__core_sleep_icache_lock_end:
+
+	# Unlock from icache
+	li	__core_sleep_icache_lock_start,gr3
+	li	__core_sleep_icache_lock_end,gr4
+1:	icul		gr3
+	addi		gr3,#L1_CACHE_BYTES,gr3
+	cmp		gr4,gr3,icc0
+	bhi		icc0,#0,1b
+
+	bralr
+
+	.size		frv_cpu_core_sleep, .-frv_cpu_core_sleep
diff --git a/arch/frv/kernel/switch_to.S b/arch/frv/kernel/switch_to.S
new file mode 100644
index 0000000..1703dc2
--- /dev/null
+++ b/arch/frv/kernel/switch_to.S
@@ -0,0 +1,496 @@
+###############################################################################
+#
+# switch_to.S: context switch operation
+#
+# Copyright (C) 2003 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 License
+# as published by the Free Software Foundation; either version
+# 2 of the License, or (at your option) any later version.
+#
+###############################################################################
+#include <linux/config.h>
+#include <linux/linkage.h>
+#include <asm/thread_info.h>
+#include <asm/processor.h>
+#include <asm/registers.h>
+#include <asm/spr-regs.h>
+
+.macro LEDS val
+	setlos		#~\val,gr27
+	st		gr27,@(gr30,gr0)
+	membar
+	dcf		@(gr30,gr0)
+.endm
+
+	.section	.sdata
+	.balign		8
+
+	# address of frame 0 (userspace) on current kernel stack
+	.globl		__kernel_frame0_ptr
+__kernel_frame0_ptr:
+	.long		init_thread_union + THREAD_SIZE - USER_CONTEXT_SIZE
+
+	# address of current task
+	.globl		__kernel_current_task
+__kernel_current_task:
+	.long		init_task
+
+	.section	.text
+	.balign		4
+
+###############################################################################
+#
+# struct task_struct *__switch_to(struct thread_struct *prev_thread,
+#				  struct thread_struct *next_thread,
+#				  struct task_struct *prev)
+#
+###############################################################################
+	.globl		__switch_to
+__switch_to:
+	# save outgoing process's context
+	sethi.p		%hi(__switch_back),gr13
+	setlo		%lo(__switch_back),gr13
+	movsg		lr,gr12
+
+	stdi		gr28,@(gr8,#__THREAD_FRAME)
+	sti		sp  ,@(gr8,#__THREAD_SP)
+	sti		fp  ,@(gr8,#__THREAD_FP)
+	stdi		gr12,@(gr8,#__THREAD_LR)
+	stdi		gr16,@(gr8,#__THREAD_GR(16))
+	stdi		gr18,@(gr8,#__THREAD_GR(18))
+	stdi		gr20,@(gr8,#__THREAD_GR(20))
+	stdi		gr22,@(gr8,#__THREAD_GR(22))
+	stdi		gr24,@(gr8,#__THREAD_GR(24))
+	stdi.p		gr26,@(gr8,#__THREAD_GR(26))
+
+	or		gr8,gr8,gr22
+	ldi.p		@(gr8,#__THREAD_USER),gr8
+	call		save_user_regs
+	or		gr22,gr22,gr8
+	
+	# retrieve the new context
+	sethi.p		%hi(__kernel_frame0_ptr),gr6
+	setlo		%lo(__kernel_frame0_ptr),gr6
+	movsg		psr,gr4
+
+	lddi.p		@(gr9,#__THREAD_FRAME),gr10
+	or		gr10,gr10,gr27		; save prev for the return value
+
+	ldi		@(gr11,#4),gr19		; get new_current->thread_info
+
+	lddi		@(gr9,#__THREAD_SP),gr12
+	ldi		@(gr9,#__THREAD_LR),gr14
+	ldi		@(gr9,#__THREAD_PC),gr18
+	ldi.p		@(gr9,#__THREAD_FRAME0),gr7
+
+	# actually switch kernel contexts with ordinary exceptions disabled
+	andi		gr4,#~PSR_ET,gr5
+	movgs		gr5,psr
+
+	or.p		gr10,gr0,gr28		; set __frame
+	or		gr11,gr0,gr29		; set __current
+	or.p		gr12,gr0,sp
+	or		gr13,gr0,fp
+	or		gr19,gr0,gr15		; set __current_thread_info
+
+	sti		gr7,@(gr6,#0)		; set __kernel_frame0_ptr
+	sti		gr29,@(gr6,#4)		; set __kernel_current_task
+
+	movgs		gr14,lr
+	bar
+
+	srli		gr15,#28,gr5
+	subicc		gr5,#0xc,gr0,icc0
+	beq		icc0,#0,111f
+	break
+	nop
+111:
+
+	# jump to __switch_back or ret_from_fork as appropriate
+	# - move prev to GR8
+	movgs		gr4,psr
+	jmpl.p		@(gr18,gr0)
+	or		gr27,gr27,gr8
+
+###############################################################################
+#
+# restore incoming process's context
+# - on entry:
+#   - SP, FP, LR, GR15, GR28 and GR29 will have been set up appropriately
+#   - GR8 will point to the outgoing task_struct
+#   - GR9 will point to the incoming thread_struct
+#
+###############################################################################
+__switch_back:
+	lddi		@(gr9,#__THREAD_GR(16)),gr16
+	lddi		@(gr9,#__THREAD_GR(18)),gr18
+	lddi		@(gr9,#__THREAD_GR(20)),gr20
+	lddi		@(gr9,#__THREAD_GR(22)),gr22
+	lddi		@(gr9,#__THREAD_GR(24)),gr24
+	lddi		@(gr9,#__THREAD_GR(26)),gr26
+
+	# fall through into restore_user_regs()
+	ldi.p		@(gr9,#__THREAD_USER),gr8
+	or		gr8,gr8,gr9
+
+###############################################################################
+#
+# restore extra general regs and FP/Media regs
+# - void *restore_user_regs(const struct user_context *target, void *retval)
+# - on entry:
+#   - GR8 will point to the user context to swap in
+#   - GR9 will contain the value to be returned in GR8 (prev task on context switch)
+#
+###############################################################################
+	.globl		restore_user_regs
+restore_user_regs:
+	movsg		hsr0,gr6
+	ori		gr6,#HSR0_GRHE|HSR0_FRLE|HSR0_FRHE,gr6
+	movgs		gr6,hsr0
+	movsg		hsr0,gr6
+
+	movsg		psr,gr7
+	ori		gr7,#PSR_EF|PSR_EM,gr7
+	movgs		gr7,psr
+	movsg		psr,gr7
+	srli		gr7,#24,gr7
+	bar
+
+	lddi		@(gr8,#__FPMEDIA_MSR(0)),gr4
+
+	movgs		gr4,msr0
+	movgs		gr5,msr1
+
+	lddfi		@(gr8,#__FPMEDIA_ACC(0)),fr16
+	lddfi		@(gr8,#__FPMEDIA_ACC(2)),fr18
+	ldbfi		@(gr8,#__FPMEDIA_ACCG(0)),fr20
+	ldbfi		@(gr8,#__FPMEDIA_ACCG(1)),fr21
+	ldbfi		@(gr8,#__FPMEDIA_ACCG(2)),fr22
+	ldbfi		@(gr8,#__FPMEDIA_ACCG(3)),fr23
+
+	mwtacc		fr16,acc0
+	mwtacc		fr17,acc1
+	mwtacc		fr18,acc2
+	mwtacc		fr19,acc3
+	mwtaccg		fr20,accg0
+	mwtaccg		fr21,accg1
+	mwtaccg		fr22,accg2
+	mwtaccg		fr23,accg3
+
+	# some CPUs have extra ACCx and ACCGx regs and maybe FSRx regs
+	subicc.p	gr7,#0x50,gr0,icc0
+	subicc		gr7,#0x31,gr0,icc1
+	beq		icc0,#0,__restore_acc_fr451
+	beq		icc1,#0,__restore_acc_fr555
+__restore_acc_cont:
+
+	# some CPU's have GR32-GR63
+	setlos		#HSR0_FRHE,gr4
+	andcc		gr6,gr4,gr0,icc0
+	beq		icc0,#1,__restore_skip_gr32_gr63
+
+	lddi		@(gr8,#__INT_GR(32)),gr32
+	lddi		@(gr8,#__INT_GR(34)),gr34
+	lddi		@(gr8,#__INT_GR(36)),gr36
+	lddi		@(gr8,#__INT_GR(38)),gr38
+	lddi		@(gr8,#__INT_GR(40)),gr40
+	lddi		@(gr8,#__INT_GR(42)),gr42
+	lddi		@(gr8,#__INT_GR(44)),gr44
+	lddi		@(gr8,#__INT_GR(46)),gr46
+	lddi		@(gr8,#__INT_GR(48)),gr48
+	lddi		@(gr8,#__INT_GR(50)),gr50
+	lddi		@(gr8,#__INT_GR(52)),gr52
+	lddi		@(gr8,#__INT_GR(54)),gr54
+	lddi		@(gr8,#__INT_GR(56)),gr56
+	lddi		@(gr8,#__INT_GR(58)),gr58
+	lddi		@(gr8,#__INT_GR(60)),gr60
+	lddi		@(gr8,#__INT_GR(62)),gr62
+__restore_skip_gr32_gr63:
+
+	# all CPU's have FR0-FR31
+	lddfi		@(gr8,#__FPMEDIA_FR( 0)),fr0
+	lddfi		@(gr8,#__FPMEDIA_FR( 2)),fr2
+	lddfi		@(gr8,#__FPMEDIA_FR( 4)),fr4
+	lddfi		@(gr8,#__FPMEDIA_FR( 6)),fr6
+	lddfi		@(gr8,#__FPMEDIA_FR( 8)),fr8
+	lddfi		@(gr8,#__FPMEDIA_FR(10)),fr10
+	lddfi		@(gr8,#__FPMEDIA_FR(12)),fr12
+	lddfi		@(gr8,#__FPMEDIA_FR(14)),fr14
+	lddfi		@(gr8,#__FPMEDIA_FR(16)),fr16
+	lddfi		@(gr8,#__FPMEDIA_FR(18)),fr18
+	lddfi		@(gr8,#__FPMEDIA_FR(20)),fr20
+	lddfi		@(gr8,#__FPMEDIA_FR(22)),fr22
+	lddfi		@(gr8,#__FPMEDIA_FR(24)),fr24
+	lddfi		@(gr8,#__FPMEDIA_FR(26)),fr26
+	lddfi		@(gr8,#__FPMEDIA_FR(28)),fr28
+	lddfi.p		@(gr8,#__FPMEDIA_FR(30)),fr30
+
+	# some CPU's have FR32-FR63
+	setlos		#HSR0_FRHE,gr4
+	andcc		gr6,gr4,gr0,icc0
+	beq		icc0,#1,__restore_skip_fr32_fr63
+
+	lddfi		@(gr8,#__FPMEDIA_FR(32)),fr32
+	lddfi		@(gr8,#__FPMEDIA_FR(34)),fr34
+	lddfi		@(gr8,#__FPMEDIA_FR(36)),fr36
+	lddfi		@(gr8,#__FPMEDIA_FR(38)),fr38
+	lddfi		@(gr8,#__FPMEDIA_FR(40)),fr40
+	lddfi		@(gr8,#__FPMEDIA_FR(42)),fr42
+	lddfi		@(gr8,#__FPMEDIA_FR(44)),fr44
+	lddfi		@(gr8,#__FPMEDIA_FR(46)),fr46
+	lddfi		@(gr8,#__FPMEDIA_FR(48)),fr48
+	lddfi		@(gr8,#__FPMEDIA_FR(50)),fr50
+	lddfi		@(gr8,#__FPMEDIA_FR(52)),fr52
+	lddfi		@(gr8,#__FPMEDIA_FR(54)),fr54
+	lddfi		@(gr8,#__FPMEDIA_FR(56)),fr56
+	lddfi		@(gr8,#__FPMEDIA_FR(58)),fr58
+	lddfi		@(gr8,#__FPMEDIA_FR(60)),fr60
+	lddfi		@(gr8,#__FPMEDIA_FR(62)),fr62
+__restore_skip_fr32_fr63:
+
+	lddi		@(gr8,#__FPMEDIA_FNER(0)),gr4
+	movsg		fner0,gr4
+	movsg		fner1,gr5
+	or.p		gr9,gr9,gr8
+	bralr
+
+	# the FR451 also has ACC8-11/ACCG8-11 regs (but not 4-7...)
+__restore_acc_fr451:
+	lddfi		@(gr8,#__FPMEDIA_ACC(4)),fr16
+	lddfi		@(gr8,#__FPMEDIA_ACC(6)),fr18
+	ldbfi		@(gr8,#__FPMEDIA_ACCG(4)),fr20
+	ldbfi		@(gr8,#__FPMEDIA_ACCG(5)),fr21
+	ldbfi		@(gr8,#__FPMEDIA_ACCG(6)),fr22
+	ldbfi		@(gr8,#__FPMEDIA_ACCG(7)),fr23
+
+	mwtacc		fr16,acc8
+	mwtacc		fr17,acc9
+	mwtacc		fr18,acc10
+	mwtacc		fr19,acc11
+	mwtaccg		fr20,accg8
+	mwtaccg		fr21,accg9
+	mwtaccg		fr22,accg10
+	mwtaccg		fr23,accg11
+	bra		__restore_acc_cont
+
+	# the FR555 also has ACC4-7/ACCG4-7 regs and an FSR0 reg
+__restore_acc_fr555:
+	lddfi		@(gr8,#__FPMEDIA_ACC(4)),fr16
+	lddfi		@(gr8,#__FPMEDIA_ACC(6)),fr18
+	ldbfi		@(gr8,#__FPMEDIA_ACCG(4)),fr20
+	ldbfi		@(gr8,#__FPMEDIA_ACCG(5)),fr21
+	ldbfi		@(gr8,#__FPMEDIA_ACCG(6)),fr22
+	ldbfi		@(gr8,#__FPMEDIA_ACCG(7)),fr23
+
+	mnop.p
+	mwtacc		fr16,acc4
+	mnop.p
+	mwtacc		fr17,acc5
+	mnop.p
+	mwtacc		fr18,acc6
+	mnop.p
+	mwtacc		fr19,acc7
+	mnop.p
+	mwtaccg		fr20,accg4
+	mnop.p
+	mwtaccg		fr21,accg5
+	mnop.p
+	mwtaccg		fr22,accg6
+	mnop.p
+	mwtaccg		fr23,accg7
+
+	ldi		@(gr8,#__FPMEDIA_FSR(0)),gr4
+	movgs		gr4,fsr0
+
+	bra		__restore_acc_cont
+
+
+###############################################################################
+#
+# save extra general regs and FP/Media regs
+# - void save_user_regs(struct user_context *target)
+#
+###############################################################################
+	.globl		save_user_regs
+save_user_regs:
+	movsg		hsr0,gr6
+	ori		gr6,#HSR0_GRHE|HSR0_FRLE|HSR0_FRHE,gr6
+	movgs		gr6,hsr0
+	movsg		hsr0,gr6
+
+	movsg		psr,gr7
+	ori		gr7,#PSR_EF|PSR_EM,gr7
+	movgs		gr7,psr
+	movsg		psr,gr7
+	srli		gr7,#24,gr7
+	bar
+
+	movsg		fner0,gr4
+	movsg		fner1,gr5
+	stdi.p		gr4,@(gr8,#__FPMEDIA_FNER(0))
+
+	# some CPU's have GR32-GR63
+	setlos		#HSR0_GRHE,gr4
+	andcc		gr6,gr4,gr0,icc0
+	beq		icc0,#1,__save_skip_gr32_gr63
+
+	stdi		gr32,@(gr8,#__INT_GR(32))
+	stdi		gr34,@(gr8,#__INT_GR(34))
+	stdi		gr36,@(gr8,#__INT_GR(36))
+	stdi		gr38,@(gr8,#__INT_GR(38))
+	stdi		gr40,@(gr8,#__INT_GR(40))
+	stdi		gr42,@(gr8,#__INT_GR(42))
+	stdi		gr44,@(gr8,#__INT_GR(44))
+	stdi		gr46,@(gr8,#__INT_GR(46))
+	stdi		gr48,@(gr8,#__INT_GR(48))
+	stdi		gr50,@(gr8,#__INT_GR(50))
+	stdi		gr52,@(gr8,#__INT_GR(52))
+	stdi		gr54,@(gr8,#__INT_GR(54))
+	stdi		gr56,@(gr8,#__INT_GR(56))
+	stdi		gr58,@(gr8,#__INT_GR(58))
+	stdi		gr60,@(gr8,#__INT_GR(60))
+	stdi		gr62,@(gr8,#__INT_GR(62))
+__save_skip_gr32_gr63:
+
+	# all CPU's have FR0-FR31
+	stdfi		fr0 ,@(gr8,#__FPMEDIA_FR( 0))
+	stdfi		fr2 ,@(gr8,#__FPMEDIA_FR( 2))
+	stdfi		fr4 ,@(gr8,#__FPMEDIA_FR( 4))
+	stdfi		fr6 ,@(gr8,#__FPMEDIA_FR( 6))
+	stdfi		fr8 ,@(gr8,#__FPMEDIA_FR( 8))
+	stdfi		fr10,@(gr8,#__FPMEDIA_FR(10))
+	stdfi		fr12,@(gr8,#__FPMEDIA_FR(12))
+	stdfi		fr14,@(gr8,#__FPMEDIA_FR(14))
+	stdfi		fr16,@(gr8,#__FPMEDIA_FR(16))
+	stdfi		fr18,@(gr8,#__FPMEDIA_FR(18))
+	stdfi		fr20,@(gr8,#__FPMEDIA_FR(20))
+	stdfi		fr22,@(gr8,#__FPMEDIA_FR(22))
+	stdfi		fr24,@(gr8,#__FPMEDIA_FR(24))
+	stdfi		fr26,@(gr8,#__FPMEDIA_FR(26))
+	stdfi		fr28,@(gr8,#__FPMEDIA_FR(28))
+	stdfi.p		fr30,@(gr8,#__FPMEDIA_FR(30))
+
+	# some CPU's have FR32-FR63
+	setlos		#HSR0_FRHE,gr4
+	andcc		gr6,gr4,gr0,icc0
+	beq		icc0,#1,__save_skip_fr32_fr63
+
+	stdfi		fr32,@(gr8,#__FPMEDIA_FR(32))
+	stdfi		fr34,@(gr8,#__FPMEDIA_FR(34))
+	stdfi		fr36,@(gr8,#__FPMEDIA_FR(36))
+	stdfi		fr38,@(gr8,#__FPMEDIA_FR(38))
+	stdfi		fr40,@(gr8,#__FPMEDIA_FR(40))
+	stdfi		fr42,@(gr8,#__FPMEDIA_FR(42))
+	stdfi		fr44,@(gr8,#__FPMEDIA_FR(44))
+	stdfi		fr46,@(gr8,#__FPMEDIA_FR(46))
+	stdfi		fr48,@(gr8,#__FPMEDIA_FR(48))
+	stdfi		fr50,@(gr8,#__FPMEDIA_FR(50))
+	stdfi		fr52,@(gr8,#__FPMEDIA_FR(52))
+	stdfi		fr54,@(gr8,#__FPMEDIA_FR(54))
+	stdfi		fr56,@(gr8,#__FPMEDIA_FR(56))
+	stdfi		fr58,@(gr8,#__FPMEDIA_FR(58))
+	stdfi		fr60,@(gr8,#__FPMEDIA_FR(60))
+	stdfi		fr62,@(gr8,#__FPMEDIA_FR(62))
+__save_skip_fr32_fr63:
+
+	mrdacc		acc0 ,fr4
+	mrdacc		acc1 ,fr5
+
+	stdfi.p		fr4 ,@(gr8,#__FPMEDIA_ACC(0))
+
+	mrdacc		acc2 ,fr6
+	mrdacc		acc3 ,fr7
+
+	stdfi.p		fr6 ,@(gr8,#__FPMEDIA_ACC(2))
+
+	mrdaccg		accg0,fr4
+	stbfi.p		fr4 ,@(gr8,#__FPMEDIA_ACCG(0))
+
+	mrdaccg		accg1,fr5
+	stbfi.p		fr5 ,@(gr8,#__FPMEDIA_ACCG(1))
+
+	mrdaccg		accg2,fr6
+	stbfi.p		fr6 ,@(gr8,#__FPMEDIA_ACCG(2))
+
+	mrdaccg		accg3,fr7
+	stbfi		fr7 ,@(gr8,#__FPMEDIA_ACCG(3))
+
+	movsg		msr0 ,gr4
+	movsg		msr1 ,gr5
+
+	stdi		gr4 ,@(gr8,#__FPMEDIA_MSR(0))
+
+	# some CPUs have extra ACCx and ACCGx regs and maybe FSRx regs
+	subicc.p	gr7,#0x50,gr0,icc0
+	subicc		gr7,#0x31,gr0,icc1
+	beq		icc0,#0,__save_acc_fr451
+	beq		icc1,#0,__save_acc_fr555
+__save_acc_cont:
+
+	lddfi		@(gr8,#__FPMEDIA_FR(4)),fr4
+	lddfi.p		@(gr8,#__FPMEDIA_FR(6)),fr6
+	bralr
+
+	# the FR451 also has ACC8-11/ACCG8-11 regs (but not 4-7...)
+__save_acc_fr451:
+	mrdacc		acc8 ,fr4
+	mrdacc		acc9 ,fr5
+
+	stdfi.p		fr4 ,@(gr8,#__FPMEDIA_ACC(4))
+
+	mrdacc		acc10,fr6
+	mrdacc		acc11,fr7
+
+	stdfi.p		fr6 ,@(gr8,#__FPMEDIA_ACC(6))
+
+	mrdaccg		accg8,fr4
+	stbfi.p		fr4 ,@(gr8,#__FPMEDIA_ACCG(4))
+
+	mrdaccg		accg9,fr5
+	stbfi.p		fr5 ,@(gr8,#__FPMEDIA_ACCG(5))
+
+	mrdaccg		accg10,fr6
+	stbfi.p		fr6 ,@(gr8,#__FPMEDIA_ACCG(6))
+
+	mrdaccg		accg11,fr7
+	stbfi		fr7 ,@(gr8,#__FPMEDIA_ACCG(7))
+	bra		__save_acc_cont
+
+	# the FR555 also has ACC4-7/ACCG4-7 regs and an FSR0 reg
+__save_acc_fr555:
+	mnop.p
+	mrdacc		acc4 ,fr4
+	mnop.p
+	mrdacc		acc5 ,fr5
+
+	stdfi		fr4 ,@(gr8,#__FPMEDIA_ACC(4))
+
+	mnop.p
+	mrdacc		acc6 ,fr6
+	mnop.p
+	mrdacc		acc7 ,fr7
+
+	stdfi		fr6 ,@(gr8,#__FPMEDIA_ACC(6))
+
+	mnop.p
+	mrdaccg		accg4,fr4
+	stbfi		fr4 ,@(gr8,#__FPMEDIA_ACCG(4))
+
+	mnop.p
+	mrdaccg		accg5,fr5
+	stbfi		fr5 ,@(gr8,#__FPMEDIA_ACCG(5))
+
+	mnop.p
+	mrdaccg		accg6,fr6
+	stbfi		fr6 ,@(gr8,#__FPMEDIA_ACCG(6))
+
+	mnop.p
+	mrdaccg		accg7,fr7
+	stbfi		fr7 ,@(gr8,#__FPMEDIA_ACCG(7))
+
+	movsg		fsr0 ,gr4
+	sti		gr4 ,@(gr8,#__FPMEDIA_FSR(0))
+	bra		__save_acc_cont
diff --git a/arch/frv/kernel/sys_frv.c b/arch/frv/kernel/sys_frv.c
new file mode 100644
index 0000000..931aa6d
--- /dev/null
+++ b/arch/frv/kernel/sys_frv.c
@@ -0,0 +1,214 @@
+/* sys_frv.c: FRV arch-specific syscall wrappers
+ *
+ * Copyright (C) 2003-5 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ * - Derived from arch/m68k/kernel/sys_m68k.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+#include <linux/sem.h>
+#include <linux/msg.h>
+#include <linux/shm.h>
+#include <linux/stat.h>
+#include <linux/mman.h>
+#include <linux/file.h>
+#include <linux/utsname.h>
+#include <linux/syscalls.h>
+
+#include <asm/setup.h>
+#include <asm/uaccess.h>
+#include <asm/ipc.h>
+
+/*
+ * sys_pipe() is the normal C calling standard for creating
+ * a pipe. It's not the way unix traditionally does this, though.
+ */
+asmlinkage long sys_pipe(unsigned long * fildes)
+{
+	int fd[2];
+	int error;
+
+	error = do_pipe(fd);
+	if (!error) {
+		if (copy_to_user(fildes, fd, 2*sizeof(int)))
+			error = -EFAULT;
+	}
+	return error;
+}
+
+asmlinkage long sys_mmap2(unsigned long addr, unsigned long len,
+			  unsigned long prot, unsigned long flags,
+			  unsigned long fd, unsigned long pgoff)
+{
+	int error = -EBADF;
+	struct file * file = NULL;
+
+	flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
+	if (!(flags & MAP_ANONYMOUS)) {
+		file = fget(fd);
+		if (!file)
+			goto out;
+	}
+
+	/* As with sparc32, make sure the shift for mmap2 is constant
+	   (12), no matter what PAGE_SIZE we have.... */
+
+	/* But unlike sparc32, don't just silently break if we're
+	   trying to map something we can't */
+	if (pgoff & ((1<<(PAGE_SHIFT-12))-1))
+		return -EINVAL;
+
+	pgoff >>= (PAGE_SHIFT - 12);
+
+	down_write(&current->mm->mmap_sem);
+	error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
+	up_write(&current->mm->mmap_sem);
+
+	if (file)
+		fput(file);
+out:
+	return error;
+}
+
+#if 0 /* DAVIDM - do we want this */
+struct mmap_arg_struct64 {
+	__u32 addr;
+	__u32 len;
+	__u32 prot;
+	__u32 flags;
+	__u64 offset; /* 64 bits */
+	__u32 fd;
+};
+
+asmlinkage long sys_mmap64(struct mmap_arg_struct64 *arg)
+{
+	int error = -EFAULT;
+	struct file * file = NULL;
+	struct mmap_arg_struct64 a;
+	unsigned long pgoff;
+
+	if (copy_from_user(&a, arg, sizeof(a)))
+		return -EFAULT;
+
+	if ((long)a.offset & ~PAGE_MASK)
+		return -EINVAL;
+
+	pgoff = a.offset >> PAGE_SHIFT;
+	if ((a.offset >> PAGE_SHIFT) != pgoff)
+		return -EINVAL;
+
+	if (!(a.flags & MAP_ANONYMOUS)) {
+		error = -EBADF;
+		file = fget(a.fd);
+		if (!file)
+			goto out;
+	}
+	a.flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
+
+	down_write(&current->mm->mmap_sem);
+	error = do_mmap_pgoff(file, a.addr, a.len, a.prot, a.flags, pgoff);
+	up_write(&current->mm->mmap_sem);
+	if (file)
+		fput(file);
+out:
+	return error;
+}
+#endif
+
+/*
+ * sys_ipc() is the de-multiplexer for the SysV IPC calls..
+ *
+ * This is really horribly ugly.
+ */
+asmlinkage long sys_ipc(unsigned long call,
+			unsigned long first,
+			unsigned long second,
+			unsigned long third,
+			void __user *ptr,
+			unsigned long fifth)
+{
+	int version, ret;
+
+	version = call >> 16; /* hack for backward compatibility */
+	call &= 0xffff;
+
+	switch (call) {
+	case SEMOP:
+		return sys_semtimedop(first, (struct sembuf __user *)ptr, second, NULL);
+	case SEMTIMEDOP:
+		return sys_semtimedop(first, (struct sembuf __user *)ptr, second,
+				      (const struct timespec __user *)fifth);
+
+	case SEMGET:
+		return sys_semget (first, second, third);
+	case SEMCTL: {
+		union semun fourth;
+		if (!ptr)
+			return -EINVAL;
+		if (get_user(fourth.__pad, (void * __user *) ptr))
+			return -EFAULT;
+		return sys_semctl (first, second, third, fourth);
+	}
+
+	case MSGSND:
+		return sys_msgsnd (first, (struct msgbuf __user *) ptr,
+				   second, third);
+	case MSGRCV:
+		switch (version) {
+		case 0: {
+			struct ipc_kludge tmp;
+			if (!ptr)
+				return -EINVAL;
+
+			if (copy_from_user(&tmp,
+					   (struct ipc_kludge __user *) ptr,
+					   sizeof (tmp)))
+				return -EFAULT;
+			return sys_msgrcv (first, tmp.msgp, second,
+					   tmp.msgtyp, third);
+		}
+		default:
+			return sys_msgrcv (first,
+					   (struct msgbuf __user *) ptr,
+					   second, fifth, third);
+		}
+	case MSGGET:
+		return sys_msgget ((key_t) first, second);
+	case MSGCTL:
+		return sys_msgctl (first, second, (struct msqid_ds __user *) ptr);
+
+	case SHMAT:
+		switch (version) {
+		default: {
+			ulong raddr;
+			ret = do_shmat (first, (char __user *) ptr, second, &raddr);
+			if (ret)
+				return ret;
+			return put_user (raddr, (ulong __user *) third);
+		}
+		case 1:	/* iBCS2 emulator entry point */
+			if (!segment_eq(get_fs(), get_ds()))
+				return -EINVAL;
+			/* The "(ulong *) third" is valid _only_ because of the kernel segment thing */
+			return do_shmat (first, (char __user *) ptr, second, (ulong *) third);
+		}
+	case SHMDT:
+		return sys_shmdt ((char __user *)ptr);
+	case SHMGET:
+		return sys_shmget (first, second, third);
+	case SHMCTL:
+		return sys_shmctl (first, second,
+				   (struct shmid_ds __user *) ptr);
+	default:
+		return -ENOSYS;
+	}
+}
diff --git a/arch/frv/kernel/sysctl.c b/arch/frv/kernel/sysctl.c
new file mode 100644
index 0000000..408b0f3
--- /dev/null
+++ b/arch/frv/kernel/sysctl.c
@@ -0,0 +1,206 @@
+/* sysctl.c: implementation of /proc/sys files relating to FRV specifically
+ *
+ * Copyright (C) 2004 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 License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/config.h>
+#include <linux/slab.h>
+#include <linux/sysctl.h>
+#include <linux/proc_fs.h>
+#include <linux/init.h>
+#include <asm/uaccess.h>
+
+static const char frv_cache_wback[] = "wback";
+static const char frv_cache_wthru[] = "wthru";
+
+static void frv_change_dcache_mode(unsigned long newmode)
+{
+	unsigned long flags, hsr0;
+
+	local_irq_save(flags);
+
+	hsr0 = __get_HSR(0);
+	hsr0 &= ~HSR0_DCE;
+	__set_HSR(0, hsr0);
+
+	asm volatile("	dcef	@(gr0,gr0),#1	\n"
+		     "	membar			\n"
+		     : : : "memory"
+		     );
+
+	hsr0 = (hsr0 & ~HSR0_CBM) | newmode;
+	__set_HSR(0, hsr0);
+	hsr0 |= HSR0_DCE;
+	__set_HSR(0, hsr0);
+
+	local_irq_restore(flags);
+
+	//printk("HSR0 now %08lx\n", hsr0);
+}
+
+/*****************************************************************************/
+/*
+ * handle requests to dynamically switch the write caching mode delivered by /proc
+ */
+static int procctl_frv_cachemode(ctl_table *table, int write, struct file *filp,
+				 void *buffer, size_t *lenp, loff_t *ppos)
+{
+	unsigned long hsr0;
+	char buff[8];
+	int len;
+
+	len = *lenp;
+
+	if (write) {
+		/* potential state change */
+		if (len <= 1 || len > sizeof(buff) - 1)
+			return -EINVAL;
+
+		if (copy_from_user(buff, buffer, len) != 0)
+			return -EFAULT;
+
+		if (buff[len - 1] == '\n')
+			buff[len - 1] = '\0';
+		else
+			buff[len] = '\0';
+
+		if (strcmp(buff, frv_cache_wback) == 0) {
+			/* switch dcache into write-back mode */
+			frv_change_dcache_mode(HSR0_CBM_COPY_BACK);
+			return 0;
+		}
+
+		if (strcmp(buff, frv_cache_wthru) == 0) {
+			/* switch dcache into write-through mode */
+			frv_change_dcache_mode(HSR0_CBM_WRITE_THRU);
+			return 0;
+		}
+
+		return -EINVAL;
+	}
+
+	/* read the state */
+	if (filp->f_pos > 0) {
+		*lenp = 0;
+		return 0;
+	}
+
+	hsr0 = __get_HSR(0);
+	switch (hsr0 & HSR0_CBM) {
+	case HSR0_CBM_WRITE_THRU:
+		memcpy(buff, frv_cache_wthru, sizeof(frv_cache_wthru) - 1);
+		buff[sizeof(frv_cache_wthru) - 1] = '\n';
+		len = sizeof(frv_cache_wthru);
+		break;
+	default:
+		memcpy(buff, frv_cache_wback, sizeof(frv_cache_wback) - 1);
+		buff[sizeof(frv_cache_wback) - 1] = '\n';
+		len = sizeof(frv_cache_wback);
+		break;
+	}
+
+	if (len > *lenp)
+		len = *lenp;
+
+	if (copy_to_user(buffer, buff, len) != 0)
+		return -EFAULT;
+
+	*lenp = len;
+	filp->f_pos = len;
+	return 0;
+
+} /* end procctl_frv_cachemode() */
+
+/*****************************************************************************/
+/*
+ * permit the mm_struct the nominated process is using have its MMU context ID pinned
+ */
+#ifdef CONFIG_MMU
+static int procctl_frv_pin_cxnr(ctl_table *table, int write, struct file *filp,
+				void *buffer, size_t *lenp, loff_t *ppos)
+{
+	pid_t pid;
+	char buff[16], *p;
+	int len;
+
+	len = *lenp;
+
+	if (write) {
+		/* potential state change */
+		if (len <= 1 || len > sizeof(buff) - 1)
+			return -EINVAL;
+
+		if (copy_from_user(buff, buffer, len) != 0)
+			return -EFAULT;
+
+		if (buff[len - 1] == '\n')
+			buff[len - 1] = '\0';
+		else
+			buff[len] = '\0';
+
+		pid = simple_strtoul(buff, &p, 10);
+		if (*p)
+			return -EINVAL;
+
+		return cxn_pin_by_pid(pid);
+	}
+
+	/* read the currently pinned CXN */
+	if (filp->f_pos > 0) {
+		*lenp = 0;
+		return 0;
+	}
+
+	len = snprintf(buff, sizeof(buff), "%d\n", cxn_pinned);
+	if (len > *lenp)
+		len = *lenp;
+
+	if (copy_to_user(buffer, buff, len) != 0)
+		return -EFAULT;
+
+	*lenp = len;
+	filp->f_pos = len;
+	return 0;
+
+} /* end procctl_frv_pin_cxnr() */
+#endif
+
+/*
+ * FR-V specific sysctls
+ */
+static struct ctl_table frv_table[] =
+{
+	{ 1, "cache-mode",	NULL, 0, 0644, NULL, &procctl_frv_cachemode },
+#ifdef CONFIG_MMU
+	{ 2, "pin-cxnr",	NULL, 0, 0644, NULL, &procctl_frv_pin_cxnr },
+#endif
+	{ 0 }
+};
+
+/*
+ * Use a temporary sysctl number. Horrid, but will be cleaned up in 2.6
+ * when all the PM interfaces exist nicely.
+ */
+#define CTL_FRV 9898
+static struct ctl_table frv_dir_table[] =
+{
+	{CTL_FRV, "frv", NULL, 0, 0555, frv_table},
+	{0}
+};
+
+/*
+ * Initialize power interface
+ */
+static int __init frv_sysctl_init(void)
+{
+	register_sysctl_table(frv_dir_table, 1);
+	return 0;
+}
+
+__initcall(frv_sysctl_init);
diff --git a/arch/frv/kernel/time.c b/arch/frv/kernel/time.c
new file mode 100644
index 0000000..075db66
--- /dev/null
+++ b/arch/frv/kernel/time.c
@@ -0,0 +1,234 @@
+/* time.c: FRV arch-specific time handling
+ *
+ * Copyright (C) 2003-5 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ * - Derived from arch/m68k/kernel/time.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/config.h> /* CONFIG_HEARTBEAT */
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/param.h>
+#include <linux/string.h>
+#include <linux/interrupt.h>
+#include <linux/profile.h>
+#include <linux/irq.h>
+#include <linux/mm.h>
+
+#include <asm/io.h>
+#include <asm/timer-regs.h>
+#include <asm/mb-regs.h>
+#include <asm/mb86943a.h>
+#include <asm/irq-routing.h>
+
+#include <linux/timex.h>
+
+#define TICK_SIZE (tick_nsec / 1000)
+
+extern unsigned long wall_jiffies;
+
+u64 jiffies_64 = INITIAL_JIFFIES;
+EXPORT_SYMBOL(jiffies_64);
+
+unsigned long __nongprelbss __clkin_clock_speed_HZ;
+unsigned long __nongprelbss __ext_bus_clock_speed_HZ;
+unsigned long __nongprelbss __res_bus_clock_speed_HZ;
+unsigned long __nongprelbss __sdram_clock_speed_HZ;
+unsigned long __nongprelbss __core_bus_clock_speed_HZ;
+unsigned long __nongprelbss __core_clock_speed_HZ;
+unsigned long __nongprelbss __dsu_clock_speed_HZ;
+unsigned long __nongprelbss __serial_clock_speed_HZ;
+unsigned long __delay_loops_MHz;
+
+static irqreturn_t timer_interrupt(int irq, void *dummy, struct pt_regs *regs);
+
+static struct irqaction timer_irq  = {
+	timer_interrupt, SA_INTERRUPT, CPU_MASK_NONE, "timer", NULL, NULL
+};
+
+static inline int set_rtc_mmss(unsigned long nowtime)
+{
+	return -1;
+}
+
+/*
+ * timer_interrupt() needs to keep up the real-time clock,
+ * as well as call the "do_timer()" routine every clocktick
+ */
+static irqreturn_t timer_interrupt(int irq, void *dummy, struct pt_regs * regs)
+{
+	/* last time the cmos clock got updated */
+	static long last_rtc_update = 0;
+
+	/*
+	 * Here we are in the timer irq handler. We just have irqs locally
+	 * disabled but we don't know if the timer_bh is running on the other
+	 * CPU. We need to avoid to SMP race with it. NOTE: we don' t need
+	 * the irq version of write_lock because as just said we have irq
+	 * locally disabled. -arca
+	 */
+	write_seqlock(&xtime_lock);
+
+	do_timer(regs);
+	update_process_times(user_mode(regs));
+	profile_tick(CPU_PROFILING, regs);
+
+	/*
+	 * If we have an externally synchronized Linux clock, then update
+	 * CMOS clock accordingly every ~11 minutes. Set_rtc_mmss() has to be
+	 * called as close as possible to 500 ms before the new second starts.
+	 */
+	if ((time_status & STA_UNSYNC) == 0 &&
+	    xtime.tv_sec > last_rtc_update + 660 &&
+	    (xtime.tv_nsec / 1000) >= 500000 - ((unsigned) TICK_SIZE) / 2 &&
+	    (xtime.tv_nsec / 1000) <= 500000 + ((unsigned) TICK_SIZE) / 2
+	    ) {
+		if (set_rtc_mmss(xtime.tv_sec) == 0)
+			last_rtc_update = xtime.tv_sec;
+		else
+			last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */
+	}
+
+#ifdef CONFIG_HEARTBEAT
+	static unsigned short n;
+	n++;
+	__set_LEDS(n);
+#endif /* CONFIG_HEARTBEAT */
+
+	write_sequnlock(&xtime_lock);
+	return IRQ_HANDLED;
+}
+
+void time_divisor_init(void)
+{
+	unsigned short base, pre, prediv;
+
+	/* set the scheduling timer going */
+	pre = 1;
+	prediv = 4;
+	base = __res_bus_clock_speed_HZ / pre / HZ / (1 << prediv);
+
+	__set_TPRV(pre);
+	__set_TxCKSL_DATA(0, prediv);
+	__set_TCTR(TCTR_SC_CTR0 | TCTR_RL_RW_LH8 | TCTR_MODE_2);
+	__set_TCSR_DATA(0, base & 0xff);
+	__set_TCSR_DATA(0, base >> 8);
+}
+
+void time_init(void)
+{
+	unsigned int year, mon, day, hour, min, sec;
+
+	extern void arch_gettod(int *year, int *mon, int *day, int *hour, int *min, int *sec);
+
+	/* FIX by dqg : Set to zero for platforms that don't have tod */
+	/* without this time is undefined and can overflow time_t, causing  */
+	/* very stange errors */
+	year = 1980;
+	mon = day = 1;
+	hour = min = sec = 0;
+	arch_gettod (&year, &mon, &day, &hour, &min, &sec);
+
+	if ((year += 1900) < 1970)
+		year += 100;
+	xtime.tv_sec = mktime(year, mon, day, hour, min, sec);
+	xtime.tv_nsec = 0;
+
+	/* install scheduling interrupt handler */
+	setup_irq(IRQ_CPU_TIMER0, &timer_irq);
+
+	time_divisor_init();
+}
+
+/*
+ * This version of gettimeofday has near microsecond resolution.
+ */
+void do_gettimeofday(struct timeval *tv)
+{
+	unsigned long seq;
+	unsigned long usec, sec;
+	unsigned long max_ntp_tick;
+
+	do {
+		unsigned long lost;
+
+		seq = read_seqbegin(&xtime_lock);
+
+		usec = 0;
+		lost = jiffies - wall_jiffies;
+
+		/*
+		 * If time_adjust is negative then NTP is slowing the clock
+		 * so make sure not to go into next possible interval.
+		 * Better to lose some accuracy than have time go backwards..
+		 */
+		if (unlikely(time_adjust < 0)) {
+			max_ntp_tick = (USEC_PER_SEC / HZ) - tickadj;
+			usec = min(usec, max_ntp_tick);
+
+			if (lost)
+				usec += lost * max_ntp_tick;
+		}
+		else if (unlikely(lost))
+			usec += lost * (USEC_PER_SEC / HZ);
+
+		sec = xtime.tv_sec;
+		usec += (xtime.tv_nsec / 1000);
+	} while (read_seqretry(&xtime_lock, seq));
+
+	while (usec >= 1000000) {
+		usec -= 1000000;
+		sec++;
+	}
+
+	tv->tv_sec = sec;
+	tv->tv_usec = usec;
+}
+
+int do_settimeofday(struct timespec *tv)
+{
+	time_t wtm_sec, sec = tv->tv_sec;
+	long wtm_nsec, nsec = tv->tv_nsec;
+
+	if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC)
+		return -EINVAL;
+
+	write_seqlock_irq(&xtime_lock);
+	/*
+	 * This is revolting. We need to set "xtime" correctly. However, the
+	 * value in this location is the value at the most recent update of
+	 * wall time.  Discover what correction gettimeofday() would have
+	 * made, and then undo it!
+	 */
+	nsec -= 0 * NSEC_PER_USEC;
+	nsec -= (jiffies - wall_jiffies) * TICK_NSEC;
+
+	wtm_sec  = wall_to_monotonic.tv_sec + (xtime.tv_sec - sec);
+	wtm_nsec = wall_to_monotonic.tv_nsec + (xtime.tv_nsec - nsec);
+
+	set_normalized_timespec(&xtime, sec, nsec);
+	set_normalized_timespec(&wall_to_monotonic, wtm_sec, wtm_nsec);
+
+	time_adjust = 0;		/* stop active adjtime() */
+	time_status |= STA_UNSYNC;
+	time_maxerror = NTP_PHASE_LIMIT;
+	time_esterror = NTP_PHASE_LIMIT;
+	write_sequnlock_irq(&xtime_lock);
+	clock_was_set();
+	return 0;
+}
+
+/*
+ * Scheduler clock - returns current time in nanosec units.
+ */
+unsigned long long sched_clock(void)
+{
+	return jiffies_64 * (1000000000 / HZ);
+}
diff --git a/arch/frv/kernel/traps.c b/arch/frv/kernel/traps.c
new file mode 100644
index 0000000..89073ca
--- /dev/null
+++ b/arch/frv/kernel/traps.c
@@ -0,0 +1,431 @@
+/* traps.c: high-level exception handler for FR-V
+ *
+ * Copyright (C) 2003 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 License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/config.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/types.h>
+#include <linux/user.h>
+#include <linux/string.h>
+#include <linux/linkage.h>
+#include <linux/init.h>
+
+#include <asm/setup.h>
+#include <asm/fpu.h>
+#include <asm/system.h>
+#include <asm/uaccess.h>
+#include <asm/pgtable.h>
+#include <asm/siginfo.h>
+#include <asm/unaligned.h>
+
+void show_backtrace(struct pt_regs *, unsigned long);
+
+extern asmlinkage void __break_hijack_kernel_event(void);
+
+/*****************************************************************************/
+/*
+ * instruction access error
+ */
+asmlinkage void insn_access_error(unsigned long esfr1, unsigned long epcr0, unsigned long esr0)
+{
+	siginfo_t info;
+
+	die_if_kernel("-- Insn Access Error --\n"
+		      "EPCR0 : %08lx\n"
+		      "ESR0  : %08lx\n",
+		      epcr0, esr0);
+
+	info.si_signo	= SIGSEGV;
+	info.si_code	= SEGV_ACCERR;
+	info.si_errno	= 0;
+	info.si_addr	= (void *) ((epcr0 & EPCR0_V) ? (epcr0 & EPCR0_PC) : __frame->pc);
+
+	force_sig_info(info.si_signo, &info, current);
+} /* end insn_access_error() */
+
+/*****************************************************************************/
+/*
+ * handler for:
+ * - illegal instruction
+ * - privileged instruction
+ * - unsupported trap
+ * - debug exceptions
+ */
+asmlinkage void illegal_instruction(unsigned long esfr1, unsigned long epcr0, unsigned long esr0)
+{
+	siginfo_t info;
+
+	die_if_kernel("-- Illegal Instruction --\n"
+		      "EPCR0 : %08lx\n"
+		      "ESR0  : %08lx\n"
+		      "ESFR1 : %08lx\n",
+		      epcr0, esr0, esfr1);
+
+	info.si_errno	= 0;
+	info.si_addr	= (void *) ((epcr0 & EPCR0_PC) ? (epcr0 & EPCR0_PC) : __frame->pc);
+
+	switch (__frame->tbr & TBR_TT) {
+	case TBR_TT_ILLEGAL_INSTR:
+		info.si_signo	= SIGILL;
+		info.si_code	= ILL_ILLOPC;
+		break;
+	case TBR_TT_PRIV_INSTR:
+		info.si_signo	= SIGILL;
+		info.si_code	= ILL_PRVOPC;
+		break;
+	case TBR_TT_TRAP2 ... TBR_TT_TRAP126:
+		info.si_signo	= SIGILL;
+		info.si_code	= ILL_ILLTRP;
+		break;
+	/* GDB uses "tira gr0, #1" as a breakpoint instruction.  */
+	case TBR_TT_TRAP1:
+	case TBR_TT_BREAK:
+		info.si_signo	= SIGTRAP;
+		info.si_code	=
+			(__frame->__status & REG__STATUS_STEPPED) ? TRAP_TRACE : TRAP_BRKPT;
+		break;
+	}
+
+	force_sig_info(info.si_signo, &info, current);
+} /* end illegal_instruction() */
+
+/*****************************************************************************/
+/*
+ *
+ */
+asmlinkage void media_exception(unsigned long msr0, unsigned long msr1)
+{
+	siginfo_t info;
+
+	die_if_kernel("-- Media Exception --\n"
+		      "MSR0 : %08lx\n"
+		      "MSR1 : %08lx\n",
+		      msr0, msr1);
+
+	info.si_signo	= SIGFPE;
+	info.si_code	= FPE_MDAOVF;
+	info.si_errno	= 0;
+	info.si_addr	= (void *) __frame->pc;
+
+	force_sig_info(info.si_signo, &info, current);
+} /* end media_exception() */
+
+/*****************************************************************************/
+/*
+ * instruction or data access exception
+ */
+asmlinkage void memory_access_exception(unsigned long esr0,
+					unsigned long ear0,
+					unsigned long epcr0)
+{
+	siginfo_t info;
+
+#ifdef CONFIG_MMU
+	unsigned long fixup;
+
+	if ((esr0 & ESRx_EC) == ESRx_EC_DATA_ACCESS)
+		if (handle_misalignment(esr0, ear0, epcr0) == 0)
+			return;
+
+	if ((fixup = search_exception_table(__frame->pc)) != 0) {
+		__frame->pc = fixup;
+		return;
+	}
+#endif
+
+	die_if_kernel("-- Memory Access Exception --\n"
+		      "ESR0  : %08lx\n"
+		      "EAR0  : %08lx\n"
+		      "EPCR0 : %08lx\n",
+		      esr0, ear0, epcr0);
+
+	info.si_signo	= SIGSEGV;
+	info.si_code	= SEGV_ACCERR;
+	info.si_errno	= 0;
+	info.si_addr	= NULL;
+
+	if ((esr0 & (ESRx_VALID | ESR0_EAV)) == (ESRx_VALID | ESR0_EAV))
+		info.si_addr = (void *) ear0;
+
+	force_sig_info(info.si_signo, &info, current);
+
+} /* end memory_access_exception() */
+
+/*****************************************************************************/
+/*
+ * data access error
+ * - double-word data load from CPU control area (0xFExxxxxx)
+ * - read performed on inactive or self-refreshing SDRAM
+ * - error notification from slave device
+ * - misaligned address
+ * - access to out of bounds memory region
+ * - user mode accessing privileged memory region
+ * - write to R/O memory region
+ */
+asmlinkage void data_access_error(unsigned long esfr1, unsigned long esr15, unsigned long ear15)
+{
+	siginfo_t info;
+
+	die_if_kernel("-- Data Access Error --\n"
+		      "ESR15 : %08lx\n"
+		      "EAR15 : %08lx\n",
+		      esr15, ear15);
+
+	info.si_signo	= SIGSEGV;
+	info.si_code	= SEGV_ACCERR;
+	info.si_errno	= 0;
+	info.si_addr	= (void *)
+		(((esr15 & (ESRx_VALID|ESR15_EAV)) == (ESRx_VALID|ESR15_EAV)) ? ear15 : 0);
+
+	force_sig_info(info.si_signo, &info, current);
+} /* end data_access_error() */
+
+/*****************************************************************************/
+/*
+ * data store error - should only happen if accessing inactive or self-refreshing SDRAM
+ */
+asmlinkage void data_store_error(unsigned long esfr1, unsigned long esr15)
+{
+	die_if_kernel("-- Data Store Error --\n"
+		      "ESR15 : %08lx\n",
+		      esr15);
+	BUG();
+} /* end data_store_error() */
+
+/*****************************************************************************/
+/*
+ *
+ */
+asmlinkage void division_exception(unsigned long esfr1, unsigned long esr0, unsigned long isr)
+{
+	siginfo_t info;
+
+	die_if_kernel("-- Division Exception --\n"
+		      "ESR0 : %08lx\n"
+		      "ISR  : %08lx\n",
+		      esr0, isr);
+
+	info.si_signo	= SIGFPE;
+	info.si_code	= FPE_INTDIV;
+	info.si_errno	= 0;
+	info.si_addr	= (void *) __frame->pc;
+
+	force_sig_info(info.si_signo, &info, current);
+} /* end division_exception() */
+
+/*****************************************************************************/
+/*
+ *
+ */
+asmlinkage void compound_exception(unsigned long esfr1,
+				   unsigned long esr0, unsigned long esr14, unsigned long esr15,
+				   unsigned long msr0, unsigned long msr1)
+{
+	die_if_kernel("-- Compound Exception --\n"
+		      "ESR0  : %08lx\n"
+		      "ESR15 : %08lx\n"
+		      "ESR15 : %08lx\n"
+		      "MSR0  : %08lx\n"
+		      "MSR1  : %08lx\n",
+		      esr0, esr14, esr15, msr0, msr1);
+	BUG();
+} /* end compound_exception() */
+
+/*****************************************************************************/
+/*
+ * The architecture-independent backtrace generator
+ */
+void dump_stack(void)
+{
+	show_stack(NULL, NULL);
+}
+
+void show_stack(struct task_struct *task, unsigned long *sp)
+{
+}
+
+void show_trace_task(struct task_struct *tsk)
+{
+	printk("CONTEXT: stack=0x%lx frame=0x%p LR=0x%lx RET=0x%lx\n",
+	       tsk->thread.sp, tsk->thread.frame, tsk->thread.lr, tsk->thread.sched_lr);
+}
+
+static const char *regnames[] = {
+	"PSR ", "ISR ", "CCR ", "CCCR",
+	"LR  ", "LCR ", "PC  ", "_stt",
+	"sys ", "GR8*", "GNE0", "GNE1",
+	"IACH", "IACL",
+	"TBR ", "SP  ", "FP  ", "GR3 ",
+	"GR4 ", "GR5 ", "GR6 ", "GR7 ",
+	"GR8 ", "GR9 ", "GR10", "GR11",
+	"GR12", "GR13", "GR14", "GR15",
+	"GR16", "GR17", "GR18", "GR19",
+	"GR20", "GR21", "GR22", "GR23",
+	"GR24", "GR25", "GR26", "GR27",
+	"EFRM", "CURR", "GR30", "BFRM"
+};
+
+void show_regs(struct pt_regs *regs)
+{
+	uint32_t *reg;
+	int loop;
+
+	printk("\n");
+
+	printk("Frame: @%08x [%s]\n",
+	       (uint32_t) regs,
+	       regs->psr & PSR_S ? "kernel" : "user");
+
+	reg = (uint32_t *) regs;
+	for (loop = 0; loop < REG__END; loop++) {
+		printk("%s %08x", regnames[loop + 0], reg[loop + 0]);
+
+		if (loop == REG__END - 1 || loop % 5 == 4)
+			printk("\n");
+		else
+			printk(" | ");
+	}
+
+	printk("Process %s (pid: %d)\n", current->comm, current->pid);
+}
+
+void die_if_kernel(const char *str, ...)
+{
+	char buffer[256];
+	va_list va;
+
+	if (user_mode(__frame))
+		return;
+
+	va_start(va, str);
+	vsprintf(buffer, str, va);
+	va_end(va);
+
+	console_verbose();
+	printk("\n===================================\n");
+	printk("%s\n", buffer);
+	show_backtrace(__frame, 0);
+
+	__break_hijack_kernel_event();
+	do_exit(SIGSEGV);
+}
+
+/*****************************************************************************/
+/*
+ * dump the contents of an exception frame
+ */
+static void show_backtrace_regs(struct pt_regs *frame)
+{
+	uint32_t *reg;
+	int loop;
+
+	/* print the registers for this frame */
+	printk("<-- %s Frame: @%p -->\n",
+	       frame->psr & PSR_S ? "Kernel Mode" : "User Mode",
+	       frame);
+
+	reg = (uint32_t *) frame;
+	for (loop = 0; loop < REG__END; loop++) {
+		printk("%s %08x", regnames[loop + 0], reg[loop + 0]);
+
+		if (loop == REG__END - 1 || loop % 5 == 4)
+			printk("\n");
+		else
+			printk(" | ");
+	}
+
+	printk("--------\n");
+} /* end show_backtrace_regs() */
+
+/*****************************************************************************/
+/*
+ * generate a backtrace of the kernel stack
+ */
+void show_backtrace(struct pt_regs *frame, unsigned long sp)
+{
+	struct pt_regs *frame0;
+	unsigned long tos = 0, stop = 0, base;
+	int format;
+
+	base = ((((unsigned long) frame) + 8191) & ~8191) - sizeof(struct user_context);
+	frame0 = (struct pt_regs *) base;
+
+	if (sp) {
+		tos = sp;
+		stop = (unsigned long) frame;
+	}
+
+	printk("\nProcess %s (pid: %d)\n\n", current->comm, current->pid);
+
+	for (;;) {
+		/* dump stack segment between frames */
+		//printk("%08lx -> %08lx\n", tos, stop);
+		format = 0;
+		while (tos < stop) {
+			if (format == 0)
+				printk(" %04lx :", tos & 0xffff);
+
+			printk(" %08lx", *(unsigned long *) tos);
+
+			tos += 4;
+			format++;
+			if (format == 8) {
+				printk("\n");
+				format = 0;
+			}
+		}
+
+		if (format > 0)
+			printk("\n");
+
+		/* dump frame 0 outside of the loop */
+		if (frame == frame0)
+			break;
+
+		tos = frame->sp;
+		if (((unsigned long) frame) + sizeof(*frame) != tos) {
+			printk("-- TOS %08lx does not follow frame %p --\n",
+			       tos, frame);
+			break;
+		}
+
+		show_backtrace_regs(frame);
+
+		/* dump the stack between this frame and the next */
+		stop = (unsigned long) frame->next_frame;
+		if (stop != base &&
+		    (stop < tos ||
+		     stop > base ||
+		     (stop < base && stop + sizeof(*frame) > base) ||
+		     stop & 3)) {
+			printk("-- next_frame %08lx is invalid (range %08lx-%08lx) --\n",
+			       stop, tos, base);
+			break;
+		}
+
+		/* move to next frame */
+		frame = frame->next_frame;
+	}
+
+	/* we can always dump frame 0, even if the rest of the stack is corrupt */
+	show_backtrace_regs(frame0);
+
+} /* end show_backtrace() */
+
+/*****************************************************************************/
+/*
+ * initialise traps
+ */
+void __init trap_init (void)
+{
+} /* end trap_init() */
diff --git a/arch/frv/kernel/uaccess.c b/arch/frv/kernel/uaccess.c
new file mode 100644
index 0000000..f3fd58a
--- /dev/null
+++ b/arch/frv/kernel/uaccess.c
@@ -0,0 +1,95 @@
+/* uaccess.c: userspace access functions
+ *
+ * Copyright (C) 2004 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 License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/mm.h>
+#include <asm/uaccess.h>
+
+/*****************************************************************************/
+/*
+ * copy a null terminated string from userspace
+ */
+long strncpy_from_user(char *dst, const char *src, long count)
+{
+	unsigned long max;
+	char *p, ch;
+	long err = -EFAULT;
+
+	if (count < 0)
+		BUG();
+
+	p = dst;
+
+#ifndef CONFIG_MMU
+	if ((unsigned long) src < memory_start)
+		goto error;
+#endif
+
+	if ((unsigned long) src >= get_addr_limit())
+		goto error;
+
+	max = get_addr_limit() - (unsigned long) src;
+	if ((unsigned long) count > max) {
+		memset(dst + max, 0, count - max);
+		count = max;
+	}
+
+	err = 0;
+	for (; count > 0; count--, p++, src++) {
+		__get_user_asm(err, ch, src, "ub", "=r");
+		if (err < 0)
+			goto error;
+		if (!ch)
+			break;
+		*p = ch;
+	}
+
+	err = p - dst; /* return length excluding NUL */
+
+ error:
+	if (count > 0)
+		memset(p, 0, count); /* clear remainder of buffer [security] */
+
+	return err;
+} /* end strncpy_from_user() */
+
+/*****************************************************************************/
+/*
+ * Return the size of a string (including the ending 0)
+ *
+ * Return 0 on exception, a value greater than N if too long
+ */
+long strnlen_user(const char *src, long count)
+{
+	const char *p;
+	long err = 0;
+	char ch;
+
+	if (count < 0)
+		BUG();
+
+#ifndef CONFIG_MMU
+	if ((unsigned long) src < memory_start)
+		return 0;
+#endif
+
+	if ((unsigned long) src >= get_addr_limit())
+		return 0;
+
+	for (p = src; count > 0; count--, p++) {
+		__get_user_asm(err, ch, p, "ub", "=r");
+		if (err < 0)
+			return 0;
+		if (!ch)
+			break;
+	}
+
+	return p - src + 1; /* return length including NUL */
+} /* end strnlen_user() */
diff --git a/arch/frv/kernel/vmlinux.lds.S b/arch/frv/kernel/vmlinux.lds.S
new file mode 100644
index 0000000..fceafd2
--- /dev/null
+++ b/arch/frv/kernel/vmlinux.lds.S
@@ -0,0 +1,187 @@
+/* ld script to make FRV Linux kernel
+ * Written by Martin Mares <mj@atrey.karlin.mff.cuni.cz>;
+ */
+OUTPUT_FORMAT("elf32-frv", "elf32-frv", "elf32-frv")
+OUTPUT_ARCH(frv)
+ENTRY(_start)
+
+#include <asm-generic/vmlinux.lds.h>
+#include <asm/processor.h>
+#include <asm/page.h>
+#include <asm/cache.h>
+#include <asm/thread_info.h>
+
+jiffies = jiffies_64 + 4;
+
+__page_offset = 0xc0000000;		/* start of area covered by struct pages */
+__kernel_image_start = __page_offset;	/* address at which kernel image resides */
+
+SECTIONS
+{
+  . = __kernel_image_start;
+
+  /* discardable initialisation code and data */
+  . = ALIGN(PAGE_SIZE);			/* Init code and data */
+  __init_begin = .;
+
+  _sinittext = .;
+  .init.text : {
+	*(.text.head)
+#ifndef CONFIG_DEBUG_INFO
+	*(.init.text)
+	*(.exit.text)
+	*(.exit.data)
+	*(.exitcall.exit)
+#endif
+  }
+  _einittext = .;
+  .init.data : { *(.init.data) }
+
+  . = ALIGN(8);
+  __setup_start = .;
+  .setup.init : { KEEP(*(.init.setup)) }
+  __setup_end = .;
+
+  __initcall_start = .;
+  .initcall.init : {
+	*(.initcall1.init)
+	*(.initcall2.init)
+	*(.initcall3.init)
+	*(.initcall4.init)
+	*(.initcall5.init)
+	*(.initcall6.init)
+	*(.initcall7.init)
+  }
+  __initcall_end = .;
+  __con_initcall_start = .;
+  .con_initcall.init : { *(.con_initcall.init) }
+  __con_initcall_end = .;
+  SECURITY_INIT
+  . = ALIGN(4);
+  __alt_instructions = .;
+  .altinstructions : { *(.altinstructions) }
+  __alt_instructions_end = .;
+ .altinstr_replacement : { *(.altinstr_replacement) }
+
+  __per_cpu_start = .;
+  .data.percpu  : { *(.data.percpu) }
+  __per_cpu_end = .;
+
+  . = ALIGN(4096);
+  __initramfs_start = .;
+  .init.ramfs : { *(.init.ramfs) }
+  __initramfs_end = .;
+
+  . = ALIGN(THREAD_SIZE);
+  __init_end = .;
+
+  /* put sections together that have massive alignment issues */
+  . = ALIGN(THREAD_SIZE);
+  .data.init_task : {
+	  /* init task record & stack */
+	  *(.data.init_task)
+  }
+
+  .trap : {
+	/* trap table management - read entry-table.S before modifying */
+	. = ALIGN(8192);
+	__trap_tables = .;
+	*(.trap.user)
+	*(.trap.kernel)
+	. = ALIGN(4096);
+	*(.trap.break)
+  }
+
+  . = ALIGN(4096);
+  .data.page_aligned : { *(.data.idt) }
+
+  . = ALIGN(L1_CACHE_BYTES);
+  .data.cacheline_aligned : { *(.data.cacheline_aligned) }
+
+  /* Text and read-only data */
+  . = ALIGN(4);
+  _text = .;
+  _stext = .;
+  .text : {
+	*(
+		.text.start .text .text.*
+#ifdef CONFIG_DEBUG_INFO
+	.init.text
+	.exit.text
+	.exitcall.exit
+#endif
+	)
+	SCHED_TEXT
+	*(.fixup)
+	*(.gnu.warning)
+	*(.exitcall.exit)
+	} = 0x9090
+
+  _etext = .;			/* End of text section */
+
+  RODATA
+
+  .rodata : {
+	*(.trap.vector)
+
+	/* this clause must not be modified - the ordering and adjacency are imperative */
+	__trap_fixup_tables = .;
+	*(.trap.fixup.user .trap.fixup.kernel)
+
+	}
+
+  . = ALIGN(8);		/* Exception table */
+  __start___ex_table = .;
+  __ex_table : { KEEP(*(__ex_table)) }
+  __stop___ex_table = .;
+
+  _sdata = .;
+  .data : {			/* Data */
+	*(.data .data.*)
+	*(.exit.data)
+	CONSTRUCTORS
+	}
+
+  _edata = .;			/* End of data section */
+
+  /* GP section */
+  . = ALIGN(L1_CACHE_BYTES);
+  _gp = . + 2048;
+  PROVIDE (gp = _gp);
+
+  .sdata : { *(.sdata .sdata.*) }
+
+  /* BSS */
+  . = ALIGN(L1_CACHE_BYTES);
+  __bss_start = .;
+
+  .sbss		: { *(.sbss .sbss.*) }
+  .bss		: { *(.bss .bss.*) }
+  .bss.stack	: { *(.bss) }
+
+  __bss_stop = .;
+  _end = . ;
+  . = ALIGN(PAGE_SIZE);
+  __kernel_image_end = .;
+
+  /* Stabs debugging sections.  */
+  .stab 0 : { *(.stab) }
+  .stabstr 0 : { *(.stabstr) }
+  .stab.excl 0 : { *(.stab.excl) }
+  .stab.exclstr 0 : { *(.stab.exclstr) }
+  .stab.index 0 : { *(.stab.index) }
+  .stab.indexstr 0 : { *(.stab.indexstr) }
+
+  .debug_line		0 : { *(.debug_line) }
+  .debug_info		0 : { *(.debug_info) }
+  .debug_abbrev		0 : { *(.debug_abbrev) }
+  .debug_aranges	0 : { *(.debug_aranges) }
+  .debug_frame		0 : { *(.debug_frame) }
+  .debug_pubnames	0 : { *(.debug_pubnames) }
+  .debug_str		0 : { *(.debug_str) }
+  .debug_ranges		0 : { *(.debug_ranges) }
+
+  .comment 0 : { *(.comment) }
+}
+
+__kernel_image_size_no_bss = __bss_start - __kernel_image_start;
diff --git a/arch/frv/lib/Makefile b/arch/frv/lib/Makefile
new file mode 100644
index 0000000..19be262
--- /dev/null
+++ b/arch/frv/lib/Makefile
@@ -0,0 +1,8 @@
+#
+# Makefile for FRV-specific library files..
+#
+
+lib-y := \
+	__ashldi3.o __lshrdi3.o __muldi3.o __ashrdi3.o __negdi2.o \
+	checksum.o memcpy.o memset.o atomic-ops.o \
+	outsl_ns.o outsl_sw.o insl_ns.o insl_sw.o cache.o
diff --git a/arch/frv/lib/__ashldi3.S b/arch/frv/lib/__ashldi3.S
new file mode 100644
index 0000000..db5b6dc37
--- /dev/null
+++ b/arch/frv/lib/__ashldi3.S
@@ -0,0 +1,40 @@
+/* __ashldi3.S:	64-bit arithmetic shift left
+ *
+ * Copyright (C) 2003 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 License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+        .text
+        .p2align	4
+
+###############################################################################
+#
+# unsigned long long __ashldi3(unsigned long long value [GR8:GR9], unsigned by [GR10])
+#
+###############################################################################
+        .globl		__ashldi3
+        .type		__ashldi3,@function
+__ashldi3:
+	andicc.p	gr10,#63,gr10,icc0
+	setlos		#32,gr5
+	andicc.p	gr10,#32,gr0,icc1
+	beqlr		icc0,#0
+	ckeq		icc1,cc4			; cc4 is true if 0<N<32
+
+	# deal with a shift in the range 1<=N<=31
+	csll.p		gr8,gr10,gr8	,cc4,#1		; MSW <<= N
+	csub		gr5,gr10,gr5	,cc4,#1		; M = 32 - N
+	csrl.p		gr9,gr5,gr4	,cc4,#1
+	csll		gr9,gr10,gr9	,cc4,#1		; LSW <<= N
+	cor.p		gr4,gr8,gr8	,cc4,#1		; MSW |= LSW >> M
+
+	# deal with a shift in the range 32<=N<=63
+	csll		gr9,gr10,gr8	,cc4,#0		; MSW = LSW << (N & 31 [implicit AND])
+	cor.p		gr0,gr0,gr9	,cc4,#0		; LSW = 0
+	bralr
+	.size		__ashldi3, .-__ashldi3
diff --git a/arch/frv/lib/__ashrdi3.S b/arch/frv/lib/__ashrdi3.S
new file mode 100644
index 0000000..5742665
--- /dev/null
+++ b/arch/frv/lib/__ashrdi3.S
@@ -0,0 +1,41 @@
+/* __ashrdi3.S:	64-bit arithmetic shift right
+ *
+ * Copyright (C) 2003 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 License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+        .text
+        .p2align	4
+
+###############################################################################
+#
+# signed long long __ashrdi3(signed long long value [GR8:GR9], unsigned by [GR10])
+#
+###############################################################################
+        .globl		__ashrdi3
+        .type		__ashrdi3,@function
+__ashrdi3:
+	andicc.p	gr10,#63,gr10,icc0
+	setlos		#32,gr5
+	andicc.p	gr10,#32,gr0,icc1
+	beqlr		icc0,#0
+	setlos.p	#31,gr6
+	ckeq		icc1,cc4			; cc4 is true if 0<N<32
+
+	# deal with a shift in the range 1<=N<=31
+	csrl.p		gr9,gr10,gr9	,cc4,#1		; LSW >>= N
+	csub		gr5,gr10,gr5	,cc4,#1		; M = 32 - N
+	csll.p		gr8,gr5,gr4	,cc4,#1
+	csra		gr8,gr10,gr8	,cc4,#1		; MSW >>= N
+	cor.p		gr4,gr9,gr9	,cc4,#1		; LSW |= MSW << M
+
+	# deal with a shift in the range 32<=N<=63
+	csra		gr8,gr10,gr9	,cc4,#0		; LSW = MSW >> (N & 31 [implicit AND])
+	csra.p		gr8,gr6,gr8	,cc4,#0		; MSW >>= 31
+	bralr
+	.size		__ashrdi3, .-__ashrdi3
diff --git a/arch/frv/lib/__lshrdi3.S b/arch/frv/lib/__lshrdi3.S
new file mode 100644
index 0000000..7b41f63
--- /dev/null
+++ b/arch/frv/lib/__lshrdi3.S
@@ -0,0 +1,40 @@
+/* __lshrdi3.S:	64-bit logical shift right
+ *
+ * Copyright (C) 2003 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 License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+        .text
+        .p2align	4
+
+###############################################################################
+#
+# unsigned long long __lshrdi3(unsigned long long value [GR8:GR9], unsigned by [GR10])
+#
+###############################################################################
+        .globl		__lshrdi3
+        .type		__lshrdi3,@function
+__lshrdi3:
+	andicc.p	gr10,#63,gr10,icc0
+	setlos		#32,gr5
+	andicc.p	gr10,#32,gr0,icc1
+	beqlr		icc0,#0
+	ckeq		icc1,cc4			; cc4 is true if 0<N<32
+
+	# deal with a shift in the range 1<=N<=31
+	csrl.p		gr9,gr10,gr9	,cc4,#1		; LSW >>= N
+	csub		gr5,gr10,gr5	,cc4,#1		; M = 32 - N
+	csll.p		gr8,gr5,gr4	,cc4,#1
+	csrl		gr8,gr10,gr8	,cc4,#1		; MSW >>= N
+	cor.p		gr4,gr9,gr9	,cc4,#1		; LSW |= MSW << M
+
+	# deal with a shift in the range 32<=N<=63
+	csrl		gr8,gr10,gr9	,cc4,#0		; LSW = MSW >> (N & 31 [implicit AND])
+	cor.p		gr0,gr0,gr8	,cc4,#0		; MSW = 0
+	bralr
+	.size		__lshrdi3, .-__lshrdi3
diff --git a/arch/frv/lib/__muldi3.S b/arch/frv/lib/__muldi3.S
new file mode 100644
index 0000000..2703d9b
--- /dev/null
+++ b/arch/frv/lib/__muldi3.S
@@ -0,0 +1,32 @@
+/* __muldi3.S:	64-bit multiply
+ *
+ * Copyright (C) 2003 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 License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+        .text
+        .p2align	4
+
+###############################################################################
+#
+# unsigned long long __muldi3(unsigned long long x [GR8:GR9],
+#                             unsigned long long y [GR10:GR11])
+#
+###############################################################################
+        .globl		__muldi3, __mulll, __umulll
+        .type		__muldi3,@function
+__muldi3:
+__mulll:
+__umulll:
+	umul		gr8,gr11,gr4		; GR4:GR5 = x.MSW * y.LSW
+	umul		gr9,gr10,gr6		; GR6:GR7 = x.LSW * y.MSW
+	umul.p		gr9,gr11,gr8		; GR8:GR9 = x.LSW * y.LSW
+	add		gr5,gr7,gr5
+	add.p		gr8,gr5,gr8		; GR8 += GR5 + GR7
+	bralr
+	.size		__muldi3, .-__muldi3
diff --git a/arch/frv/lib/__negdi2.S b/arch/frv/lib/__negdi2.S
new file mode 100644
index 0000000..d1747bf
--- /dev/null
+++ b/arch/frv/lib/__negdi2.S
@@ -0,0 +1,28 @@
+/* __negdi2.S: 64-bit negate
+ *
+ * Copyright (C) 2003 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 License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+
+        .text
+        .p2align	4
+
+###############################################################################
+#
+# unsigned long long __negdi2(unsigned long long value [GR8:GR9])
+#
+###############################################################################
+        .globl		__negdi2
+        .type		__negdi2,@function
+__negdi2:
+	subcc		gr0,gr9,gr9,icc0
+	subx		gr0,gr8,gr8,icc0
+	bralr
+	.size		__negdi2, .-__negdi2
+
diff --git a/arch/frv/lib/atomic-ops.S b/arch/frv/lib/atomic-ops.S
new file mode 100644
index 0000000..b03d510
--- /dev/null
+++ b/arch/frv/lib/atomic-ops.S
@@ -0,0 +1,265 @@
+/* atomic-ops.S: kernel atomic operations
+ *
+ * For an explanation of how atomic ops work in this arch, see:
+ *   Documentation/fujitsu/frv/atomic-ops.txt
+ *
+ * Copyright (C) 2004 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 License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <asm/spr-regs.h>
+
+	.text
+	.balign 4
+
+###############################################################################
+#
+# unsigned long atomic_test_and_ANDNOT_mask(unsigned long mask, volatile unsigned long *v);
+#
+###############################################################################
+	.globl		atomic_test_and_ANDNOT_mask
+        .type		atomic_test_and_ANDNOT_mask,@function
+atomic_test_and_ANDNOT_mask:
+	not.p		gr8,gr10
+0:
+	orcc		gr0,gr0,gr0,icc3		/* set ICC3.Z */
+	ckeq		icc3,cc7
+	ld.p		@(gr9,gr0),gr8			/* LD.P/ORCR must be atomic */
+	orcr		cc7,cc7,cc3			/* set CC3 to true */
+	and		gr8,gr10,gr11
+	cst.p		gr11,@(gr9,gr0)		,cc3,#1
+	corcc		gr29,gr29,gr0		,cc3,#1	/* clear ICC3.Z if store happens */
+	beq		icc3,#0,0b
+	bralr
+
+	.size		atomic_test_and_ANDNOT_mask, .-atomic_test_and_ANDNOT_mask
+
+###############################################################################
+#
+# unsigned long atomic_test_and_OR_mask(unsigned long mask, volatile unsigned long *v);
+#
+###############################################################################
+	.globl		atomic_test_and_OR_mask
+        .type		atomic_test_and_OR_mask,@function
+atomic_test_and_OR_mask:
+	or.p		gr8,gr8,gr10
+0:
+	orcc		gr0,gr0,gr0,icc3		/* set ICC3.Z */
+	ckeq		icc3,cc7
+	ld.p		@(gr9,gr0),gr8			/* LD.P/ORCR must be atomic */
+	orcr		cc7,cc7,cc3			/* set CC3 to true */
+	or		gr8,gr10,gr11
+	cst.p		gr11,@(gr9,gr0)		,cc3,#1
+	corcc		gr29,gr29,gr0		,cc3,#1	/* clear ICC3.Z if store happens */
+	beq		icc3,#0,0b
+	bralr
+
+	.size		atomic_test_and_OR_mask, .-atomic_test_and_OR_mask
+
+###############################################################################
+#
+# unsigned long atomic_test_and_XOR_mask(unsigned long mask, volatile unsigned long *v);
+#
+###############################################################################
+	.globl		atomic_test_and_XOR_mask
+        .type		atomic_test_and_XOR_mask,@function
+atomic_test_and_XOR_mask:
+	or.p		gr8,gr8,gr10
+0:
+	orcc		gr0,gr0,gr0,icc3		/* set ICC3.Z */
+	ckeq		icc3,cc7
+	ld.p		@(gr9,gr0),gr8			/* LD.P/ORCR must be atomic */
+	orcr		cc7,cc7,cc3			/* set CC3 to true */
+	xor		gr8,gr10,gr11
+	cst.p		gr11,@(gr9,gr0)		,cc3,#1
+	corcc		gr29,gr29,gr0		,cc3,#1	/* clear ICC3.Z if store happens */
+	beq		icc3,#0,0b
+	bralr
+
+	.size		atomic_test_and_XOR_mask, .-atomic_test_and_XOR_mask
+
+###############################################################################
+#
+# int atomic_add_return(int i, atomic_t *v)
+#
+###############################################################################
+	.globl		atomic_add_return
+        .type		atomic_add_return,@function
+atomic_add_return:
+	or.p		gr8,gr8,gr10
+0:
+	orcc		gr0,gr0,gr0,icc3		/* set ICC3.Z */
+	ckeq		icc3,cc7
+	ld.p		@(gr9,gr0),gr8			/* LD.P/ORCR must be atomic */
+	orcr		cc7,cc7,cc3			/* set CC3 to true */
+	add		gr8,gr10,gr8
+	cst.p		gr8,@(gr9,gr0)		,cc3,#1
+	corcc		gr29,gr29,gr0		,cc3,#1	/* clear ICC3.Z if store happens */
+	beq		icc3,#0,0b
+	bralr
+
+	.size		atomic_add_return, .-atomic_add_return
+
+###############################################################################
+#
+# int atomic_sub_return(int i, atomic_t *v)
+#
+###############################################################################
+	.globl		atomic_sub_return
+        .type		atomic_sub_return,@function
+atomic_sub_return:
+	or.p		gr8,gr8,gr10
+0:
+	orcc		gr0,gr0,gr0,icc3		/* set ICC3.Z */
+	ckeq		icc3,cc7
+	ld.p		@(gr9,gr0),gr8			/* LD.P/ORCR must be atomic */
+	orcr		cc7,cc7,cc3			/* set CC3 to true */
+	sub		gr8,gr10,gr8
+	cst.p		gr8,@(gr9,gr0)		,cc3,#1
+	corcc		gr29,gr29,gr0		,cc3,#1	/* clear ICC3.Z if store happens */
+	beq		icc3,#0,0b
+	bralr
+
+	.size		atomic_sub_return, .-atomic_sub_return
+
+###############################################################################
+#
+# uint8_t __xchg_8(uint8_t i, uint8_t *v)
+#
+###############################################################################
+	.globl		__xchg_8
+        .type		__xchg_8,@function
+__xchg_8:
+	or.p		gr8,gr8,gr10
+0:
+	orcc		gr0,gr0,gr0,icc3		/* set ICC3.Z */
+	ckeq		icc3,cc7
+	ldub.p		@(gr9,gr0),gr8			/* LD.P/ORCR must be atomic */
+	orcr		cc7,cc7,cc3			/* set CC3 to true */
+	cstb.p		gr10,@(gr9,gr0)		,cc3,#1
+	corcc		gr29,gr29,gr0		,cc3,#1	/* clear ICC3.Z if store happens */
+	beq		icc3,#0,0b
+	bralr
+
+	.size		__xchg_8, .-__xchg_8
+
+###############################################################################
+#
+# uint16_t __xchg_16(uint16_t i, uint16_t *v)
+#
+###############################################################################
+	.globl		__xchg_16
+        .type		__xchg_16,@function
+__xchg_16:
+	or.p		gr8,gr8,gr10
+0:
+	orcc		gr0,gr0,gr0,icc3		/* set ICC3.Z */
+	ckeq		icc3,cc7
+	lduh.p		@(gr9,gr0),gr8			/* LD.P/ORCR must be atomic */
+	orcr		cc7,cc7,cc3			/* set CC3 to true */
+	csth.p		gr10,@(gr9,gr0)		,cc3,#1
+	corcc		gr29,gr29,gr0		,cc3,#1	/* clear ICC3.Z if store happens */
+	beq		icc3,#0,0b
+	bralr
+
+	.size		__xchg_16, .-__xchg_16
+
+###############################################################################
+#
+# uint32_t __xchg_32(uint32_t i, uint32_t *v)
+#
+###############################################################################
+	.globl		__xchg_32
+        .type		__xchg_32,@function
+__xchg_32:
+	or.p		gr8,gr8,gr10
+0:
+	orcc		gr0,gr0,gr0,icc3		/* set ICC3.Z */
+	ckeq		icc3,cc7
+	ld.p		@(gr9,gr0),gr8			/* LD.P/ORCR must be atomic */
+	orcr		cc7,cc7,cc3			/* set CC3 to true */
+	cst.p		gr10,@(gr9,gr0)		,cc3,#1
+	corcc		gr29,gr29,gr0		,cc3,#1	/* clear ICC3.Z if store happens */
+	beq		icc3,#0,0b
+	bralr
+
+	.size		__xchg_32, .-__xchg_32
+
+###############################################################################
+#
+# uint8_t __cmpxchg_8(uint8_t *v, uint8_t test, uint8_t new)
+#
+###############################################################################
+	.globl		__cmpxchg_8
+        .type		__cmpxchg_8,@function
+__cmpxchg_8:
+	or.p		gr8,gr8,gr11
+0:
+	orcc		gr0,gr0,gr0,icc3
+	ckeq		icc3,cc7
+	ldub.p		@(gr11,gr0),gr8
+	orcr		cc7,cc7,cc3
+	sub		gr8,gr9,gr7
+	sllicc		gr7,#24,gr0,icc0
+	bne		icc0,#0,1f
+	cstb.p		gr10,@(gr11,gr0)	,cc3,#1
+	corcc		gr29,gr29,gr0		,cc3,#1
+	beq		icc3,#0,0b
+1:
+	bralr
+
+	.size		__cmpxchg_8, .-__cmpxchg_8
+
+###############################################################################
+#
+# uint16_t __cmpxchg_16(uint16_t *v, uint16_t test, uint16_t new)
+#
+###############################################################################
+	.globl		__cmpxchg_16
+        .type		__cmpxchg_16,@function
+__cmpxchg_16:
+	or.p		gr8,gr8,gr11
+0:
+	orcc		gr0,gr0,gr0,icc3
+	ckeq		icc3,cc7
+	lduh.p		@(gr11,gr0),gr8
+	orcr		cc7,cc7,cc3
+	sub		gr8,gr9,gr7
+	sllicc		gr7,#16,gr0,icc0
+	bne		icc0,#0,1f
+	csth.p		gr10,@(gr11,gr0)	,cc3,#1
+	corcc		gr29,gr29,gr0		,cc3,#1
+	beq		icc3,#0,0b
+1:
+	bralr
+
+	.size		__cmpxchg_16, .-__cmpxchg_16
+
+###############################################################################
+#
+# uint32_t __cmpxchg_32(uint32_t *v, uint32_t test, uint32_t new)
+#
+###############################################################################
+	.globl		__cmpxchg_32
+        .type		__cmpxchg_32,@function
+__cmpxchg_32:
+	or.p		gr8,gr8,gr11
+0:
+	orcc		gr0,gr0,gr0,icc3
+	ckeq		icc3,cc7
+	ld.p		@(gr11,gr0),gr8
+	orcr		cc7,cc7,cc3
+	subcc		gr8,gr9,gr7,icc0
+	bne		icc0,#0,1f
+	cst.p		gr10,@(gr11,gr0)	,cc3,#1
+	corcc		gr29,gr29,gr0		,cc3,#1
+	beq		icc3,#0,0b
+1:
+	bralr
+
+	.size		__cmpxchg_32, .-__cmpxchg_32
diff --git a/arch/frv/lib/cache.S b/arch/frv/lib/cache.S
new file mode 100644
index 0000000..0e10ad8d
--- /dev/null
+++ b/arch/frv/lib/cache.S
@@ -0,0 +1,98 @@
+/* cache.S: cache managment routines
+ *
+ * Copyright (C) 2004 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 License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <asm/spr-regs.h>
+#include <asm/cache.h>
+
+        .text
+        .p2align	4
+
+###############################################################################
+#
+# Write back a range of dcache
+# - void frv_dcache_writeback(unsigned long start [GR8], unsigned long size [GR9])
+#
+###############################################################################
+        .globl		frv_dcache_writeback
+        .type		frv_dcache_writeback,@function
+frv_dcache_writeback:
+	andi		gr8,~(L1_CACHE_BYTES-1),gr8
+
+2:	dcf		@(gr8,gr0)
+	addi		gr8,#L1_CACHE_BYTES,gr8
+	cmp		gr9,gr8,icc0
+	bhi		icc0,#2,2b
+
+	membar
+	bralr
+	.size		frv_dcache_writeback, .-frv_dcache_writeback
+
+##############################################################################
+#
+# Invalidate a range of dcache and icache
+# - void frv_cache_invalidate(unsigned long start [GR8], unsigned long end [GR9]);
+#
+###############################################################################
+        .globl		frv_cache_invalidate
+        .type		frv_cache_invalidate,@function
+frv_cache_invalidate:
+	andi		gr8,~(L1_CACHE_BYTES-1),gr8
+
+2:	dci		@(gr8,gr0)
+	ici		@(gr8,gr0)
+	addi		gr8,#L1_CACHE_BYTES,gr8
+	cmp		gr9,gr8,icc0
+	bhi		icc0,#2,2b
+
+	membar
+	bralr
+	.size		frv_cache_invalidate, .-frv_cache_invalidate
+
+##############################################################################
+#
+# Invalidate a range of icache
+# - void frv_icache_invalidate(unsigned long start [GR8], unsigned long end [GR9]);
+#
+###############################################################################
+        .globl		frv_icache_invalidate
+        .type		frv_icache_invalidate,@function
+frv_icache_invalidate:
+	andi		gr8,~(L1_CACHE_BYTES-1),gr8
+
+2:	ici		@(gr8,gr0)
+	addi		gr8,#L1_CACHE_BYTES,gr8
+	cmp		gr9,gr8,icc0
+	bhi		icc0,#2,2b
+
+	membar
+	bralr
+	.size		frv_icache_invalidate, .-frv_icache_invalidate
+
+###############################################################################
+#
+# Write back and invalidate a range of dcache and icache
+# - void frv_cache_wback_inv(unsigned long start [GR8], unsigned long end [GR9])
+#
+###############################################################################
+        .globl		frv_cache_wback_inv
+        .type		frv_cache_wback_inv,@function
+frv_cache_wback_inv:
+	andi		gr8,~(L1_CACHE_BYTES-1),gr8
+
+2:	dcf		@(gr8,gr0)
+	ici		@(gr8,gr0)
+	addi		gr8,#L1_CACHE_BYTES,gr8
+	cmp		gr9,gr8,icc0
+	bhi		icc0,#2,2b
+
+	membar
+	bralr
+	.size		frv_cache_wback_inv, .-frv_cache_wback_inv
diff --git a/arch/frv/lib/checksum.c b/arch/frv/lib/checksum.c
new file mode 100644
index 0000000..7bf5bd6
--- /dev/null
+++ b/arch/frv/lib/checksum.c
@@ -0,0 +1,148 @@
+/*
+ * INET		An implementation of the TCP/IP protocol suite for the LINUX
+ *		operating system.  INET is implemented using the  BSD Socket
+ *		interface as the means of communication with the user level.
+ *
+ *		IP/TCP/UDP checksumming routines
+ *
+ * Authors:	Jorge Cwik, <jorge@laser.satlink.net>
+ *		Arnt Gulbrandsen, <agulbra@nvg.unit.no>
+ *		Tom May, <ftom@netcom.com>
+ *		Andreas Schwab, <schwab@issan.informatik.uni-dortmund.de>
+ *		Lots of code moved from tcp.c and ip.c; see those files
+ *		for more names.
+ *
+ * 03/02/96	Jes Sorensen, Andreas Schwab, Roman Hodek:
+ *		Fixed some nasty bugs, causing some horrible crashes.
+ *		A: At some points, the sum (%0) was used as
+ *		length-counter instead of the length counter
+ *		(%1). Thanks to Roman Hodek for pointing this out.
+ *		B: GCC seems to mess up if one uses too many
+ *		data-registers to hold input values and one tries to
+ *		specify d0 and d1 as scratch registers. Letting gcc choose these
+ *      registers itself solves the problem.
+ *
+ *		This program is free software; you can redistribute it and/or
+ *		modify it under the terms of the GNU General Public License
+ *		as published by the Free Software Foundation; either version
+ *		2 of the License, or (at your option) any later version.
+ */
+
+/* Revised by Kenneth Albanowski for m68knommu. Basic problem: unaligned access kills, so most
+   of the assembly has to go. */
+
+#include <net/checksum.h>
+#include <asm/checksum.h>
+
+static inline unsigned short from32to16(unsigned long x)
+{
+	/* add up 16-bit and 16-bit for 16+c bit */
+	x = (x & 0xffff) + (x >> 16);
+	/* add up carry.. */
+	x = (x & 0xffff) + (x >> 16);
+	return x;
+}
+
+static unsigned long do_csum(const unsigned char * buff, int len)
+{
+	int odd, count;
+	unsigned long result = 0;
+
+	if (len <= 0)
+		goto out;
+	odd = 1 & (unsigned long) buff;
+	if (odd) {
+		result = *buff;
+		len--;
+		buff++;
+	}
+	count = len >> 1;		/* nr of 16-bit words.. */
+	if (count) {
+		if (2 & (unsigned long) buff) {
+			result += *(unsigned short *) buff;
+			count--;
+			len -= 2;
+			buff += 2;
+		}
+		count >>= 1;		/* nr of 32-bit words.. */
+		if (count) {
+		        unsigned long carry = 0;
+			do {
+				unsigned long w = *(unsigned long *) buff;
+				count--;
+				buff += 4;
+				result += carry;
+				result += w;
+				carry = (w > result);
+			} while (count);
+			result += carry;
+			result = (result & 0xffff) + (result >> 16);
+		}
+		if (len & 2) {
+			result += *(unsigned short *) buff;
+			buff += 2;
+		}
+	}
+	if (len & 1)
+		result += (*buff << 8);
+	result = from32to16(result);
+	if (odd)
+		result = ((result >> 8) & 0xff) | ((result & 0xff) << 8);
+out:
+	return result;
+}
+
+/*
+ * computes the checksum of a memory block at buff, length len,
+ * and adds in "sum" (32-bit)
+ *
+ * returns a 32-bit number suitable for feeding into itself
+ * or csum_tcpudp_magic
+ *
+ * this function must be called with even lengths, except
+ * for the last fragment, which may be odd
+ *
+ * it's best to have buff aligned on a 32-bit boundary
+ */
+unsigned int csum_partial(const unsigned char * buff, int len, unsigned int sum)
+{
+	unsigned int result = do_csum(buff, len);
+
+	/* add in old sum, and carry.. */
+	result += sum;
+	if (sum > result)
+		result += 1;
+	return result;
+}
+
+/*
+ * this routine is used for miscellaneous IP-like checksums, mainly
+ * in icmp.c
+ */
+unsigned short ip_compute_csum(const unsigned char * buff, int len)
+{
+	return ~do_csum(buff,len);
+}
+
+/*
+ * copy from fs while checksumming, otherwise like csum_partial
+ */
+
+unsigned int
+csum_partial_copy_from_user(const char *src, char *dst, int len, int sum, int *csum_err)
+{
+	if (csum_err) *csum_err = 0;
+	memcpy(dst, src, len);
+	return csum_partial(dst, len, sum);
+}
+
+/*
+ * copy from ds while checksumming, otherwise like csum_partial
+ */
+
+unsigned int
+csum_partial_copy(const char *src, char *dst, int len, int sum)
+{
+	memcpy(dst, src, len);
+	return csum_partial(dst, len, sum);
+}
diff --git a/arch/frv/lib/insl_ns.S b/arch/frv/lib/insl_ns.S
new file mode 100644
index 0000000..d165842
--- /dev/null
+++ b/arch/frv/lib/insl_ns.S
@@ -0,0 +1,52 @@
+/* insl_ns.S: input array of 4b words from device port without byte swapping
+ *
+ * Copyright (C) 2003 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 License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+
+        .text
+        .p2align	4
+
+###############################################################################
+#
+# void __insl_ns(unsigned int port, void *buf, int n)
+#
+###############################################################################
+        .globl		__insl_ns
+        .type		__insl_ns,@function
+__insl_ns:
+	andicc.p	gr9,#3,gr0,icc0
+	setlos		#4,gr4
+	bne		icc0,#0,__insl_ns_misaligned
+	subi		gr9,#4,gr9
+0:
+	ldi.p		@(gr8,#0),gr5
+	subicc		gr10,#1,gr10,icc0
+	stu.p		gr5,@(gr9,gr4)
+	bhi		icc0,#2,0b
+	bralr
+
+__insl_ns_misaligned:
+	subi.p		gr9,#1,gr9
+	setlos		#1,gr4
+0:
+	ldi		@(gr8,#0),gr5
+
+	srli		gr5,#24,gr6
+	stbu.p		gr6,@(gr9,gr4)
+	srli		gr5,#16,gr6
+	stbu.p		gr6,@(gr9,gr4)
+	srli		gr5,#8,gr6
+	stbu.p		gr6,@(gr9,gr4)
+	subicc		gr10,#1,gr10,icc0
+	stbu.p		gr5,@(gr9,gr4)
+	bhi		icc0,#2,0b
+	bralr
+
+	.size		__insl_ns, .-__insl_ns
diff --git a/arch/frv/lib/insl_sw.S b/arch/frv/lib/insl_sw.S
new file mode 100644
index 0000000..9b5aa95
--- /dev/null
+++ b/arch/frv/lib/insl_sw.S
@@ -0,0 +1,40 @@
+/* insl_sw.S: input array of 4b words from device port with byte swapping
+ *
+ * Copyright (C) 2003 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 License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+
+        .text
+        .p2align	4
+
+###############################################################################
+#
+# void __insl_sw(unsigned int port, void *buf, int n)
+#
+###############################################################################
+        .globl		__insl_sw
+        .type		__insl_sw,@function
+__insl_sw:
+	subi.p		gr9,#1,gr9
+	setlos		#1,gr4
+0:
+	ldi.p		@(gr8,#0),gr5		; get 0xAABBCCDD
+	subicc		gr10,#1,gr10,icc0
+
+	stbu.p		gr5,@(gr9,gr4)		; write 0xDD
+	srli		gr5,#8,gr5
+	stbu.p		gr5,@(gr9,gr4)		; write 0xCC
+	srli		gr5,#8,gr5
+	stbu.p		gr5,@(gr9,gr4)		; write 0xBB
+	srli		gr5,#8,gr5
+	stbu.p		gr5,@(gr9,gr4)		; write 0xAA
+	bhi		icc0,#2,0b
+	bralr
+
+	.size		__insl_sw, .-__insl_sw
diff --git a/arch/frv/lib/memcpy.S b/arch/frv/lib/memcpy.S
new file mode 100644
index 0000000..9c59652
--- /dev/null
+++ b/arch/frv/lib/memcpy.S
@@ -0,0 +1,135 @@
+/* memcpy.S: optimised assembly memcpy
+ *
+ * Copyright (C) 2003 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 License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+
+        .text
+        .p2align	4
+
+###############################################################################
+#
+# void *memcpy(void *to, const char *from, size_t count)
+#
+# - NOTE: must not use any stack. exception detection performs function return
+#         to caller's fixup routine, aborting the remainder of the copy
+#
+###############################################################################
+        .globl		memcpy,__memcpy_end
+        .type		memcpy,@function
+memcpy:
+	or.p		gr8,gr9,gr4
+	orcc		gr10,gr0,gr0,icc3
+	or.p		gr10,gr4,gr4
+	beqlr		icc3,#0
+
+	# optimise based on best common alignment for to, from & count
+	andicc.p	gr4,#0x0f,gr0,icc0
+	setlos		#8,gr11
+	andicc.p	gr4,#0x07,gr0,icc1
+	beq		icc0,#0,memcpy_16
+	andicc.p	gr4,#0x03,gr0,icc0
+	beq		icc1,#0,memcpy_8
+	andicc.p	gr4,#0x01,gr0,icc1
+	beq		icc0,#0,memcpy_4
+	setlos.p	#1,gr11
+	beq		icc1,#0,memcpy_2
+
+	# do byte by byte copy
+	sub.p		gr8,gr11,gr3
+	sub		gr9,gr11,gr9
+0:	ldubu.p		@(gr9,gr11),gr4
+	subicc		gr10,#1,gr10,icc0
+	stbu.p		gr4,@(gr3,gr11)
+	bne		icc0,#2,0b
+	bralr
+
+	# do halfword by halfword copy
+memcpy_2:
+	setlos		#2,gr11
+	sub.p		gr8,gr11,gr3
+	sub		gr9,gr11,gr9
+0:	lduhu.p		@(gr9,gr11),gr4
+	subicc		gr10,#2,gr10,icc0
+	sthu.p		gr4,@(gr3,gr11)
+	bne		icc0,#2,0b
+	bralr
+
+	# do word by word copy
+memcpy_4:
+	setlos		#4,gr11
+	sub.p		gr8,gr11,gr3
+	sub		gr9,gr11,gr9
+0:	ldu.p		@(gr9,gr11),gr4
+	subicc		gr10,#4,gr10,icc0
+	stu.p		gr4,@(gr3,gr11)
+	bne		icc0,#2,0b
+	bralr
+
+	# do double-word by double-word copy
+memcpy_8:
+	sub.p		gr8,gr11,gr3
+	sub		gr9,gr11,gr9
+0:	lddu.p		@(gr9,gr11),gr4
+	subicc		gr10,#8,gr10,icc0
+	stdu.p		gr4,@(gr3,gr11)
+	bne		icc0,#2,0b
+	bralr
+
+	# do quad-word by quad-word copy
+memcpy_16:
+	sub.p		gr8,gr11,gr3
+	sub		gr9,gr11,gr9
+0:	lddu		@(gr9,gr11),gr4
+	lddu.p		@(gr9,gr11),gr6
+	subicc		gr10,#16,gr10,icc0
+	stdu		gr4,@(gr3,gr11)
+	stdu.p		gr6,@(gr3,gr11)
+	bne		icc0,#2,0b
+	bralr
+__memcpy_end:
+
+	.size		memcpy, __memcpy_end-memcpy
+
+###############################################################################
+#
+# copy to/from userspace
+# - return the number of bytes that could not be copied (0 on complete success)
+#
+# long __memcpy_user(void *dst, const void *src, size_t count)
+#
+###############################################################################
+        .globl		__memcpy_user, __memcpy_user_error_lr, __memcpy_user_error_handler
+        .type		__memcpy_user,@function
+__memcpy_user:
+	movsg		lr,gr7
+	subi.p		sp,#8,sp
+	add		gr8,gr10,gr6		; calculate expected end address
+	stdi		gr6,@(sp,#0)
+
+	# abuse memcpy to do the dirty work
+	call		memcpy
+__memcpy_user_error_lr:
+	ldi.p		@(sp,#4),gr7
+	setlos		#0,gr8
+	jmpl.p		@(gr7,gr0)
+	addi		sp,#8,sp
+
+	# deal any exception generated by memcpy
+	# GR8 - memcpy's current dest address
+	# GR11 - memset's step value (index register for store insns)
+__memcpy_user_error_handler:
+	lddi.p		@(sp,#0),gr4		; load GR4 with dst+count, GR5 with ret addr
+	add		gr11,gr3,gr7
+	sub.p		gr4,gr7,gr8
+
+	addi		sp,#8,sp
+	jmpl		@(gr5,gr0)
+
+	.size		__memcpy_user, .-__memcpy_user
diff --git a/arch/frv/lib/memset.S b/arch/frv/lib/memset.S
new file mode 100644
index 0000000..55a3526
--- /dev/null
+++ b/arch/frv/lib/memset.S
@@ -0,0 +1,182 @@
+/* memset.S: optimised assembly memset
+ *
+ * Copyright (C) 2003 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 License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+
+        .text
+        .p2align	4
+
+###############################################################################
+#
+# void *memset(void *p, char ch, size_t count)
+#
+# - NOTE: must not use any stack. exception detection performs function return
+#         to caller's fixup routine, aborting the remainder of the set
+#         GR4, GR7, GR8, and GR11 must be managed
+#
+###############################################################################
+        .globl		memset,__memset_end
+        .type		memset,@function
+memset:
+	orcc.p		gr10,gr0,gr5,icc3		; GR5 = count
+	andi		gr9,#0xff,gr9
+	or.p		gr8,gr0,gr4			; GR4 = address
+	beqlr		icc3,#0
+
+	# conditionally write a byte to 2b-align the address
+	setlos.p	#1,gr6
+	andicc		gr4,#1,gr0,icc0
+	ckne		icc0,cc7
+	cstb.p		gr9,@(gr4,gr0)		,cc7,#1
+	csubcc		gr5,gr6,gr5		,cc7,#1	; also set ICC3
+	cadd.p		gr4,gr6,gr4		,cc7,#1
+	beqlr		icc3,#0
+
+	# conditionally write a word to 4b-align the address
+	andicc.p	gr4,#2,gr0,icc0
+	subicc		gr5,#2,gr0,icc1
+	setlos.p	#2,gr6
+	ckne		icc0,cc7
+	slli.p		gr9,#8,gr12			; need to double up the pattern
+	cknc		icc1,cc5
+	or.p		gr9,gr12,gr12
+	andcr		cc7,cc5,cc7
+
+	csth.p		gr12,@(gr4,gr0)		,cc7,#1
+	csubcc		gr5,gr6,gr5		,cc7,#1	; also set ICC3
+	cadd.p		gr4,gr6,gr4		,cc7,#1
+	beqlr		icc3,#0
+
+	# conditionally write a dword to 8b-align the address
+	andicc.p	gr4,#4,gr0,icc0
+	subicc		gr5,#4,gr0,icc1
+	setlos.p	#4,gr6
+	ckne		icc0,cc7
+	slli.p		gr12,#16,gr13			; need to quadruple-up the pattern
+	cknc		icc1,cc5
+	or.p		gr13,gr12,gr12
+	andcr		cc7,cc5,cc7
+
+	cst.p		gr12,@(gr4,gr0)		,cc7,#1
+	csubcc		gr5,gr6,gr5		,cc7,#1	; also set ICC3
+	cadd.p		gr4,gr6,gr4		,cc7,#1
+	beqlr		icc3,#0
+
+	or.p		gr12,gr12,gr13			; need to octuple-up the pattern
+
+	# the address is now 8b-aligned - loop around writing 64b chunks
+	setlos		#8,gr7
+	subi.p		gr4,#8,gr4			; store with update index does weird stuff
+	setlos		#64,gr6
+
+	subicc		gr5,#64,gr0,icc0
+0:	cknc		icc0,cc7
+	cstdu		gr12,@(gr4,gr7)		,cc7,#1
+	cstdu		gr12,@(gr4,gr7)		,cc7,#1
+	cstdu		gr12,@(gr4,gr7)		,cc7,#1
+	cstdu		gr12,@(gr4,gr7)		,cc7,#1
+	cstdu		gr12,@(gr4,gr7)		,cc7,#1
+	cstdu.p		gr12,@(gr4,gr7)		,cc7,#1
+	csubcc		gr5,gr6,gr5		,cc7,#1	; also set ICC3
+	cstdu.p		gr12,@(gr4,gr7)		,cc7,#1
+	subicc		gr5,#64,gr0,icc0
+	cstdu.p		gr12,@(gr4,gr7)		,cc7,#1
+	beqlr		icc3,#0
+	bnc		icc0,#2,0b
+
+	# now do 32-byte remnant
+	subicc.p	gr5,#32,gr0,icc0
+	setlos		#32,gr6
+	cknc		icc0,cc7
+	cstdu.p		gr12,@(gr4,gr7)		,cc7,#1
+	csubcc		gr5,gr6,gr5		,cc7,#1	; also set ICC3
+	cstdu.p		gr12,@(gr4,gr7)		,cc7,#1
+	setlos		#16,gr6
+	cstdu.p		gr12,@(gr4,gr7)		,cc7,#1
+	subicc		gr5,#16,gr0,icc0
+	cstdu.p		gr12,@(gr4,gr7)		,cc7,#1
+	beqlr		icc3,#0
+
+	# now do 16-byte remnant
+	cknc		icc0,cc7
+	cstdu.p		gr12,@(gr4,gr7)		,cc7,#1
+	csubcc		gr5,gr6,gr5		,cc7,#1	; also set ICC3
+	cstdu.p		gr12,@(gr4,gr7)		,cc7,#1
+	beqlr		icc3,#0
+
+	# now do 8-byte remnant
+	subicc		gr5,#8,gr0,icc1
+	cknc		icc1,cc7
+	cstdu.p		gr12,@(gr4,gr7)		,cc7,#1
+	csubcc		gr5,gr7,gr5		,cc7,#1	; also set ICC3
+	setlos.p	#4,gr7
+	beqlr		icc3,#0
+
+	# now do 4-byte remnant
+	subicc		gr5,#4,gr0,icc0
+	addi.p		gr4,#4,gr4
+	cknc		icc0,cc7
+	cstu.p		gr12,@(gr4,gr7)		,cc7,#1
+	csubcc		gr5,gr7,gr5		,cc7,#1	; also set ICC3
+	subicc.p	gr5,#2,gr0,icc1
+	beqlr		icc3,#0
+
+	# now do 2-byte remnant
+	setlos		#2,gr7
+	addi.p		gr4,#2,gr4
+	cknc		icc1,cc7
+	csthu.p		gr12,@(gr4,gr7)		,cc7,#1
+	csubcc		gr5,gr7,gr5		,cc7,#1	; also set ICC3
+	subicc.p	gr5,#1,gr0,icc0
+	beqlr		icc3,#0
+
+	# now do 1-byte remnant
+	setlos		#0,gr7
+	addi.p		gr4,#2,gr4
+	cknc		icc0,cc7
+	cstb.p		gr12,@(gr4,gr0)		,cc7,#1
+	bralr
+__memset_end:
+
+	.size		memset, __memset_end-memset
+
+###############################################################################
+#
+# clear memory in userspace
+# - return the number of bytes that could not be cleared (0 on complete success)
+#
+# long __memset_user(void *p, size_t count)
+#
+###############################################################################
+        .globl		__memset_user, __memset_user_error_lr, __memset_user_error_handler
+        .type		__memset_user,@function
+__memset_user:
+	movsg		lr,gr11
+
+	# abuse memset to do the dirty work
+	or.p		gr9,gr9,gr10
+	setlos		#0,gr9
+	call		memset
+__memset_user_error_lr:
+	jmpl.p		@(gr11,gr0)
+	setlos		#0,gr8
+
+	# deal any exception generated by memset
+	# GR4  - memset's address tracking pointer
+	# GR7  - memset's step value (index register for store insns)
+	# GR8  - memset's original start address
+	# GR10 - memset's original count
+__memset_user_error_handler:
+	add.p		gr4,gr7,gr4
+	add		gr8,gr10,gr8
+	jmpl.p		@(gr11,gr0)
+	sub		gr8,gr4,gr8		; we return the amount left uncleared
+
+	.size		__memset_user, .-__memset_user
diff --git a/arch/frv/lib/outsl_ns.S b/arch/frv/lib/outsl_ns.S
new file mode 100644
index 0000000..4cd4c46
--- /dev/null
+++ b/arch/frv/lib/outsl_ns.S
@@ -0,0 +1,59 @@
+/* outsl_ns.S: output array of 4b words to device without byte swapping
+ *
+ * Copyright (C) 2003 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 License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+
+        .text
+        .p2align	4
+
+###############################################################################
+#
+# void __outsl_ns(unsigned int port, const void *buf, int n)
+#
+###############################################################################
+        .globl		__outsl_ns
+        .type		__outsl_ns,@function
+__outsl_ns:
+	andicc.p	gr9,#3,gr0,icc0
+	setlos		#4,gr4
+	bne		icc0,#0,__outsl_ns_misaligned
+	subi		gr9,#4,gr9
+0:
+	ldu.p		@(gr9,gr4),gr5
+	subicc		gr10,#1,gr10,icc0
+	sti.p		gr5,@(gr8,#0)
+	bhi		icc0,#2,0b
+
+	membar
+	bralr
+
+__outsl_ns_misaligned:
+	subi.p		gr9,#1,gr9
+	setlos		#1,gr4
+0:
+	ldubu		@(gr9,gr4),gr5
+	ldubu.p		@(gr9,gr4),gr6
+	slli		gr5,#8,gr5
+	ldubu.p		@(gr9,gr4),gr7
+	or		gr5,gr6,gr5
+	ldubu.p		@(gr9,gr4),gr6
+	slli		gr5,#16,gr5
+	slli.p		gr7,#8,gr7
+	or		gr5,gr6,gr5
+	subicc.p	gr10,#1,gr10,icc0
+	or		gr5,gr7,gr5
+
+	sti.p		gr5,@(gr8,#0)
+	bhi		icc0,#2,0b
+
+	membar
+	bralr
+
+	.size		__outsl_ns, .-__outsl_ns
diff --git a/arch/frv/lib/outsl_sw.S b/arch/frv/lib/outsl_sw.S
new file mode 100644
index 0000000..7eb56d3
--- /dev/null
+++ b/arch/frv/lib/outsl_sw.S
@@ -0,0 +1,45 @@
+/* outsl_ns.S: output array of 4b words to device with byte swapping
+ *
+ * Copyright (C) 2003 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 License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+
+        .text
+        .p2align	4
+
+###############################################################################
+#
+# void __outsl_sw(unsigned int port, const void *buf, int n)
+#
+###############################################################################
+        .globl		__outsl_sw
+        .type		__outsl_sw,@function
+__outsl_sw:
+	subi.p		gr9,#1,gr9
+	setlos		#1,gr4
+0:
+	ldubu		@(gr9,gr4),gr5
+	ldubu		@(gr9,gr4),gr6
+	slli		gr6,#8,gr6
+	ldubu.p		@(gr9,gr4),gr7
+	or		gr5,gr6,gr5
+	ldubu.p		@(gr9,gr4),gr6
+	slli		gr7,#16,gr7
+	slli.p		gr6,#24,gr6
+	or		gr5,gr7,gr5
+	subicc.p	gr10,#1,gr10,icc0
+	or		gr5,gr6,gr5
+
+	sti.p		gr5,@(gr8,#0)
+	bhi		icc0,#2,0b
+
+	membar
+	bralr
+
+	.size		__outsl_sw, .-__outsl_sw
diff --git a/arch/frv/mb93090-mb00/Makefile b/arch/frv/mb93090-mb00/Makefile
new file mode 100644
index 0000000..3faf0f8
--- /dev/null
+++ b/arch/frv/mb93090-mb00/Makefile
@@ -0,0 +1,13 @@
+#
+# Makefile for the MB93090-MB00 motherboard stuff
+#
+
+ifeq "$(CONFIG_PCI)" "y"
+obj-y := pci-frv.o pci-irq.o pci-vdk.o
+
+ifeq "$(CONFIG_MMU)" "y"
+obj-y += pci-dma.o
+else
+obj-y += pci-dma-nommu.o
+endif
+endif
diff --git a/arch/frv/mb93090-mb00/pci-dma-nommu.c b/arch/frv/mb93090-mb00/pci-dma-nommu.c
new file mode 100644
index 0000000..819895c
--- /dev/null
+++ b/arch/frv/mb93090-mb00/pci-dma-nommu.c
@@ -0,0 +1,152 @@
+/* pci-dma-nommu.c: Dynamic DMA mapping support for the FRV
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Woodhouse (dwmw2@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <linux/list.h>
+#include <linux/pci.h>
+#include <asm/io.h>
+
+#if 1
+#define DMA_SRAM_START	dma_coherent_mem_start
+#define DMA_SRAM_END	dma_coherent_mem_end
+#else // Use video RAM on Matrox
+#define DMA_SRAM_START	0xe8900000
+#define DMA_SRAM_END	0xe8a00000
+#endif
+
+struct dma_alloc_record {
+	struct list_head	list;
+	unsigned long		ofs;
+	unsigned long		len;
+};
+
+static DEFINE_SPINLOCK(dma_alloc_lock);
+static LIST_HEAD(dma_alloc_list);
+
+void *dma_alloc_coherent(struct device *hwdev, size_t size, dma_addr_t *dma_handle, int gfp)
+{
+	struct dma_alloc_record *new;
+	struct list_head *this = &dma_alloc_list;
+	unsigned long flags;
+	unsigned long start = DMA_SRAM_START;
+	unsigned long end;
+
+	if (!DMA_SRAM_START) {
+		printk("%s called without any DMA area reserved!\n", __func__);
+		return NULL;
+	}
+
+	new = kmalloc(sizeof (*new), GFP_ATOMIC);
+	if (!new)
+		return NULL;
+
+	/* Round up to a reasonable alignment */
+	new->len = (size + 31) & ~31;
+
+	spin_lock_irqsave(&dma_alloc_lock, flags);
+
+	list_for_each (this, &dma_alloc_list) {
+		struct dma_alloc_record *this_r = list_entry(this, struct dma_alloc_record, list);
+		end = this_r->ofs;
+
+		if (end - start >= size)
+			goto gotone;
+
+		start = this_r->ofs + this_r->len;
+	}
+	/* Reached end of list. */
+	end = DMA_SRAM_END;
+	this = &dma_alloc_list;
+
+	if (end - start >= size) {
+	gotone:
+		new->ofs = start;
+		list_add_tail(&new->list, this);
+		spin_unlock_irqrestore(&dma_alloc_lock, flags);
+
+		*dma_handle = start;
+		return (void *)start;
+	}
+
+	kfree(new);
+	spin_unlock_irqrestore(&dma_alloc_lock, flags);
+	return NULL;
+}
+
+void dma_free_coherent(struct device *hwdev, size_t size, void *vaddr, dma_addr_t dma_handle)
+{
+	struct dma_alloc_record *rec;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dma_alloc_lock, flags);
+
+	list_for_each_entry(rec, &dma_alloc_list, list) {
+		if (rec->ofs == dma_handle) {
+			list_del(&rec->list);
+			kfree(rec);
+			spin_unlock_irqrestore(&dma_alloc_lock, flags);
+			return;
+		}
+	}
+	spin_unlock_irqrestore(&dma_alloc_lock, flags);
+	BUG();
+}
+
+/*
+ * Map a single buffer of the indicated size for DMA in streaming mode.
+ * The 32-bit bus address to use is returned.
+ *
+ * Once the device is given the dma address, the device owns this memory
+ * until either pci_unmap_single or pci_dma_sync_single is performed.
+ */
+dma_addr_t dma_map_single(struct device *dev, void *ptr, size_t size,
+			  enum dma_data_direction direction)
+{
+	if (direction == DMA_NONE)
+                BUG();
+
+	frv_cache_wback_inv((unsigned long) ptr, (unsigned long) ptr + size);
+
+	return virt_to_bus(ptr);
+}
+
+/*
+ * Map a set of buffers described by scatterlist in streaming
+ * mode for DMA.  This is the scather-gather version of the
+ * above pci_map_single interface.  Here the scatter gather list
+ * elements are each tagged with the appropriate dma address
+ * and length.  They are obtained via sg_dma_{address,length}(SG).
+ *
+ * NOTE: An implementation may be able to use a smaller number of
+ *       DMA address/length pairs than there are SG table elements.
+ *       (for example via virtual mapping capabilities)
+ *       The routine returns the number of addr/length pairs actually
+ *       used, at most nents.
+ *
+ * Device ownership issues as mentioned above for pci_map_single are
+ * the same here.
+ */
+int dma_map_sg(struct device *dev, struct scatterlist *sg, int nents,
+	       enum dma_data_direction direction)
+{
+	int i;
+
+	for (i=0; i<nents; i++)
+		frv_cache_wback_inv(sg_dma_address(&sg[i]),
+				    sg_dma_address(&sg[i]) + sg_dma_len(&sg[i]));
+
+	if (direction == DMA_NONE)
+                BUG();
+
+	return nents;
+}
diff --git a/arch/frv/mb93090-mb00/pci-dma.c b/arch/frv/mb93090-mb00/pci-dma.c
new file mode 100644
index 0000000..27eb120
--- /dev/null
+++ b/arch/frv/mb93090-mb00/pci-dma.c
@@ -0,0 +1,105 @@
+/* pci-dma.c: Dynamic DMA mapping support for the FRV CPUs that have MMUs
+ *
+ * Copyright (C) 2004 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 License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <linux/list.h>
+#include <linux/pci.h>
+#include <linux/highmem.h>
+#include <asm/io.h>
+
+void *dma_alloc_coherent(struct device *hwdev, size_t size, dma_addr_t *dma_handle, int gfp)
+{
+	void *ret;
+
+	ret = consistent_alloc(gfp, size, dma_handle);
+	if (ret)
+		memset(ret, 0, size);
+
+	return ret;
+}
+
+void dma_free_coherent(struct device *hwdev, size_t size, void *vaddr, dma_addr_t dma_handle)
+{
+	consistent_free(vaddr);
+}
+
+/*
+ * Map a single buffer of the indicated size for DMA in streaming mode.
+ * The 32-bit bus address to use is returned.
+ *
+ * Once the device is given the dma address, the device owns this memory
+ * until either pci_unmap_single or pci_dma_sync_single is performed.
+ */
+dma_addr_t dma_map_single(struct device *dev, void *ptr, size_t size,
+			  enum dma_data_direction direction)
+{
+	if (direction == DMA_NONE)
+                BUG();
+
+	frv_cache_wback_inv((unsigned long) ptr, (unsigned long) ptr + size);
+
+	return virt_to_bus(ptr);
+}
+
+/*
+ * Map a set of buffers described by scatterlist in streaming
+ * mode for DMA.  This is the scather-gather version of the
+ * above pci_map_single interface.  Here the scatter gather list
+ * elements are each tagged with the appropriate dma address
+ * and length.  They are obtained via sg_dma_{address,length}(SG).
+ *
+ * NOTE: An implementation may be able to use a smaller number of
+ *       DMA address/length pairs than there are SG table elements.
+ *       (for example via virtual mapping capabilities)
+ *       The routine returns the number of addr/length pairs actually
+ *       used, at most nents.
+ *
+ * Device ownership issues as mentioned above for pci_map_single are
+ * the same here.
+ */
+int dma_map_sg(struct device *dev, struct scatterlist *sg, int nents,
+	       enum dma_data_direction direction)
+{
+	unsigned long dampr2;
+	void *vaddr;
+	int i;
+
+	if (direction == DMA_NONE)
+                BUG();
+
+	dampr2 = __get_DAMPR(2);
+
+	for (i = 0; i < nents; i++) {
+		vaddr = kmap_atomic(sg[i].page, __KM_CACHE);
+
+		frv_dcache_writeback((unsigned long) vaddr,
+				     (unsigned long) vaddr + PAGE_SIZE);
+
+	}
+
+	kunmap_atomic(vaddr, __KM_CACHE);
+	if (dampr2) {
+		__set_DAMPR(2, dampr2);
+		__set_IAMPR(2, dampr2);
+	}
+
+	return nents;
+}
+
+dma_addr_t dma_map_page(struct device *dev, struct page *page, unsigned long offset,
+			size_t size, enum dma_data_direction direction)
+{
+	BUG_ON(direction == DMA_NONE);
+	flush_dcache_page(page);
+	return (dma_addr_t) page_to_phys(page) + offset;
+}
diff --git a/arch/frv/mb93090-mb00/pci-frv.c b/arch/frv/mb93090-mb00/pci-frv.c
new file mode 100644
index 0000000..83e5489
--- /dev/null
+++ b/arch/frv/mb93090-mb00/pci-frv.c
@@ -0,0 +1,288 @@
+/* pci-frv.c: low-level PCI access routines
+ *
+ * Copyright (C) 2003-5 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ * - Derived from the i386 equivalent stuff
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/errno.h>
+
+#include "pci-frv.h"
+
+#if 0
+void
+pcibios_update_resource(struct pci_dev *dev, struct resource *root,
+			struct resource *res, int resource)
+{
+	u32 new, check;
+	int reg;
+
+	new = res->start | (res->flags & PCI_REGION_FLAG_MASK);
+	if (resource < 6) {
+		reg = PCI_BASE_ADDRESS_0 + 4*resource;
+	} else if (resource == PCI_ROM_RESOURCE) {
+		res->flags |= IORESOURCE_ROM_ENABLE;
+		new |= PCI_ROM_ADDRESS_ENABLE;
+		reg = dev->rom_base_reg;
+	} else {
+		/* Somebody might have asked allocation of a non-standard resource */
+		return;
+	}
+
+	pci_write_config_dword(dev, reg, new);
+	pci_read_config_dword(dev, reg, &check);
+	if ((new ^ check) & ((new & PCI_BASE_ADDRESS_SPACE_IO) ? PCI_BASE_ADDRESS_IO_MASK : PCI_BASE_ADDRESS_MEM_MASK)) {
+		printk(KERN_ERR "PCI: Error while updating region "
+		       "%s/%d (%08x != %08x)\n", pci_name(dev), resource,
+		       new, check);
+	}
+}
+#endif
+
+/*
+ * We need to avoid collisions with `mirrored' VGA ports
+ * and other strange ISA hardware, so we always want the
+ * addresses to be allocated in the 0x000-0x0ff region
+ * modulo 0x400.
+ *
+ * Why? Because some silly external IO cards only decode
+ * the low 10 bits of the IO address. The 0x00-0xff region
+ * is reserved for motherboard devices that decode all 16
+ * bits, so it's ok to allocate at, say, 0x2800-0x28ff,
+ * but we want to try to avoid allocating at 0x2900-0x2bff
+ * which might have be mirrored at 0x0100-0x03ff..
+ */
+void
+pcibios_align_resource(void *data, struct resource *res,
+		       unsigned long size, unsigned long align)
+{
+	if (res->flags & IORESOURCE_IO) {
+		unsigned long start = res->start;
+
+		if (start & 0x300) {
+			start = (start + 0x3ff) & ~0x3ff;
+			res->start = start;
+		}
+	}
+}
+
+
+/*
+ *  Handle resources of PCI devices.  If the world were perfect, we could
+ *  just allocate all the resource regions and do nothing more.  It isn't.
+ *  On the other hand, we cannot just re-allocate all devices, as it would
+ *  require us to know lots of host bridge internals.  So we attempt to
+ *  keep as much of the original configuration as possible, but tweak it
+ *  when it's found to be wrong.
+ *
+ *  Known BIOS problems we have to work around:
+ *	- I/O or memory regions not configured
+ *	- regions configured, but not enabled in the command register
+ *	- bogus I/O addresses above 64K used
+ *	- expansion ROMs left enabled (this may sound harmless, but given
+ *	  the fact the PCI specs explicitly allow address decoders to be
+ *	  shared between expansion ROMs and other resource regions, it's
+ *	  at least dangerous)
+ *
+ *  Our solution:
+ *	(1) Allocate resources for all buses behind PCI-to-PCI bridges.
+ *	    This gives us fixed barriers on where we can allocate.
+ *	(2) Allocate resources for all enabled devices.  If there is
+ *	    a collision, just mark the resource as unallocated. Also
+ *	    disable expansion ROMs during this step.
+ *	(3) Try to allocate resources for disabled devices.  If the
+ *	    resources were assigned correctly, everything goes well,
+ *	    if they weren't, they won't disturb allocation of other
+ *	    resources.
+ *	(4) Assign new addresses to resources which were either
+ *	    not configured at all or misconfigured.  If explicitly
+ *	    requested by the user, configure expansion ROM address
+ *	    as well.
+ */
+
+static void __init pcibios_allocate_bus_resources(struct list_head *bus_list)
+{
+	struct list_head *ln;
+	struct pci_bus *bus;
+	struct pci_dev *dev;
+	int idx;
+	struct resource *r, *pr;
+
+	/* Depth-First Search on bus tree */
+	for (ln=bus_list->next; ln != bus_list; ln=ln->next) {
+		bus = pci_bus_b(ln);
+		if ((dev = bus->self)) {
+			for (idx = PCI_BRIDGE_RESOURCES; idx < PCI_NUM_RESOURCES; idx++) {
+				r = &dev->resource[idx];
+				if (!r->start)
+					continue;
+				pr = pci_find_parent_resource(dev, r);
+				if (!pr || request_resource(pr, r) < 0)
+					printk(KERN_ERR "PCI: Cannot allocate resource region %d of bridge %s\n", idx, pci_name(dev));
+			}
+		}
+		pcibios_allocate_bus_resources(&bus->children);
+	}
+}
+
+static void __init pcibios_allocate_resources(int pass)
+{
+	struct pci_dev *dev = NULL;
+	int idx, disabled;
+	u16 command;
+	struct resource *r, *pr;
+
+	while (dev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, dev),
+	       dev != NULL
+	       ) {
+		pci_read_config_word(dev, PCI_COMMAND, &command);
+		for(idx = 0; idx < 6; idx++) {
+			r = &dev->resource[idx];
+			if (r->parent)		/* Already allocated */
+				continue;
+			if (!r->start)		/* Address not assigned at all */
+				continue;
+			if (r->flags & IORESOURCE_IO)
+				disabled = !(command & PCI_COMMAND_IO);
+			else
+				disabled = !(command & PCI_COMMAND_MEMORY);
+			if (pass == disabled) {
+				DBG("PCI: Resource %08lx-%08lx (f=%lx, d=%d, p=%d)\n",
+				    r->start, r->end, r->flags, disabled, pass);
+				pr = pci_find_parent_resource(dev, r);
+				if (!pr || request_resource(pr, r) < 0) {
+					printk(KERN_ERR "PCI: Cannot allocate resource region %d of device %s\n", idx, pci_name(dev));
+					/* We'll assign a new address later */
+					r->end -= r->start;
+					r->start = 0;
+				}
+			}
+		}
+		if (!pass) {
+			r = &dev->resource[PCI_ROM_RESOURCE];
+			if (r->flags & IORESOURCE_ROM_ENABLE) {
+				/* Turn the ROM off, leave the resource region, but keep it unregistered. */
+				u32 reg;
+				DBG("PCI: Switching off ROM of %s\n", pci_name(dev));
+				r->flags &= ~IORESOURCE_ROM_ENABLE;
+				pci_read_config_dword(dev, dev->rom_base_reg, &reg);
+				pci_write_config_dword(dev, dev->rom_base_reg, reg & ~PCI_ROM_ADDRESS_ENABLE);
+			}
+		}
+	}
+}
+
+static void __init pcibios_assign_resources(void)
+{
+	struct pci_dev *dev = NULL;
+	int idx;
+	struct resource *r;
+
+	while (dev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, dev),
+	       dev != NULL
+	       ) {
+		int class = dev->class >> 8;
+
+		/* Don't touch classless devices and host bridges */
+		if (!class || class == PCI_CLASS_BRIDGE_HOST)
+			continue;
+
+		for(idx=0; idx<6; idx++) {
+			r = &dev->resource[idx];
+
+			/*
+			 *  Don't touch IDE controllers and I/O ports of video cards!
+			 */
+			if ((class == PCI_CLASS_STORAGE_IDE && idx < 4) ||
+			    (class == PCI_CLASS_DISPLAY_VGA && (r->flags & IORESOURCE_IO)))
+				continue;
+
+			/*
+			 *  We shall assign a new address to this resource, either because
+			 *  the BIOS forgot to do so or because we have decided the old
+			 *  address was unusable for some reason.
+			 */
+			if (!r->start && r->end)
+				pci_assign_resource(dev, idx);
+		}
+
+		if (pci_probe & PCI_ASSIGN_ROMS) {
+			r = &dev->resource[PCI_ROM_RESOURCE];
+			r->end -= r->start;
+			r->start = 0;
+			if (r->end)
+				pci_assign_resource(dev, PCI_ROM_RESOURCE);
+		}
+	}
+}
+
+void __init pcibios_resource_survey(void)
+{
+	DBG("PCI: Allocating resources\n");
+	pcibios_allocate_bus_resources(&pci_root_buses);
+	pcibios_allocate_resources(0);
+	pcibios_allocate_resources(1);
+	pcibios_assign_resources();
+}
+
+int pcibios_enable_resources(struct pci_dev *dev, int mask)
+{
+	u16 cmd, old_cmd;
+	int idx;
+	struct resource *r;
+
+	pci_read_config_word(dev, PCI_COMMAND, &cmd);
+	old_cmd = cmd;
+	for(idx=0; idx<6; idx++) {
+		/* Only set up the requested stuff */
+		if (!(mask & (1<<idx)))
+			continue;
+
+		r = &dev->resource[idx];
+		if (!r->start && r->end) {
+			printk(KERN_ERR "PCI: Device %s not available because of resource collisions\n", pci_name(dev));
+			return -EINVAL;
+		}
+		if (r->flags & IORESOURCE_IO)
+			cmd |= PCI_COMMAND_IO;
+		if (r->flags & IORESOURCE_MEM)
+			cmd |= PCI_COMMAND_MEMORY;
+	}
+	if (dev->resource[PCI_ROM_RESOURCE].start)
+		cmd |= PCI_COMMAND_MEMORY;
+	if (cmd != old_cmd) {
+		printk("PCI: Enabling device %s (%04x -> %04x)\n", pci_name(dev), old_cmd, cmd);
+		pci_write_config_word(dev, PCI_COMMAND, cmd);
+	}
+	return 0;
+}
+
+/*
+ *  If we set up a device for bus mastering, we need to check the latency
+ *  timer as certain crappy BIOSes forget to set it properly.
+ */
+unsigned int pcibios_max_latency = 255;
+
+void pcibios_set_master(struct pci_dev *dev)
+{
+	u8 lat;
+	pci_read_config_byte(dev, PCI_LATENCY_TIMER, &lat);
+	if (lat < 16)
+		lat = (64 <= pcibios_max_latency) ? 64 : pcibios_max_latency;
+	else if (lat > pcibios_max_latency)
+		lat = pcibios_max_latency;
+	else
+		return;
+	printk(KERN_DEBUG "PCI: Setting latency timer of device %s to %d\n", pci_name(dev), lat);
+	pci_write_config_byte(dev, PCI_LATENCY_TIMER, lat);
+}
diff --git a/arch/frv/mb93090-mb00/pci-frv.h b/arch/frv/mb93090-mb00/pci-frv.h
new file mode 100644
index 0000000..7481797
--- /dev/null
+++ b/arch/frv/mb93090-mb00/pci-frv.h
@@ -0,0 +1,47 @@
+/*
+ *	Low-Level PCI Access for FRV machines.
+ *
+ *	(c) 1999 Martin Mares <mj@ucw.cz>
+ */
+
+#include <asm/sections.h>
+
+#undef DEBUG
+
+#ifdef DEBUG
+#define DBG(x...) printk(x)
+#else
+#define DBG(x...)
+#endif
+
+#define PCI_PROBE_BIOS		0x0001
+#define PCI_PROBE_CONF1		0x0002
+#define PCI_PROBE_CONF2		0x0004
+#define PCI_NO_SORT		0x0100
+#define PCI_BIOS_SORT		0x0200
+#define PCI_NO_CHECKS		0x0400
+#define PCI_ASSIGN_ROMS		0x1000
+#define PCI_BIOS_IRQ_SCAN	0x2000
+#define PCI_ASSIGN_ALL_BUSSES	0x4000
+
+extern unsigned int __nongpreldata pci_probe;
+
+/* pci-frv.c */
+
+extern unsigned int pcibios_max_latency;
+
+void pcibios_resource_survey(void);
+int pcibios_enable_resources(struct pci_dev *, int);
+
+/* pci-vdk.c */
+
+extern int __nongpreldata pcibios_last_bus;
+extern struct pci_bus *__nongpreldata pci_root_bus;
+extern struct pci_ops *__nongpreldata pci_root_ops;
+
+/* pci-irq.c */
+extern unsigned int pcibios_irq_mask;
+
+void pcibios_irq_init(void);
+void pcibios_fixup_irqs(void);
+void pcibios_enable_irq(struct pci_dev *dev);
diff --git a/arch/frv/mb93090-mb00/pci-irq.c b/arch/frv/mb93090-mb00/pci-irq.c
new file mode 100644
index 0000000..24622d8
--- /dev/null
+++ b/arch/frv/mb93090-mb00/pci-irq.c
@@ -0,0 +1,70 @@
+/* pci-irq.c: PCI IRQ routing on the FRV motherboard
+ *
+ * Copyright (C) 2003 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ * derived from: arch/i386/kernel/pci-irq.c: (c) 1999--2000 Martin Mares <mj@suse.cz>
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+
+#include <asm/io.h>
+#include <asm/smp.h>
+#include <asm/irq-routing.h>
+
+#include "pci-frv.h"
+
+/*
+ *	DEVICE	DEVNO	INT#A	INT#B	INT#C	INT#D
+ *	=======	=======	=======	=======	=======	=======
+ *	MB86943	0	fpga.10	-	-	-
+ *	RTL8029	16	fpga.12	-	-	-
+ *	SLOT 1	19	fpga.6	fpga.5	fpga.4	fpga.3
+ *	SLOT 2	18	fpga.5	fpga.4	fpga.3	fpga.6
+ *	SLOT 3	17	fpga.4	fpga.3	fpga.6	fpga.5
+ *
+ */
+
+static const uint8_t __initdata pci_bus0_irq_routing[32][4] = {
+	[0 ] {	IRQ_FPGA_MB86943_PCI_INTA },
+	[16] {	IRQ_FPGA_RTL8029_INTA },
+	[17] {	IRQ_FPGA_PCI_INTC, IRQ_FPGA_PCI_INTD, IRQ_FPGA_PCI_INTA, IRQ_FPGA_PCI_INTB },
+	[18] {	IRQ_FPGA_PCI_INTB, IRQ_FPGA_PCI_INTC, IRQ_FPGA_PCI_INTD, IRQ_FPGA_PCI_INTA },
+	[19] {	IRQ_FPGA_PCI_INTA, IRQ_FPGA_PCI_INTB, IRQ_FPGA_PCI_INTC, IRQ_FPGA_PCI_INTD },
+};
+
+void __init pcibios_irq_init(void)
+{
+}
+
+void __init pcibios_fixup_irqs(void)
+{
+	struct pci_dev *dev = NULL;
+	uint8_t line, pin;
+
+	while (dev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, dev),
+	       dev != NULL
+	       ) {
+		pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin);
+		if (pin) {
+			dev->irq = pci_bus0_irq_routing[PCI_SLOT(dev->devfn)][pin - 1];
+			pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq);
+		}
+		pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &line);
+	}
+}
+
+void __init pcibios_penalize_isa_irq(int irq)
+{
+}
+
+void pcibios_enable_irq(struct pci_dev *dev)
+{
+	pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq);
+}
diff --git a/arch/frv/mb93090-mb00/pci-vdk.c b/arch/frv/mb93090-mb00/pci-vdk.c
new file mode 100644
index 0000000..c8817f7
--- /dev/null
+++ b/arch/frv/mb93090-mb00/pci-vdk.c
@@ -0,0 +1,467 @@
+/* pci-vdk.c: MB93090-MB00 (VDK) PCI support
+ *
+ * Copyright (C) 2003, 2004 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 License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+
+#include <asm/segment.h>
+#include <asm/io.h>
+#include <asm/mb-regs.h>
+#include <asm/mb86943a.h>
+#include "pci-frv.h"
+
+unsigned int __nongpreldata pci_probe = 1;
+
+int  __nongpreldata pcibios_last_bus = -1;
+struct pci_bus *__nongpreldata pci_root_bus;
+struct pci_ops *__nongpreldata pci_root_ops;
+
+/*
+ * Functions for accessing PCI configuration space
+ */
+
+#define CONFIG_CMD(bus, dev, where) \
+	(0x80000000 | (bus->number << 16) | (devfn << 8) | (where & ~3))
+
+#define __set_PciCfgAddr(A) writel((A), (volatile void __iomem *) __region_CS1 + 0x80)
+
+#define __get_PciCfgDataB(A) readb((volatile void __iomem *) __region_CS1 + 0x88 + ((A) & 3))
+#define __get_PciCfgDataW(A) readw((volatile void __iomem *) __region_CS1 + 0x88 + ((A) & 2))
+#define __get_PciCfgDataL(A) readl((volatile void __iomem *) __region_CS1 + 0x88)
+
+#define __set_PciCfgDataB(A,V) \
+	writeb((V), (volatile void __iomem *) __region_CS1 + 0x88 + (3 - ((A) & 3)))
+
+#define __set_PciCfgDataW(A,V) \
+	writew((V), (volatile void __iomem *) __region_CS1 + 0x88 + (2 - ((A) & 2)))
+
+#define __set_PciCfgDataL(A,V) \
+	writel((V), (volatile void __iomem *) __region_CS1 + 0x88)
+
+#define __get_PciBridgeDataB(A) readb((volatile void __iomem *) __region_CS1 + 0x800 + (A))
+#define __get_PciBridgeDataW(A) readw((volatile void __iomem *) __region_CS1 + 0x800 + (A))
+#define __get_PciBridgeDataL(A) readl((volatile void __iomem *) __region_CS1 + 0x800 + (A))
+
+#define __set_PciBridgeDataB(A,V) writeb((V), (volatile void __iomem *) __region_CS1 + 0x800 + (A))
+#define __set_PciBridgeDataW(A,V) writew((V), (volatile void __iomem *) __region_CS1 + 0x800 + (A))
+#define __set_PciBridgeDataL(A,V) writel((V), (volatile void __iomem *) __region_CS1 + 0x800 + (A))
+
+static inline int __query(const struct pci_dev *dev)
+{
+//	return dev->bus->number==0 && (dev->devfn==PCI_DEVFN(0,0));
+//	return dev->bus->number==1;
+//	return dev->bus->number==0 &&
+//		(dev->devfn==PCI_DEVFN(2,0) || dev->devfn==PCI_DEVFN(3,0));
+	return 0;
+}
+
+/*****************************************************************************/
+/*
+ *
+ */
+static int pci_frv_read_config(struct pci_bus *bus, unsigned int devfn, int where, int size,
+			       u32 *val)
+{
+	u32 _value;
+
+	if (bus->number == 0 && devfn == PCI_DEVFN(0, 0)) {
+		_value = __get_PciBridgeDataL(where & ~3);
+	}
+	else {
+		__set_PciCfgAddr(CONFIG_CMD(bus, devfn, where));
+		_value = __get_PciCfgDataL(where & ~3);
+	}
+
+	switch (size) {
+	case 1:
+		_value = _value >> ((where & 3) * 8);
+		break;
+
+	case 2:
+		_value = _value >> ((where & 2) * 8);
+		break;
+
+	case 4:
+		break;
+
+	default:
+		BUG();
+	}
+
+	*val = _value;
+	return PCIBIOS_SUCCESSFUL;
+}
+
+static int pci_frv_write_config(struct pci_bus *bus, unsigned int devfn, int where, int size,
+				u32 value)
+{
+	switch (size) {
+	case 1:
+		if (bus->number == 0 && devfn == PCI_DEVFN(0, 0)) {
+			__set_PciBridgeDataB(where, value);
+		}
+		else {
+			__set_PciCfgAddr(CONFIG_CMD(bus, devfn, where));
+			__set_PciCfgDataB(where, value);
+		}
+		break;
+
+	case 2:
+		if (bus->number == 0 && devfn == PCI_DEVFN(0, 0)) {
+			__set_PciBridgeDataW(where, value);
+		}
+		else {
+			__set_PciCfgAddr(CONFIG_CMD(bus, devfn, where));
+			__set_PciCfgDataW(where, value);
+		}
+		break;
+
+	case 4:
+		if (bus->number == 0 && devfn == PCI_DEVFN(0, 0)) {
+			__set_PciBridgeDataL(where, value);
+		}
+		else {
+			__set_PciCfgAddr(CONFIG_CMD(bus, devfn, where));
+			__set_PciCfgDataL(where, value);
+		}
+		break;
+
+	default:
+		BUG();
+	}
+
+	return PCIBIOS_SUCCESSFUL;
+}
+
+static struct pci_ops pci_direct_frv = {
+	pci_frv_read_config,
+	pci_frv_write_config,
+};
+
+/*
+ * Before we decide to use direct hardware access mechanisms, we try to do some
+ * trivial checks to ensure it at least _seems_ to be working -- we just test
+ * whether bus 00 contains a host bridge (this is similar to checking
+ * techniques used in XFree86, but ours should be more reliable since we
+ * attempt to make use of direct access hints provided by the PCI BIOS).
+ *
+ * This should be close to trivial, but it isn't, because there are buggy
+ * chipsets (yes, you guessed it, by Intel and Compaq) that have no class ID.
+ */
+static int __init pci_sanity_check(struct pci_ops *o)
+{
+	struct pci_bus bus;		/* Fake bus and device */
+	u32 id;
+
+	bus.number	= 0;
+
+	if (o->read(&bus, 0, PCI_VENDOR_ID, 4, &id) == PCIBIOS_SUCCESSFUL) {
+		printk("PCI: VDK Bridge device:vendor: %08x\n", id);
+		if (id == 0x200e10cf)
+			return 1;
+	}
+
+	printk("PCI: VDK Bridge: Sanity check failed\n");
+	return 0;
+}
+
+static struct pci_ops * __init pci_check_direct(void)
+{
+	unsigned long flags;
+
+	local_irq_save(flags);
+
+	/* check if access works */
+	if (pci_sanity_check(&pci_direct_frv)) {
+		local_irq_restore(flags);
+		printk("PCI: Using configuration frv\n");
+//		request_mem_region(0xBE040000, 256, "FRV bridge");
+//		request_mem_region(0xBFFFFFF4, 12, "PCI frv");
+		return &pci_direct_frv;
+	}
+
+	local_irq_restore(flags);
+	return NULL;
+}
+
+/*
+ * Several buggy motherboards address only 16 devices and mirror
+ * them to next 16 IDs. We try to detect this `feature' on all
+ * primary buses (those containing host bridges as they are
+ * expected to be unique) and remove the ghost devices.
+ */
+
+static void __init pcibios_fixup_ghosts(struct pci_bus *b)
+{
+	struct list_head *ln, *mn;
+	struct pci_dev *d, *e;
+	int mirror = PCI_DEVFN(16,0);
+	int seen_host_bridge = 0;
+	int i;
+
+	for (ln=b->devices.next; ln != &b->devices; ln=ln->next) {
+		d = pci_dev_b(ln);
+		if ((d->class >> 8) == PCI_CLASS_BRIDGE_HOST)
+			seen_host_bridge++;
+		for (mn=ln->next; mn != &b->devices; mn=mn->next) {
+			e = pci_dev_b(mn);
+			if (e->devfn != d->devfn + mirror ||
+			    e->vendor != d->vendor ||
+			    e->device != d->device ||
+			    e->class != d->class)
+				continue;
+			for(i=0; i<PCI_NUM_RESOURCES; i++)
+				if (e->resource[i].start != d->resource[i].start ||
+				    e->resource[i].end != d->resource[i].end ||
+				    e->resource[i].flags != d->resource[i].flags)
+					continue;
+			break;
+		}
+		if (mn == &b->devices)
+			return;
+	}
+	if (!seen_host_bridge)
+		return;
+	printk("PCI: Ignoring ghost devices on bus %02x\n", b->number);
+
+	ln = &b->devices;
+	while (ln->next != &b->devices) {
+		d = pci_dev_b(ln->next);
+		if (d->devfn >= mirror) {
+			list_del(&d->global_list);
+			list_del(&d->bus_list);
+			kfree(d);
+		} else
+			ln = ln->next;
+	}
+}
+
+/*
+ * Discover remaining PCI buses in case there are peer host bridges.
+ * We use the number of last PCI bus provided by the PCI BIOS.
+ */
+static void __init pcibios_fixup_peer_bridges(void)
+{
+	struct pci_bus bus;
+	struct pci_dev dev;
+	int n;
+	u16 l;
+
+	if (pcibios_last_bus <= 0 || pcibios_last_bus >= 0xff)
+		return;
+	printk("PCI: Peer bridge fixup\n");
+	for (n=0; n <= pcibios_last_bus; n++) {
+		if (pci_find_bus(0, n))
+			continue;
+		bus.number = n;
+		bus.ops = pci_root_ops;
+		dev.bus = &bus;
+		for(dev.devfn=0; dev.devfn<256; dev.devfn += 8)
+			if (!pci_read_config_word(&dev, PCI_VENDOR_ID, &l) &&
+			    l != 0x0000 && l != 0xffff) {
+				printk("Found device at %02x:%02x [%04x]\n", n, dev.devfn, l);
+				printk("PCI: Discovered peer bus %02x\n", n);
+				pci_scan_bus(n, pci_root_ops, NULL);
+				break;
+			}
+	}
+}
+
+/*
+ * Exceptions for specific devices. Usually work-arounds for fatal design flaws.
+ */
+
+static void __init pci_fixup_umc_ide(struct pci_dev *d)
+{
+	/*
+	 * UM8886BF IDE controller sets region type bits incorrectly,
+	 * therefore they look like memory despite of them being I/O.
+	 */
+	int i;
+
+	printk("PCI: Fixing base address flags for device %s\n", pci_name(d));
+	for(i=0; i<4; i++)
+		d->resource[i].flags |= PCI_BASE_ADDRESS_SPACE_IO;
+}
+
+static void __init pci_fixup_ide_bases(struct pci_dev *d)
+{
+	int i;
+
+	/*
+	 * PCI IDE controllers use non-standard I/O port decoding, respect it.
+	 */
+	if ((d->class >> 8) != PCI_CLASS_STORAGE_IDE)
+		return;
+	printk("PCI: IDE base address fixup for %s\n", pci_name(d));
+	for(i=0; i<4; i++) {
+		struct resource *r = &d->resource[i];
+		if ((r->start & ~0x80) == 0x374) {
+			r->start |= 2;
+			r->end = r->start;
+		}
+	}
+}
+
+static void __init pci_fixup_ide_trash(struct pci_dev *d)
+{
+	int i;
+
+	/*
+	 * There exist PCI IDE controllers which have utter garbage
+	 * in first four base registers. Ignore that.
+	 */
+	printk("PCI: IDE base address trash cleared for %s\n", pci_name(d));
+	for(i=0; i<4; i++)
+		d->resource[i].start = d->resource[i].end = d->resource[i].flags = 0;
+}
+
+static void __devinit  pci_fixup_latency(struct pci_dev *d)
+{
+	/*
+	 *  SiS 5597 and 5598 chipsets require latency timer set to
+	 *  at most 32 to avoid lockups.
+	 */
+	DBG("PCI: Setting max latency to 32\n");
+	pcibios_max_latency = 32;
+}
+
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_UMC, PCI_DEVICE_ID_UMC_UM8886BF, pci_fixup_umc_ide);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_5513, pci_fixup_ide_trash);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_5597, pci_fixup_latency);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_5598, pci_fixup_latency);
+DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, pci_fixup_ide_bases);
+
+/*
+ *  Called after each bus is probed, but before its children
+ *  are examined.
+ */
+
+void __init pcibios_fixup_bus(struct pci_bus *bus)
+{
+#if 0
+	printk("### PCIBIOS_FIXUP_BUS(%d)\n",bus->number);
+#endif
+	pcibios_fixup_ghosts(bus);
+	pci_read_bridge_bases(bus);
+
+	if (bus->number == 0) {
+		struct list_head *ln;
+		struct pci_dev *dev;
+		for (ln=bus->devices.next; ln != &bus->devices; ln=ln->next) {
+			dev = pci_dev_b(ln);
+			if (dev->devfn == 0) {
+				dev->resource[0].start = 0;
+				dev->resource[0].end = 0;
+			}
+		}
+	}
+}
+
+/*
+ * Initialization. Try all known PCI access methods. Note that we support
+ * using both PCI BIOS and direct access: in such cases, we use I/O ports
+ * to access config space, but we still keep BIOS order of cards to be
+ * compatible with 2.0.X. This should go away some day.
+ */
+
+int __init pcibios_init(void)
+{
+	struct pci_ops *dir = NULL;
+
+	if (!mb93090_mb00_detected)
+		return -ENXIO;
+
+	__reg_MB86943_sl_ctl |= MB86943_SL_CTL_DRCT_MASTER_SWAP | MB86943_SL_CTL_DRCT_SLAVE_SWAP;
+
+	__reg_MB86943_ecs_base(1)	= ((__region_CS2 + 0x01000000) >> 9) | 0x08000000;
+	__reg_MB86943_ecs_base(2)	= ((__region_CS2 + 0x00000000) >> 9) | 0x08000000;
+
+	*(volatile uint32_t *) (__region_CS1 + 0x848) = 0xe0000000;
+	*(volatile uint32_t *) (__region_CS1 + 0x8b8) = 0x00000000;
+
+	__reg_MB86943_sl_pci_io_base	= (__region_CS2 + 0x04000000) >> 9;
+	__reg_MB86943_sl_pci_mem_base	= (__region_CS2 + 0x08000000) >> 9;
+	__reg_MB86943_pci_sl_io_base	= __region_CS2 + 0x04000000;
+	__reg_MB86943_pci_sl_mem_base	= __region_CS2 + 0x08000000;
+	mb();
+
+	*(volatile unsigned long *)(__region_CS2+0x01300014) == 1;
+
+	ioport_resource.start	= (__reg_MB86943_sl_pci_io_base << 9) & 0xfffffc00;
+	ioport_resource.end	= (__reg_MB86943_sl_pci_io_range << 9) | 0x3ff;
+	ioport_resource.end	+= ioport_resource.start;
+
+	printk("PCI IO window:  %08lx-%08lx\n", ioport_resource.start, ioport_resource.end);
+
+	iomem_resource.start	= (__reg_MB86943_sl_pci_mem_base << 9) & 0xfffffc00;
+
+	/* Reserve somewhere to write to flush posted writes. */
+	iomem_resource.start += 0x400;
+
+	iomem_resource.end	= (__reg_MB86943_sl_pci_mem_range << 9) | 0x3ff;
+	iomem_resource.end	+= iomem_resource.start;
+
+	printk("PCI MEM window: %08lx-%08lx\n", iomem_resource.start, iomem_resource.end);
+	printk("PCI DMA memory: %08lx-%08lx\n", dma_coherent_mem_start, dma_coherent_mem_end);
+
+	if (!pci_probe)
+		return -ENXIO;
+
+	dir = pci_check_direct();
+	if (dir)
+		pci_root_ops = dir;
+	else {
+		printk("PCI: No PCI bus detected\n");
+		return -ENXIO;
+	}
+
+	printk("PCI: Probing PCI hardware\n");
+	pci_root_bus = pci_scan_bus(0, pci_root_ops, NULL);
+
+	pcibios_irq_init();
+	pcibios_fixup_peer_bridges();
+	pcibios_fixup_irqs();
+	pcibios_resource_survey();
+
+	return 0;
+}
+
+arch_initcall(pcibios_init);
+
+char * __init pcibios_setup(char *str)
+{
+	if (!strcmp(str, "off")) {
+		pci_probe = 0;
+		return NULL;
+	} else if (!strncmp(str, "lastbus=", 8)) {
+		pcibios_last_bus = simple_strtol(str+8, NULL, 0);
+		return NULL;
+	}
+	return str;
+}
+
+int pcibios_enable_device(struct pci_dev *dev, int mask)
+{
+	int err;
+
+	if ((err = pcibios_enable_resources(dev, mask)) < 0)
+		return err;
+	pcibios_enable_irq(dev);
+	return 0;
+}
diff --git a/arch/frv/mm/Makefile b/arch/frv/mm/Makefile
new file mode 100644
index 0000000..fb8b1d8
--- /dev/null
+++ b/arch/frv/mm/Makefile
@@ -0,0 +1,9 @@
+#
+# Makefile for the arch-specific parts of the memory manager.
+#
+
+obj-y := init.o kmap.o
+
+obj-$(CONFIG_MMU) += \
+	pgalloc.o highmem.o fault.o extable.o cache-page.o tlb-flush.o tlb-miss.o \
+	mmu-context.o dma-alloc.o unaligned.o elf-fdpic.o
diff --git a/arch/frv/mm/cache-page.c b/arch/frv/mm/cache-page.c
new file mode 100644
index 0000000..683b5e3
--- /dev/null
+++ b/arch/frv/mm/cache-page.c
@@ -0,0 +1,66 @@
+/* cache-page.c: whole-page cache wrangling functions for MMU linux
+ *
+ * Copyright (C) 2004 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 License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/highmem.h>
+#include <asm/pgalloc.h>
+
+/*****************************************************************************/
+/*
+ * DCF takes a virtual address and the page may not currently have one
+ * - temporarily hijack a kmap_atomic() slot and attach the page to it
+ */
+void flush_dcache_page(struct page *page)
+{
+	unsigned long dampr2;
+	void *vaddr;
+
+	dampr2 = __get_DAMPR(2);
+
+	vaddr = kmap_atomic(page, __KM_CACHE);
+
+	frv_dcache_writeback((unsigned long) vaddr, (unsigned long) vaddr + PAGE_SIZE);
+
+	kunmap_atomic(vaddr, __KM_CACHE);
+
+	if (dampr2) {
+		__set_DAMPR(2, dampr2);
+		__set_IAMPR(2, dampr2);
+	}
+
+} /* end flush_dcache_page() */
+
+/*****************************************************************************/
+/*
+ * ICI takes a virtual address and the page may not currently have one
+ * - so we temporarily attach the page to a bit of virtual space so that is can be flushed
+ */
+void flush_icache_user_range(struct vm_area_struct *vma, struct page *page,
+			     unsigned long start, unsigned long len)
+{
+	unsigned long dampr2;
+	void *vaddr;
+
+	dampr2 = __get_DAMPR(2);
+
+	vaddr = kmap_atomic(page, __KM_CACHE);
+
+	start = (start & ~PAGE_MASK) | (unsigned long) vaddr;
+	frv_cache_wback_inv(start, start + len);
+
+	kunmap_atomic(vaddr, __KM_CACHE);
+
+	if (dampr2) {
+		__set_DAMPR(2, dampr2);
+		__set_IAMPR(2, dampr2);
+	}
+
+} /* end flush_icache_user_range() */
diff --git a/arch/frv/mm/dma-alloc.c b/arch/frv/mm/dma-alloc.c
new file mode 100644
index 0000000..4b38d45
--- /dev/null
+++ b/arch/frv/mm/dma-alloc.c
@@ -0,0 +1,188 @@
+/* dma-alloc.c: consistent DMA memory allocation
+ *
+ * Derived from arch/ppc/mm/cachemap.c
+ *
+ *  PowerPC version derived from arch/arm/mm/consistent.c
+ *    Copyright (C) 2001 Dan Malek (dmalek@jlc.net)
+ *
+ *  linux/arch/arm/mm/consistent.c
+ *
+ *  Copyright (C) 2000 Russell King
+ *
+ * Consistent memory allocators.  Used for DMA devices that want to
+ * share uncached memory with the processor core.  The function return
+ * is the virtual address and 'dma_handle' is the physical address.
+ * Mostly stolen from the ARM port, with some changes for PowerPC.
+ *						-- Dan
+ * Modified for 36-bit support.  -Matt
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/ptrace.h>
+#include <linux/mman.h>
+#include <linux/mm.h>
+#include <linux/swap.h>
+#include <linux/stddef.h>
+#include <linux/vmalloc.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+
+#include <asm/pgalloc.h>
+#include <asm/io.h>
+#include <asm/hardirq.h>
+#include <asm/mmu_context.h>
+#include <asm/pgtable.h>
+#include <asm/mmu.h>
+#include <asm/uaccess.h>
+#include <asm/smp.h>
+
+static int map_page(unsigned long va, unsigned long pa, pgprot_t prot)
+{
+	pgd_t *pge;
+	pud_t *pue;
+	pmd_t *pme;
+	pte_t *pte;
+	int err = -ENOMEM;
+
+	spin_lock(&init_mm.page_table_lock);
+
+	/* Use upper 10 bits of VA to index the first level map */
+	pge = pgd_offset_k(va);
+	pue = pud_offset(pge, va);
+	pme = pmd_offset(pue, va);
+
+	/* Use middle 10 bits of VA to index the second-level map */
+	pte = pte_alloc_kernel(&init_mm, pme, va);
+	if (pte != 0) {
+		err = 0;
+		set_pte(pte, mk_pte_phys(pa & PAGE_MASK, prot));
+	}
+
+	spin_unlock(&init_mm.page_table_lock);
+	return err;
+}
+
+/*
+ * This function will allocate the requested contiguous pages and
+ * map them into the kernel's vmalloc() space.  This is done so we
+ * get unique mapping for these pages, outside of the kernel's 1:1
+ * virtual:physical mapping.  This is necessary so we can cover large
+ * portions of the kernel with single large page TLB entries, and
+ * still get unique uncached pages for consistent DMA.
+ */
+void *consistent_alloc(int gfp, size_t size, dma_addr_t *dma_handle)
+{
+	struct vm_struct *area;
+	unsigned long page, va, pa;
+	void *ret;
+	int order, err, i;
+
+	if (in_interrupt())
+		BUG();
+
+	/* only allocate page size areas */
+	size = PAGE_ALIGN(size);
+	order = get_order(size);
+
+	page = __get_free_pages(gfp, order);
+	if (!page) {
+		BUG();
+		return NULL;
+	}
+
+	/* allocate some common virtual space to map the new pages */
+	area = get_vm_area(size, VM_ALLOC);
+	if (area == 0) {
+		free_pages(page, order);
+		return NULL;
+	}
+	va = VMALLOC_VMADDR(area->addr);
+	ret = (void *) va;
+
+	/* this gives us the real physical address of the first page */
+	*dma_handle = pa = virt_to_bus((void *) page);
+
+	/* set refcount=1 on all pages in an order>0 allocation so that vfree() will actually free
+	 * all pages that were allocated.
+	 */
+	if (order > 0) {
+		struct page *rpage = virt_to_page(page);
+
+		for (i = 1; i < (1 << order); i++)
+			set_page_count(rpage + i, 1);
+	}
+
+	err = 0;
+	for (i = 0; i < size && err == 0; i += PAGE_SIZE)
+		err = map_page(va + i, pa + i, PAGE_KERNEL_NOCACHE);
+
+	if (err) {
+		vfree((void *) va);
+		return NULL;
+	}
+
+	/* we need to ensure that there are no cachelines in use, or worse dirty in this area
+	 * - can't do until after virtual address mappings are created
+	 */
+	frv_cache_invalidate(va, va + size);
+
+	return ret;
+}
+
+/*
+ * free page(s) as defined by the above mapping.
+ */
+void consistent_free(void *vaddr)
+{
+	if (in_interrupt())
+		BUG();
+	vfree(vaddr);
+}
+
+/*
+ * make an area consistent.
+ */
+void consistent_sync(void *vaddr, size_t size, int direction)
+{
+	unsigned long start = (unsigned long) vaddr;
+	unsigned long end   = start + size;
+
+	switch (direction) {
+	case PCI_DMA_NONE:
+		BUG();
+	case PCI_DMA_FROMDEVICE:	/* invalidate only */
+		frv_cache_invalidate(start, end);
+		break;
+	case PCI_DMA_TODEVICE:		/* writeback only */
+		frv_dcache_writeback(start, end);
+		break;
+	case PCI_DMA_BIDIRECTIONAL:	/* writeback and invalidate */
+		frv_dcache_writeback(start, end);
+		break;
+	}
+}
+
+/*
+ * consistent_sync_page make a page are consistent. identical
+ * to consistent_sync, but takes a struct page instead of a virtual address
+ */
+
+void consistent_sync_page(struct page *page, unsigned long offset,
+			  size_t size, int direction)
+{
+	void *start;
+
+	start = page_address(page) + offset;
+	consistent_sync(start, size, direction);
+}
diff --git a/arch/frv/mm/elf-fdpic.c b/arch/frv/mm/elf-fdpic.c
new file mode 100644
index 0000000..f5a6530
--- /dev/null
+++ b/arch/frv/mm/elf-fdpic.c
@@ -0,0 +1,123 @@
+/* elf-fdpic.c: ELF FDPIC memory layout management
+ *
+ * Copyright (C) 2004 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 License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/fs.h>
+#include <linux/elf-fdpic.h>
+
+/*****************************************************************************/
+/*
+ * lay out the userspace VM according to our grand design
+ */
+#ifdef CONFIG_MMU
+void elf_fdpic_arch_lay_out_mm(struct elf_fdpic_params *exec_params,
+			       struct elf_fdpic_params *interp_params,
+			       unsigned long *start_stack,
+			       unsigned long *start_brk)
+{
+	*start_stack = 0x02200000UL;
+
+	/* if the only executable is a shared object, assume that it is an interpreter rather than
+	 * a true executable, and map it such that "ld.so --list" comes out right
+	 */
+	if (!(interp_params->flags & ELF_FDPIC_FLAG_PRESENT) &&
+	    exec_params->hdr.e_type != ET_EXEC
+	    ) {
+		exec_params->load_addr = PAGE_SIZE;
+
+		*start_brk = 0x80000000UL;
+	}
+	else {
+		exec_params->load_addr = 0x02200000UL;
+
+		if ((exec_params->flags & ELF_FDPIC_FLAG_ARRANGEMENT) ==
+		    ELF_FDPIC_FLAG_INDEPENDENT
+		    ) {
+			exec_params->flags &= ~ELF_FDPIC_FLAG_ARRANGEMENT;
+			exec_params->flags |= ELF_FDPIC_FLAG_CONSTDISP;
+		}
+	}
+
+} /* end elf_fdpic_arch_lay_out_mm() */
+#endif
+
+/*****************************************************************************/
+/*
+ * place non-fixed mmaps firstly in the bottom part of memory, working up, and then in the top part
+ * of memory, working down
+ */
+unsigned long arch_get_unmapped_area(struct file *filp, unsigned long addr, unsigned long len,
+				     unsigned long pgoff, unsigned long flags)
+{
+	struct vm_area_struct *vma;
+	unsigned long limit;
+
+	if (len > TASK_SIZE)
+		return -ENOMEM;
+
+	/* only honour a hint if we're not going to clobber something doing so */
+	if (addr) {
+		addr = PAGE_ALIGN(addr);
+		vma = find_vma(current->mm, addr);
+		if (TASK_SIZE - len >= addr &&
+		    (!vma || addr + len <= vma->vm_start))
+			goto success;
+	}
+
+	/* search between the bottom of user VM and the stack grow area */
+	addr = PAGE_SIZE;
+	limit = (current->mm->start_stack - 0x00200000);
+	if (addr + len <= limit) {
+		limit -= len;
+
+		if (addr <= limit) {
+			vma = find_vma(current->mm, PAGE_SIZE);
+			for (; vma; vma = vma->vm_next) {
+				if (addr > limit)
+					break;
+				if (addr + len <= vma->vm_start)
+					goto success;
+				addr = vma->vm_end;
+			}
+		}
+	}
+
+	/* search from just above the WorkRAM area to the top of memory */
+	addr = PAGE_ALIGN(0x80000000);
+	limit = TASK_SIZE - len;
+	if (addr <= limit) {
+		vma = find_vma(current->mm, addr);
+		for (; vma; vma = vma->vm_next) {
+			if (addr > limit)
+				break;
+			if (addr + len <= vma->vm_start)
+				goto success;
+			addr = vma->vm_end;
+		}
+
+		if (!vma && addr <= limit)
+			goto success;
+	}
+
+#if 0
+	printk("[area] l=%lx (ENOMEM) f='%s'\n",
+	       len, filp ? filp->f_dentry->d_name.name : "");
+#endif
+	return -ENOMEM;
+
+ success:
+#if 0
+	printk("[area] l=%lx ad=%lx f='%s'\n",
+	       len, addr, filp ? filp->f_dentry->d_name.name : "");
+#endif
+	return addr;
+} /* end arch_get_unmapped_area() */
diff --git a/arch/frv/mm/extable.c b/arch/frv/mm/extable.c
new file mode 100644
index 0000000..41be112
--- /dev/null
+++ b/arch/frv/mm/extable.c
@@ -0,0 +1,91 @@
+/*
+ * linux/arch/frv/mm/extable.c
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <asm/uaccess.h>
+
+extern const struct exception_table_entry __attribute__((aligned(8))) __start___ex_table[];
+extern const struct exception_table_entry __attribute__((aligned(8))) __stop___ex_table[];
+extern const void __memset_end, __memset_user_error_lr, __memset_user_error_handler;
+extern const void __memcpy_end, __memcpy_user_error_lr, __memcpy_user_error_handler;
+extern spinlock_t modlist_lock;
+
+/*****************************************************************************/
+/*
+ *
+ */
+static inline unsigned long search_one_table(const struct exception_table_entry *first,
+					     const struct exception_table_entry *last,
+					     unsigned long value)
+{
+        while (first <= last) {
+		const struct exception_table_entry __attribute__((aligned(8))) *mid;
+		long diff;
+
+		mid = (last - first) / 2 + first;
+		diff = mid->insn - value;
+                if (diff == 0)
+                        return mid->fixup;
+                else if (diff < 0)
+                        first = mid + 1;
+                else
+                        last = mid - 1;
+        }
+        return 0;
+} /* end search_one_table() */
+
+/*****************************************************************************/
+/*
+ * see if there's a fixup handler available to deal with a kernel fault
+ */
+unsigned long search_exception_table(unsigned long pc)
+{
+	unsigned long ret = 0;
+
+	/* determine if the fault lay during a memcpy_user or a memset_user */
+	if (__frame->lr == (unsigned long) &__memset_user_error_lr &&
+	    (unsigned long) &memset <= pc && pc < (unsigned long) &__memset_end
+	    ) {
+		/* the fault occurred in a protected memset
+		 * - we search for the return address (in LR) instead of the program counter
+		 * - it was probably during a clear_user()
+		 */
+		return (unsigned long) &__memset_user_error_handler;
+	}
+	else if (__frame->lr == (unsigned long) &__memcpy_user_error_lr &&
+		 (unsigned long) &memcpy <= pc && pc < (unsigned long) &__memcpy_end
+		 ) {
+		/* the fault occurred in a protected memset
+		 * - we search for the return address (in LR) instead of the program counter
+		 * - it was probably during a copy_to/from_user()
+		 */
+		return (unsigned long) &__memcpy_user_error_handler;
+	}
+
+#ifndef CONFIG_MODULES
+	/* there is only the kernel to search.  */
+	ret = search_one_table(__start___ex_table, __stop___ex_table - 1, pc);
+	return ret;
+
+#else
+	/* the kernel is the last "module" -- no need to treat it special */
+	unsigned long flags;
+	struct module *mp;
+
+	spin_lock_irqsave(&modlist_lock, flags);
+
+	for (mp = module_list; mp != NULL; mp = mp->next) {
+		if (mp->ex_table_start == NULL || !(mp->flags & (MOD_RUNNING | MOD_INITIALIZING)))
+			continue;
+		ret = search_one_table(mp->ex_table_start, mp->ex_table_end - 1, pc);
+		if (ret)
+			break;
+	}
+
+	spin_unlock_irqrestore(&modlist_lock, flags);
+	return ret;
+#endif
+} /* end search_exception_table() */
diff --git a/arch/frv/mm/fault.c b/arch/frv/mm/fault.c
new file mode 100644
index 0000000..41d02ac
--- /dev/null
+++ b/arch/frv/mm/fault.c
@@ -0,0 +1,325 @@
+/*
+ *  linux/arch/frv/mm/fault.c
+ *
+ * Copyright (C) 2003 Red Hat, Inc. All Rights Reserved.
+ * - Written by David Howells (dhowells@redhat.com)
+ * - Derived from arch/m68knommu/mm/fault.c
+ *   - Copyright (C) 1998  D. Jeff Dionne <jeff@lineo.ca>,
+ *   - Copyright (C) 2000  Lineo, Inc.  (www.lineo.com)
+ *
+ *  Based on:
+ *
+ *  linux/arch/m68k/mm/fault.c
+ *
+ *  Copyright (C) 1995  Hamish Macdonald
+ */
+
+#include <linux/mman.h>
+#include <linux/mm.h>
+#include <linux/kernel.h>
+#include <linux/ptrace.h>
+#include <linux/hardirq.h>
+
+#include <asm/system.h>
+#include <asm/pgtable.h>
+#include <asm/uaccess.h>
+#include <asm/gdb-stub.h>
+
+/*****************************************************************************/
+/*
+ * This routine handles page faults.  It determines the problem, and
+ * then passes it off to one of the appropriate routines.
+ */
+asmlinkage void do_page_fault(int datammu, unsigned long esr0, unsigned long ear0)
+{
+	struct vm_area_struct *vma;
+	struct mm_struct *mm;
+	unsigned long _pme, lrai, lrad, fixup;
+	siginfo_t info;
+	pgd_t *pge;
+	pud_t *pue;
+	pte_t *pte;
+	int write;
+
+#if 0
+	const char *atxc[16] = {
+		[0x0] = "mmu-miss", [0x8] = "multi-dat", [0x9] = "multi-sat",
+		[0xa] = "tlb-miss", [0xc] = "privilege", [0xd] = "write-prot",
+	};
+
+	printk("do_page_fault(%d,%lx [%s],%lx)\n",
+	       datammu, esr0, atxc[esr0 >> 20 & 0xf], ear0);
+#endif
+
+	mm = current->mm;
+
+	/*
+	 * We fault-in kernel-space virtual memory on-demand. The
+	 * 'reference' page table is init_mm.pgd.
+	 *
+	 * NOTE! We MUST NOT take any locks for this case. We may
+	 * be in an interrupt or a critical region, and should
+	 * only copy the information from the master page table,
+	 * nothing more.
+	 *
+	 * This verifies that the fault happens in kernel space
+	 * and that the fault was a page not present (invalid) error
+	 */
+	if (!user_mode(__frame) && (esr0 & ESR0_ATXC) == ESR0_ATXC_AMRTLB_MISS) {
+		if (ear0 >= VMALLOC_START && ear0 < VMALLOC_END)
+			goto kernel_pte_fault;
+		if (ear0 >= PKMAP_BASE && ear0 < PKMAP_END)
+			goto kernel_pte_fault;
+	}
+
+	info.si_code = SEGV_MAPERR;
+
+	/*
+	 * If we're in an interrupt or have no user
+	 * context, we must not take the fault..
+	 */
+	if (in_interrupt() || !mm)
+		goto no_context;
+
+	down_read(&mm->mmap_sem);
+
+	vma = find_vma(mm, ear0);
+	if (!vma)
+		goto bad_area;
+	if (vma->vm_start <= ear0)
+		goto good_area;
+	if (!(vma->vm_flags & VM_GROWSDOWN))
+		goto bad_area;
+
+	if (user_mode(__frame)) {
+		/*
+		 * accessing the stack below %esp is always a bug.
+		 * The "+ 32" is there due to some instructions (like
+		 * pusha) doing post-decrement on the stack and that
+		 * doesn't show up until later..
+		 */
+		if ((ear0 & PAGE_MASK) + 2 * PAGE_SIZE < __frame->sp) {
+#if 0
+			printk("[%d] ### Access below stack @%lx (sp=%lx)\n",
+			       current->pid, ear0, __frame->sp);
+			show_registers(__frame);
+			printk("[%d] ### Code: [%08lx] %02x %02x %02x %02x %02x %02x %02x %02x\n",
+			       current->pid,
+			       __frame->pc,
+			       ((u8*)__frame->pc)[0],
+			       ((u8*)__frame->pc)[1],
+			       ((u8*)__frame->pc)[2],
+			       ((u8*)__frame->pc)[3],
+			       ((u8*)__frame->pc)[4],
+			       ((u8*)__frame->pc)[5],
+			       ((u8*)__frame->pc)[6],
+			       ((u8*)__frame->pc)[7]
+			       );
+#endif
+			goto bad_area;
+		}
+	}
+
+	if (expand_stack(vma, ear0))
+		goto bad_area;
+
+/*
+ * Ok, we have a good vm_area for this memory access, so
+ * we can handle it..
+ */
+ good_area:
+	info.si_code = SEGV_ACCERR;
+	write = 0;
+	switch (esr0 & ESR0_ATXC) {
+	default:
+		/* handle write to write protected page */
+	case ESR0_ATXC_WP_EXCEP:
+#ifdef TEST_VERIFY_AREA
+		if (!(user_mode(__frame)))
+			printk("WP fault at %08lx\n", __frame->pc);
+#endif
+		if (!(vma->vm_flags & VM_WRITE))
+			goto bad_area;
+		write = 1;
+		break;
+
+		 /* handle read from protected page */
+	case ESR0_ATXC_PRIV_EXCEP:
+		goto bad_area;
+
+		 /* handle read, write or exec on absent page
+		  * - can't support write without permitting read
+		  * - don't support execute without permitting read and vice-versa
+		  */
+	case ESR0_ATXC_AMRTLB_MISS:
+		if (!(vma->vm_flags & (VM_READ | VM_WRITE | VM_EXEC)))
+			goto bad_area;
+		break;
+	}
+
+	/*
+	 * If for any reason at all we couldn't handle the fault,
+	 * make sure we exit gracefully rather than endlessly redo
+	 * the fault.
+	 */
+	switch (handle_mm_fault(mm, vma, ear0, write)) {
+	case 1:
+		current->min_flt++;
+		break;
+	case 2:
+		current->maj_flt++;
+		break;
+	case 0:
+		goto do_sigbus;
+	default:
+		goto out_of_memory;
+	}
+
+	up_read(&mm->mmap_sem);
+	return;
+
+/*
+ * Something tried to access memory that isn't in our memory map..
+ * Fix it, but check if it's kernel or user first..
+ */
+ bad_area:
+	up_read(&mm->mmap_sem);
+
+	/* User mode accesses just cause a SIGSEGV */
+	if (user_mode(__frame)) {
+		info.si_signo = SIGSEGV;
+		info.si_errno = 0;
+		/* info.si_code has been set above */
+		info.si_addr = (void *) ear0;
+		force_sig_info(SIGSEGV, &info, current);
+		return;
+	}
+
+ no_context:
+	/* are we prepared to handle this kernel fault? */
+	if ((fixup = search_exception_table(__frame->pc)) != 0) {
+		__frame->pc = fixup;
+		return;
+	}
+
+/*
+ * Oops. The kernel tried to access some bad page. We'll have to
+ * terminate things with extreme prejudice.
+ */
+
+	bust_spinlocks(1);
+
+	if (ear0 < PAGE_SIZE)
+		printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference");
+	else
+		printk(KERN_ALERT "Unable to handle kernel paging request");
+	printk(" at virtual addr %08lx\n", ear0);
+	printk("  PC  : %08lx\n", __frame->pc);
+	printk("  EXC : esr0=%08lx ear0=%08lx\n", esr0, ear0);
+
+	asm("lrai %1,%0,#1,#0,#0" : "=&r"(lrai) : "r"(ear0));
+	asm("lrad %1,%0,#1,#0,#0" : "=&r"(lrad) : "r"(ear0));
+
+	printk(KERN_ALERT "  LRAI: %08lx\n", lrai);
+	printk(KERN_ALERT "  LRAD: %08lx\n", lrad);
+
+	__break_hijack_kernel_event();
+
+	pge = pgd_offset(current->mm, ear0);
+	pue = pud_offset(pge, ear0);
+	_pme = pue->pue[0].ste[0];
+
+	printk(KERN_ALERT "  PGE : %8p { PME %08lx }\n", pge, _pme);
+
+	if (_pme & xAMPRx_V) {
+		unsigned long dampr, damlr, val;
+
+		asm volatile("movsg dampr2,%0 ! movgs %2,dampr2 ! movsg damlr2,%1"
+			     : "=&r"(dampr), "=r"(damlr)
+			     : "r" (_pme | xAMPRx_L|xAMPRx_SS_16Kb|xAMPRx_S|xAMPRx_C|xAMPRx_V)
+			     );
+
+		pte = (pte_t *) damlr + __pte_index(ear0);
+		val = pte_val(*pte);
+
+		asm volatile("movgs %0,dampr2" :: "r" (dampr));
+
+		printk(KERN_ALERT "  PTE : %8p { %08lx }\n", pte, val);
+	}
+
+	die_if_kernel("Oops\n");
+	do_exit(SIGKILL);
+
+/*
+ * We ran out of memory, or some other thing happened to us that made
+ * us unable to handle the page fault gracefully.
+ */
+ out_of_memory:
+	up_read(&mm->mmap_sem);
+	printk("VM: killing process %s\n", current->comm);
+	if (user_mode(__frame))
+		do_exit(SIGKILL);
+	goto no_context;
+
+ do_sigbus:
+	up_read(&mm->mmap_sem);
+
+	/*
+	 * Send a sigbus, regardless of whether we were in kernel
+	 * or user mode.
+	 */
+	info.si_signo = SIGBUS;
+	info.si_errno = 0;
+	info.si_code = BUS_ADRERR;
+	info.si_addr = (void *) ear0;
+	force_sig_info(SIGBUS, &info, current);
+
+	/* Kernel mode? Handle exceptions or die */
+	if (!user_mode(__frame))
+		goto no_context;
+	return;
+
+/*
+ * The fault was caused by a kernel PTE (such as installed by vmalloc or kmap)
+ */
+ kernel_pte_fault:
+	{
+		/*
+		 * Synchronize this task's top level page-table
+		 * with the 'reference' page table.
+		 *
+		 * Do _not_ use "tsk" here. We might be inside
+		 * an interrupt in the middle of a task switch..
+		 */
+		int index = pgd_index(ear0);
+		pgd_t *pgd, *pgd_k;
+		pud_t *pud, *pud_k;
+		pmd_t *pmd, *pmd_k;
+		pte_t *pte_k;
+
+		pgd = (pgd_t *) __get_TTBR();
+		pgd = (pgd_t *)__va(pgd) + index;
+		pgd_k = ((pgd_t *)(init_mm.pgd)) + index;
+
+		if (!pgd_present(*pgd_k))
+			goto no_context;
+		//set_pgd(pgd, *pgd_k); /////// gcc ICE's on this line
+
+		pud_k = pud_offset(pgd_k, ear0);
+		if (!pud_present(*pud_k))
+			goto no_context;
+
+		pmd_k = pmd_offset(pud_k, ear0);
+		if (!pmd_present(*pmd_k))
+			goto no_context;
+
+		pud = pud_offset(pgd, ear0);
+		pmd = pmd_offset(pud, ear0);
+		set_pmd(pmd, *pmd_k);
+
+		pte_k = pte_offset_kernel(pmd_k, ear0);
+		if (!pte_present(*pte_k))
+			goto no_context;
+		return;
+	}
+} /* end do_page_fault() */
diff --git a/arch/frv/mm/highmem.c b/arch/frv/mm/highmem.c
new file mode 100644
index 0000000..7dc8fbf
--- /dev/null
+++ b/arch/frv/mm/highmem.c
@@ -0,0 +1,33 @@
+/* highmem.c: arch-specific highmem stuff
+ *
+ * Copyright (C) 2004 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 License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#include <linux/highmem.h>
+
+void *kmap(struct page *page)
+{
+	might_sleep();
+	if (!PageHighMem(page))
+		return page_address(page);
+	return kmap_high(page);
+}
+
+void kunmap(struct page *page)
+{
+	if (in_interrupt())
+		BUG();
+	if (!PageHighMem(page))
+		return;
+	kunmap_high(page);
+}
+
+struct page *kmap_atomic_to_page(void *ptr)
+{
+	return virt_to_page(ptr);
+}
diff --git a/arch/frv/mm/init.c b/arch/frv/mm/init.c
new file mode 100644
index 0000000..41958f5
--- /dev/null
+++ b/arch/frv/mm/init.c
@@ -0,0 +1,241 @@
+/* init.c: memory initialisation for FRV
+ *
+ * Copyright (C) 2004 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 License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Derived from:
+ *  - linux/arch/m68knommu/mm/init.c
+ *    - Copyright (C) 1998  D. Jeff Dionne <jeff@lineo.ca>, Kenneth Albanowski <kjahds@kjahds.com>,
+ *    - Copyright (C) 2000  Lineo, Inc.  (www.lineo.com)
+ *  - linux/arch/m68k/mm/init.c
+ *    - Copyright (C) 1995  Hamish Macdonald
+ */
+
+#include <linux/config.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/pagemap.h>
+#include <linux/swap.h>
+#include <linux/mm.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/bootmem.h>
+#include <linux/highmem.h>
+
+#include <asm/setup.h>
+#include <asm/segment.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/system.h>
+#include <asm/mmu_context.h>
+#include <asm/virtconvert.h>
+#include <asm/sections.h>
+#include <asm/tlb.h>
+
+#undef DEBUG
+
+DEFINE_PER_CPU(struct mmu_gather, mmu_gathers);
+
+/*
+ * BAD_PAGE is the page that is used for page faults when linux
+ * is out-of-memory. Older versions of linux just did a
+ * do_exit(), but using this instead means there is less risk
+ * for a process dying in kernel mode, possibly leaving a inode
+ * unused etc..
+ *
+ * BAD_PAGETABLE is the accompanying page-table: it is initialized
+ * to point to BAD_PAGE entries.
+ *
+ * ZERO_PAGE is a special page that is used for zero-initialized
+ * data and COW.
+ */
+static unsigned long empty_bad_page_table;
+static unsigned long empty_bad_page;
+unsigned long empty_zero_page;
+
+/*****************************************************************************/
+/*
+ *
+ */
+void show_mem(void)
+{
+	unsigned long i;
+	int free = 0, total = 0, reserved = 0, shared = 0;
+
+	printk("\nMem-info:\n");
+	show_free_areas();
+	i = max_mapnr;
+	while (i-- > 0) {
+		struct page *page = &mem_map[i];
+
+		total++;
+		if (PageReserved(page))
+			reserved++;
+		else if (!page_count(page))
+			free++;
+		else
+			shared += page_count(page) - 1;
+	}
+
+	printk("%d pages of RAM\n",total);
+	printk("%d free pages\n",free);
+	printk("%d reserved pages\n",reserved);
+	printk("%d pages shared\n",shared);
+
+} /* end show_mem() */
+
+/*****************************************************************************/
+/*
+ * paging_init() continues the virtual memory environment setup which
+ * was begun by the code in arch/head.S.
+ * The parameters are pointers to where to stick the starting and ending
+ * addresses  of available kernel virtual memory.
+ */
+void __init paging_init(void)
+{
+	unsigned long zones_size[MAX_NR_ZONES] = {0, 0, 0};
+
+	/* allocate some pages for kernel housekeeping tasks */
+	empty_bad_page_table	= (unsigned long) alloc_bootmem_pages(PAGE_SIZE);
+	empty_bad_page		= (unsigned long) alloc_bootmem_pages(PAGE_SIZE);
+	empty_zero_page		= (unsigned long) alloc_bootmem_pages(PAGE_SIZE);
+
+	memset((void *) empty_zero_page, 0, PAGE_SIZE);
+
+#if CONFIG_HIGHMEM
+	if (num_physpages - num_mappedpages) {
+		pgd_t *pge;
+		pud_t *pue;
+		pmd_t *pme;
+
+		pkmap_page_table = alloc_bootmem_pages(PAGE_SIZE);
+
+		memset(pkmap_page_table, 0, PAGE_SIZE);
+
+		pge = swapper_pg_dir + pgd_index_k(PKMAP_BASE);
+		pue = pud_offset(pge, PKMAP_BASE);
+		pme = pmd_offset(pue, PKMAP_BASE);
+		__set_pmd(pme, virt_to_phys(pkmap_page_table) | _PAGE_TABLE);
+	}
+#endif
+
+	/* distribute the allocatable pages across the various zones and pass them to the allocator
+	 */
+	zones_size[ZONE_DMA]     = max_low_pfn - min_low_pfn;
+	zones_size[ZONE_NORMAL]  = 0;
+#ifdef CONFIG_HIGHMEM
+	zones_size[ZONE_HIGHMEM] = num_physpages - num_mappedpages;
+#endif
+
+	free_area_init(zones_size);
+
+#ifdef CONFIG_MMU
+	/* initialise init's MMU context */
+	init_new_context(&init_task, &init_mm);
+#endif
+
+} /* end paging_init() */
+
+/*****************************************************************************/
+/*
+ *
+ */
+void __init mem_init(void)
+{
+	unsigned long npages = (memory_end - memory_start) >> PAGE_SHIFT;
+	unsigned long tmp;
+#ifdef CONFIG_MMU
+	unsigned long loop, pfn;
+	int datapages = 0;
+#endif
+	int codek = 0, datak = 0;
+
+	/* this will put all memory onto the freelists */
+	totalram_pages = free_all_bootmem();
+
+#ifdef CONFIG_MMU
+	for (loop = 0 ; loop < npages ; loop++)
+		if (PageReserved(&mem_map[loop]))
+			datapages++;
+
+#ifdef CONFIG_HIGHMEM
+	for (pfn = num_physpages - 1; pfn >= num_mappedpages; pfn--) {
+		struct page *page = &mem_map[pfn];
+
+		ClearPageReserved(page);
+		set_bit(PG_highmem, &page->flags);
+		set_page_count(page, 1);
+		__free_page(page);
+		totalram_pages++;
+	}
+#endif
+
+	codek = ((unsigned long) &_etext - (unsigned long) &_stext) >> 10;
+	datak = datapages << (PAGE_SHIFT - 10);
+
+#else
+	codek = (_etext - _stext) >> 10;
+	datak = 0; //(_ebss - _sdata) >> 10;
+#endif
+
+	tmp = nr_free_pages() << PAGE_SHIFT;
+	printk("Memory available: %luKiB/%luKiB RAM, %luKiB/%luKiB ROM (%dKiB kernel code, %dKiB data)\n",
+	       tmp >> 10,
+	       npages << (PAGE_SHIFT - 10),
+	       (rom_length > 0) ? ((rom_length >> 10) - codek) : 0,
+	       rom_length >> 10,
+	       codek,
+	       datak
+	       );
+
+} /* end mem_init() */
+
+/*****************************************************************************/
+/*
+ * free the memory that was only required for initialisation
+ */
+void __init free_initmem(void)
+{
+#if defined(CONFIG_RAMKERNEL) && !defined(CONFIG_PROTECT_KERNEL)
+	unsigned long start, end, addr;
+
+	start = PAGE_ALIGN((unsigned long) &__init_begin);	/* round up */
+	end   = ((unsigned long) &__init_end) & PAGE_MASK;	/* round down */
+
+	/* next to check that the page we free is not a partial page */
+	for (addr = start; addr < end; addr += PAGE_SIZE) {
+		ClearPageReserved(virt_to_page(addr));
+		set_page_count(virt_to_page(addr), 1);
+		free_page(addr);
+		totalram_pages++;
+	}
+
+	printk("Freeing unused kernel memory: %ldKiB freed (0x%lx - 0x%lx)\n",
+	       (end - start) >> 10, start, end);
+#endif
+} /* end free_initmem() */
+
+/*****************************************************************************/
+/*
+ * free the initial ramdisk memory
+ */
+#ifdef CONFIG_BLK_DEV_INITRD
+void __init free_initrd_mem(unsigned long start, unsigned long end)
+{
+	int pages = 0;
+	for (; start < end; start += PAGE_SIZE) {
+		ClearPageReserved(virt_to_page(start));
+		set_page_count(virt_to_page(start), 1);
+		free_page(start);
+		totalram_pages++;
+		pages++;
+	}
+	printk("Freeing initrd memory: %dKiB freed\n", (pages * PAGE_SIZE) >> 10);
+} /* end free_initrd_mem() */
+#endif
diff --git a/arch/frv/mm/kmap.c b/arch/frv/mm/kmap.c
new file mode 100644
index 0000000..539f45e6
--- /dev/null
+++ b/arch/frv/mm/kmap.c
@@ -0,0 +1,62 @@
+/* kmap.c: ioremapping handlers
+ *
+ * Copyright (C) 2003-5 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ * - Derived from arch/m68k/mm/kmap.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/config.h>
+#include <linux/mm.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/vmalloc.h>
+
+#include <asm/setup.h>
+#include <asm/segment.h>
+#include <asm/page.h>
+#include <asm/pgalloc.h>
+#include <asm/io.h>
+#include <asm/system.h>
+
+#undef DEBUG
+
+/*****************************************************************************/
+/*
+ * Map some physical address range into the kernel address space.
+ */
+
+void *__ioremap(unsigned long physaddr, unsigned long size, int cacheflag)
+{
+	return (void *)physaddr;
+}
+
+/*
+ * Unmap a ioremap()ed region again
+ */
+void iounmap(void *addr)
+{
+}
+
+/*
+ * __iounmap unmaps nearly everything, so be careful
+ * it doesn't free currently pointer/page tables anymore but it
+ * wans't used anyway and might be added later.
+ */
+void __iounmap(void *addr, unsigned long size)
+{
+}
+
+/*
+ * Set new cache mode for some kernel address space.
+ * The caller must push data for that range itself, if such data may already
+ * be in the cache.
+ */
+void kernel_set_cachemode(void *addr, unsigned long size, int cmode)
+{
+}
diff --git a/arch/frv/mm/mmu-context.c b/arch/frv/mm/mmu-context.c
new file mode 100644
index 0000000..f2c6866
--- /dev/null
+++ b/arch/frv/mm/mmu-context.c
@@ -0,0 +1,208 @@
+/* mmu-context.c: MMU context allocation and management
+ *
+ * Copyright (C) 2004 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 License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <asm/tlbflush.h>
+
+#define NR_CXN	4096
+
+static unsigned long cxn_bitmap[NR_CXN / (sizeof(unsigned long) * 8)];
+static LIST_HEAD(cxn_owners_lru);
+static DEFINE_SPINLOCK(cxn_owners_lock);
+
+int __nongpreldata cxn_pinned = -1;
+
+
+/*****************************************************************************/
+/*
+ * initialise a new context
+ */
+int init_new_context(struct task_struct *tsk, struct mm_struct *mm)
+{
+	memset(&mm->context, 0, sizeof(mm->context));
+	INIT_LIST_HEAD(&mm->context.id_link);
+	mm->context.itlb_cached_pge = 0xffffffffUL;
+	mm->context.dtlb_cached_pge = 0xffffffffUL;
+
+	return 0;
+} /* end init_new_context() */
+
+/*****************************************************************************/
+/*
+ * make sure a kernel MMU context has a CPU context number
+ * - call with cxn_owners_lock held
+ */
+static unsigned get_cxn(mm_context_t *ctx)
+{
+	struct list_head *_p;
+	mm_context_t *p;
+	unsigned cxn;
+
+	if (!list_empty(&ctx->id_link)) {
+		list_move_tail(&ctx->id_link, &cxn_owners_lru);
+	}
+	else {
+		/* find the first unallocated context number
+		 * - 0 is reserved for the kernel
+		 */
+		cxn = find_next_zero_bit(&cxn_bitmap, NR_CXN, 1);
+		if (cxn < NR_CXN) {
+			set_bit(cxn, &cxn_bitmap);
+		}
+		else {
+			/* none remaining - need to steal someone else's cxn */
+			p = NULL;
+			list_for_each(_p, &cxn_owners_lru) {
+				p = list_entry(_p, mm_context_t, id_link);
+				if (!p->id_busy && p->id != cxn_pinned)
+					break;
+			}
+
+			BUG_ON(_p == &cxn_owners_lru);
+
+			cxn = p->id;
+			p->id = 0;
+			list_del_init(&p->id_link);
+			__flush_tlb_mm(cxn);
+		}
+
+		ctx->id = cxn;
+		list_add_tail(&ctx->id_link, &cxn_owners_lru);
+	}
+
+	return ctx->id;
+} /* end get_cxn() */
+
+/*****************************************************************************/
+/*
+ * restore the current TLB miss handler mapped page tables into the MMU context and set up a
+ * mapping for the page directory
+ */
+void change_mm_context(mm_context_t *old, mm_context_t *ctx, pgd_t *pgd)
+{
+	unsigned long _pgd;
+
+	_pgd = virt_to_phys(pgd);
+
+	/* save the state of the outgoing MMU context */
+	old->id_busy = 0;
+
+	asm volatile("movsg scr0,%0"   : "=r"(old->itlb_cached_pge));
+	asm volatile("movsg dampr4,%0" : "=r"(old->itlb_ptd_mapping));
+	asm volatile("movsg scr1,%0"   : "=r"(old->dtlb_cached_pge));
+	asm volatile("movsg dampr5,%0" : "=r"(old->dtlb_ptd_mapping));
+
+	/* select an MMU context number */
+	spin_lock(&cxn_owners_lock);
+	get_cxn(ctx);
+	ctx->id_busy = 1;
+	spin_unlock(&cxn_owners_lock);
+
+	asm volatile("movgs %0,cxnr"   : : "r"(ctx->id));
+
+	/* restore the state of the incoming MMU context */
+	asm volatile("movgs %0,scr0"   : : "r"(ctx->itlb_cached_pge));
+	asm volatile("movgs %0,dampr4" : : "r"(ctx->itlb_ptd_mapping));
+	asm volatile("movgs %0,scr1"   : : "r"(ctx->dtlb_cached_pge));
+	asm volatile("movgs %0,dampr5" : : "r"(ctx->dtlb_ptd_mapping));
+
+	/* map the PGD into uncached virtual memory */
+	asm volatile("movgs %0,ttbr"   : : "r"(_pgd));
+	asm volatile("movgs %0,dampr3"
+		     :: "r"(_pgd | xAMPRx_L | xAMPRx_M | xAMPRx_SS_16Kb |
+			    xAMPRx_S | xAMPRx_C | xAMPRx_V));
+
+} /* end change_mm_context() */
+
+/*****************************************************************************/
+/*
+ * finished with an MMU context number
+ */
+void destroy_context(struct mm_struct *mm)
+{
+	mm_context_t *ctx = &mm->context;
+
+	spin_lock(&cxn_owners_lock);
+
+	if (!list_empty(&ctx->id_link)) {
+		if (ctx->id == cxn_pinned)
+			cxn_pinned = -1;
+
+		list_del_init(&ctx->id_link);
+		clear_bit(ctx->id, &cxn_bitmap);
+		__flush_tlb_mm(ctx->id);
+		ctx->id = 0;
+	}
+
+	spin_unlock(&cxn_owners_lock);
+} /* end destroy_context() */
+
+/*****************************************************************************/
+/*
+ * display the MMU context currently a process is currently using
+ */
+#ifdef CONFIG_PROC_FS
+char *proc_pid_status_frv_cxnr(struct mm_struct *mm, char *buffer)
+{
+	spin_lock(&cxn_owners_lock);
+	buffer += sprintf(buffer, "CXNR: %u\n", mm->context.id);
+	spin_unlock(&cxn_owners_lock);
+
+	return buffer;
+} /* end proc_pid_status_frv_cxnr() */
+#endif
+
+/*****************************************************************************/
+/*
+ * (un)pin a process's mm_struct's MMU context ID
+ */
+int cxn_pin_by_pid(pid_t pid)
+{
+	struct task_struct *tsk;
+	struct mm_struct *mm = NULL;
+	int ret;
+
+	/* unpin if pid is zero */
+	if (pid == 0) {
+		cxn_pinned = -1;
+		return 0;
+	}
+
+	ret = -ESRCH;
+
+	/* get a handle on the mm_struct */
+	read_lock(&tasklist_lock);
+	tsk = find_task_by_pid(pid);
+	if (tsk) {
+		ret = -EINVAL;
+
+		task_lock(tsk);
+		if (tsk->mm) {
+			mm = tsk->mm;
+			atomic_inc(&mm->mm_users);
+			ret = 0;
+		}
+		task_unlock(tsk);
+	}
+	read_unlock(&tasklist_lock);
+
+	if (ret < 0)
+		return ret;
+
+	/* make sure it has a CXN and pin it */
+	spin_lock(&cxn_owners_lock);
+	cxn_pinned = get_cxn(&mm->context);
+	spin_unlock(&cxn_owners_lock);
+
+	mmput(mm);
+	return 0;
+} /* end cxn_pin_by_pid() */
diff --git a/arch/frv/mm/pgalloc.c b/arch/frv/mm/pgalloc.c
new file mode 100644
index 0000000..4eaec0f
--- /dev/null
+++ b/arch/frv/mm/pgalloc.c
@@ -0,0 +1,159 @@
+/* pgalloc.c: page directory & page table allocation
+ *
+ * Copyright (C) 2004 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 License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/highmem.h>
+#include <asm/pgalloc.h>
+#include <asm/page.h>
+#include <asm/cacheflush.h>
+
+pgd_t swapper_pg_dir[PTRS_PER_PGD] __attribute__((aligned(PAGE_SIZE)));
+kmem_cache_t *pgd_cache;
+
+pte_t *pte_alloc_one_kernel(struct mm_struct *mm, unsigned long address)
+{
+	pte_t *pte = (pte_t *)__get_free_page(GFP_KERNEL|__GFP_REPEAT);
+	if (pte)
+		clear_page(pte);
+	return pte;
+}
+
+struct page *pte_alloc_one(struct mm_struct *mm, unsigned long address)
+{
+	struct page *page;
+
+#ifdef CONFIG_HIGHPTE
+	page = alloc_pages(GFP_KERNEL|__GFP_HIGHMEM|__GFP_REPEAT, 0);
+#else
+	page = alloc_pages(GFP_KERNEL|__GFP_REPEAT, 0);
+#endif
+	if (page)
+		clear_highpage(page);
+	flush_dcache_page(page);
+	return page;
+}
+
+void __set_pmd(pmd_t *pmdptr, unsigned long pmd)
+{
+	unsigned long *__ste_p = pmdptr->ste;
+	int loop;
+
+	if (!pmd) {
+		memset(__ste_p, 0, PME_SIZE);
+	}
+	else {
+		BUG_ON(pmd & (0x3f00 | xAMPRx_SS | 0xe));
+
+		for (loop = PME_SIZE; loop > 0; loop -= 4) {
+			*__ste_p++ = pmd;
+			pmd += __frv_PT_SIZE;
+		}
+	}
+
+	frv_dcache_writeback((unsigned long) pmdptr, (unsigned long) (pmdptr + 1));
+}
+
+/*
+ * List of all pgd's needed for non-PAE so it can invalidate entries
+ * in both cached and uncached pgd's; not needed for PAE since the
+ * kernel pmd is shared. If PAE were not to share the pmd a similar
+ * tactic would be needed. This is essentially codepath-based locking
+ * against pageattr.c; it is the unique case in which a valid change
+ * of kernel pagetables can't be lazily synchronized by vmalloc faults.
+ * vmalloc faults work because attached pagetables are never freed.
+ * If the locking proves to be non-performant, a ticketing scheme with
+ * checks at dup_mmap(), exec(), and other mmlist addition points
+ * could be used. The locking scheme was chosen on the basis of
+ * manfred's recommendations and having no core impact whatsoever.
+ * -- wli
+ */
+DEFINE_SPINLOCK(pgd_lock);
+struct page *pgd_list;
+
+static inline void pgd_list_add(pgd_t *pgd)
+{
+	struct page *page = virt_to_page(pgd);
+	page->index = (unsigned long) pgd_list;
+	if (pgd_list)
+		pgd_list->private = (unsigned long) &page->index;
+	pgd_list = page;
+	page->private = (unsigned long) &pgd_list;
+}
+
+static inline void pgd_list_del(pgd_t *pgd)
+{
+	struct page *next, **pprev, *page = virt_to_page(pgd);
+	next = (struct page *) page->index;
+	pprev = (struct page **) page->private;
+	*pprev = next;
+	if (next)
+		next->private = (unsigned long) pprev;
+}
+
+void pgd_ctor(void *pgd, kmem_cache_t *cache, unsigned long unused)
+{
+	unsigned long flags;
+
+	if (PTRS_PER_PMD == 1)
+		spin_lock_irqsave(&pgd_lock, flags);
+
+	memcpy((pgd_t *) pgd + USER_PGDS_IN_LAST_PML4,
+	       swapper_pg_dir + USER_PGDS_IN_LAST_PML4,
+	       (PTRS_PER_PGD - USER_PGDS_IN_LAST_PML4) * sizeof(pgd_t));
+
+	if (PTRS_PER_PMD > 1)
+		return;
+
+	pgd_list_add(pgd);
+	spin_unlock_irqrestore(&pgd_lock, flags);
+	memset(pgd, 0, USER_PGDS_IN_LAST_PML4 * sizeof(pgd_t));
+}
+
+/* never called when PTRS_PER_PMD > 1 */
+void pgd_dtor(void *pgd, kmem_cache_t *cache, unsigned long unused)
+{
+	unsigned long flags; /* can be called from interrupt context */
+
+	spin_lock_irqsave(&pgd_lock, flags);
+	pgd_list_del(pgd);
+	spin_unlock_irqrestore(&pgd_lock, flags);
+}
+
+pgd_t *pgd_alloc(struct mm_struct *mm)
+{
+	pgd_t *pgd;
+
+	pgd = kmem_cache_alloc(pgd_cache, GFP_KERNEL);
+	if (!pgd)
+		return pgd;
+
+	return pgd;
+}
+
+void pgd_free(pgd_t *pgd)
+{
+	/* in the non-PAE case, clear_page_tables() clears user pgd entries */
+	kmem_cache_free(pgd_cache, pgd);
+}
+
+void __init pgtable_cache_init(void)
+{
+	pgd_cache = kmem_cache_create("pgd",
+				      PTRS_PER_PGD * sizeof(pgd_t),
+				      PTRS_PER_PGD * sizeof(pgd_t),
+				      0,
+				      pgd_ctor,
+				      pgd_dtor);
+	if (!pgd_cache)
+		panic("pgtable_cache_init(): Cannot create pgd cache");
+}
diff --git a/arch/frv/mm/tlb-flush.S b/arch/frv/mm/tlb-flush.S
new file mode 100644
index 0000000..6f43c74
--- /dev/null
+++ b/arch/frv/mm/tlb-flush.S
@@ -0,0 +1,185 @@
+/* tlb-flush.S: TLB flushing routines
+ *
+ * Copyright (C) 2004 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 License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/sys.h>
+#include <linux/config.h>
+#include <linux/linkage.h>
+#include <asm/page.h>
+#include <asm/ptrace.h>
+#include <asm/spr-regs.h>
+
+.macro DEBUG ch
+#	sethi.p		%hi(0xfeff9c00),gr4
+#	setlo		%lo(0xfeff9c00),gr4
+#	setlos		#\ch,gr5
+#	stbi		gr5,@(gr4,#0)
+#	membar
+.endm
+
+	.section	.rodata
+
+	# sizes corresponding to TPXR.LMAX
+	.balign		1
+__tlb_lmax_sizes:
+	.byte		0, 64, 0, 0
+	.byte		0, 0, 0, 0
+	.byte		0, 0, 0, 0
+	.byte		0, 0, 0, 0
+
+	.section	.text
+	.balign		4
+
+###############################################################################
+#
+# flush everything
+# - void __flush_tlb_all(void)
+#
+###############################################################################
+	.globl		__flush_tlb_all
+	.type		__flush_tlb_all,@function
+__flush_tlb_all:
+	DEBUG		'A'
+
+	# kill cached PGE value
+	setlos		#0xffffffff,gr4
+	movgs		gr4,scr0
+	movgs		gr4,scr1
+
+	# kill AMPR-cached TLB values
+	movgs		gr0,iamlr1
+	movgs		gr0,iampr1
+	movgs		gr0,damlr1
+	movgs		gr0,dampr1
+
+	# find out how many lines there are
+	movsg		tpxr,gr5
+	sethi.p		%hi(__tlb_lmax_sizes),gr4
+	srli		gr5,#TPXR_LMAX_SHIFT,gr5
+	setlo.p		%lo(__tlb_lmax_sizes),gr4
+	andi		gr5,#TPXR_LMAX_SMASK,gr5
+	ldub		@(gr4,gr5),gr4
+
+	# now, we assume that the TLB line step is page size in size
+	setlos.p	#PAGE_SIZE,gr5
+	setlos		#0,gr6
+1:
+	tlbpr		gr6,gr0,#6,#0
+	subicc.p	gr4,#1,gr4,icc0
+	add		gr6,gr5,gr6
+	bne		icc0,#2,1b
+
+	DEBUG		'B'
+	bralr
+
+	.size		__flush_tlb_all, .-__flush_tlb_all
+
+###############################################################################
+#
+# flush everything to do with one context
+# - void __flush_tlb_mm(unsigned long contextid [GR8])
+#
+###############################################################################
+	.globl		__flush_tlb_mm
+	.type		__flush_tlb_mm,@function
+__flush_tlb_mm:
+	DEBUG		'M'
+
+	# kill cached PGE value
+	setlos		#0xffffffff,gr4
+	movgs		gr4,scr0
+	movgs		gr4,scr1
+
+	# specify the context we want to flush
+	movgs		gr8,tplr
+
+	# find out how many lines there are
+	movsg		tpxr,gr5
+	sethi.p		%hi(__tlb_lmax_sizes),gr4
+	srli		gr5,#TPXR_LMAX_SHIFT,gr5
+	setlo.p		%lo(__tlb_lmax_sizes),gr4
+	andi		gr5,#TPXR_LMAX_SMASK,gr5
+	ldub		@(gr4,gr5),gr4
+
+	# now, we assume that the TLB line step is page size in size
+	setlos.p	#PAGE_SIZE,gr5
+	setlos		#0,gr6
+0:
+	tlbpr		gr6,gr0,#5,#0
+	subicc.p	gr4,#1,gr4,icc0
+	add		gr6,gr5,gr6
+	bne		icc0,#2,0b
+
+	DEBUG		'N'
+	bralr
+
+	.size		__flush_tlb_mm, .-__flush_tlb_mm
+
+###############################################################################
+#
+# flush a range of addresses from the TLB
+# - void __flush_tlb_page(unsigned long contextid [GR8],
+#			  unsigned long start [GR9])
+#
+###############################################################################
+	.globl		__flush_tlb_page
+	.type		__flush_tlb_page,@function
+__flush_tlb_page:
+	# kill cached PGE value
+	setlos		#0xffffffff,gr4
+	movgs		gr4,scr0
+	movgs		gr4,scr1
+
+	# specify the context we want to flush
+	movgs		gr8,tplr
+
+	# zap the matching TLB line and AMR values
+	setlos		#~(PAGE_SIZE-1),gr5
+	and		gr9,gr5,gr9
+	tlbpr		gr9,gr0,#5,#0
+
+	bralr
+
+	.size		__flush_tlb_page, .-__flush_tlb_page
+
+###############################################################################
+#
+# flush a range of addresses from the TLB
+# - void __flush_tlb_range(unsigned long contextid [GR8],
+#			   unsigned long start [GR9],
+#			   unsigned long end [GR10])
+#
+###############################################################################
+	.globl		__flush_tlb_range
+	.type		__flush_tlb_range,@function
+__flush_tlb_range:
+	# kill cached PGE value
+	setlos		#0xffffffff,gr4
+	movgs		gr4,scr0
+	movgs		gr4,scr1
+
+	# specify the context we want to flush
+	movgs		gr8,tplr
+
+	# round the start down to beginning of TLB line and end up to beginning of next TLB line
+	setlos.p	#~(PAGE_SIZE-1),gr5
+	setlos		#PAGE_SIZE,gr6
+	subi.p		gr10,#1,gr10
+	and		gr9,gr5,gr9
+	and		gr10,gr5,gr10
+2:
+	tlbpr		gr9,gr0,#5,#0
+	subcc.p		gr9,gr10,gr0,icc0
+	add		gr9,gr6,gr9
+	bne		icc0,#0,2b		; most likely a 1-page flush
+
+	bralr
+
+	.size		__flush_tlb_range, .-__flush_tlb_range
diff --git a/arch/frv/mm/tlb-miss.S b/arch/frv/mm/tlb-miss.S
new file mode 100644
index 0000000..8729f7d
--- /dev/null
+++ b/arch/frv/mm/tlb-miss.S
@@ -0,0 +1,631 @@
+/* tlb-miss.S: TLB miss handlers
+ *
+ * Copyright (C) 2004 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 License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/sys.h>
+#include <linux/config.h>
+#include <linux/linkage.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/highmem.h>
+#include <asm/spr-regs.h>
+
+	.section	.text
+	.balign		4
+
+	.globl		__entry_insn_mmu_miss
+__entry_insn_mmu_miss:
+	break
+	nop
+
+	.globl		__entry_insn_mmu_exception
+__entry_insn_mmu_exception:
+	break
+	nop
+
+	.globl		__entry_data_mmu_miss
+__entry_data_mmu_miss:
+	break
+	nop
+
+	.globl		__entry_data_mmu_exception
+__entry_data_mmu_exception:
+	break
+	nop
+
+###############################################################################
+#
+# handle a lookup failure of one sort or another in a kernel TLB handler
+# On entry:
+#   GR29 - faulting address
+#   SCR2 - saved CCR
+#
+###############################################################################
+	.type		__tlb_kernel_fault,@function
+__tlb_kernel_fault:
+	# see if we're supposed to re-enable single-step mode upon return
+	sethi.p		%hi(__break_tlb_miss_return_break),gr30
+	setlo		%lo(__break_tlb_miss_return_break),gr30
+	movsg		pcsr,gr31
+
+	subcc		gr31,gr30,gr0,icc0
+	beq		icc0,#0,__tlb_kernel_fault_sstep
+
+	movsg		scr2,gr30
+	movgs		gr30,ccr
+	movgs		gr29,scr2			/* save EAR0 value */
+	sethi.p		%hi(__kernel_current_task),gr29
+	setlo		%lo(__kernel_current_task),gr29
+	ldi.p		@(gr29,#0),gr29			/* restore GR29 */
+
+	bra		__entry_kernel_handle_mmu_fault
+
+	# we've got to re-enable single-stepping
+__tlb_kernel_fault_sstep:
+	sethi.p		%hi(__break_tlb_miss_real_return_info),gr30
+	setlo		%lo(__break_tlb_miss_real_return_info),gr30
+	lddi		@(gr30,0),gr30
+	movgs		gr30,pcsr
+	movgs		gr31,psr
+
+	movsg		scr2,gr30
+	movgs		gr30,ccr
+	movgs		gr29,scr2			/* save EAR0 value */
+	sethi.p		%hi(__kernel_current_task),gr29
+	setlo		%lo(__kernel_current_task),gr29
+	ldi.p		@(gr29,#0),gr29			/* restore GR29 */
+	bra		__entry_kernel_handle_mmu_fault_sstep
+
+	.size		__tlb_kernel_fault, .-__tlb_kernel_fault
+
+###############################################################################
+#
+# handle a lookup failure of one sort or another in a user TLB handler
+# On entry:
+#   GR28 - faulting address
+#   SCR2 - saved CCR
+#
+###############################################################################
+	.type		__tlb_user_fault,@function
+__tlb_user_fault:
+	# see if we're supposed to re-enable single-step mode upon return
+	sethi.p		%hi(__break_tlb_miss_return_break),gr30
+	setlo		%lo(__break_tlb_miss_return_break),gr30
+	movsg		pcsr,gr31
+	subcc		gr31,gr30,gr0,icc0
+	beq		icc0,#0,__tlb_user_fault_sstep
+
+	movsg		scr2,gr30
+	movgs		gr30,ccr
+	bra		__entry_uspace_handle_mmu_fault
+
+	# we've got to re-enable single-stepping
+__tlb_user_fault_sstep:
+	sethi.p		%hi(__break_tlb_miss_real_return_info),gr30
+	setlo		%lo(__break_tlb_miss_real_return_info),gr30
+	lddi		@(gr30,0),gr30
+	movgs		gr30,pcsr
+	movgs		gr31,psr
+	movsg		scr2,gr30
+	movgs		gr30,ccr
+	bra		__entry_uspace_handle_mmu_fault_sstep
+
+	.size		__tlb_user_fault, .-__tlb_user_fault
+
+###############################################################################
+#
+# Kernel instruction TLB miss handler
+# On entry:
+#   GR1   - kernel stack pointer
+#   GR28  - saved exception frame pointer
+#   GR29  - faulting address
+#   GR31  - EAR0 ^ SCR0
+#   SCR0  - base of virtual range covered by cached PGE from last ITLB miss (or 0xffffffff)
+#   DAMR3 - mapped page directory
+#   DAMR4 - mapped page table as matched by SCR0
+#
+###############################################################################
+	.globl		__entry_kernel_insn_tlb_miss
+	.type		__entry_kernel_insn_tlb_miss,@function
+__entry_kernel_insn_tlb_miss:
+#if 0
+	sethi.p		%hi(0xe1200004),gr30
+	setlo		%lo(0xe1200004),gr30
+	st		gr0,@(gr30,gr0)
+	sethi.p		%hi(0xffc00100),gr30
+	setlo		%lo(0xffc00100),gr30
+	sth		gr30,@(gr30,gr0)
+	membar
+#endif
+
+	movsg		ccr,gr30			/* save CCR */
+	movgs		gr30,scr2
+
+	# see if the cached page table mapping is appropriate
+	srlicc.p	gr31,#26,gr0,icc0
+	setlos		0x3ffc,gr30
+	srli.p		gr29,#12,gr31			/* use EAR0[25:14] as PTE index */
+	bne		icc0,#0,__itlb_k_PTD_miss
+
+__itlb_k_PTD_mapped:
+	# access the PTD with EAR0[25:14]
+	# - DAMLR4 points to the virtual address of the appropriate page table
+	# - the PTD holds 4096 PTEs
+	# - the PTD must be accessed uncached
+	# - the PTE must be marked accessed if it was valid
+	#
+	and		gr31,gr30,gr31
+	movsg		damlr4,gr30
+	add		gr30,gr31,gr31
+	ldi		@(gr31,#0),gr30			/* fetch the PTE */
+	andicc		gr30,#_PAGE_PRESENT,gr0,icc0
+	ori.p		gr30,#_PAGE_ACCESSED,gr30
+	beq		icc0,#0,__tlb_kernel_fault	/* jump if PTE invalid */
+	sti.p		gr30,@(gr31,#0)			/* update the PTE */
+	andi		gr30,#~_PAGE_ACCESSED,gr30
+
+	# we're using IAMR1 as an extra TLB entry
+	# - punt the entry here (if valid) to the real TLB and then replace with the new PTE
+	# - need to check DAMR1 lest we cause an multiple-DAT-hit exception
+	# - IAMPR1 has no WP bit, and we mustn't lose WP information
+	movsg		iampr1,gr31
+	andicc		gr31,#xAMPRx_V,gr0,icc0
+	setlos.p	0xfffff000,gr31
+	beq		icc0,#0,__itlb_k_nopunt		/* punt not required */
+
+	movsg		iamlr1,gr31
+	movgs		gr31,tplr			/* set TPLR.CXN */
+	tlbpr		gr31,gr0,#4,#0			/* delete matches from TLB, IAMR1, DAMR1 */
+
+	movsg		dampr1,gr31
+	ori		gr31,#xAMPRx_V,gr31		/* entry was invalidated by tlbpr #4 */
+	movgs		gr31,tppr
+	movsg		iamlr1,gr31			/* set TPLR.CXN */
+	movgs		gr31,tplr
+	tlbpr		gr31,gr0,#2,#0			/* save to the TLB */
+	movsg		tpxr,gr31			/* check the TLB write error flag */
+	andicc.p	gr31,#TPXR_E,gr0,icc0
+	setlos		#0xfffff000,gr31
+	bne		icc0,#0,__tlb_kernel_fault
+
+__itlb_k_nopunt:
+
+	# assemble the new TLB entry
+	and		gr29,gr31,gr29
+	movsg		cxnr,gr31
+	or		gr29,gr31,gr29
+	movgs		gr29,iamlr1			/* xAMLR = address | context number */
+	movgs		gr30,iampr1
+	movgs		gr29,damlr1
+	movgs		gr30,dampr1
+
+	# return, restoring registers
+	movsg		scr2,gr30
+	movgs		gr30,ccr
+	sethi.p		%hi(__kernel_current_task),gr29
+	setlo		%lo(__kernel_current_task),gr29
+	ldi		@(gr29,#0),gr29
+	rett		#0
+	beq		icc0,#3,0			/* prevent icache prefetch */
+
+	# the PTE we want wasn't in the PTD we have mapped, so we need to go looking for a more
+	# appropriate page table and map that instead
+	#   - access the PGD with EAR0[31:26]
+	#   - DAMLR3 points to the virtual address of the page directory
+	#   - the PGD holds 64 PGEs and each PGE/PME points to a set of page tables
+__itlb_k_PTD_miss:
+	srli		gr29,#26,gr31			/* calculate PGE offset */
+	slli		gr31,#8,gr31			/* and clear bottom bits */
+
+	movsg		damlr3,gr30
+	ld		@(gr31,gr30),gr30		/* access the PGE */
+
+	andicc.p	gr30,#_PAGE_PRESENT,gr0,icc0
+	andicc		gr30,#xAMPRx_SS,gr0,icc1
+
+	# map this PTD instead and record coverage address
+	ori.p		gr30,#xAMPRx_L|xAMPRx_SS_16Kb|xAMPRx_S|xAMPRx_C|xAMPRx_V,gr30
+	beq		icc0,#0,__tlb_kernel_fault	/* jump if PGE not present */
+	slli.p		gr31,#18,gr31
+	bne		icc1,#0,__itlb_k_bigpage
+	movgs		gr30,dampr4
+	movgs		gr31,scr0
+
+	# we can now resume normal service
+	setlos		0x3ffc,gr30
+	srli.p		gr29,#12,gr31			/* use EAR0[25:14] as PTE index */
+	bra		__itlb_k_PTD_mapped
+
+__itlb_k_bigpage:
+	break
+	nop
+
+	.size		__entry_kernel_insn_tlb_miss, .-__entry_kernel_insn_tlb_miss
+
+###############################################################################
+#
+# Kernel data TLB miss handler
+# On entry:
+#   GR1   - kernel stack pointer
+#   GR28  - saved exception frame pointer
+#   GR29  - faulting address
+#   GR31  - EAR0 ^ SCR1
+#   SCR1  - base of virtual range covered by cached PGE from last DTLB miss (or 0xffffffff)
+#   DAMR3 - mapped page directory
+#   DAMR5 - mapped page table as matched by SCR1
+#
+###############################################################################
+	.globl		__entry_kernel_data_tlb_miss
+	.type		__entry_kernel_data_tlb_miss,@function
+__entry_kernel_data_tlb_miss:
+#if 0
+	sethi.p		%hi(0xe1200004),gr30
+	setlo		%lo(0xe1200004),gr30
+	st		gr0,@(gr30,gr0)
+	sethi.p		%hi(0xffc00100),gr30
+	setlo		%lo(0xffc00100),gr30
+	sth		gr30,@(gr30,gr0)
+	membar
+#endif
+
+	movsg		ccr,gr30			/* save CCR */
+	movgs		gr30,scr2
+
+	# see if the cached page table mapping is appropriate
+	srlicc.p	gr31,#26,gr0,icc0
+	setlos		0x3ffc,gr30
+	srli.p		gr29,#12,gr31			/* use EAR0[25:14] as PTE index */
+	bne		icc0,#0,__dtlb_k_PTD_miss
+
+__dtlb_k_PTD_mapped:
+	# access the PTD with EAR0[25:14]
+	# - DAMLR5 points to the virtual address of the appropriate page table
+	# - the PTD holds 4096 PTEs
+	# - the PTD must be accessed uncached
+	# - the PTE must be marked accessed if it was valid
+	#
+	and		gr31,gr30,gr31
+	movsg		damlr5,gr30
+	add		gr30,gr31,gr31
+	ldi		@(gr31,#0),gr30			/* fetch the PTE */
+	andicc		gr30,#_PAGE_PRESENT,gr0,icc0
+	ori.p		gr30,#_PAGE_ACCESSED,gr30
+	beq		icc0,#0,__tlb_kernel_fault	/* jump if PTE invalid */
+	sti.p		gr30,@(gr31,#0)			/* update the PTE */
+	andi		gr30,#~_PAGE_ACCESSED,gr30
+
+	# we're using DAMR1 as an extra TLB entry
+	# - punt the entry here (if valid) to the real TLB and then replace with the new PTE
+	# - need to check IAMR1 lest we cause an multiple-DAT-hit exception
+	movsg		dampr1,gr31
+	andicc		gr31,#xAMPRx_V,gr0,icc0
+	setlos.p	0xfffff000,gr31
+	beq		icc0,#0,__dtlb_k_nopunt		/* punt not required */
+
+	movsg		damlr1,gr31
+	movgs		gr31,tplr			/* set TPLR.CXN */
+	tlbpr		gr31,gr0,#4,#0			/* delete matches from TLB, IAMR1, DAMR1 */
+
+	movsg		dampr1,gr31
+	ori		gr31,#xAMPRx_V,gr31		/* entry was invalidated by tlbpr #4 */
+	movgs		gr31,tppr
+	movsg		damlr1,gr31			/* set TPLR.CXN */
+	movgs		gr31,tplr
+	tlbpr		gr31,gr0,#2,#0			/* save to the TLB */
+	movsg		tpxr,gr31			/* check the TLB write error flag */
+	andicc.p	gr31,#TPXR_E,gr0,icc0
+	setlos		#0xfffff000,gr31
+	bne		icc0,#0,__tlb_kernel_fault
+
+__dtlb_k_nopunt:
+
+	# assemble the new TLB entry
+	and		gr29,gr31,gr29
+	movsg		cxnr,gr31
+	or		gr29,gr31,gr29
+	movgs		gr29,iamlr1			/* xAMLR = address | context number */
+	movgs		gr30,iampr1
+	movgs		gr29,damlr1
+	movgs		gr30,dampr1
+
+	# return, restoring registers
+	movsg		scr2,gr30
+	movgs		gr30,ccr
+	sethi.p		%hi(__kernel_current_task),gr29
+	setlo		%lo(__kernel_current_task),gr29
+	ldi		@(gr29,#0),gr29
+	rett		#0
+	beq		icc0,#3,0			/* prevent icache prefetch */
+
+	# the PTE we want wasn't in the PTD we have mapped, so we need to go looking for a more
+	# appropriate page table and map that instead
+	#   - access the PGD with EAR0[31:26]
+	#   - DAMLR3 points to the virtual address of the page directory
+	#   - the PGD holds 64 PGEs and each PGE/PME points to a set of page tables
+__dtlb_k_PTD_miss:
+	srli		gr29,#26,gr31			/* calculate PGE offset */
+	slli		gr31,#8,gr31			/* and clear bottom bits */
+
+	movsg		damlr3,gr30
+	ld		@(gr31,gr30),gr30		/* access the PGE */
+
+	andicc.p	gr30,#_PAGE_PRESENT,gr0,icc0
+	andicc		gr30,#xAMPRx_SS,gr0,icc1
+
+	# map this PTD instead and record coverage address
+	ori.p		gr30,#xAMPRx_L|xAMPRx_SS_16Kb|xAMPRx_S|xAMPRx_C|xAMPRx_V,gr30
+	beq		icc0,#0,__tlb_kernel_fault	/* jump if PGE not present */
+	slli.p		gr31,#18,gr31
+	bne		icc1,#0,__dtlb_k_bigpage
+	movgs		gr30,dampr5
+	movgs		gr31,scr1
+
+	# we can now resume normal service
+	setlos		0x3ffc,gr30
+	srli.p		gr29,#12,gr31			/* use EAR0[25:14] as PTE index */
+	bra		__dtlb_k_PTD_mapped
+
+__dtlb_k_bigpage:
+	break
+	nop
+
+	.size		__entry_kernel_data_tlb_miss, .-__entry_kernel_data_tlb_miss
+
+###############################################################################
+#
+# Userspace instruction TLB miss handler (with PGE prediction)
+# On entry:
+#   GR28  - faulting address
+#   GR31  - EAR0 ^ SCR0
+#   SCR0  - base of virtual range covered by cached PGE from last ITLB miss (or 0xffffffff)
+#   DAMR3 - mapped page directory
+#   DAMR4 - mapped page table as matched by SCR0
+#
+###############################################################################
+	.globl		__entry_user_insn_tlb_miss
+	.type		__entry_user_insn_tlb_miss,@function
+__entry_user_insn_tlb_miss:
+#if 0
+	sethi.p		%hi(0xe1200004),gr30
+	setlo		%lo(0xe1200004),gr30
+	st		gr0,@(gr30,gr0)
+	sethi.p		%hi(0xffc00100),gr30
+	setlo		%lo(0xffc00100),gr30
+	sth		gr30,@(gr30,gr0)
+	membar
+#endif
+
+	movsg		ccr,gr30			/* save CCR */
+	movgs		gr30,scr2
+
+	# see if the cached page table mapping is appropriate
+	srlicc.p	gr31,#26,gr0,icc0
+	setlos		0x3ffc,gr30
+	srli.p		gr28,#12,gr31			/* use EAR0[25:14] as PTE index */
+	bne		icc0,#0,__itlb_u_PTD_miss
+
+__itlb_u_PTD_mapped:
+	# access the PTD with EAR0[25:14]
+	# - DAMLR4 points to the virtual address of the appropriate page table
+	# - the PTD holds 4096 PTEs
+	# - the PTD must be accessed uncached
+	# - the PTE must be marked accessed if it was valid
+	#
+	and		gr31,gr30,gr31
+	movsg		damlr4,gr30
+	add		gr30,gr31,gr31
+	ldi		@(gr31,#0),gr30			/* fetch the PTE */
+	andicc		gr30,#_PAGE_PRESENT,gr0,icc0
+	ori.p		gr30,#_PAGE_ACCESSED,gr30
+	beq		icc0,#0,__tlb_user_fault	/* jump if PTE invalid */
+	sti.p		gr30,@(gr31,#0)			/* update the PTE */
+	andi		gr30,#~_PAGE_ACCESSED,gr30
+
+	# we're using IAMR1/DAMR1 as an extra TLB entry
+	# - punt the entry here (if valid) to the real TLB and then replace with the new PTE
+	movsg		dampr1,gr31
+	andicc		gr31,#xAMPRx_V,gr0,icc0
+	setlos.p	0xfffff000,gr31
+	beq		icc0,#0,__itlb_u_nopunt		/* punt not required */
+
+	movsg		dampr1,gr31
+	movgs		gr31,tppr
+	movsg		damlr1,gr31			/* set TPLR.CXN */
+	movgs		gr31,tplr
+	tlbpr		gr31,gr0,#2,#0			/* save to the TLB */
+	movsg		tpxr,gr31			/* check the TLB write error flag */
+	andicc.p	gr31,#TPXR_E,gr0,icc0
+	setlos		#0xfffff000,gr31
+	bne		icc0,#0,__tlb_user_fault
+
+__itlb_u_nopunt:
+
+	# assemble the new TLB entry
+	and		gr28,gr31,gr28
+	movsg		cxnr,gr31
+	or		gr28,gr31,gr28
+	movgs		gr28,iamlr1			/* xAMLR = address | context number */
+	movgs		gr30,iampr1
+	movgs		gr28,damlr1
+	movgs		gr30,dampr1
+
+	# return, restoring registers
+	movsg		scr2,gr30
+	movgs		gr30,ccr
+	rett		#0
+	beq		icc0,#3,0			/* prevent icache prefetch */
+
+	# the PTE we want wasn't in the PTD we have mapped, so we need to go looking for a more
+	# appropriate page table and map that instead
+	#   - access the PGD with EAR0[31:26]
+	#   - DAMLR3 points to the virtual address of the page directory
+	#   - the PGD holds 64 PGEs and each PGE/PME points to a set of page tables
+__itlb_u_PTD_miss:
+	srli		gr28,#26,gr31			/* calculate PGE offset */
+	slli		gr31,#8,gr31			/* and clear bottom bits */
+
+	movsg		damlr3,gr30
+	ld		@(gr31,gr30),gr30		/* access the PGE */
+
+	andicc.p	gr30,#_PAGE_PRESENT,gr0,icc0
+	andicc		gr30,#xAMPRx_SS,gr0,icc1
+
+	# map this PTD instead and record coverage address
+	ori.p		gr30,#xAMPRx_L|xAMPRx_SS_16Kb|xAMPRx_S|xAMPRx_C|xAMPRx_V,gr30
+	beq		icc0,#0,__tlb_user_fault	/* jump if PGE not present */
+	slli.p		gr31,#18,gr31
+	bne		icc1,#0,__itlb_u_bigpage
+	movgs		gr30,dampr4
+	movgs		gr31,scr0
+
+	# we can now resume normal service
+	setlos		0x3ffc,gr30
+	srli.p		gr28,#12,gr31			/* use EAR0[25:14] as PTE index */
+	bra		__itlb_u_PTD_mapped
+
+__itlb_u_bigpage:
+	break
+	nop
+
+	.size		__entry_user_insn_tlb_miss, .-__entry_user_insn_tlb_miss
+
+###############################################################################
+#
+# Userspace data TLB miss handler
+# On entry:
+#   GR28  - faulting address
+#   GR31  - EAR0 ^ SCR1
+#   SCR1  - base of virtual range covered by cached PGE from last DTLB miss (or 0xffffffff)
+#   DAMR3 - mapped page directory
+#   DAMR5 - mapped page table as matched by SCR1
+#
+###############################################################################
+	.globl		__entry_user_data_tlb_miss
+	.type		__entry_user_data_tlb_miss,@function
+__entry_user_data_tlb_miss:
+#if 0
+	sethi.p		%hi(0xe1200004),gr30
+	setlo		%lo(0xe1200004),gr30
+	st		gr0,@(gr30,gr0)
+	sethi.p		%hi(0xffc00100),gr30
+	setlo		%lo(0xffc00100),gr30
+	sth		gr30,@(gr30,gr0)
+	membar
+#endif
+
+	movsg		ccr,gr30			/* save CCR */
+	movgs		gr30,scr2
+
+	# see if the cached page table mapping is appropriate
+	srlicc.p	gr31,#26,gr0,icc0
+	setlos		0x3ffc,gr30
+	srli.p		gr28,#12,gr31			/* use EAR0[25:14] as PTE index */
+	bne		icc0,#0,__dtlb_u_PTD_miss
+
+__dtlb_u_PTD_mapped:
+	# access the PTD with EAR0[25:14]
+	# - DAMLR5 points to the virtual address of the appropriate page table
+	# - the PTD holds 4096 PTEs
+	# - the PTD must be accessed uncached
+	# - the PTE must be marked accessed if it was valid
+	#
+	and		gr31,gr30,gr31
+	movsg		damlr5,gr30
+
+__dtlb_u_using_iPTD:
+	add		gr30,gr31,gr31
+	ldi		@(gr31,#0),gr30			/* fetch the PTE */
+	andicc		gr30,#_PAGE_PRESENT,gr0,icc0
+	ori.p		gr30,#_PAGE_ACCESSED,gr30
+	beq		icc0,#0,__tlb_user_fault	/* jump if PTE invalid */
+	sti.p		gr30,@(gr31,#0)			/* update the PTE */
+	andi		gr30,#~_PAGE_ACCESSED,gr30
+
+	# we're using DAMR1 as an extra TLB entry
+	# - punt the entry here (if valid) to the real TLB and then replace with the new PTE
+	movsg		dampr1,gr31
+	andicc		gr31,#xAMPRx_V,gr0,icc0
+	setlos.p	0xfffff000,gr31
+	beq		icc0,#0,__dtlb_u_nopunt		/* punt not required */
+
+	movsg		dampr1,gr31
+	movgs		gr31,tppr
+	movsg		damlr1,gr31			/* set TPLR.CXN */
+	movgs		gr31,tplr
+	tlbpr		gr31,gr0,#2,#0			/* save to the TLB */
+	movsg		tpxr,gr31			/* check the TLB write error flag */
+	andicc.p	gr31,#TPXR_E,gr0,icc0
+	setlos		#0xfffff000,gr31
+	bne		icc0,#0,__tlb_user_fault
+
+__dtlb_u_nopunt:
+
+	# assemble the new TLB entry
+	and		gr28,gr31,gr28
+	movsg		cxnr,gr31
+	or		gr28,gr31,gr28
+	movgs		gr28,iamlr1			/* xAMLR = address | context number */
+	movgs		gr30,iampr1
+	movgs		gr28,damlr1
+	movgs		gr30,dampr1
+
+	# return, restoring registers
+	movsg		scr2,gr30
+	movgs		gr30,ccr
+	rett		#0
+	beq		icc0,#3,0			/* prevent icache prefetch */
+
+	# the PTE we want wasn't in the PTD we have mapped, so we need to go looking for a more
+	# appropriate page table and map that instead
+	#   - first of all, check the insn PGE cache - we may well get a hit there
+	#   - access the PGD with EAR0[31:26]
+	#   - DAMLR3 points to the virtual address of the page directory
+	#   - the PGD holds 64 PGEs and each PGE/PME points to a set of page tables
+__dtlb_u_PTD_miss:
+	movsg		scr0,gr31			/* consult the insn-PGE-cache key */
+	xor		gr28,gr31,gr31
+	srlicc		gr31,#26,gr0,icc0
+	srli		gr28,#12,gr31			/* use EAR0[25:14] as PTE index */
+	bne		icc0,#0,__dtlb_u_iPGE_miss
+
+	# what we're looking for is covered by the insn-PGE-cache
+	setlos		0x3ffc,gr30
+	and		gr31,gr30,gr31
+	movsg		damlr4,gr30
+	bra		__dtlb_u_using_iPTD
+
+__dtlb_u_iPGE_miss:
+	srli		gr28,#26,gr31			/* calculate PGE offset */
+	slli		gr31,#8,gr31			/* and clear bottom bits */
+
+	movsg		damlr3,gr30
+	ld		@(gr31,gr30),gr30		/* access the PGE */
+
+	andicc.p	gr30,#_PAGE_PRESENT,gr0,icc0
+	andicc		gr30,#xAMPRx_SS,gr0,icc1
+
+	# map this PTD instead and record coverage address
+	ori.p		gr30,#xAMPRx_L|xAMPRx_SS_16Kb|xAMPRx_S|xAMPRx_C|xAMPRx_V,gr30
+	beq		icc0,#0,__tlb_user_fault	/* jump if PGE not present */
+	slli.p		gr31,#18,gr31
+	bne		icc1,#0,__dtlb_u_bigpage
+	movgs		gr30,dampr5
+	movgs		gr31,scr1
+
+	# we can now resume normal service
+	setlos		0x3ffc,gr30
+	srli.p		gr28,#12,gr31			/* use EAR0[25:14] as PTE index */
+	bra		__dtlb_u_PTD_mapped
+
+__dtlb_u_bigpage:
+	break
+	nop
+
+	.size		__entry_user_data_tlb_miss, .-__entry_user_data_tlb_miss
diff --git a/arch/frv/mm/unaligned.c b/arch/frv/mm/unaligned.c
new file mode 100644
index 0000000..09b3614
--- /dev/null
+++ b/arch/frv/mm/unaligned.c
@@ -0,0 +1,218 @@
+/* unaligned.c: unalignment fixup handler for CPUs on which it is supported (FR451 only)
+ *
+ * Copyright (C) 2004 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 License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/config.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/types.h>
+#include <linux/user.h>
+#include <linux/string.h>
+#include <linux/linkage.h>
+#include <linux/init.h>
+
+#include <asm/setup.h>
+#include <asm/system.h>
+#include <asm/uaccess.h>
+
+#if 0
+#define kdebug(fmt, ...) printk("FDPIC "fmt"\n" ,##__VA_ARGS__ )
+#else
+#define kdebug(fmt, ...) do {} while(0)
+#endif
+
+#define _MA_SIGNED	0x01
+#define _MA_HALF	0x02
+#define _MA_WORD	0x04
+#define _MA_DWORD	0x08
+#define _MA_SZ_MASK	0x0e
+#define _MA_LOAD	0x10
+#define _MA_STORE	0x20
+#define _MA_UPDATE	0x40
+#define _MA_IMM		0x80
+
+#define _MA_LDxU	_MA_LOAD | _MA_UPDATE
+#define _MA_LDxI	_MA_LOAD | _MA_IMM
+#define _MA_STxU	_MA_STORE | _MA_UPDATE
+#define _MA_STxI	_MA_STORE | _MA_IMM
+
+static const uint8_t tbl_LDGRk_reg[0x40] = {
+	[0x02] = _MA_LOAD | _MA_HALF | _MA_SIGNED,	/* LDSH  @(GRi,GRj),GRk */
+	[0x03] = _MA_LOAD | _MA_HALF,			/* LDUH  @(GRi,GRj),GRk */
+	[0x04] = _MA_LOAD | _MA_WORD,			/* LD	 @(GRi,GRj),GRk */
+	[0x05] = _MA_LOAD | _MA_DWORD,			/* LDD	 @(GRi,GRj),GRk */
+	[0x12] = _MA_LDxU | _MA_HALF | _MA_SIGNED,	/* LDSHU @(GRi,GRj),GRk */
+	[0x13] = _MA_LDxU | _MA_HALF,			/* LDUHU @(GRi,GRj),GRk */
+	[0x14] = _MA_LDxU | _MA_WORD,			/* LDU	 @(GRi,GRj),GRk */
+	[0x15] = _MA_LDxU | _MA_DWORD,			/* LDDU	 @(GRi,GRj),GRk */
+};
+
+static const uint8_t tbl_STGRk_reg[0x40] = {
+	[0x01] = _MA_STORE | _MA_HALF,			/* STH   @(GRi,GRj),GRk */
+	[0x02] = _MA_STORE | _MA_WORD,			/* ST	 @(GRi,GRj),GRk */
+	[0x03] = _MA_STORE | _MA_DWORD,			/* STD	 @(GRi,GRj),GRk */
+	[0x11] = _MA_STxU  | _MA_HALF,			/* STHU  @(GRi,GRj),GRk */
+	[0x12] = _MA_STxU  | _MA_WORD,			/* STU	 @(GRi,GRj),GRk */
+	[0x13] = _MA_STxU  | _MA_DWORD,			/* STDU	 @(GRi,GRj),GRk */
+};
+
+static const uint8_t tbl_LDSTGRk_imm[0x80] = {
+	[0x31] = _MA_LDxI | _MA_HALF | _MA_SIGNED,	/* LDSHI @(GRi,d12),GRk */
+	[0x32] = _MA_LDxI | _MA_WORD,			/* LDI   @(GRi,d12),GRk */
+	[0x33] = _MA_LDxI | _MA_DWORD,			/* LDDI  @(GRi,d12),GRk */
+	[0x36] = _MA_LDxI | _MA_HALF,			/* LDUHI @(GRi,d12),GRk */
+	[0x51] = _MA_STxI | _MA_HALF,			/* STHI  @(GRi,d12),GRk */
+	[0x52] = _MA_STxI | _MA_WORD,			/* STI   @(GRi,d12),GRk */
+	[0x53] = _MA_STxI | _MA_DWORD,			/* STDI  @(GRi,d12),GRk */
+};
+
+
+/*****************************************************************************/
+/*
+ * see if we can handle the exception by fixing up a misaligned memory access
+ */
+int handle_misalignment(unsigned long esr0, unsigned long ear0, unsigned long epcr0)
+{
+	unsigned long insn, addr, *greg;
+	int GRi, GRj, GRk, D12, op;
+
+	union {
+		uint64_t _64;
+		uint32_t _32[2];
+		uint16_t _16;
+		uint8_t _8[8];
+	} x;
+
+	if (!(esr0 & ESR0_EAV) || !(epcr0 & EPCR0_V) || !(ear0 & 7))
+		return -EAGAIN;
+
+	epcr0 &= EPCR0_PC;
+
+	if (__frame->pc != epcr0) {
+		kdebug("MISALIGN: Execution not halted on excepting instruction\n");
+		BUG();
+	}
+
+	if (__get_user(insn, (unsigned long *) epcr0) < 0)
+		return -EFAULT;
+
+	/* determine the instruction type first */
+	switch ((insn >> 18) & 0x7f) {
+	case 0x2:
+		/* LDx @(GRi,GRj),GRk */
+		op = tbl_LDGRk_reg[(insn >> 6) & 0x3f];
+		break;
+
+	case 0x3:
+		/* STx GRk,@(GRi,GRj) */
+		op = tbl_STGRk_reg[(insn >> 6) & 0x3f];
+		break;
+
+	default:
+		op = tbl_LDSTGRk_imm[(insn >> 18) & 0x7f];
+		break;
+	}
+
+	if (!op)
+		return -EAGAIN;
+
+	kdebug("MISALIGN: pc=%08lx insn=%08lx ad=%08lx op=%02x\n", epcr0, insn, ear0, op);
+
+	memset(&x, 0xba, 8);
+
+	/* validate the instruction parameters */
+	greg = (unsigned long *) &__frame->tbr;
+
+	GRi = (insn >> 12) & 0x3f;
+	GRk = (insn >> 25) & 0x3f;
+
+	if (GRi > 31 || GRk > 31)
+		return -ENOENT;
+
+	if (op & _MA_DWORD && GRk & 1)
+		return -EINVAL;
+
+	if (op & _MA_IMM) {
+		D12 = insn & 0xfff;
+		asm ("slli %0,#20,%0 ! srai %0,#20,%0" : "=r"(D12) : "0"(D12)); /* sign extend */
+		addr = (GRi ? greg[GRi] : 0) + D12;
+	}
+	else {
+		GRj = (insn >>  0) & 0x3f;
+		if (GRj > 31)
+			return -ENOENT;
+		addr = (GRi ? greg[GRi] : 0) + (GRj ? greg[GRj] : 0);
+	}
+
+	if (addr != ear0) {
+		kdebug("MISALIGN: Calculated addr (%08lx) does not match EAR0 (%08lx)\n",
+		       addr, ear0);
+		return -EFAULT;
+	}
+
+	/* check the address is okay */
+	if (user_mode(__frame) && ___range_ok(ear0, 8) < 0)
+		return -EFAULT;
+
+	/* perform the memory op */
+	if (op & _MA_STORE) {
+		/* perform a store */
+		x._32[0] = 0;
+		if (GRk != 0) {
+			if (op & _MA_HALF) {
+				x._16 = greg[GRk];
+			}
+			else {
+				x._32[0] = greg[GRk];
+			}
+		}
+		if (op & _MA_DWORD)
+			x._32[1] = greg[GRk + 1];
+
+		kdebug("MISALIGN: Store GR%d { %08x:%08x } -> %08lx (%dB)\n",
+		       GRk, x._32[1], x._32[0], addr, op & _MA_SZ_MASK);
+
+		if (__memcpy_user((void *) addr, &x, op & _MA_SZ_MASK) != 0)
+			return -EFAULT;
+	}
+	else {
+		/* perform a load */
+		if (__memcpy_user(&x, (void *) addr, op & _MA_SZ_MASK) != 0)
+			return -EFAULT;
+
+		if (op & _MA_HALF) {
+			if (op & _MA_SIGNED)
+				asm ("slli %0,#16,%0 ! srai %0,#16,%0"
+				     : "=r"(x._32[0]) : "0"(x._16));
+			else
+				asm ("sethi #0,%0"
+				     : "=r"(x._32[0]) : "0"(x._16));
+		}
+
+		kdebug("MISALIGN: Load %08lx (%dB) -> GR%d, { %08x:%08x }\n",
+		       addr, op & _MA_SZ_MASK, GRk, x._32[1], x._32[0]);
+
+		if (GRk != 0)
+			greg[GRk] = x._32[0];
+		if (op & _MA_DWORD)
+			greg[GRk + 1] = x._32[1];
+	}
+
+	/* update the base pointer if required */
+	if (op & _MA_UPDATE)
+		greg[GRi] = addr;
+
+	/* well... we've done that insn */
+	__frame->pc = __frame->pc + 4;
+
+	return 0;
+} /* end handle_misalignment() */