summaryrefslogtreecommitdiff
path: root/arch/frv
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /arch/frv
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
Diffstat (limited to 'arch/frv')
-rw-r--r--arch/frv/Kconfig357
-rw-r--r--arch/frv/Kconfig.debug74
-rw-r--r--arch/frv/Makefile118
-rw-r--r--arch/frv/boot/Makefile73
-rw-r--r--arch/frv/kernel/Makefile22
-rw-r--r--arch/frv/kernel/break.S720
-rw-r--r--arch/frv/kernel/cmode.S190
-rw-r--r--arch/frv/kernel/debug-stub.c259
-rw-r--r--arch/frv/kernel/dma.c464
-rw-r--r--arch/frv/kernel/entry-table.S295
-rw-r--r--arch/frv/kernel/entry.S1428
-rw-r--r--arch/frv/kernel/frv_ksyms.c124
-rw-r--r--arch/frv/kernel/gdb-io.c216
-rw-r--r--arch/frv/kernel/gdb-io.h55
-rw-r--r--arch/frv/kernel/gdb-stub.c2084
-rw-r--r--arch/frv/kernel/head-mmu-fr451.S374
-rw-r--r--arch/frv/kernel/head-uc-fr401.S311
-rw-r--r--arch/frv/kernel/head-uc-fr451.S174
-rw-r--r--arch/frv/kernel/head-uc-fr555.S347
-rw-r--r--arch/frv/kernel/head.S639
-rw-r--r--arch/frv/kernel/head.inc50
-rw-r--r--arch/frv/kernel/init_task.c39
-rw-r--r--arch/frv/kernel/irq-mb93091.c116
-rw-r--r--arch/frv/kernel/irq-mb93093.c99
-rw-r--r--arch/frv/kernel/irq-mb93493.c108
-rw-r--r--arch/frv/kernel/irq-routing.c291
-rw-r--r--arch/frv/kernel/irq.c764
-rw-r--r--arch/frv/kernel/kernel_thread.S77
-rw-r--r--arch/frv/kernel/local.h56
-rw-r--r--arch/frv/kernel/pm-mb93093.c66
-rw-r--r--arch/frv/kernel/pm.c432
-rw-r--r--arch/frv/kernel/process.c388
-rw-r--r--arch/frv/kernel/ptrace.c764
-rw-r--r--arch/frv/kernel/semaphore.c156
-rw-r--r--arch/frv/kernel/setup.c1194
-rw-r--r--arch/frv/kernel/signal.c588
-rw-r--r--arch/frv/kernel/sleep.S374
-rw-r--r--arch/frv/kernel/switch_to.S496
-rw-r--r--arch/frv/kernel/sys_frv.c214
-rw-r--r--arch/frv/kernel/sysctl.c206
-rw-r--r--arch/frv/kernel/time.c234
-rw-r--r--arch/frv/kernel/traps.c431
-rw-r--r--arch/frv/kernel/uaccess.c95
-rw-r--r--arch/frv/kernel/vmlinux.lds.S187
-rw-r--r--arch/frv/lib/Makefile8
-rw-r--r--arch/frv/lib/__ashldi3.S40
-rw-r--r--arch/frv/lib/__ashrdi3.S41
-rw-r--r--arch/frv/lib/__lshrdi3.S40
-rw-r--r--arch/frv/lib/__muldi3.S32
-rw-r--r--arch/frv/lib/__negdi2.S28
-rw-r--r--arch/frv/lib/atomic-ops.S265
-rw-r--r--arch/frv/lib/cache.S98
-rw-r--r--arch/frv/lib/checksum.c148
-rw-r--r--arch/frv/lib/insl_ns.S52
-rw-r--r--arch/frv/lib/insl_sw.S40
-rw-r--r--arch/frv/lib/memcpy.S135
-rw-r--r--arch/frv/lib/memset.S182
-rw-r--r--arch/frv/lib/outsl_ns.S59
-rw-r--r--arch/frv/lib/outsl_sw.S45
-rw-r--r--arch/frv/mb93090-mb00/Makefile13
-rw-r--r--arch/frv/mb93090-mb00/pci-dma-nommu.c152
-rw-r--r--arch/frv/mb93090-mb00/pci-dma.c105
-rw-r--r--arch/frv/mb93090-mb00/pci-frv.c288
-rw-r--r--arch/frv/mb93090-mb00/pci-frv.h47
-rw-r--r--arch/frv/mb93090-mb00/pci-irq.c70
-rw-r--r--arch/frv/mb93090-mb00/pci-vdk.c467
-rw-r--r--arch/frv/mm/Makefile9
-rw-r--r--arch/frv/mm/cache-page.c66
-rw-r--r--arch/frv/mm/dma-alloc.c188
-rw-r--r--arch/frv/mm/elf-fdpic.c123
-rw-r--r--arch/frv/mm/extable.c91
-rw-r--r--arch/frv/mm/fault.c325
-rw-r--r--arch/frv/mm/highmem.c33
-rw-r--r--arch/frv/mm/init.c241
-rw-r--r--arch/frv/mm/kmap.c62
-rw-r--r--arch/frv/mm/mmu-context.c208
-rw-r--r--arch/frv/mm/pgalloc.c159
-rw-r--r--arch/frv/mm/tlb-flush.S185
-rw-r--r--arch/frv/mm/tlb-miss.S631
-rw-r--r--arch/frv/mm/unaligned.c218
80 files changed, 20643 insertions, 0 deletions
diff --git a/arch/frv/Kconfig b/arch/frv/Kconfig
new file mode 100644
index 00000000000..2b19372767e
--- /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 00000000000..0034b654995
--- /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 00000000000..54046d2386f
--- /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 00000000000..d75e0d77136
--- /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 00000000000..981c2c7dec0
--- /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 00000000000..33233dc23e2
--- /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 00000000000..6591e6a37ae
--- /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 00000000000..4761cc4b4a9
--- /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 00000000000..f5de6cf7df4
--- /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 00000000000..9b9243e2103
--- /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 00000000000..ad10ea59545
--- /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 00000000000..62cfbd9b4f9
--- /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 00000000000..c997bccb922
--- /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 00000000000..138714bacc4
--- /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 00000000000..8f860d9c494
--- /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 00000000000..a143c2f66ee
--- /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 00000000000..4ccf8414ae4
--- /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 00000000000..31cb54a6f08
--- /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 00000000000..d088db2699b
--- /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 00000000000..c73b4fe9f6c
--- /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 00000000000..d424cd2eb21
--- /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 00000000000..22993932b3f
--- /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 00000000000..9778e0ff7c1
--- /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 00000000000..21ca2b29824
--- /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 00000000000..c003ae5e2b3
--- /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 00000000000..d4776d1f4e8
--- /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 00000000000..8c524cdd271
--- /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 00000000000..4531c830d20
--- /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 00000000000..e9471761d78
--- /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 00000000000..34d01d7dcc3
--- /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 00000000000..1a1e8a119c3
--- /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 00000000000..3001b82b151
--- /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 00000000000..2a0efb739ad
--- /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 00000000000..5cba9c1f2b3
--- /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 00000000000..ef6865f0b97
--- /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 00000000000..d8d8f3d4304
--- /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 00000000000..e6079b8cac6
--- /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 00000000000..1703dc20174
--- /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 00000000000..931aa6d895e
--- /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 00000000000..408b0f382b4
--- /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 00000000000..075db664469
--- /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 00000000000..89073cae4b5
--- /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 00000000000..f3fd58a5bc4
--- /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 00000000000..fceafd2cc20
--- /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 00000000000..19be2626d5e
--- /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 00000000000..db5b6dc37a1
--- /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 00000000000..5742665bfd2
--- /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 00000000000..7b41f6304f0
--- /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 00000000000..2703d9b7936
--- /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 00000000000..d1747bf2499
--- /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 00000000000..b03d510a89e
--- /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 00000000000..0e10ad8dc46
--- /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 00000000000..7bf5bd6cac8
--- /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 00000000000..d1658425a9f
--- /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 00000000000..9b5aa95d069
--- /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 00000000000..9c596527342
--- /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 00000000000..55a35263cbe
--- /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 00000000000..4cd4c46a696
--- /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 00000000000..7eb56d35a95
--- /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 00000000000..3faf0f8cf9b
--- /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 00000000000..819895cf0b9
--- /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 00000000000..27eb1206650
--- /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 00000000000..83e5489cf03
--- /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 00000000000..7481797ab38
--- /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 00000000000..24622d89b1c
--- /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 00000000000..c8817f7b860
--- /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 00000000000..fb8b1d860f4
--- /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 00000000000..683b5e34431
--- /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 00000000000..4b38d45435f
--- /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 00000000000..f5a653033fe
--- /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 00000000000..41be1128dc6
--- /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 00000000000..41d02ac4823
--- /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 00000000000..7dc8fbf3af9
--- /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 00000000000..41958f57c83
--- /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 00000000000..539f45e6d15
--- /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 00000000000..f2c6866fc88
--- /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 00000000000..4eaec0f3525
--- /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 00000000000..6f43c74c5d9
--- /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 00000000000..8729f7d7c6e
--- /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 00000000000..09b361443fc
--- /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() */