summaryrefslogtreecommitdiffstats
path: root/arch/xtensa
diff options
context:
space:
mode:
Diffstat (limited to 'arch/xtensa')
-rw-r--r--arch/xtensa/Kconfig260
-rw-r--r--arch/xtensa/Kconfig.debug7
-rw-r--r--arch/xtensa/Makefile104
-rw-r--r--arch/xtensa/boot/Makefile33
-rw-r--r--arch/xtensa/boot/boot-elf/Makefile52
-rw-r--r--arch/xtensa/boot/boot-elf/boot.ld71
-rw-r--r--arch/xtensa/boot/boot-elf/bootstrap.S37
-rw-r--r--arch/xtensa/boot/boot-redboot/Makefile35
-rw-r--r--arch/xtensa/boot/boot-redboot/boot.ld66
-rw-r--r--arch/xtensa/boot/boot-redboot/bootstrap.S246
-rw-r--r--arch/xtensa/boot/lib/Makefile17
-rw-r--r--arch/xtensa/boot/lib/zmem.c79
-rw-r--r--arch/xtensa/boot/ramdisk/Makefile23
-rw-r--r--arch/xtensa/configs/common_defconfig662
-rw-r--r--arch/xtensa/configs/iss_defconfig531
-rw-r--r--arch/xtensa/kernel/Makefile18
-rw-r--r--arch/xtensa/kernel/align.S459
-rw-r--r--arch/xtensa/kernel/asm-offsets.c94
-rw-r--r--arch/xtensa/kernel/coprocessor.S201
-rw-r--r--arch/xtensa/kernel/entry.S1996
-rw-r--r--arch/xtensa/kernel/head.S237
-rw-r--r--arch/xtensa/kernel/irq.c192
-rw-r--r--arch/xtensa/kernel/module.c78
-rw-r--r--arch/xtensa/kernel/pci-dma.c73
-rw-r--r--arch/xtensa/kernel/pci.c478
-rw-r--r--arch/xtensa/kernel/platform.c49
-rw-r--r--arch/xtensa/kernel/process.c482
-rw-r--r--arch/xtensa/kernel/ptrace.c408
-rw-r--r--arch/xtensa/kernel/semaphore.c226
-rw-r--r--arch/xtensa/kernel/setup.c520
-rw-r--r--arch/xtensa/kernel/signal.c713
-rw-r--r--arch/xtensa/kernel/syscalls.c418
-rw-r--r--arch/xtensa/kernel/syscalls.h248
-rw-r--r--arch/xtensa/kernel/time.c227
-rw-r--r--arch/xtensa/kernel/traps.c498
-rw-r--r--arch/xtensa/kernel/vectors.S464
-rw-r--r--arch/xtensa/kernel/vmlinux.lds.S341
-rw-r--r--arch/xtensa/kernel/xtensa_ksyms.c123
-rw-r--r--arch/xtensa/lib/Makefile7
-rw-r--r--arch/xtensa/lib/checksum.S410
-rw-r--r--arch/xtensa/lib/memcopy.S315
-rw-r--r--arch/xtensa/lib/memset.S160
-rw-r--r--arch/xtensa/lib/pci-auto.c352
-rw-r--r--arch/xtensa/lib/strcasecmp.c32
-rw-r--r--arch/xtensa/lib/strncpy_user.S224
-rw-r--r--arch/xtensa/lib/strnlen_user.S147
-rw-r--r--arch/xtensa/lib/usercopy.S321
-rw-r--r--arch/xtensa/mm/Makefile13
-rw-r--r--arch/xtensa/mm/fault.c241
-rw-r--r--arch/xtensa/mm/init.c551
-rw-r--r--arch/xtensa/mm/misc.S374
-rw-r--r--arch/xtensa/mm/pgtable.c76
-rw-r--r--arch/xtensa/mm/tlb.c545
-rw-r--r--arch/xtensa/platform-iss/Makefile13
-rw-r--r--arch/xtensa/platform-iss/console.c300
-rw-r--r--arch/xtensa/platform-iss/io.c32
-rw-r--r--arch/xtensa/platform-iss/network.c855
-rw-r--r--arch/xtensa/platform-iss/setup.c112
58 files changed, 15846 insertions, 0 deletions
diff --git a/arch/xtensa/Kconfig b/arch/xtensa/Kconfig
new file mode 100644
index 000000000000..c9b5d298e3c4
--- /dev/null
+++ b/arch/xtensa/Kconfig
@@ -0,0 +1,260 @@
+# For a description of the syntax of this configuration file,
+# see Documentation/kbuild/config-language.txt.
+
+mainmenu "Linux/Xtensa Kernel Configuration"
+
+config FRAME_POINTER
+ bool
+ default n
+
+config XTENSA
+ bool
+ default y
+ help
+ Xtensa processors are 32-bit RISC machines designed by Tensilica
+ primarily for embedded systems. These processors are both
+ configurable and extensible. The Linux port to the Xtensa
+ architecture supports all processor configurations and extensions,
+ with reasonable minimum requirements. The Xtensa Linux project has
+ a home page at <http://xtensa.sourceforge.net/>.
+
+config UID16
+ bool
+ default n
+
+config RWSEM_XCHGADD_ALGORITHM
+ bool
+ default y
+
+config HAVE_DEC_LOCK
+ bool
+ default y
+
+config GENERIC_HARDIRQS
+ bool
+ default y
+
+source "init/Kconfig"
+
+menu "Processor type and features"
+
+choice
+ prompt "Xtensa Processor Configuration"
+ default XTENSA_CPU_LINUX_BE
+
+config XTENSA_CPU_LINUX_BE
+ bool "linux_be"
+ ---help---
+ The linux_be processor configuration is the baseline Xtensa
+ configurations included in this kernel and also used by
+ binutils, gcc, and gdb. It contains no TIE, no coprocessors,
+ and the following configuration options:
+
+ Code Density Option 2 Misc Special Registers
+ NSA/NSAU Instructions 128-bit Data Bus Width
+ Processor ID 8K, 2-way I and D Caches
+ Zero-Overhead Loops 2 Inst Address Break Registers
+ Big Endian 2 Data Address Break Registers
+ 64 General-Purpose Registers JTAG Interface and Trace Port
+ 17 Interrupts MMU w/ TLBs and Autorefill
+ 3 Interrupt Levels 8 Autorefill Ways (I/D TLBs)
+ 3 Timers Unaligned Exceptions
+endchoice
+
+config MMU
+ bool
+ default y
+
+config XTENSA_UNALIGNED_USER
+ bool "Unaligned memory access in use space"
+ ---help---
+ The Xtensa architecture currently does not handle unaligned
+ memory accesses in hardware but through an exception handler.
+ Per default, unaligned memory accesses are disabled in user space.
+
+ Say Y here to enable unaligned memory access in user space.
+
+config PREEMPT
+ bool "Preemptible Kernel"
+ ---help---
+ This option reduces the latency of the kernel when reacting to
+ real-time or interactive events by allowing a low priority process to
+ be preempted even if it is in kernel mode executing a system call.
+ Unfortunately the kernel code has some race conditions if both
+ CONFIG_SMP and CONFIG_PREEMPT are enabled, so this option is
+ currently disabled if you are building an SMP kernel.
+
+ Say Y here if you are building a kernel for a desktop, embedded
+ or real-time system. Say N if you are unsure.
+
+config MATH_EMULATION
+ bool "Math emulation"
+ help
+ Can we use information of configuration file?
+
+config HIGHMEM
+ bool "High memory support"
+
+endmenu
+
+menu "Platform options"
+
+choice
+ prompt "Xtensa System Type"
+ default XTENSA_PLATFORM_ISS
+
+config XTENSA_PLATFORM_ISS
+ bool "ISS"
+ help
+ ISS is an acronym for Tensilica's Instruction Set Simulator.
+
+config XTENSA_PLATFORM_XT2000
+ bool "XT2000"
+ help
+ XT2000 is the name of Tensilica's feature-rich emulation platform.
+ This hardware is capable of running a full Linux distribution.
+
+endchoice
+
+
+config XTENSA_CALIBRATE_CCOUNT
+ bool "Auto calibration of the CPU clock rate"
+ ---help---
+ On some platforms (XT2000, for example), the CPU clock rate can
+ vary. The frequency can be determined, however, by measuring
+ against a well known, fixed frequency, such as an UART oscillator.
+
+config XTENSA_CPU_CLOCK
+ int "CPU clock rate [MHz]"
+ depends on !XTENSA_CALIBRATE_CCOUNT
+ default "16"
+
+config GENERIC_CALIBRATE_DELAY
+ bool "Auto calibration of the BogoMIPS value"
+ ---help---
+ The BogoMIPS value can easily be derived from the CPU frequency.
+
+config CMDLINE_BOOL
+ bool "Default bootloader kernel arguments"
+
+config CMDLINE
+ string "Initial kernel command string"
+ depends on CMDLINE_BOOL
+ default "console=ttyS0,38400 root=/dev/ram"
+ help
+ On some architectures (EBSA110 and CATS), there is currently no way
+ for the boot loader to pass arguments to the kernel. For these
+ architectures, you should supply some command-line options at build
+ time by entering them here. As a minimum, you should specify the
+ memory size and the root device (e.g., mem=64M root=/dev/nfs).
+
+config SERIAL_CONSOLE
+ bool
+ depends on XTENSA_PLATFORM_ISS
+ default y
+
+config XTENSA_ISS_NETWORK
+ bool
+ depends on XTENSA_PLATFORM_ISS
+ default y
+
+source "mm/Kconfig"
+
+endmenu
+
+menu "Bus options"
+
+config PCI
+ bool "PCI support" if !XTENSA_PLATFORM_ISS
+ depends on !XTENSA_PLATFORM_ISS
+ default y
+ help
+ Find out whether you have a PCI motherboard. PCI is the name of a
+ bus system, i.e. the way the CPU talks to the other stuff inside
+ your box. Other bus systems are ISA, EISA, MicroChannel (MCA) or
+ VESA. If you have PCI, say Y, otherwise N.
+
+ The PCI-HOWTO, available from
+ <http://www.linuxdoc.org/docs.html#howto>, contains valuable
+ information about which PCI hardware does work under Linux and which
+ doesn't
+
+source "drivers/pci/Kconfig"
+
+config HOTPLUG
+
+ bool "Support for hot-pluggable devices"
+ ---help---
+ Say Y here if you want to plug devices into your computer while
+ the system is running, and be able to use them quickly. In many
+ cases, the devices can likewise be unplugged at any time too.
+
+ One well known example of this is PCMCIA- or PC-cards, credit-card
+ size devices such as network cards, modems or hard drives which are
+ plugged into slots found on all modern laptop computers. Another
+ example, used on modern desktops as well as laptops, is USB.
+
+ Enable HOTPLUG and KMOD, and build a modular kernel. Get agent
+ software (at <http://linux-hotplug.sourceforge.net/>) and install it.
+ Then your kernel will automatically call out to a user mode "policy
+ agent" (/sbin/hotplug) to load modules and set up software needed
+ to use devices as you hotplug them.
+
+source "drivers/pcmcia/Kconfig"
+
+source "drivers/pci/hotplug/Kconfig"
+
+endmenu
+
+menu "Exectuable file formats"
+
+# only elf supported
+config KCORE_ELF
+ bool
+ depends on PROC_FS
+ default y
+ help
+ If you enabled support for /proc file system then the file
+ /proc/kcore will contain the kernel core image in ELF format. This
+ can be used in gdb:
+
+ $ cd /usr/src/linux ; gdb vmlinux /proc/kcore
+
+ This is especially useful if you have compiled the kernel with the
+ "-g" option to preserve debugging information. It is mainly used
+ for examining kernel data structures on the live kernel.
+
+source "fs/Kconfig.binfmt"
+
+endmenu
+
+source "drivers/Kconfig"
+
+source "fs/Kconfig"
+
+menu "Xtensa initrd options"
+ depends on BLK_DEV_INITRD
+
+ config EMBEDDED_RAMDISK
+ bool "Embed root filesystem ramdisk into the kernel"
+
+config EMBEDDED_RAMDISK_IMAGE
+ string "Filename of gziped ramdisk image"
+ depends on EMBEDDED_RAMDISK
+ default "ramdisk.gz"
+ help
+ This is the filename of the ramdisk image to be built into the
+ kernel. Relative pathnames are relative to arch/xtensa/boot/ramdisk/.
+ The ramdisk image is not part of the kernel distribution; you must
+ provide one yourself.
+endmenu
+
+source "arch/xtensa/Kconfig.debug"
+
+source "security/Kconfig"
+
+source "crypto/Kconfig"
+
+source "lib/Kconfig"
+
+
diff --git a/arch/xtensa/Kconfig.debug b/arch/xtensa/Kconfig.debug
new file mode 100644
index 000000000000..11c585295dd7
--- /dev/null
+++ b/arch/xtensa/Kconfig.debug
@@ -0,0 +1,7 @@
+menu "Kernel hacking"
+
+source "lib/Kconfig.debug"
+
+endmenu
+
+
diff --git a/arch/xtensa/Makefile b/arch/xtensa/Makefile
new file mode 100644
index 000000000000..27847e4ffcbf
--- /dev/null
+++ b/arch/xtensa/Makefile
@@ -0,0 +1,104 @@
+#
+# 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) 2001 - 2005 Tensilica Inc.
+#
+# 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
+
+# Core configuration.
+# (Use CPU=<xtensa_config> to use another default compiler.)
+
+cpu-$(CONFIG_XTENSA_CPU_LINUX_BE) := linux_be
+cpu-$(CONFIG_XTENSA_CPU_LINUX_CUSTOM) := linux_custom
+
+CPU = $(cpu-y)
+export CPU
+
+# Platform configuration
+
+platform-$(CONFIG_XTENSA_PLATFORM_XT2000) := xt2000
+platform-$(CONFIG_XTENSA_PLATFORM_ISS) := iss
+
+PLATFORM = $(platform-y)
+export PLATFORM
+
+CPPFLAGS += $(if $(KBUILD_SRC),-I$(srctree)/include/asm-xtensa/)
+CPPFLAGS += -Iinclude/asm
+CFLAGS += -pipe -mlongcalls
+
+KBUILD_DEFCONFIG := iss_defconfig
+
+# ramdisk/initrd support
+# You need a compressed ramdisk image, named ramdisk.gz in
+# arch/xtensa/boot/ramdisk
+
+core-$(CONFIG_EMBEDDED_RAMDISK) += arch/xtensa/boot/ramdisk/
+
+# Test for cross compiling
+
+ifneq ($(CPU),)
+ COMPILE_ARCH = $(shell uname -m)
+
+ ifneq ($(COMPILE_ARCH), xtensa)
+ ifndef CROSS_COMPILE
+ CROSS_COMPILE = xtensa_$(CPU)-
+ endif
+ endif
+endif
+
+#
+
+LIBGCC := $(shell $(CC) $(CFLAGS) -print-libgcc-file-name)
+
+head-y := arch/xtensa/kernel/head.o
+core-y += arch/xtensa/kernel/ arch/xtensa/mm/
+ifneq ($(PLATFORM),)
+core-y += arch/xtensa/platform-$(PLATFORM)/
+endif
+libs-y += arch/xtensa/lib/ $(LIBGCC)
+
+boot := arch/xtensa/boot
+
+archinc := include/asm-xtensa
+
+arch/xtensa/kernel/asm-offsets.s: \
+ arch/xtensa/kernel/asm-offsets.c $(archinc)/.platform
+
+include/asm-xtensa/offsets.h: arch/xtensa/kernel/asm-offsets.s
+ $(call filechk,gen-asm-offsets)
+
+prepare: $(archinc)/.platform $(archinc)/offsets.h
+
+# Update machine cpu and platform symlinks if something which affects
+# them changed.
+
+$(archinc)/.platform: $(wildcard include/config/arch/*.h) include/config/MARKER
+ @echo ' SYMLINK $(archinc)/xtensa/config -> $(archinc)/xtensa/config-$(CPU)'
+ $(Q)mkdir -p $(archinc)
+ $(Q)mkdir -p $(archinc)/xtensa
+ $(Q)ln -fsn $(srctree)/$(archinc)/xtensa/config-$(CPU) $(archinc)/xtensa/config
+ @echo ' SYMLINK $(archinc)/platform -> $(archinc)/platform-$(PLATFORM)'
+ $(Q)ln -fsn $(srctree)/$(archinc)/platform-$(PLATFORM) $(archinc)/platform
+ @touch $@
+
+
+all: zImage
+
+bzImage : zImage
+
+zImage zImage.initrd: vmlinux
+ $(Q)$(MAKE) $(build)=$(boot) $@
+
+CLEAN_FILES += arch/xtensa/vmlinux.lds $(archinc)/offset.h \
+ $(archinc)/platform $(archinc)/xtensa/config \
+ $(archinc)/.platform
+
+define archhelp
+ @echo '* zImage - Compressed kernel image (arch/xtensa/boot/images/zImage.*)'
+endef
+
diff --git a/arch/xtensa/boot/Makefile b/arch/xtensa/boot/Makefile
new file mode 100644
index 000000000000..820b31d10ae4
--- /dev/null
+++ b/arch/xtensa/boot/Makefile
@@ -0,0 +1,33 @@
+#
+# arch/xtensa/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.
+#
+#
+
+
+CFLAGS += -fno-builtin -Iarch/$(ARCH)/boot/include
+HOSTFLAGS += -Iarch/$(ARCH)/boot/include
+
+BIG_ENDIAN := $(shell echo -e __XTENSA_EB__ | $(CC) -E - | grep -v "\#")
+
+export CFLAGS
+export AFLAGS
+export BIG_ENDIAN
+
+subdir-y := lib
+
+# Subdirs for the boot loader(s)
+
+bootdir-$(CONFIG_XTENSA_PLATFORM_ISS) += boot-elf
+bootdir-$(CONFIG_XTENSA_PLATFORM_XT2000) += boot-redboot boot-elf
+
+
+zImage zImage.initrd Image Image.initrd: $(bootdir-y)
+
+$(bootdir-y): $(addprefix $(obj)/,$(subdir-y)) \
+ $(addprefix $(obj)/,$(host-progs))
+ $(Q)$(MAKE) $(build)=$(obj)/$@ $(MAKECMDGOALS)
+
diff --git a/arch/xtensa/boot/boot-elf/Makefile b/arch/xtensa/boot/boot-elf/Makefile
new file mode 100644
index 000000000000..734db7f76583
--- /dev/null
+++ b/arch/xtensa/boot/boot-elf/Makefile
@@ -0,0 +1,52 @@
+#
+# 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.
+#
+
+GZIP = gzip
+GZIP_FLAGS = -v9fc
+
+ifeq ($(BIG_ENDIAN),1)
+OBJCOPY_ARGS := -O elf32-xtensa-be
+else
+OBJCOPY_ARGS := -O elf32-xtensa-le
+endif
+
+export OBJCOPY_ARGS
+
+boot-y := bootstrap.o
+
+OBJS := $(addprefix $(obj)/,$(boot-y))
+
+Image: vmlinux $(OBJS)
+ $(OBJCOPY) --strip-all -R .comment -R .xt.insn -O binary \
+ vmlinux vmlinux.tmp
+ $(OBJCOPY) $(OBJCOPY_ARGS) -R .comment \
+ --add-section image=vmlinux.tmp \
+ --set-section-flags image=contents,alloc,load,load,data \
+ $(OBJS) $@.tmp
+ $(LD) $(LDFLAGS) $(LDFLAGS_vmlinux) \
+ -T $(srctree)/arch/$(ARCH)/boot/boot-elf/boot.ld \
+ -o arch/$(ARCH)/boot/$@.elf $@.tmp
+ rm -f $@.tmp vmlinux.tmp
+
+Image.initrd: vmlinux $(OBJS)
+ $(OBJCOPY) --strip-all -R .comment -R .xt.insn -O binary \
+ --add-section .initrd=arch/$(ARCH)/boot/ramdisk \
+ --set-section-flags .initrd=contents,alloc,load,load,data \
+ vmlinux vmlinux.tmp
+ $(OBJCOPY) $(OBJCOPY_ARGS) -R .comment \
+ --add-section image=vmlinux.tmp \
+ --set-section-flags image=contents,alloc,load,load,data \
+ $(OBJS) $@.tmp
+ $(LD) $(LDFLAGS) $(LDFLAGS_vmlinux) \
+ -T $(srctree)/arch/$(ARCH)/boot/boot-elf/boot.ld \
+ -o arch/$(ARCH)/boot/$@.elf $@.tmp
+ rm -f $@.tmp vmlinux.tmp
+
+
+zImage: Image
+
+zImage.initrd: Image.initrd
+
diff --git a/arch/xtensa/boot/boot-elf/boot.ld b/arch/xtensa/boot/boot-elf/boot.ld
new file mode 100644
index 000000000000..4ab06a0a7a6b
--- /dev/null
+++ b/arch/xtensa/boot/boot-elf/boot.ld
@@ -0,0 +1,71 @@
+OUTPUT_ARCH(xtensa)
+
+SECTIONS
+{
+ .start 0xD0000000 : { *(.start) }
+
+ .text 0xD0000000:
+ {
+ __reloc_start = . ;
+ _text_start = . ;
+ *(.literal .text.literal .text)
+ _text_end = . ;
+ }
+
+ .rodata ALIGN(0x04):
+ {
+ *(.rodata)
+ *(.rodata1)
+ }
+
+ .data ALIGN(0x04):
+ {
+ *(.data)
+ *(.data1)
+ *(.sdata)
+ *(.sdata2)
+ *(.got.plt)
+ *(.got)
+ *(.dynamic)
+ }
+
+ __reloc_end = . ;
+
+ .initrd ALIGN(0x10) :
+ {
+ boot_initrd_start = . ;
+ *(.initrd)
+ boot_initrd_end = .;
+ }
+
+ . = ALIGN(0x10);
+ __image_load = . ;
+ .image 0xd0001000:
+ {
+ _image_start = .;
+ *(image)
+ . = (. + 3) & ~ 3;
+ _image_end = . ;
+ }
+
+
+ .bss ((LOADADDR(.image) + SIZEOF(.image) + 3) & ~ 3):
+ {
+ __bss_start = .;
+ *(.sbss)
+ *(.scommon)
+ *(.dynbss)
+ *(.bss)
+ __bss_end = .;
+ }
+ _end = .;
+ _param_start = .;
+
+ .ResetVector.text 0xfe000020 :
+ {
+ *(.ResetVector.text)
+ }
+
+
+ PROVIDE (end = .);
+}
diff --git a/arch/xtensa/boot/boot-elf/bootstrap.S b/arch/xtensa/boot/boot-elf/bootstrap.S
new file mode 100644
index 000000000000..7cba94abdab8
--- /dev/null
+++ b/arch/xtensa/boot/boot-elf/bootstrap.S
@@ -0,0 +1,37 @@
+
+#include <xtensa/config/specreg.h>
+#include <xtensa/config/core.h>
+
+#include <linux/config.h>
+#include <asm/bootparam.h>
+
+
+/* ResetVector
+ */
+ .section .ResetVector.text, "ax"
+ .global _ResetVector
+_ResetVector:
+ _j reset
+ .align 4
+RomInitAddr:
+ .word 0xd0001000
+RomBootParam:
+ .word _bootparam
+reset:
+ l32r a0, RomInitAddr
+ l32r a2, RomBootParam
+ movi a3, 0
+ movi a4, 0
+ jx a0
+
+ .align 4
+ .section .bootstrap.data, "aw"
+
+ .globl _bootparam
+_bootparam:
+ .short BP_TAG_FIRST
+ .short 4
+ .long BP_VERSION
+ .short BP_TAG_LAST
+ .short 0
+ .long 0
diff --git a/arch/xtensa/boot/boot-redboot/Makefile b/arch/xtensa/boot/boot-redboot/Makefile
new file mode 100644
index 000000000000..f53262c2e1f3
--- /dev/null
+++ b/arch/xtensa/boot/boot-redboot/Makefile
@@ -0,0 +1,35 @@
+#
+# 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.
+#
+
+GZIP = gzip
+GZIP_FLAGS = -v9fc
+ifeq ($(BIG_ENDIAN),1)
+OBJCOPY_ARGS := -O elf32-xtensa-be
+else
+OBJCOPY_ARGS := -O elf32-xtensa-le
+endif
+
+LD_ARGS = -T $(srctree)/$(obj)/boot.ld
+
+boot-y := bootstrap.o
+
+OBJS := $(addprefix $(obj)/,$(boot-y))
+LIBS := arch/xtensa/boot/lib/lib.a arch/xtensa/lib/lib.a
+
+LIBGCC := $(shell $(CC) $(CFLAGS) -print-libgcc-file-name)
+
+zImage: vmlinux $(OBJS) $(LIBS)
+ $(OBJCOPY) --strip-all -R .comment -R .xt.insn -O binary \
+ vmlinux vmlinux.tmp
+ gzip -vf9 vmlinux.tmp
+ $(OBJCOPY) $(OBJCOPY_ARGS) -R .comment \
+ --add-section image=vmlinux.tmp.gz \
+ --set-section-flags image=contents,alloc,load,load,data \
+ $(OBJS) $@.tmp
+ $(LD) $(LD_ARGS) -o $@.elf $@.tmp $(LIBS) -L/xtensa-elf/lib $(LIBGCC)
+ $(OBJCOPY) -S -O binary $@.elf arch/$(ARCH)/boot/$@.redboot
+ rm -f $@.tmp $@.elf vmlinux.tmp.gz
+
diff --git a/arch/xtensa/boot/boot-redboot/boot.ld b/arch/xtensa/boot/boot-redboot/boot.ld
new file mode 100644
index 000000000000..65b726410e8a
--- /dev/null
+++ b/arch/xtensa/boot/boot-redboot/boot.ld
@@ -0,0 +1,66 @@
+OUTPUT_ARCH(xtensa)
+
+SECTIONS
+{
+ .start 0xD0200000 : { *(.start) }
+
+ .text :
+ {
+ __reloc_start = . ;
+ _text_start = . ;
+ *(.literal .text.literal .text)
+ _text_end = . ;
+ }
+
+ .rodata ALIGN(0x04):
+ {
+ *(.rodata)
+ *(.rodata1)
+ }
+
+ .data ALIGN(0x04):
+ {
+ *(.data)
+ *(.data1)
+ *(.sdata)
+ *(.sdata2)
+ *(.got.plt)
+ *(.got)
+ *(.dynamic)
+ }
+
+ __reloc_end = . ;
+
+ .initrd ALIGN(0x10) :
+ {
+ boot_initrd_start = . ;
+ *(.initrd)
+ boot_initrd_end = .;
+ }
+
+ . = ALIGN(0x10);
+ __image_load = . ;
+ .image 0xd0001000: AT(__image_load)
+ {
+ _image_start = .;
+ *(image)
+ . = (. + 3) & ~ 3;
+ _image_end = . ;
+ }
+
+
+ .bss ((LOADADDR(.image) + SIZEOF(.image) + 3) & ~ 3):
+ {
+ __bss_start = .;
+ *(.sbss)
+ *(.scommon)
+ *(.dynbss)
+ *(.bss)
+ __bss_end = .;
+ }
+ _end = .;
+ _param_start = .;
+
+
+ PROVIDE (end = .);
+}
diff --git a/arch/xtensa/boot/boot-redboot/bootstrap.S b/arch/xtensa/boot/boot-redboot/bootstrap.S
new file mode 100644
index 000000000000..ee636b0da81c
--- /dev/null
+++ b/arch/xtensa/boot/boot-redboot/bootstrap.S
@@ -0,0 +1,246 @@
+
+#define _ASMLANGUAGE
+#include <xtensa/config/specreg.h>
+#include <xtensa/config/core.h>
+#include <xtensa/cacheasm.h>
+
+ /*
+ * RB-Data: RedBoot data/bss
+ * P: Boot-Parameters
+ * L: Kernel-Loader
+ *
+ * The Linux-Kernel image including the loader must be loaded
+ * to a position so that the kernel and the boot parameters
+ * can fit in the space before the load address.
+ * ______________________________________________________
+ * |_RB-Data_|_P_|__________|_L_|___Linux-Kernel___|______|
+ * ^
+ * ^ Load address
+ * ______________________________________________________
+ * |___Linux-Kernel___|_P_|_L_|___________________________|
+ *
+ * The loader copies the parameter to the position that will
+ * be the end of the kernel and itself to the end of the
+ * parameter list.
+ */
+
+/* Make sure we have enough space for the 'uncompressor' */
+
+#define STACK_SIZE 32768
+#define HEAP_SIZE (131072*4)
+
+ # a2: Parameter list
+ # a3: Size of parameter list
+
+ .section .start, "ax"
+
+ .globl __start
+ /* this must be the first byte of the loader! */
+__start:
+ entry sp, 32 # we do not intend to return
+ _call0 _start
+__start_a0:
+ .align 4
+
+ .section .text, "ax"
+ .begin literal_prefix .text
+
+ /* put literals in here! */
+
+ .globl _start
+_start:
+
+ /* 'reset' window registers */
+
+ movi a4, 1
+ wsr a4, PS
+ rsync
+
+ rsr a5, WINDOWBASE
+ ssl a5
+ sll a4, a4
+ wsr a4, WINDOWSTART
+ rsync
+
+ movi a4, 0x00040000
+ wsr a4, PS
+ rsync
+
+ /* copy the loader to its address
+ * Note: The loader itself is a very small piece, so we assume we
+ * don't partially overlap. We also assume (even more important)
+ * that the kernel image is out of the way. Usually, when the
+ * load address of this image is not at an arbitrary address,
+ * but aligned to some 10K's we shouldn't overlap.
+ */
+
+ /* Note: The assembler cannot relax "addi a0, a0, ..." to an
+ l32r, so we load to a4 first. */
+
+ addi a4, a0, __start - __start_a0
+ mov a0, a4
+ movi a4, __start
+ movi a5, __reloc_end
+
+ # a0: address where this code has been loaded
+ # a4: compiled address of __start
+ # a5: compiled end address
+
+ mov.n a7, a0
+ mov.n a8, a4
+
+1:
+ l32i a10, a7, 0
+ l32i a11, a7, 4
+ s32i a10, a8, 0
+ s32i a11, a8, 4
+ l32i a10, a7, 8
+ l32i a11, a7, 12
+ s32i a10, a8, 8
+ s32i a11, a8, 12
+ addi a8, a8, 16
+ addi a7, a7, 16
+ blt a8, a5, 1b
+
+
+ /* We have to flush and invalidate the caches here before we jump. */
+
+#if XCHAL_DCACHE_IS_WRITEBACK
+ dcache_writeback_all a5, a6
+#endif
+ icache_invalidate_all a5, a6
+
+ movi a11, _reloc
+ jx a11
+
+ .globl _reloc
+_reloc:
+
+ /* RedBoot is now at the end of the memory, so we don't have
+ * to copy the parameter list. Keep the code around; in case
+ * we need it again. */
+#if 0
+ # a0: load address
+ # a2: start address of parameter list
+ # a3: length of parameter list
+ # a4: __start
+
+ /* copy the parameter list out of the way */
+
+ movi a6, _param_start
+ add a3, a2, a3
+2:
+ l32i a8, a2, 0
+ s32i a8, a6, 0
+ addi a2, a2, 4
+ addi a6, a6, 4
+ blt a2, a3, 2b
+#endif
+
+ /* clear BSS section */
+ movi a6, __bss_start
+ movi a7, __bss_end
+ movi.n a5, 0
+3:
+ s32i a5, a6, 0
+ addi a6, a6, 4
+ blt a6, a7, 3b
+
+ movi a5, -16
+ movi a1, _stack + STACK_SIZE
+ and a1, a1, a5
+
+ /* Uncompress the kernel */
+
+ # a0: load address
+ # a2: boot parameter
+ # a4: __start
+
+ movi a3, __image_load
+ sub a4, a3, a4
+ add a8, a0, a4
+
+ # a1 Stack
+ # a8(a4) Load address of the image
+
+ movi a6, _image_start
+ movi a10, _image_end
+ movi a7, 0x1000000
+ sub a11, a10, a6
+ movi a9, complen
+ s32i a11, a9, 0
+
+ movi a0, 0
+
+ # a6 destination
+ # a7 maximum size of destination
+ # a8 source
+ # a9 ptr to length
+
+ .extern gunzip
+ movi a4, gunzip
+ beqz a4, 1f
+
+ callx4 a4
+
+ j 2f
+
+
+ # a6 destination start
+ # a7 maximum size of destination
+ # a8 source start
+ # a9 ptr to length
+ # a10 destination end
+
+1:
+ l32i a9, a8, 0
+ l32i a11, a8, 4
+ s32i a9, a6, 0
+ s32i a11, a6, 4
+ l32i a9, a8, 8
+ l32i a11, a8, 12
+ s32i a9, a6, 8
+ s32i a11, a6, 12
+ addi a6, a6, 16
+ addi a8, a8, 16
+ blt a6, a10, 1b
+
+
+ /* jump to the kernel */
+2:
+#if XCHAL_DCACHE_IS_WRITEBACK
+ dcache_writeback_all a5, a6
+#endif
+ icache_invalidate_all a5, a6
+
+ movi a5, __start
+ movi a3, boot_initrd_start
+ movi a4, boot_initrd_end
+ sub a3, a3, a5
+ sub a4, a4, a5
+ add a3, a0, a3
+ add a4, a0, a4
+
+ # a2 Boot parameter list
+ # a3 initrd_start (virtual load address)
+ # a4 initrd_end (virtual load address)
+
+ movi a0, _image_start
+ jx a0
+
+ .align 16
+ .data
+ .globl avail_ram
+avail_ram:
+ .long _heap
+ .globl end_avail
+end_avail:
+ .long _heap + HEAP_SIZE
+
+ .comm _stack, STACK_SIZE
+ .comm _heap, HEAP_SIZE
+
+ .globl end_avail
+ .comm complen, 4
+
+ .end literal_prefix
diff --git a/arch/xtensa/boot/lib/Makefile b/arch/xtensa/boot/lib/Makefile
new file mode 100644
index 000000000000..9e73bb8aeb7a
--- /dev/null
+++ b/arch/xtensa/boot/lib/Makefile
@@ -0,0 +1,17 @@
+#
+# Makefile for some libs needed by zImage.
+#
+
+zlib := infblock.c infcodes.c inffast.c inflate.c inftrees.c infutil.c
+
+lib-y += $(zlib:.c=.o) zmem.o
+
+EXTRA_CFLAGS += -Ilib/zlib_inflate
+
+quiet_cmd_copy_zlib = COPY $@
+ cmd_copy_zlib = cat $< > $@
+
+$(addprefix $(obj)/,$(zlib)): $(obj)/%: $(srctree)/lib/zlib_inflate/%
+ $(call cmd,copy_zlib)
+
+clean-files := $(zlib)
diff --git a/arch/xtensa/boot/lib/zmem.c b/arch/xtensa/boot/lib/zmem.c
new file mode 100644
index 000000000000..d9862aa8ca25
--- /dev/null
+++ b/arch/xtensa/boot/lib/zmem.c
@@ -0,0 +1,79 @@
+#include <linux/zlib.h>
+
+/* bits taken from ppc */
+
+extern void *avail_ram, *end_avail;
+
+void exit (void)
+{
+ for (;;);
+}
+
+void *zalloc(unsigned size)
+{
+ void *p = avail_ram;
+
+ size = (size + 7) & -8;
+ avail_ram += size;
+ if (avail_ram > end_avail) {
+ //puts("oops... out of memory\n");
+ //pause();
+ exit ();
+ }
+ return p;
+}
+
+#define HEAD_CRC 2
+#define EXTRA_FIELD 4
+#define ORIG_NAME 8
+#define COMMENT 0x10
+#define RESERVED 0xe0
+
+#define DEFLATED 8
+
+void gunzip (void *dst, int dstlen, unsigned char *src, int *lenp)
+{
+ z_stream s;
+ int r, i, flags;
+
+ /* skip header */
+ i = 10;
+ flags = src[3];
+ if (src[2] != DEFLATED || (flags & RESERVED) != 0) {
+ //puts("bad gzipped data\n");
+ exit();
+ }
+ if ((flags & EXTRA_FIELD) != 0)
+ i = 12 + src[10] + (src[11] << 8);
+ if ((flags & ORIG_NAME) != 0)
+ while (src[i++] != 0)
+ ;
+ if ((flags & COMMENT) != 0)
+ while (src[i++] != 0)
+ ;
+ if ((flags & HEAD_CRC) != 0)
+ i += 2;
+ if (i >= *lenp) {
+ //puts("gunzip: ran out of data in header\n");
+ exit();
+ }
+
+ s.workspace = zalloc(zlib_inflate_workspacesize());
+ r = zlib_inflateInit2(&s, -MAX_WBITS);
+ if (r != Z_OK) {
+ //puts("inflateInit2 returned "); puthex(r); puts("\n");
+ exit();
+ }
+ s.next_in = src + i;
+ s.avail_in = *lenp - i;
+ s.next_out = dst;
+ s.avail_out = dstlen;
+ r = zlib_inflate(&s, Z_FINISH);
+ if (r != Z_OK && r != Z_STREAM_END) {
+ //puts("inflate returned "); puthex(r); puts("\n");
+ exit();
+ }
+ *lenp = s.next_out - (unsigned char *) dst;
+ zlib_inflateEnd(&s);
+}
+
diff --git a/arch/xtensa/boot/ramdisk/Makefile b/arch/xtensa/boot/ramdisk/Makefile
new file mode 100644
index 000000000000..b12f76352438
--- /dev/null
+++ b/arch/xtensa/boot/ramdisk/Makefile
@@ -0,0 +1,23 @@
+#
+# Makefile for a ramdisk image
+#
+
+BIG_ENDIAN := $(shell echo -e "\#ifdef __XTENSA_EL__\nint little;\n\#else\nint big;\n\#endif" | $(CC) -E -|grep -c big)
+
+ifeq ($(BIG_ENDIAN),1)
+OBJCOPY_ARGS := -O elf32-xtensa-be
+else
+OBJCOPY_ARGS := -O elf32-xtensa-le
+endif
+
+obj-y = ramdisk.o
+
+RAMDISK_IMAGE = arch/$(ARCH)/boot/ramdisk/$(CONFIG_EMBEDDED_RAMDISK_IMAGE)
+
+arch/$(ARCH)/boot/ramdisk/ramdisk.o:
+ $(Q)echo -e "dummy:" | $(AS) -o $@;
+ $(Q)$(OBJCOPY) $(OBJCOPY_ARGS) \
+ --add-section .initrd=$(RAMDISK_IMAGE) \
+ --set-section-flags .initrd=contents,alloc,load,load,data \
+ arch/$(ARCH)/boot/ramdisk/ramdisk.o $@
+
diff --git a/arch/xtensa/configs/common_defconfig b/arch/xtensa/configs/common_defconfig
new file mode 100644
index 000000000000..1d230ee081b4
--- /dev/null
+++ b/arch/xtensa/configs/common_defconfig
@@ -0,0 +1,662 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.11-rc2
+# Tue Mar 1 16:36:53 2005
+#
+# CONFIG_FRAME_POINTER is not set
+CONFIG_XTENSA=y
+# CONFIG_UID16 is not set
+CONFIG_RWSEM_XCHGADD_ALGORITHM=y
+CONFIG_HAVE_DEC_LOCK=y
+CONFIG_GENERIC_HARDIRQS=y
+
+#
+# Code maturity level options
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_CLEAN_COMPILE=y
+CONFIG_BROKEN_ON_SMP=y
+
+#
+# General setup
+#
+CONFIG_LOCALVERSION=""
+CONFIG_SWAP=y
+CONFIG_SYSVIPC=y
+# CONFIG_POSIX_MQUEUE is not set
+CONFIG_BSD_PROCESS_ACCT=y
+# CONFIG_BSD_PROCESS_ACCT_V3 is not set
+CONFIG_SYSCTL=y
+# CONFIG_AUDIT is not set
+CONFIG_LOG_BUF_SHIFT=14
+# CONFIG_HOTPLUG is not set
+CONFIG_KOBJECT_UEVENT=y
+# CONFIG_IKCONFIG is not set
+# CONFIG_EMBEDDED is not set
+CONFIG_KALLSYMS=y
+# CONFIG_KALLSYMS_ALL is not set
+# CONFIG_KALLSYMS_EXTRA_PASS is not set
+CONFIG_FUTEX=y
+CONFIG_EPOLL=y
+# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
+CONFIG_SHMEM=y
+CONFIG_CC_ALIGN_FUNCTIONS=0
+CONFIG_CC_ALIGN_LABELS=0
+CONFIG_CC_ALIGN_LOOPS=0
+CONFIG_CC_ALIGN_JUMPS=0
+# CONFIG_TINY_SHMEM is not set
+
+#
+# Loadable module support
+#
+CONFIG_MODULES=y
+# CONFIG_MODULE_UNLOAD is not set
+CONFIG_OBSOLETE_MODPARM=y
+CONFIG_MODVERSIONS=y
+# CONFIG_MODULE_SRCVERSION_ALL is not set
+CONFIG_KMOD=y
+
+#
+# Processor type and features
+#
+CONFIG_XTENSA_ARCH_LINUX_BE=y
+# CONFIG_XTENSA_ARCH_LINUX_LE is not set
+# CONFIG_XTENSA_ARCH_LINUX_TEST is not set
+# CONFIG_XTENSA_ARCH_S5 is not set
+# CONFIG_XTENSA_CUSTOM is not set
+CONFIG_MMU=y
+# CONFIG_XTENSA_UNALIGNED_USER is not set
+# CONFIG_PREEMPT is not set
+# CONFIG_MATH_EMULATION is not set
+# CONFIG_HIGHMEM is not set
+
+#
+# Platform options
+#
+# CONFIG_XTENSA_PLATFORM_ISS is not set
+CONFIG_XTENSA_PLATFORM_XT2000=y
+CONFIG_XTENSA_CALIBRATE_CCOUNT=y
+CONFIG_GENERIC_CALIBRATE_DELAY=y
+CONFIG_CMDLINE_BOOL=y
+CONFIG_CMDLINE="console=ttyS0,38400 ip=bootp root=nfs nfsroot=/opt/montavista/pro/devkit/xtensa/linux_be/target"
+
+#
+# Bus options
+#
+CONFIG_PCI=y
+# CONFIG_PCI_LEGACY_PROC is not set
+# CONFIG_PCI_NAMES is not set
+
+#
+# PCCARD (PCMCIA/CardBus) support
+#
+# CONFIG_PCCARD is not set
+
+#
+# PC-card bridges
+#
+
+#
+# PCI Hotplug Support
+#
+# CONFIG_HOTPLUG_PCI is not set
+
+#
+# Exectuable file formats
+#
+CONFIG_KCORE_ELF=y
+CONFIG_BINFMT_ELF=y
+CONFIG_BINFMT_MISC=y
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+CONFIG_STANDALONE=y
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+# CONFIG_FW_LOADER is not set
+# CONFIG_DEBUG_DRIVER is not set
+
+#
+# Memory Technology Devices (MTD)
+#
+# CONFIG_MTD is not set
+
+#
+# Parallel port support
+#
+# CONFIG_PARPORT is not set
+
+#
+# Plug and Play support
+#
+
+#
+# Block devices
+#
+# CONFIG_BLK_DEV_FD is not set
+# CONFIG_BLK_CPQ_DA is not set
+# CONFIG_BLK_CPQ_CISS_DA is not set
+# CONFIG_BLK_DEV_DAC960 is not set
+# CONFIG_BLK_DEV_UMEM is not set
+# CONFIG_BLK_DEV_COW_COMMON is not set
+# CONFIG_BLK_DEV_LOOP is not set
+# CONFIG_BLK_DEV_NBD is not set
+# CONFIG_BLK_DEV_SX8 is not set
+# CONFIG_BLK_DEV_RAM is not set
+CONFIG_BLK_DEV_RAM_COUNT=16
+CONFIG_INITRAMFS_SOURCE=""
+# CONFIG_CDROM_PKTCDVD is not set
+
+#
+# IO Schedulers
+#
+CONFIG_IOSCHED_NOOP=y
+CONFIG_IOSCHED_AS=y
+CONFIG_IOSCHED_DEADLINE=y
+CONFIG_IOSCHED_CFQ=y
+# CONFIG_ATA_OVER_ETH is not set
+
+#
+# ATA/ATAPI/MFM/RLL support
+#
+# CONFIG_IDE is not set
+
+#
+# SCSI device support
+#
+# CONFIG_SCSI is not set
+
+#
+# Multi-device support (RAID and LVM)
+#
+# CONFIG_MD is not set
+
+#
+# Fusion MPT device support
+#
+
+#
+# IEEE 1394 (FireWire) support
+#
+# CONFIG_IEEE1394 is not set
+
+#
+# I2O device support
+#
+# CONFIG_I2O is not set
+
+#
+# Networking support
+#
+CONFIG_NET=y
+
+#
+# Networking options
+#
+# CONFIG_PACKET is not set
+# CONFIG_NETLINK_DEV is not set
+CONFIG_UNIX=y
+# CONFIG_NET_KEY is not set
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+CONFIG_IP_ADVANCED_ROUTER=y
+CONFIG_IP_MULTIPLE_TABLES=y
+CONFIG_IP_ROUTE_MULTIPATH=y
+CONFIG_IP_ROUTE_VERBOSE=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP_BOOTP=y
+CONFIG_IP_PNP_RARP=y
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
+# CONFIG_IP_MROUTE is not set
+# CONFIG_ARPD is not set
+# CONFIG_SYN_COOKIES is not set
+# CONFIG_INET_AH is not set
+# CONFIG_INET_ESP is not set
+# CONFIG_INET_IPCOMP is not set
+# CONFIG_INET_TUNNEL is not set
+# CONFIG_IP_TCPDIAG is not set
+# CONFIG_IP_TCPDIAG_IPV6 is not set
+# CONFIG_IPV6 is not set
+# CONFIG_NETFILTER is not set
+
+#
+# SCTP Configuration (EXPERIMENTAL)
+#
+# CONFIG_IP_SCTP is not set
+# CONFIG_ATM is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_VLAN_8021Q is not set
+# CONFIG_DECNET is not set
+# CONFIG_LLC2 is not set
+# CONFIG_IPX is not set
+# CONFIG_ATALK is not set
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_NET_DIVERT is not set
+# CONFIG_ECONET is not set
+# CONFIG_WAN_ROUTER is not set
+
+#
+# QoS and/or fair queueing
+#
+CONFIG_NET_SCHED=y
+CONFIG_NET_SCH_CLK_JIFFIES=y
+# CONFIG_NET_SCH_CLK_GETTIMEOFDAY is not set
+# CONFIG_NET_SCH_CLK_CPU is not set
+CONFIG_NET_SCH_CBQ=m
+CONFIG_NET_SCH_HTB=m
+# CONFIG_NET_SCH_HFSC is not set
+CONFIG_NET_SCH_PRIO=m
+CONFIG_NET_SCH_RED=m
+CONFIG_NET_SCH_SFQ=m
+CONFIG_NET_SCH_TEQL=m
+CONFIG_NET_SCH_TBF=m
+CONFIG_NET_SCH_GRED=m
+CONFIG_NET_SCH_DSMARK=m
+# CONFIG_NET_SCH_NETEM is not set
+CONFIG_NET_SCH_INGRESS=m
+CONFIG_NET_QOS=y
+CONFIG_NET_ESTIMATOR=y
+CONFIG_NET_CLS=y
+CONFIG_NET_CLS_TCINDEX=m
+CONFIG_NET_CLS_ROUTE4=m
+CONFIG_NET_CLS_ROUTE=y
+CONFIG_NET_CLS_FW=m
+CONFIG_NET_CLS_U32=m
+# CONFIG_CLS_U32_PERF is not set
+# CONFIG_NET_CLS_IND is not set
+CONFIG_NET_CLS_RSVP=m
+CONFIG_NET_CLS_RSVP6=m
+# CONFIG_NET_CLS_ACT is not set
+CONFIG_NET_CLS_POLICE=y
+
+#
+# Network testing
+#
+# CONFIG_NET_PKTGEN is not set
+# CONFIG_NETPOLL is not set
+# CONFIG_NET_POLL_CONTROLLER is not set
+# CONFIG_HAMRADIO is not set
+# CONFIG_IRDA is not set
+# CONFIG_BT is not set
+CONFIG_NETDEVICES=y
+CONFIG_DUMMY=y
+# CONFIG_BONDING is not set
+# CONFIG_EQUALIZER is not set
+# CONFIG_TUN is not set
+
+#
+# ARCnet devices
+#
+# CONFIG_ARCNET is not set
+
+#
+# Ethernet (10 or 100Mbit)
+#
+CONFIG_NET_ETHERNET=y
+# CONFIG_MII is not set
+CONFIG_XT2000_SONIC=y
+# CONFIG_HAPPYMEAL is not set
+# CONFIG_SUNGEM is not set
+# CONFIG_NET_VENDOR_3COM is not set
+
+#
+# Tulip family network device support
+#
+# CONFIG_NET_TULIP is not set
+# CONFIG_HP100 is not set
+# CONFIG_NET_PCI is not set
+
+#
+# Ethernet (1000 Mbit)
+#
+# CONFIG_ACENIC is not set
+# CONFIG_DL2K is not set
+# CONFIG_E1000 is not set
+# CONFIG_NS83820 is not set
+# CONFIG_HAMACHI is not set
+# CONFIG_YELLOWFIN is not set
+# CONFIG_R8169 is not set
+# CONFIG_SK98LIN is not set
+# CONFIG_TIGON3 is not set
+
+#
+# Ethernet (10000 Mbit)
+#
+# CONFIG_IXGB is not set
+# CONFIG_S2IO is not set
+
+#
+# Token Ring devices
+#
+# CONFIG_TR is not set
+
+#
+# Wireless LAN (non-hamradio)
+#
+CONFIG_NET_RADIO=y
+
+#
+# Obsolete Wireless cards support (pre-802.11)
+#
+CONFIG_STRIP=m
+
+#
+# Wireless 802.11b ISA/PCI cards support
+#
+CONFIG_HERMES=m
+# CONFIG_PLX_HERMES is not set
+# CONFIG_TMD_HERMES is not set
+# CONFIG_PCI_HERMES is not set
+# CONFIG_ATMEL is not set
+
+#
+# Prism GT/Duette 802.11(a/b/g) PCI/Cardbus support
+#
+# CONFIG_PRISM54 is not set
+CONFIG_NET_WIRELESS=y
+
+#
+# Wan interfaces
+#
+# CONFIG_WAN is not set
+# CONFIG_FDDI is not set
+# CONFIG_HIPPI is not set
+# CONFIG_PPP is not set
+# CONFIG_SLIP is not set
+# CONFIG_SHAPER is not set
+# CONFIG_NETCONSOLE is not set
+
+#
+# ISDN subsystem
+#
+# CONFIG_ISDN is not set
+
+#
+# Telephony Support
+#
+# CONFIG_PHONE is not set
+
+#
+# Input device support
+#
+CONFIG_INPUT=y
+
+#
+# Userland interfaces
+#
+CONFIG_INPUT_MOUSEDEV=y
+# CONFIG_INPUT_MOUSEDEV_PSAUX is not set
+CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024
+CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768
+# CONFIG_INPUT_JOYDEV is not set
+# CONFIG_INPUT_TSDEV is not set
+# CONFIG_INPUT_EVDEV is not set
+# CONFIG_INPUT_EVBUG is not set
+
+#
+# Input I/O drivers
+#
+# CONFIG_GAMEPORT is not set
+CONFIG_SOUND_GAMEPORT=y
+CONFIG_SERIO=y
+# CONFIG_SERIO_I8042 is not set
+# CONFIG_SERIO_SERPORT is not set
+# CONFIG_SERIO_CT82C710 is not set
+# CONFIG_SERIO_PCIPS2 is not set
+# CONFIG_SERIO_RAW is not set
+
+#
+# Input Device Drivers
+#
+# CONFIG_INPUT_KEYBOARD is not set
+# CONFIG_INPUT_MOUSE is not set
+# CONFIG_INPUT_JOYSTICK is not set
+# CONFIG_INPUT_TOUCHSCREEN is not set
+# CONFIG_INPUT_MISC is not set
+
+#
+# Character devices
+#
+CONFIG_VT=y
+CONFIG_VT_CONSOLE=y
+CONFIG_HW_CONSOLE=y
+# CONFIG_SERIAL_NONSTANDARD is not set
+
+#
+# Serial drivers
+#
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
+CONFIG_SERIAL_8250_NR_UARTS=4
+# CONFIG_SERIAL_8250_EXTENDED is not set
+
+#
+# Non-8250 serial port support
+#
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+CONFIG_UNIX98_PTYS=y
+CONFIG_LEGACY_PTYS=y
+CONFIG_LEGACY_PTY_COUNT=256
+
+#
+# IPMI
+#
+# CONFIG_IPMI_HANDLER is not set
+
+#
+# Watchdog Cards
+#
+# CONFIG_WATCHDOG is not set
+# CONFIG_RTC is not set
+# CONFIG_GEN_RTC is not set
+# CONFIG_DTLK is not set
+# CONFIG_R3964 is not set
+# CONFIG_APPLICOM is not set
+
+#
+# Ftape, the floppy tape device driver
+#
+# CONFIG_DRM is not set
+# CONFIG_RAW_DRIVER is not set
+
+#
+# I2C support
+#
+# CONFIG_I2C is not set
+
+#
+# Dallas's 1-wire bus
+#
+# CONFIG_W1 is not set
+
+#
+# Misc devices
+#
+
+#
+# Multimedia devices
+#
+# CONFIG_VIDEO_DEV is not set
+
+#
+# Digital Video Broadcasting Devices
+#
+# CONFIG_DVB is not set
+
+#
+# Graphics support
+#
+# CONFIG_FB is not set
+
+#
+# Console display driver support
+#
+# CONFIG_VGA_CONSOLE is not set
+CONFIG_DUMMY_CONSOLE=y
+# CONFIG_BACKLIGHT_LCD_SUPPORT is not set
+
+#
+# Sound
+#
+# CONFIG_SOUND is not set
+
+#
+# USB support
+#
+# CONFIG_USB is not set
+CONFIG_USB_ARCH_HAS_HCD=y
+CONFIG_USB_ARCH_HAS_OHCI=y
+
+#
+# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' may also be needed; see USB_STORAGE Help for more information
+#
+
+#
+# USB Gadget Support
+#
+# CONFIG_USB_GADGET is not set
+
+#
+# MMC/SD Card support
+#
+# CONFIG_MMC is not set
+
+#
+# InfiniBand support
+#
+# CONFIG_INFINIBAND is not set
+
+#
+# File systems
+#
+# CONFIG_EXT2_FS is not set
+# CONFIG_EXT3_FS is not set
+# CONFIG_JBD is not set
+# CONFIG_REISERFS_FS is not set
+# CONFIG_JFS_FS is not set
+# CONFIG_XFS_FS is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_ROMFS_FS is not set
+# CONFIG_QUOTA is not set
+CONFIG_DNOTIFY=y
+# CONFIG_AUTOFS_FS is not set
+# CONFIG_AUTOFS4_FS is not set
+
+#
+# CD-ROM/DVD Filesystems
+#
+# CONFIG_ISO9660_FS is not set
+# CONFIG_UDF_FS is not set
+
+#
+# DOS/FAT/NT Filesystems
+#
+# CONFIG_MSDOS_FS is not set
+# CONFIG_VFAT_FS is not set
+# CONFIG_NTFS_FS is not set
+
+#
+# Pseudo filesystems
+#
+CONFIG_PROC_FS=y
+# CONFIG_PROC_KCORE is not set
+CONFIG_SYSFS=y
+CONFIG_DEVFS_FS=y
+# CONFIG_DEVFS_MOUNT is not set
+# CONFIG_DEVFS_DEBUG is not set
+# CONFIG_DEVPTS_FS_XATTR is not set
+# CONFIG_TMPFS is not set
+# CONFIG_HUGETLB_PAGE is not set
+CONFIG_RAMFS=y
+
+#
+# Miscellaneous filesystems
+#
+# CONFIG_ADFS_FS is not set
+# CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
+# CONFIG_HFSPLUS_FS is not set
+# CONFIG_BEFS_FS is not set
+# CONFIG_BFS_FS is not set
+# CONFIG_EFS_FS is not set
+# CONFIG_CRAMFS is not set
+# CONFIG_VXFS_FS is not set
+# CONFIG_HPFS_FS is not set
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_SYSV_FS is not set
+# CONFIG_UFS_FS is not set
+
+#
+# Network File Systems
+#
+CONFIG_NFS_FS=y
+CONFIG_NFS_V3=y
+# CONFIG_NFS_V4 is not set
+# CONFIG_NFS_DIRECTIO is not set
+# CONFIG_NFSD is not set
+CONFIG_ROOT_NFS=y
+CONFIG_LOCKD=y
+CONFIG_LOCKD_V4=y
+# CONFIG_EXPORTFS is not set
+CONFIG_SUNRPC=y
+# CONFIG_RPCSEC_GSS_KRB5 is not set
+# CONFIG_RPCSEC_GSS_SPKM3 is not set
+# CONFIG_SMB_FS is not set
+# CONFIG_CIFS is not set
+# CONFIG_NCP_FS is not set
+# CONFIG_CODA_FS is not set
+# CONFIG_AFS_FS is not set
+
+#
+# Partition Types
+#
+# CONFIG_PARTITION_ADVANCED is not set
+CONFIG_MSDOS_PARTITION=y
+
+#
+# Native Language Support
+#
+# CONFIG_NLS is not set
+
+#
+# Kernel hacking
+#
+CONFIG_DEBUG_KERNEL=y
+# CONFIG_DEBUG_STACKOVERFLOW is not set
+# CONFIG_DEBUG_SLAB is not set
+CONFIG_MAGIC_SYSRQ=y
+# CONFIG_DEBUG_SPINLOCK is not set
+# CONFIG_DEBUG_PAGEALLOC is not set
+# CONFIG_DEBUG_INFO is not set
+# CONFIG_DEBUG_SPINLOCK_SLEEP is not set
+# CONFIG_KGDB is not set
+
+#
+# Security options
+#
+# CONFIG_KEYS is not set
+# CONFIG_SECURITY is not set
+
+#
+# Cryptographic options
+#
+# CONFIG_CRYPTO is not set
+
+#
+# Hardware crypto devices
+#
+
+#
+# Library routines
+#
+# CONFIG_CRC_CCITT is not set
+# CONFIG_CRC32 is not set
+# CONFIG_LIBCRC32C is not set
diff --git a/arch/xtensa/configs/iss_defconfig b/arch/xtensa/configs/iss_defconfig
new file mode 100644
index 000000000000..802621dd4867
--- /dev/null
+++ b/arch/xtensa/configs/iss_defconfig
@@ -0,0 +1,531 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.11-rc2
+# Fri Feb 25 19:21:24 2005
+#
+CONFIG_FRAME_POINTER=y
+CONFIG_XTENSA=y
+# CONFIG_UID16 is not set
+CONFIG_RWSEM_XCHGADD_ALGORITHM=y
+CONFIG_HAVE_DEC_LOCK=y
+CONFIG_GENERIC_HARDIRQS=y
+
+#
+# Code maturity level options
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_CLEAN_COMPILE=y
+CONFIG_BROKEN_ON_SMP=y
+
+#
+# General setup
+#
+CONFIG_LOCALVERSION=""
+CONFIG_SWAP=y
+CONFIG_SYSVIPC=y
+# CONFIG_POSIX_MQUEUE is not set
+# CONFIG_BSD_PROCESS_ACCT is not set
+CONFIG_SYSCTL=y
+# CONFIG_AUDIT is not set
+CONFIG_LOG_BUF_SHIFT=14
+# CONFIG_HOTPLUG is not set
+# CONFIG_KOBJECT_UEVENT is not set
+# CONFIG_IKCONFIG is not set
+CONFIG_EMBEDDED=y
+CONFIG_KALLSYMS=y
+# CONFIG_KALLSYMS_ALL is not set
+# CONFIG_KALLSYMS_EXTRA_PASS is not set
+CONFIG_FUTEX=y
+CONFIG_EPOLL=y
+# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
+CONFIG_SHMEM=y
+CONFIG_CC_ALIGN_FUNCTIONS=0
+CONFIG_CC_ALIGN_LABELS=0
+CONFIG_CC_ALIGN_LOOPS=0
+CONFIG_CC_ALIGN_JUMPS=0
+# CONFIG_TINY_SHMEM is not set
+
+#
+# Loadable module support
+#
+# CONFIG_MODULES is not set
+
+#
+# Processor type and features
+#
+CONFIG_XTENSA_ARCH_LINUX_BE=y
+# CONFIG_XTENSA_ARCH_LINUX_LE is not set
+# CONFIG_XTENSA_ARCH_LINUX_TEST is not set
+# CONFIG_XTENSA_ARCH_S5 is not set
+# CONFIG_XTENSA_CUSTOM is not set
+CONFIG_MMU=y
+# CONFIG_XTENSA_UNALIGNED_USER is not set
+# CONFIG_PREEMPT is not set
+# CONFIG_MATH_EMULATION is not set
+# CONFIG_HIGHMEM is not set
+
+#
+# Platform options
+#
+CONFIG_XTENSA_PLATFORM_ISS=y
+# CONFIG_XTENSA_PLATFORM_XT2000 is not set
+# CONFIG_XTENSA_PLATFORM_ARUBA is not set
+# CONFIG_XTENSA_CALIBRATE_CCOUNT is not set
+CONFIG_XTENSA_CPU_CLOCK=10
+# CONFIG_GENERIC_CALIBRATE_DELAY is not set
+CONFIG_CMDLINE_BOOL=y
+CONFIG_CMDLINE="console=ttyS0,38400 eth0=tuntap,,tap0 ip=192.168.168.5:192.168.168.1 root=nfs nfsroot=192.168.168.1:/opt/montavista/pro/devkit/xtensa/linux_be/target"
+CONFIG_SERIAL_CONSOLE=y
+CONFIG_XTENSA_ISS_NETWORK=y
+
+#
+# Bus options
+#
+
+#
+# PCCARD (PCMCIA/CardBus) support
+#
+# CONFIG_PCCARD is not set
+
+#
+# PC-card bridges
+#
+
+#
+# PCI Hotplug Support
+#
+
+#
+# Exectuable file formats
+#
+CONFIG_KCORE_ELF=y
+CONFIG_BINFMT_ELF=y
+# CONFIG_BINFMT_MISC is not set
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+# CONFIG_STANDALONE is not set
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+# CONFIG_FW_LOADER is not set
+# CONFIG_DEBUG_DRIVER is not set
+
+#
+# Memory Technology Devices (MTD)
+#
+# CONFIG_MTD is not set
+
+#
+# Parallel port support
+#
+# CONFIG_PARPORT is not set
+
+#
+# Plug and Play support
+#
+
+#
+# Block devices
+#
+# CONFIG_BLK_DEV_FD is not set
+# CONFIG_BLK_DEV_COW_COMMON is not set
+# CONFIG_BLK_DEV_LOOP is not set
+# CONFIG_BLK_DEV_NBD is not set
+# CONFIG_BLK_DEV_RAM is not set
+CONFIG_BLK_DEV_RAM_COUNT=16
+CONFIG_INITRAMFS_SOURCE=""
+# CONFIG_CDROM_PKTCDVD is not set
+
+#
+# IO Schedulers
+#
+CONFIG_IOSCHED_NOOP=y
+# CONFIG_IOSCHED_AS is not set
+# CONFIG_IOSCHED_DEADLINE is not set
+# CONFIG_IOSCHED_CFQ is not set
+# CONFIG_ATA_OVER_ETH is not set
+
+#
+# ATA/ATAPI/MFM/RLL support
+#
+# CONFIG_IDE is not set
+
+#
+# SCSI device support
+#
+# CONFIG_SCSI is not set
+
+#
+# Multi-device support (RAID and LVM)
+#
+# CONFIG_MD is not set
+
+#
+# Fusion MPT device support
+#
+
+#
+# IEEE 1394 (FireWire) support
+#
+
+#
+# I2O device support
+#
+
+#
+# Networking support
+#
+CONFIG_NET=y
+
+#
+# Networking options
+#
+CONFIG_PACKET=y
+# CONFIG_PACKET_MMAP is not set
+# CONFIG_NETLINK_DEV is not set
+CONFIG_UNIX=y
+# CONFIG_NET_KEY is not set
+CONFIG_INET=y
+# CONFIG_IP_MULTICAST is not set
+# CONFIG_IP_ADVANCED_ROUTER is not set
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP_BOOTP=y
+CONFIG_IP_PNP_RARP=y
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
+# CONFIG_ARPD is not set
+# CONFIG_SYN_COOKIES is not set
+# CONFIG_INET_AH is not set
+# CONFIG_INET_ESP is not set
+# CONFIG_INET_IPCOMP is not set
+# CONFIG_INET_TUNNEL is not set
+# CONFIG_IP_TCPDIAG is not set
+# CONFIG_IP_TCPDIAG_IPV6 is not set
+# CONFIG_IPV6 is not set
+# CONFIG_NETFILTER is not set
+
+#
+# SCTP Configuration (EXPERIMENTAL)
+#
+# CONFIG_IP_SCTP is not set
+# CONFIG_SCTP_HMAC_NONE is not set
+# CONFIG_SCTP_HMAC_SHA1 is not set
+# CONFIG_SCTP_HMAC_MD5 is not set
+# CONFIG_ATM is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_VLAN_8021Q is not set
+# CONFIG_DECNET is not set
+# CONFIG_LLC2 is not set
+# CONFIG_IPX is not set
+# CONFIG_ATALK is not set
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_NET_DIVERT is not set
+# CONFIG_ECONET is not set
+# CONFIG_WAN_ROUTER is not set
+
+#
+# QoS and/or fair queueing
+#
+# CONFIG_NET_SCHED is not set
+# CONFIG_NET_SCH_CLK_JIFFIES is not set
+# CONFIG_NET_SCH_CLK_GETTIMEOFDAY is not set
+# CONFIG_NET_SCH_CLK_CPU is not set
+# CONFIG_NET_CLS_ROUTE is not set
+
+#
+# Network testing
+#
+# CONFIG_NET_PKTGEN is not set
+# CONFIG_NETPOLL is not set
+# CONFIG_NET_POLL_CONTROLLER is not set
+# CONFIG_HAMRADIO is not set
+# CONFIG_IRDA is not set
+# CONFIG_BT is not set
+# CONFIG_NETDEVICES is not set
+
+#
+# ISDN subsystem
+#
+# CONFIG_ISDN is not set
+
+#
+# Telephony Support
+#
+# CONFIG_PHONE is not set
+
+#
+# Input device support
+#
+CONFIG_INPUT=y
+
+#
+# Userland interfaces
+#
+# CONFIG_INPUT_MOUSEDEV is not set
+# CONFIG_INPUT_JOYDEV is not set
+# CONFIG_INPUT_TSDEV is not set
+# CONFIG_INPUT_EVDEV is not set
+# CONFIG_INPUT_EVBUG is not set
+
+#
+# Input I/O drivers
+#
+# CONFIG_GAMEPORT is not set
+CONFIG_SOUND_GAMEPORT=y
+# CONFIG_SERIO is not set
+# CONFIG_SERIO_I8042 is not set
+
+#
+# Input Device Drivers
+#
+# CONFIG_INPUT_KEYBOARD is not set
+# CONFIG_INPUT_MOUSE is not set
+# CONFIG_INPUT_JOYSTICK is not set
+# CONFIG_INPUT_TOUCHSCREEN is not set
+# CONFIG_INPUT_MISC is not set
+
+#
+# Character devices
+#
+CONFIG_VT=y
+CONFIG_VT_CONSOLE=y
+CONFIG_HW_CONSOLE=y
+# CONFIG_SERIAL_NONSTANDARD is not set
+
+#
+# Serial drivers
+#
+# CONFIG_SERIAL_8250 is not set
+
+#
+# Non-8250 serial port support
+#
+CONFIG_UNIX98_PTYS=y
+CONFIG_LEGACY_PTYS=y
+CONFIG_LEGACY_PTY_COUNT=256
+
+#
+# IPMI
+#
+# CONFIG_IPMI_HANDLER is not set
+
+#
+# Watchdog Cards
+#
+CONFIG_WATCHDOG=y
+CONFIG_WATCHDOG_NOWAYOUT=y
+
+#
+# Watchdog Device Drivers
+#
+CONFIG_SOFT_WATCHDOG=y
+# CONFIG_RTC is not set
+# CONFIG_GEN_RTC is not set
+# CONFIG_DTLK is not set
+# CONFIG_R3964 is not set
+
+#
+# Ftape, the floppy tape device driver
+#
+# CONFIG_DRM is not set
+# CONFIG_RAW_DRIVER is not set
+
+#
+# I2C support
+#
+# CONFIG_I2C is not set
+
+#
+# Dallas's 1-wire bus
+#
+# CONFIG_W1 is not set
+
+#
+# Misc devices
+#
+
+#
+# Multimedia devices
+#
+# CONFIG_VIDEO_DEV is not set
+
+#
+# Digital Video Broadcasting Devices
+#
+# CONFIG_DVB is not set
+
+#
+# Graphics support
+#
+# CONFIG_FB is not set
+
+#
+# Console display driver support
+#
+# CONFIG_VGA_CONSOLE is not set
+CONFIG_DUMMY_CONSOLE=y
+# CONFIG_BACKLIGHT_LCD_SUPPORT is not set
+
+#
+# Sound
+#
+# CONFIG_SOUND is not set
+
+#
+# USB support
+#
+# CONFIG_USB_ARCH_HAS_HCD is not set
+# CONFIG_USB_ARCH_HAS_OHCI is not set
+
+#
+# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' may also be needed; see USB_STORAGE Help for more information
+#
+
+#
+# USB Gadget Support
+#
+# CONFIG_USB_GADGET is not set
+
+#
+# MMC/SD Card support
+#
+# CONFIG_MMC is not set
+
+#
+# InfiniBand support
+#
+# CONFIG_INFINIBAND is not set
+
+#
+# File systems
+#
+# CONFIG_EXT2_FS is not set
+# CONFIG_EXT3_FS is not set
+# CONFIG_JBD is not set
+# CONFIG_REISERFS_FS is not set
+# CONFIG_JFS_FS is not set
+# CONFIG_XFS_FS is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_ROMFS_FS is not set
+# CONFIG_QUOTA is not set
+# CONFIG_DNOTIFY is not set
+# CONFIG_AUTOFS_FS is not set
+# CONFIG_AUTOFS4_FS is not set
+
+#
+# CD-ROM/DVD Filesystems
+#
+# CONFIG_ISO9660_FS is not set
+# CONFIG_UDF_FS is not set
+
+#
+# DOS/FAT/NT Filesystems
+#
+# CONFIG_MSDOS_FS is not set
+# CONFIG_VFAT_FS is not set
+# CONFIG_NTFS_FS is not set
+
+#
+# Pseudo filesystems
+#
+CONFIG_PROC_FS=y
+CONFIG_PROC_KCORE=y
+CONFIG_SYSFS=y
+CONFIG_DEVFS_FS=y
+CONFIG_DEVFS_MOUNT=y
+# CONFIG_DEVFS_DEBUG is not set
+# CONFIG_DEVPTS_FS_XATTR is not set
+CONFIG_TMPFS=y
+# CONFIG_TMPFS_XATTR is not set
+# CONFIG_HUGETLB_PAGE is not set
+CONFIG_RAMFS=y
+
+#
+# Miscellaneous filesystems
+#
+# CONFIG_ADFS_FS is not set
+# CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
+# CONFIG_HFSPLUS_FS is not set
+# CONFIG_BEFS_FS is not set
+# CONFIG_BFS_FS is not set
+# CONFIG_EFS_FS is not set
+# CONFIG_CRAMFS is not set
+# CONFIG_VXFS_FS is not set
+# CONFIG_HPFS_FS is not set
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_SYSV_FS is not set
+# CONFIG_UFS_FS is not set
+
+#
+# Network File Systems
+#
+CONFIG_NFS_FS=y
+CONFIG_NFS_V3=y
+# CONFIG_NFS_V4 is not set
+CONFIG_NFS_DIRECTIO=y
+# CONFIG_NFSD is not set
+CONFIG_ROOT_NFS=y
+CONFIG_LOCKD=y
+CONFIG_LOCKD_V4=y
+# CONFIG_EXPORTFS is not set
+CONFIG_SUNRPC=y
+# CONFIG_RPCSEC_GSS_KRB5 is not set
+# CONFIG_RPCSEC_GSS_SPKM3 is not set
+# CONFIG_SMB_FS is not set
+# CONFIG_CIFS is not set
+# CONFIG_NCP_FS is not set
+# CONFIG_CODA_FS is not set
+# CONFIG_AFS_FS is not set
+
+#
+# Partition Types
+#
+# CONFIG_PARTITION_ADVANCED is not set
+CONFIG_MSDOS_PARTITION=y
+
+#
+# Native Language Support
+#
+# CONFIG_NLS is not set
+
+#
+# Kernel hacking
+#
+CONFIG_DEBUG_KERNEL=y
+# CONFIG_DEBUG_STACKOVERFLOW is not set
+# CONFIG_DEBUG_SLAB is not set
+# CONFIG_MAGIC_SYSRQ is not set
+# CONFIG_DEBUG_SPINLOCK is not set
+# CONFIG_DEBUG_PAGEALLOC is not set
+# CONFIG_DEBUG_INFO is not set
+# CONFIG_DEBUG_SPINLOCK_SLEEP is not set
+# CONFIG_KGDB is not set
+
+#
+# Security options
+#
+# CONFIG_KEYS is not set
+# CONFIG_SECURITY is not set
+
+#
+# Cryptographic options
+#
+# CONFIG_CRYPTO is not set
+
+#
+# Hardware crypto devices
+#
+
+#
+# Library routines
+#
+# CONFIG_CRC_CCITT is not set
+# CONFIG_CRC32 is not set
+# CONFIG_LIBCRC32C is not set
diff --git a/arch/xtensa/kernel/Makefile b/arch/xtensa/kernel/Makefile
new file mode 100644
index 000000000000..d573017a5dde
--- /dev/null
+++ b/arch/xtensa/kernel/Makefile
@@ -0,0 +1,18 @@
+#
+# Makefile for the Linux/Xtensa kernel.
+#
+
+extra-y := head.o vmlinux.lds
+
+
+obj-y := align.o entry.o irq.o coprocessor.o process.o ptrace.o semaphore.o \
+ setup.o signal.o syscalls.o time.o traps.o vectors.o platform.o \
+ pci-dma.o
+
+## windowspill.o
+
+obj-$(CONFIG_KGDB) += xtensa-stub.o
+obj-$(CONFIG_PCI) += pci.o
+obj-$(CONFIG_MODULES) += xtensa_ksyms.o module.o
+
+
diff --git a/arch/xtensa/kernel/align.S b/arch/xtensa/kernel/align.S
new file mode 100644
index 000000000000..74b1e90ef08c
--- /dev/null
+++ b/arch/xtensa/kernel/align.S
@@ -0,0 +1,459 @@
+/*
+ * arch/xtensa/kernel/align.S
+ *
+ * Handle unalignment exceptions in kernel space.
+ *
+ * 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) 2001 - 2005 Tensilica, Inc.
+ *
+ * Rewritten by Chris Zankel <chris@zankel.net>
+ *
+ * Based on work from Joe Taylor <joe@tensilica.com, joetylr@yahoo.com>
+ * and Marc Gauthier <marc@tensilica.com, marc@alimni.uwaterloo.ca>
+ */
+
+#include <linux/linkage.h>
+#include <asm/ptrace.h>
+#include <asm/ptrace.h>
+#include <asm/current.h>
+#include <asm/offsets.h>
+#include <asm/pgtable.h>
+#include <asm/processor.h>
+#include <asm/page.h>
+#include <asm/thread_info.h>
+
+#if XCHAL_UNALIGNED_LOAD_EXCEPTION || XCHAL_UNALIGNED_STORE_EXCEPTION
+
+/* First-level exception handler for unaligned exceptions.
+ *
+ * Note: This handler works only for kernel exceptions. Unaligned user
+ * access should get a seg fault.
+ */
+
+/* Big and little endian 16-bit values are located in
+ * different halves of a register. HWORD_START helps to
+ * abstract the notion of extracting a 16-bit value from a
+ * register.
+ * We also have to define new shifting instructions because
+ * lsb and msb are on 'opposite' ends in a register for
+ * different endian machines.
+ *
+ * Assume a memory region in ascending address:
+ * 0 1 2 3|4 5 6 7
+ *
+ * When loading one word into a register, the content of that register is:
+ * LE 3 2 1 0, 7 6 5 4
+ * BE 0 1 2 3, 4 5 6 7
+ *
+ * Masking the bits of the higher/lower address means:
+ * LE X X 0 0, 0 0 X X
+ * BE 0 0 X X, X X 0 0
+ *
+ * Shifting to higher/lower addresses, means:
+ * LE shift left / shift right
+ * BE shift right / shift left
+ *
+ * Extracting 16 bits from a 32 bit reg. value to higher/lower address means:
+ * LE mask 0 0 X X / shift left
+ * BE shift left / mask 0 0 X X
+ */
+
+#define UNALIGNED_USER_EXCEPTION
+
+#if XCHAL_HAVE_BE
+
+#define HWORD_START 16
+#define INSN_OP0 28
+#define INSN_T 24
+#define INSN_OP1 16
+
+.macro __src_b r, w0, w1; src \r, \w0, \w1; .endm
+.macro __ssa8 r; ssa8b \r; .endm
+.macro __ssa8r r; ssa8l \r; .endm
+.macro __sh r, s; srl \r, \s; .endm
+.macro __sl r, s; sll \r, \s; .endm
+.macro __exth r, s; extui \r, \s, 0, 16; .endm
+.macro __extl r, s; slli \r, \s, 16; .endm
+
+#else
+
+#define HWORD_START 0
+#define INSN_OP0 0
+#define INSN_T 4
+#define INSN_OP1 12
+
+.macro __src_b r, w0, w1; src \r, \w1, \w0; .endm
+.macro __ssa8 r; ssa8l \r; .endm
+.macro __ssa8r r; ssa8b \r; .endm
+.macro __sh r, s; sll \r, \s; .endm
+.macro __sl r, s; srl \r, \s; .endm
+.macro __exth r, s; slli \r, \s, 16; .endm
+.macro __extl r, s; extui \r, \s, 0, 16; .endm
+
+#endif
+
+/*
+ * xxxx xxxx = imm8 field
+ * yyyy = imm4 field
+ * ssss = s field
+ * tttt = t field
+ *
+ * 16 0
+ * -------------------
+ * L32I.N yyyy ssss tttt 1000
+ * S32I.N yyyy ssss tttt 1001
+ *
+ * 23 0
+ * -----------------------------
+ * res 0000 0010
+ * L16UI xxxx xxxx 0001 ssss tttt 0010
+ * L32I xxxx xxxx 0010 ssss tttt 0010
+ * XXX 0011 ssss tttt 0010
+ * XXX 0100 ssss tttt 0010
+ * S16I xxxx xxxx 0101 ssss tttt 0010
+ * S32I xxxx xxxx 0110 ssss tttt 0010
+ * XXX 0111 ssss tttt 0010
+ * XXX 1000 ssss tttt 0010
+ * L16SI xxxx xxxx 1001 ssss tttt 0010
+ * XXX 1010 0010
+ * **L32AI xxxx xxxx 1011 ssss tttt 0010 unsupported
+ * XXX 1100 0010
+ * XXX 1101 0010
+ * XXX 1110 0010
+ * **S32RI xxxx xxxx 1111 ssss tttt 0010 unsupported
+ * -----------------------------
+ * ^ ^ ^
+ * sub-opcode (NIBBLE_R) -+ | |
+ * t field (NIBBLE_T) -----------+ |
+ * major opcode (NIBBLE_OP0) --------------+
+ */
+
+#define OP0_L32I_N 0x8 /* load immediate narrow */
+#define OP0_S32I_N 0x9 /* store immediate narrow */
+#define OP1_SI_MASK 0x4 /* OP1 bit set for stores */
+#define OP1_SI_BIT 2 /* OP1 bit number for stores */
+
+#define OP1_L32I 0x2
+#define OP1_L16UI 0x1
+#define OP1_L16SI 0x9
+#define OP1_L32AI 0xb
+
+#define OP1_S32I 0x6
+#define OP1_S16I 0x5
+#define OP1_S32RI 0xf
+
+/*
+ * Entry condition:
+ *
+ * a0: trashed, original value saved on stack (PT_AREG0)
+ * a1: a1
+ * a2: new stack pointer, original in DEPC
+ * a3: dispatch table
+ * depc: a2, original value saved on stack (PT_DEPC)
+ * excsave_1: a3
+ *
+ * PT_DEPC >= VALID_DOUBLE_EXCEPTION_ADDRESS: double exception, DEPC
+ * < VALID_DOUBLE_EXCEPTION_ADDRESS: regular exception
+ */
+
+
+ENTRY(fast_unaligned)
+
+ /* Note: We don't expect the address to be aligned on a word
+ * boundary. After all, the processor generated that exception
+ * and it would be a hardware fault.
+ */
+
+ /* Save some working register */
+
+ s32i a4, a2, PT_AREG4
+ s32i a5, a2, PT_AREG5
+ s32i a6, a2, PT_AREG6
+ s32i a7, a2, PT_AREG7
+ s32i a8, a2, PT_AREG8
+
+ rsr a0, DEPC
+ xsr a3, EXCSAVE_1
+ s32i a0, a2, PT_AREG2
+ s32i a3, a2, PT_AREG3
+
+ /* Keep value of SAR in a0 */
+
+ rsr a0, SAR
+ rsr a8, EXCVADDR # load unaligned memory address
+
+ /* Now, identify one of the following load/store instructions.
+ *
+ * The only possible danger of a double exception on the
+ * following l32i instructions is kernel code in vmalloc
+ * memory. The processor was just executing at the EPC_1
+ * address, and indeed, already fetched the instruction. That
+ * guarantees a TLB mapping, which hasn't been replaced by
+ * this unaligned exception handler that uses only static TLB
+ * mappings. However, high-level interrupt handlers might
+ * modify TLB entries, so for the generic case, we register a
+ * TABLE_FIXUP handler here, too.
+ */
+
+ /* a3...a6 saved on stack, a2 = SP */
+
+ /* Extract the instruction that caused the unaligned access. */
+
+ rsr a7, EPC_1 # load exception address
+ movi a3, ~3
+ and a3, a3, a7 # mask lower bits
+
+ l32i a4, a3, 0 # load 2 words
+ l32i a5, a3, 4
+
+ __ssa8 a7
+ __src_b a4, a4, a5 # a4 has the instruction
+
+ /* Analyze the instruction (load or store?). */
+
+ extui a5, a4, INSN_OP0, 4 # get insn.op0 nibble
+
+#if XCHAL_HAVE_NARROW
+ _beqi a5, OP0_L32I_N, .Lload # L32I.N, jump
+ addi a6, a5, -OP0_S32I_N
+ _beqz a6, .Lstore # S32I.N, do a store
+#endif
+ /* 'store indicator bit' not set, jump */
+ _bbci.l a4, OP1_SI_BIT + INSN_OP1, .Lload
+
+ /* Store: Jump to table entry to get the value in the source register.*/
+
+.Lstore:movi a5, .Lstore_table # table
+ extui a6, a4, INSN_T, 4 # get source register
+ addx8 a5, a6, a5
+ jx a5 # jump into table
+
+ /* Invalid instruction, CRITICAL! */
+.Linvalid_instruction_load:
+ j .Linvalid_instruction
+
+ /* Load: Load memory address. */
+
+.Lload: movi a3, ~3
+ and a3, a3, a8 # align memory address
+
+ __ssa8 a8
+#ifdef UNALIGNED_USER_EXCEPTION
+ addi a3, a3, 8
+ l32e a5, a3, -8
+ l32e a6, a3, -4
+#else
+ l32i a5, a3, 0
+ l32i a6, a3, 4
+#endif
+ __src_b a3, a5, a6 # a3 has the data word
+
+#if XCHAL_HAVE_NARROW
+ addi a7, a7, 2 # increment PC (assume 16-bit insn)
+
+ extui a5, a4, INSN_OP0, 4
+ _beqi a5, OP0_L32I_N, 1f # l32i.n: jump
+
+ addi a7, a7, 1
+#else
+ addi a7, a7, 3
+#endif
+
+ extui a5, a4, INSN_OP1, 4
+ _beqi a5, OP1_L32I, 1f # l32i: jump
+
+ extui a3, a3, 0, 16 # extract lower 16 bits
+ _beqi a5, OP1_L16UI, 1f
+ addi a5, a5, -OP1_L16SI
+ _bnez a5, .Linvalid_instruction_load
+
+ /* sign extend value */
+
+ slli a3, a3, 16
+ srai a3, a3, 16
+
+ /* Set target register. */
+
+1:
+
+#if XCHAL_HAVE_LOOP
+ rsr a3, LEND # check if we reached LEND
+ bne a7, a3, 1f
+ rsr a3, LCOUNT # and LCOUNT != 0
+ beqz a3, 1f
+ addi a3, a3, -1 # decrement LCOUNT and set
+ rsr a7, LBEG # set PC to LBEGIN
+ wsr a3, LCOUNT
+#endif
+
+1: wsr a7, EPC_1 # skip load instruction
+ extui a4, a4, INSN_T, 4 # extract target register
+ movi a5, .Lload_table
+ addx8 a4, a4, a5
+ jx a4 # jump to entry for target register
+
+ .align 8
+.Lload_table:
+ s32i a3, a2, PT_AREG0; _j .Lexit; .align 8
+ mov a1, a3; _j .Lexit; .align 8 # fishy??
+ s32i a3, a2, PT_AREG2; _j .Lexit; .align 8
+ s32i a3, a2, PT_AREG3; _j .Lexit; .align 8
+ s32i a3, a2, PT_AREG4; _j .Lexit; .align 8
+ s32i a3, a2, PT_AREG5; _j .Lexit; .align 8
+ s32i a3, a2, PT_AREG6; _j .Lexit; .align 8
+ s32i a3, a2, PT_AREG7; _j .Lexit; .align 8
+ s32i a3, a2, PT_AREG8; _j .Lexit; .align 8
+ mov a9, a3 ; _j .Lexit; .align 8
+ mov a10, a3 ; _j .Lexit; .align 8
+ mov a11, a3 ; _j .Lexit; .align 8
+ mov a12, a3 ; _j .Lexit; .align 8
+ mov a13, a3 ; _j .Lexit; .align 8
+ mov a14, a3 ; _j .Lexit; .align 8
+ mov a15, a3 ; _j .Lexit; .align 8
+
+.Lstore_table:
+ l32i a3, a2, PT_AREG0; _j 1f; .align 8
+ mov a3, a1; _j 1f; .align 8 # fishy??
+ l32i a3, a2, PT_AREG2; _j 1f; .align 8
+ l32i a3, a2, PT_AREG3; _j 1f; .align 8
+ l32i a3, a2, PT_AREG4; _j 1f; .align 8
+ l32i a3, a2, PT_AREG5; _j 1f; .align 8
+ l32i a3, a2, PT_AREG6; _j 1f; .align 8
+ l32i a3, a2, PT_AREG7; _j 1f; .align 8
+ l32i a3, a2, PT_AREG8; _j 1f; .align 8
+ mov a3, a9 ; _j 1f; .align 8
+ mov a3, a10 ; _j 1f; .align 8
+ mov a3, a11 ; _j 1f; .align 8
+ mov a3, a12 ; _j 1f; .align 8
+ mov a3, a13 ; _j 1f; .align 8
+ mov a3, a14 ; _j 1f; .align 8
+ mov a3, a15 ; _j 1f; .align 8
+
+1: # a7: instruction pointer, a4: instruction, a3: value
+
+ movi a6, 0 # mask: ffffffff:00000000
+
+#if XCHAL_HAVE_NARROW
+ addi a7, a7, 2 # incr. PC,assume 16-bit instruction
+
+ extui a5, a4, INSN_OP0, 4 # extract OP0
+ addi a5, a5, -OP0_S32I_N
+ _beqz a5, 1f # s32i.n: jump
+
+ addi a7, a7, 1 # increment PC, 32-bit instruction
+#else
+ addi a7, a7, 3 # increment PC, 32-bit instruction
+#endif
+
+ extui a5, a4, INSN_OP1, 4 # extract OP1
+ _beqi a5, OP1_S32I, 1f # jump if 32 bit store
+ _bnei a5, OP1_S16I, .Linvalid_instruction_store
+
+ movi a5, -1
+ __extl a3, a3 # get 16-bit value
+ __exth a6, a5 # get 16-bit mask ffffffff:ffff0000
+
+ /* Get memory address */
+
+1:
+#if XCHAL_HAVE_LOOP
+ rsr a3, LEND # check if we reached LEND
+ bne a7, a3, 1f
+ rsr a3, LCOUNT # and LCOUNT != 0
+ beqz a3, 1f
+ addi a3, a3, -1 # decrement LCOUNT and set
+ rsr a7, LBEG # set PC to LBEGIN
+ wsr a3, LCOUNT
+#endif
+
+1: wsr a7, EPC_1 # skip store instruction
+ movi a4, ~3
+ and a4, a4, a8 # align memory address
+
+ /* Insert value into memory */
+
+ movi a5, -1 # mask: ffffffff:XXXX0000
+#ifdef UNALIGNED_USER_EXCEPTION
+ addi a4, a4, 8
+#endif
+
+ __ssa8r a8
+ __src_b a7, a5, a6 # lo-mask F..F0..0 (BE) 0..0F..F (LE)
+ __src_b a6, a6, a5 # hi-mask 0..0F..F (BE) F..F0..0 (LE)
+#ifdef UNALIGNED_USER_EXCEPTION
+ l32e a5, a4, -8
+#else
+ l32i a5, a4, 0 # load lower address word
+#endif
+ and a5, a5, a7 # mask
+ __sh a7, a3 # shift value
+ or a5, a5, a7 # or with original value
+#ifdef UNALIGNED_USER_EXCEPTION
+ s32e a5, a4, -8
+ l32e a7, a4, -4
+#else
+ s32i a5, a4, 0 # store
+ l32i a7, a4, 4 # same for upper address word
+#endif
+ __sl a5, a3
+ and a6, a7, a6
+ or a6, a6, a5
+#ifdef UNALIGNED_USER_EXCEPTION
+ s32e a6, a4, -4
+#else
+ s32i a6, a4, 4
+#endif
+
+ /* Done. restore stack and return */
+
+.Lexit:
+ movi a4, 0
+ rsr a3, EXCSAVE_1
+ s32i a4, a3, EXC_TABLE_FIXUP
+
+ /* Restore working register */
+
+ l32i a7, a2, PT_AREG7
+ l32i a6, a2, PT_AREG6
+ l32i a5, a2, PT_AREG5
+ l32i a4, a2, PT_AREG4
+ l32i a3, a2, PT_AREG3
+
+ /* restore SAR and return */
+
+ wsr a0, SAR
+ l32i a0, a2, PT_AREG0
+ l32i a2, a2, PT_AREG2
+ rfe
+
+ /* We cannot handle this exception. */
+
+ .extern _kernel_exception
+.Linvalid_instruction_store:
+.Linvalid_instruction:
+
+ /* Restore a4...a8 and SAR, set SP, and jump to default exception. */
+
+ l32i a8, a2, PT_AREG8
+ l32i a7, a2, PT_AREG7
+ l32i a6, a2, PT_AREG6
+ l32i a5, a2, PT_AREG5
+ l32i a4, a2, PT_AREG4
+ wsr a0, SAR
+ mov a1, a2
+
+ rsr a0, PS
+ bbsi.l a2, PS_UM_SHIFT, 1f # jump if user mode
+
+ movi a0, _kernel_exception
+ jx a0
+
+1: movi a0, _user_exception
+ jx a0
+
+
+#endif /* XCHAL_UNALIGNED_LOAD_EXCEPTION || XCHAL_UNALIGNED_STORE_EXCEPTION */
+
diff --git a/arch/xtensa/kernel/asm-offsets.c b/arch/xtensa/kernel/asm-offsets.c
new file mode 100644
index 000000000000..840cd9a1d3d2
--- /dev/null
+++ b/arch/xtensa/kernel/asm-offsets.c
@@ -0,0 +1,94 @@
+/*
+ * arch/xtensa/kernel/asm-offsets.c
+ *
+ * Generates definitions from c-type structures used by assembly sources.
+ *
+ * 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) 2005 Tensilica Inc.
+ *
+ * Chris Zankel <chris@zankel.net>
+ */
+
+#include <asm/processor.h>
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/stddef.h>
+#include <linux/thread_info.h>
+#include <linux/ptrace.h>
+#include <asm/ptrace.h>
+#include <asm/processor.h>
+#include <asm/uaccess.h>
+
+#define DEFINE(sym, val) asm volatile("\n->" #sym " %0 " #val : : "i" (val))
+#define BLANK() asm volatile("\n->" : : )
+
+int main(void)
+{
+ /* struct pt_regs */
+ DEFINE(PT_PC, offsetof (struct pt_regs, pc));
+ DEFINE(PT_PS, offsetof (struct pt_regs, ps));
+ DEFINE(PT_DEPC, offsetof (struct pt_regs, depc));
+ DEFINE(PT_EXCCAUSE, offsetof (struct pt_regs, exccause));
+ DEFINE(PT_EXCVADDR, offsetof (struct pt_regs, excvaddr));
+ DEFINE(PT_DEBUGCAUSE, offsetof (struct pt_regs, debugcause));
+ DEFINE(PT_WMASK, offsetof (struct pt_regs, wmask));
+ DEFINE(PT_LBEG, offsetof (struct pt_regs, lbeg));
+ DEFINE(PT_LEND, offsetof (struct pt_regs, lend));
+ DEFINE(PT_LCOUNT, offsetof (struct pt_regs, lcount));
+ DEFINE(PT_SAR, offsetof (struct pt_regs, sar));
+ DEFINE(PT_SYSCALL, offsetof (struct pt_regs, syscall));
+ DEFINE(PT_AREG, offsetof (struct pt_regs, areg[0]));
+ DEFINE(PT_AREG0, offsetof (struct pt_regs, areg[0]));
+ DEFINE(PT_AREG1, offsetof (struct pt_regs, areg[1]));
+ DEFINE(PT_AREG2, offsetof (struct pt_regs, areg[2]));
+ DEFINE(PT_AREG3, offsetof (struct pt_regs, areg[3]));
+ DEFINE(PT_AREG4, offsetof (struct pt_regs, areg[4]));
+ DEFINE(PT_AREG5, offsetof (struct pt_regs, areg[5]));
+ DEFINE(PT_AREG6, offsetof (struct pt_regs, areg[6]));
+ DEFINE(PT_AREG7, offsetof (struct pt_regs, areg[7]));
+ DEFINE(PT_AREG8, offsetof (struct pt_regs, areg[8]));
+ DEFINE(PT_AREG9, offsetof (struct pt_regs, areg[9]));
+ DEFINE(PT_AREG10, offsetof (struct pt_regs, areg[10]));
+ DEFINE(PT_AREG11, offsetof (struct pt_regs, areg[11]));
+ DEFINE(PT_AREG12, offsetof (struct pt_regs, areg[12]));
+ DEFINE(PT_AREG13, offsetof (struct pt_regs, areg[13]));
+ DEFINE(PT_AREG14, offsetof (struct pt_regs, areg[14]));
+ DEFINE(PT_AREG15, offsetof (struct pt_regs, areg[15]));
+ DEFINE(PT_WINDOWBASE, offsetof (struct pt_regs, windowbase));
+ DEFINE(PT_WINDOWSTART, offsetof(struct pt_regs, windowstart));
+ DEFINE(PT_SIZE, sizeof(struct pt_regs));
+ DEFINE(PT_AREG_END, offsetof (struct pt_regs, areg[XCHAL_NUM_AREGS]));
+ DEFINE(PT_USER_SIZE, offsetof(struct pt_regs, areg[XCHAL_NUM_AREGS]));
+ BLANK();
+
+ /* struct task_struct */
+ DEFINE(TASK_PTRACE, offsetof (struct task_struct, ptrace));
+ DEFINE(TASK_MM, offsetof (struct task_struct, mm));
+ DEFINE(TASK_ACTIVE_MM, offsetof (struct task_struct, active_mm));
+ DEFINE(TASK_PID, offsetof (struct task_struct, pid));
+ DEFINE(TASK_THREAD, offsetof (struct task_struct, thread));
+ DEFINE(TASK_THREAD_INFO, offsetof (struct task_struct, thread_info));
+ DEFINE(TASK_STRUCT_SIZE, sizeof (struct task_struct));
+ BLANK();
+
+ /* struct thread_info (offset from start_struct) */
+ DEFINE(THREAD_RA, offsetof (struct task_struct, thread.ra));
+ DEFINE(THREAD_SP, offsetof (struct task_struct, thread.sp));
+ DEFINE(THREAD_CP_SAVE, offsetof (struct task_struct, thread.cp_save));
+ DEFINE(THREAD_CURRENT_DS, offsetof (struct task_struct, thread.current_ds));
+ BLANK();
+
+ /* struct mm_struct */
+ DEFINE(MM_USERS, offsetof(struct mm_struct, mm_users));
+ DEFINE(MM_PGD, offsetof (struct mm_struct, pgd));
+ DEFINE(MM_CONTEXT, offsetof (struct mm_struct, context));
+ BLANK();
+ DEFINE(PT_SINGLESTEP_BIT, PT_SINGLESTEP_BIT);
+ return 0;
+}
+
+
diff --git a/arch/xtensa/kernel/coprocessor.S b/arch/xtensa/kernel/coprocessor.S
new file mode 100644
index 000000000000..356192a4d39d
--- /dev/null
+++ b/arch/xtensa/kernel/coprocessor.S
@@ -0,0 +1,201 @@
+/*
+ * arch/xtensa/kernel/coprocessor.S
+ *
+ * Xtensa processor configuration-specific table of coprocessor and
+ * other custom register layout information.
+ *
+ * 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 - 2005 Tensilica Inc.
+ *
+ * Marc Gauthier <marc@tensilica.com> <marc@alumni.uwaterloo.ca>
+ */
+
+/*
+ * This module contains a table that describes the layout of the various
+ * custom registers and states associated with each coprocessor, as well
+ * as those not associated with any coprocessor ("extra state").
+ * This table is included with core dumps and is available via the ptrace
+ * interface, allowing the layout of such register/state information to
+ * be modified in the kernel without affecting the debugger. Each
+ * register or state is identified using a 32-bit "libdb target number"
+ * assigned when the Xtensa processor is generated.
+ */
+
+#include <linux/config.h>
+#include <linux/linkage.h>
+#include <asm/processor.h>
+
+#if XCHAL_HAVE_CP
+
+#define CP_LAST ((XCHAL_CP_MAX - 1) * COPROCESSOR_INFO_SIZE)
+
+ENTRY(release_coprocessors)
+
+ entry a1, 16
+ # a2: task
+ movi a3, 1 << XCHAL_CP_MAX # a3: coprocessor-bit
+ movi a4, coprocessor_info+CP_LAST # a4: owner-table
+ # a5: tmp
+ movi a6, 0 # a6: 0
+ rsil a7, LOCKLEVEL # a7: PS
+
+1: /* Check if task is coprocessor owner of coprocessor[i]. */
+
+ l32i a5, a4, COPROCESSOR_INFO_OWNER
+ srli a3, a3, 1
+ beqz a3, 1f
+ addi a4, a4, -8
+ beq a2, a5, 1b
+
+ /* Found an entry: Clear entry CPENABLE bit to disable CP. */
+
+ rsr a5, CPENABLE
+ s32i a6, a4, COPROCESSOR_INFO_OWNER
+ xor a5, a3, a5
+ wsr a5, CPENABLE
+
+ bnez a3, 1b
+
+1: wsr a7, PS
+ rsync
+ retw
+
+
+ENTRY(disable_coprocessor)
+ entry sp, 16
+ rsil a7, LOCKLEVEL
+ rsr a3, CPENABLE
+ movi a4, 1
+ ssl a2
+ sll a4, a4
+ and a4, a3, a4
+ xor a3, a3, a4
+ wsr a3, CPENABLE
+ wsr a7, PS
+ rsync
+ retw
+
+ENTRY(enable_coprocessor)
+ entry sp, 16
+ rsil a7, LOCKLEVEL
+ rsr a3, CPENABLE
+ movi a4, 1
+ ssl a2
+ sll a4, a4
+ or a3, a3, a4
+ wsr a3, CPENABLE
+ wsr a7, PS
+ rsync
+ retw
+
+#endif
+
+ENTRY(save_coprocessor_extra)
+ entry sp, 16
+ xchal_extra_store_funcbody
+ retw
+
+ENTRY(restore_coprocessor_extra)
+ entry sp, 16
+ xchal_extra_load_funcbody
+ retw
+
+ENTRY(save_coprocessor_registers)
+ entry sp, 16
+ xchal_cpi_store_funcbody
+ retw
+
+ENTRY(restore_coprocessor_registers)
+ entry sp, 16
+ xchal_cpi_load_funcbody
+ retw
+
+
+/*
+ * The Xtensa compile-time HAL (core.h) XCHAL_*_SA_CONTENTS_LIBDB macros
+ * describe the contents of coprocessor & extra save areas in terms of
+ * undefined CONTENTS_LIBDB_{SREG,UREG,REGF} macros. We define these
+ * latter macros here; they expand into a table of the format we want.
+ * The general format is:
+ *
+ * CONTENTS_LIBDB_SREG(libdbnum, offset, size, align, rsv1, name, sregnum,
+ * bitmask, rsv2, rsv3)
+ * CONTENTS_LIBDB_UREG(libdbnum, offset, size, align, rsv1, name, uregnum,
+ * bitmask, rsv2, rsv3)
+ * CONTENTS_LIBDB_REGF(libdbnum, offset, size, align, rsv1, name, index,
+ * numentries, contentsize, regname_base,
+ * regfile_name, rsv2, rsv3)
+ *
+ * For this table, we only care about the <libdbnum>, <offset> and <size>
+ * fields.
+ */
+
+/* Map all XCHAL CONTENTS macros to the reg_entry asm macro defined below: */
+
+#define CONTENTS_LIBDB_SREG(libdbnum,offset,size,align,rsv1,name,sregnum, \
+ bitmask, rsv2, rsv3) \
+ reg_entry libdbnum, offset, size ;
+#define CONTENTS_LIBDB_UREG(libdbnum,offset,size,align,rsv1,name,uregnum, \
+ bitmask, rsv2, rsv3) \
+ reg_entry libdbnum, offset, size ;
+#define CONTENTS_LIBDB_REGF(libdbnum, offset, size, align, rsv1, name, index, \
+ numentries, contentsize, regname_base, \
+ regfile_name, rsv2, rsv3) \
+ reg_entry libdbnum, offset, size ;
+
+/* A single table entry: */
+ .macro reg_entry libdbnum, offset, size
+ .ifne (__last_offset-(__last_group_offset+\offset))
+ /* padding entry */
+ .word (0xFC000000+__last_offset-(__last_group_offset+\offset))
+ .endif
+ .word \libdbnum /* actual entry */
+ .set __last_offset, __last_group_offset+\offset+\size
+ .endm /* reg_entry */
+
+
+/* Table entry that marks the beginning of a group (coprocessor or "extra"): */
+ .macro reg_group cpnum, num_entries, align
+ .set __last_group_offset, (__last_offset + \align- 1) & -\align
+ .ifne \num_entries
+ .word 0xFD000000+(\cpnum<<16)+\num_entries
+ .endif
+ .endm /* reg_group */
+
+/*
+ * Register info tables.
+ */
+
+ .section .rodata, "a"
+ .globl _xtensa_reginfo_tables
+ .globl _xtensa_reginfo_table_size
+ .align 4
+_xtensa_reginfo_table_size:
+ .word _xtensa_reginfo_table_end - _xtensa_reginfo_tables
+
+_xtensa_reginfo_tables:
+ .set __last_offset, 0
+ reg_group 0xFF, XCHAL_EXTRA_SA_CONTENTS_LIBDB_NUM, XCHAL_EXTRA_SA_ALIGN
+ XCHAL_EXTRA_SA_CONTENTS_LIBDB
+ reg_group 0, XCHAL_CP0_SA_CONTENTS_LIBDB_NUM, XCHAL_CP0_SA_ALIGN
+ XCHAL_CP0_SA_CONTENTS_LIBDB
+ reg_group 1, XCHAL_CP1_SA_CONTENTS_LIBDB_NUM, XCHAL_CP1_SA_ALIGN
+ XCHAL_CP1_SA_CONTENTS_LIBDB
+ reg_group 2, XCHAL_CP2_SA_CONTENTS_LIBDB_NUM, XCHAL_CP2_SA_ALIGN
+ XCHAL_CP2_SA_CONTENTS_LIBDB
+ reg_group 3, XCHAL_CP3_SA_CONTENTS_LIBDB_NUM, XCHAL_CP3_SA_ALIGN
+ XCHAL_CP3_SA_CONTENTS_LIBDB
+ reg_group 4, XCHAL_CP4_SA_CONTENTS_LIBDB_NUM, XCHAL_CP4_SA_ALIGN
+ XCHAL_CP4_SA_CONTENTS_LIBDB
+ reg_group 5, XCHAL_CP5_SA_CONTENTS_LIBDB_NUM, XCHAL_CP5_SA_ALIGN
+ XCHAL_CP5_SA_CONTENTS_LIBDB
+ reg_group 6, XCHAL_CP6_SA_CONTENTS_LIBDB_NUM, XCHAL_CP6_SA_ALIGN
+ XCHAL_CP6_SA_CONTENTS_LIBDB
+ reg_group 7, XCHAL_CP7_SA_CONTENTS_LIBDB_NUM, XCHAL_CP7_SA_ALIGN
+ XCHAL_CP7_SA_CONTENTS_LIBDB
+ .word 0xFC000000 /* invalid register number,marks end of table*/
+_xtensa_reginfo_table_end:
+
diff --git a/arch/xtensa/kernel/entry.S b/arch/xtensa/kernel/entry.S
new file mode 100644
index 000000000000..c64a01f71de6
--- /dev/null
+++ b/arch/xtensa/kernel/entry.S
@@ -0,0 +1,1996 @@
+/*
+ * arch/xtensa/kernel/entry.S
+ *
+ * Low-level exception handling
+ *
+ * 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) 2004-2005 by Tensilica Inc.
+ *
+ * Chris Zankel <chris@zankel.net>
+ *
+ */
+
+#include <linux/linkage.h>
+#include <asm/offsets.h>
+#include <asm/processor.h>
+#include <asm/thread_info.h>
+#include <asm/uaccess.h>
+#include <asm/unistd.h>
+#include <asm/ptrace.h>
+#include <asm/current.h>
+#include <asm/pgtable.h>
+#include <asm/page.h>
+#include <asm/signal.h>
+#include <xtensa/coreasm.h>
+
+/* Unimplemented features. */
+
+#undef SIGNAL_HANDLING_IN_DOUBLE_EXCEPTION
+#undef KERNEL_STACK_OVERFLOW_CHECK
+#undef PREEMPTIBLE_KERNEL
+#undef ALLOCA_EXCEPTION_IN_IRAM
+
+/* Not well tested.
+ *
+ * - fast_coprocessor
+ */
+
+/*
+ * Macro to find first bit set in WINDOWBASE from the left + 1
+ *
+ * 100....0 -> 1
+ * 010....0 -> 2
+ * 000....1 -> WSBITS
+ */
+
+ .macro ffs_ws bit mask
+
+#if XCHAL_HAVE_NSA
+ nsau \bit, \mask # 32-WSBITS ... 31 (32 iff 0)
+ addi \bit, \bit, WSBITS - 32 + 1 # uppest bit set -> return 1
+#else
+ movi \bit, WSBITS
+#if WSBITS > 16
+ _bltui \mask, 0x10000, 99f
+ addi \bit, \bit, -16
+ extui \mask, \mask, 16, 16
+#endif
+#if WSBITS > 8
+99: _bltui \mask, 0x100, 99f
+ addi \bit, \bit, -8
+ srli \mask, \mask, 8
+#endif
+99: _bltui \mask, 0x10, 99f
+ addi \bit, \bit, -4
+ srli \mask, \mask, 4
+99: _bltui \mask, 0x4, 99f
+ addi \bit, \bit, -2
+ srli \mask, \mask, 2
+99: _bltui \mask, 0x2, 99f
+ addi \bit, \bit, -1
+99:
+
+#endif
+ .endm
+
+/* ----------------- DEFAULT FIRST LEVEL EXCEPTION HANDLERS ----------------- */
+
+/*
+ * First-level exception handler for user exceptions.
+ * Save some special registers, extra states and all registers in the AR
+ * register file that were in use in the user task, and jump to the common
+ * exception code.
+ * We save SAR (used to calculate WMASK), and WB and WS (we don't have to
+ * save them for kernel exceptions).
+ *
+ * Entry condition for user_exception:
+ *
+ * a0: trashed, original value saved on stack (PT_AREG0)
+ * a1: a1
+ * a2: new stack pointer, original value in depc
+ * a3: dispatch table
+ * depc: a2, original value saved on stack (PT_DEPC)
+ * excsave1: a3
+ *
+ * PT_DEPC >= VALID_DOUBLE_EXCEPTION_ADDRESS: double exception, DEPC
+ * < VALID_DOUBLE_EXCEPTION_ADDRESS: regular exception
+ *
+ * Entry condition for _user_exception:
+ *
+ * a0-a3 and depc have been saved to PT_AREG0...PT_AREG3 and PT_DEPC
+ * excsave has been restored, and
+ * stack pointer (a1) has been set.
+ *
+ * Note: _user_exception might be at an odd adress. Don't use call0..call12
+ */
+
+ENTRY(user_exception)
+
+ /* Save a2, a3, and depc, restore excsave_1 and set SP. */
+
+ xsr a3, EXCSAVE_1
+ rsr a0, DEPC
+ s32i a1, a2, PT_AREG1
+ s32i a0, a2, PT_AREG2
+ s32i a3, a2, PT_AREG3
+ mov a1, a2
+
+ .globl _user_exception
+_user_exception:
+
+ /* Save SAR and turn off single stepping */
+
+ movi a2, 0
+ rsr a3, SAR
+ wsr a2, ICOUNTLEVEL
+ s32i a3, a1, PT_SAR
+
+ /* Rotate ws so that the current windowbase is at bit0. */
+ /* Assume ws = xxwww1yyyy. Rotate ws right, so that a2 = yyyyxxwww1 */
+
+ rsr a2, WINDOWBASE
+ rsr a3, WINDOWSTART
+ ssr a2
+ s32i a2, a1, PT_WINDOWBASE
+ s32i a3, a1, PT_WINDOWSTART
+ slli a2, a3, 32-WSBITS
+ src a2, a3, a2
+ srli a2, a2, 32-WSBITS
+ s32i a2, a1, PT_WMASK # needed for restoring registers
+
+ /* Save only live registers. */
+
+ _bbsi.l a2, 1, 1f
+ s32i a4, a1, PT_AREG4
+ s32i a5, a1, PT_AREG5
+ s32i a6, a1, PT_AREG6
+ s32i a7, a1, PT_AREG7
+ _bbsi.l a2, 2, 1f
+ s32i a8, a1, PT_AREG8
+ s32i a9, a1, PT_AREG9
+ s32i a10, a1, PT_AREG10
+ s32i a11, a1, PT_AREG11
+ _bbsi.l a2, 3, 1f
+ s32i a12, a1, PT_AREG12
+ s32i a13, a1, PT_AREG13
+ s32i a14, a1, PT_AREG14
+ s32i a15, a1, PT_AREG15
+ _bnei a2, 1, 1f # only one valid frame?
+
+ /* Only one valid frame, skip saving regs. */
+
+ j 2f
+
+ /* Save the remaining registers.
+ * We have to save all registers up to the first '1' from
+ * the right, except the current frame (bit 0).
+ * Assume a2 is: 001001000110001
+ * All regiser frames starting from the top fiel to the marked '1'
+ * must be saved.
+ */
+
+1: addi a3, a2, -1 # eliminate '1' in bit 0: yyyyxxww0
+ neg a3, a3 # yyyyxxww0 -> YYYYXXWW1+1
+ and a3, a3, a2 # max. only one bit is set
+
+ /* Find number of frames to save */
+
+ ffs_ws a0, a3 # number of frames to the '1' from left
+
+ /* Store information into WMASK:
+ * bits 0..3: xxx1 masked lower 4 bits of the rotated windowstart,
+ * bits 4...: number of valid 4-register frames
+ */
+
+ slli a3, a0, 4 # number of frames to save in bits 8..4
+ extui a2, a2, 0, 4 # mask for the first 16 registers
+ or a2, a3, a2
+ s32i a2, a1, PT_WMASK # needed when we restore the reg-file
+
+ /* Save 4 registers at a time */
+
+1: rotw -1
+ s32i a0, a5, PT_AREG_END - 16
+ s32i a1, a5, PT_AREG_END - 12
+ s32i a2, a5, PT_AREG_END - 8
+ s32i a3, a5, PT_AREG_END - 4
+ addi a0, a4, -1
+ addi a1, a5, -16
+ _bnez a0, 1b
+
+ /* WINDOWBASE still in SAR! */
+
+ rsr a2, SAR # original WINDOWBASE
+ movi a3, 1
+ ssl a2
+ sll a3, a3
+ wsr a3, WINDOWSTART # set corresponding WINDOWSTART bit
+ wsr a2, WINDOWBASE # and WINDOWSTART
+ rsync
+
+ /* We are back to the original stack pointer (a1) */
+
+2:
+#if XCHAL_EXTRA_SA_SIZE
+
+ /* For user exceptions, save the extra state into the user's TCB.
+ * Note: We must assume that xchal_extra_store_funcbody destroys a2..a15
+ */
+
+ GET_CURRENT(a2,a1)
+ addi a2, a2, THREAD_CP_SAVE
+ xchal_extra_store_funcbody
+#endif
+
+ /* Now, jump to the common exception handler. */
+
+ j common_exception
+
+
+/*
+ * First-level exit handler for kernel exceptions
+ * Save special registers and the live window frame.
+ * Note: Even though we changes the stack pointer, we don't have to do a
+ * MOVSP here, as we do that when we return from the exception.
+ * (See comment in the kernel exception exit code)
+ *
+ * Entry condition for kernel_exception:
+ *
+ * a0: trashed, original value saved on stack (PT_AREG0)
+ * a1: a1
+ * a2: new stack pointer, original in DEPC
+ * a3: dispatch table
+ * depc: a2, original value saved on stack (PT_DEPC)
+ * excsave_1: a3
+ *
+ * PT_DEPC >= VALID_DOUBLE_EXCEPTION_ADDRESS: double exception, DEPC
+ * < VALID_DOUBLE_EXCEPTION_ADDRESS: regular exception
+ *
+ * Entry condition for _kernel_exception:
+ *
+ * a0-a3 and depc have been saved to PT_AREG0...PT_AREG3 and PT_DEPC
+ * excsave has been restored, and
+ * stack pointer (a1) has been set.
+ *
+ * Note: _kernel_exception might be at an odd adress. Don't use call0..call12
+ */
+
+ENTRY(kernel_exception)
+
+ /* Save a0, a2, a3, DEPC and set SP. */
+
+ xsr a3, EXCSAVE_1 # restore a3, excsave_1
+ rsr a0, DEPC # get a2
+ s32i a1, a2, PT_AREG1
+ s32i a0, a2, PT_AREG2
+ s32i a3, a2, PT_AREG3
+ mov a1, a2
+
+ .globl _kernel_exception
+_kernel_exception:
+
+ /* Save SAR and turn off single stepping */
+
+ movi a2, 0
+ rsr a3, SAR
+ wsr a2, ICOUNTLEVEL
+ s32i a3, a1, PT_SAR
+
+ /* Rotate ws so that the current windowbase is at bit0. */
+ /* Assume ws = xxwww1yyyy. Rotate ws right, so that a2 = yyyyxxwww1 */
+
+ rsr a2, WINDOWBASE # don't need to save these, we only
+ rsr a3, WINDOWSTART # need shifted windowstart: windowmask
+ ssr a2
+ slli a2, a3, 32-WSBITS
+ src a2, a3, a2
+ srli a2, a2, 32-WSBITS
+ s32i a2, a1, PT_WMASK # needed for kernel_exception_exit
+
+ /* Save only the live window-frame */
+
+ _bbsi.l a2, 1, 1f
+ s32i a4, a1, PT_AREG4
+ s32i a5, a1, PT_AREG5
+ s32i a6, a1, PT_AREG6
+ s32i a7, a1, PT_AREG7
+ _bbsi.l a2, 2, 1f
+ s32i a8, a1, PT_AREG8
+ s32i a9, a1, PT_AREG9
+ s32i a10, a1, PT_AREG10
+ s32i a11, a1, PT_AREG11
+ _bbsi.l a2, 3, 1f
+ s32i a12, a1, PT_AREG12
+ s32i a13, a1, PT_AREG13
+ s32i a14, a1, PT_AREG14
+ s32i a15, a1, PT_AREG15
+
+1:
+
+#ifdef KERNEL_STACK_OVERFLOW_CHECK
+
+ /* Stack overflow check, for debugging */
+ extui a2, a1, TASK_SIZE_BITS,XX
+ movi a3, SIZE??
+ _bge a2, a3, out_of_stack_panic
+
+#endif
+
+/*
+ * This is the common exception handler.
+ * We get here from the user exception handler or simply by falling through
+ * from the kernel exception handler.
+ * Save the remaining special registers, switch to kernel mode, and jump
+ * to the second-level exception handler.
+ *
+ */
+
+common_exception:
+
+ /* Save EXCVADDR, DEBUGCAUSE, and PC, and clear LCOUNT */
+
+ rsr a2, DEBUGCAUSE
+ rsr a3, EPC_1
+ s32i a2, a1, PT_DEBUGCAUSE
+ s32i a3, a1, PT_PC
+
+ rsr a3, EXCVADDR
+ movi a2, 0
+ s32i a3, a1, PT_EXCVADDR
+ xsr a2, LCOUNT
+ s32i a2, a1, PT_LCOUNT
+
+ /* It is now save to restore the EXC_TABLE_FIXUP variable. */
+
+ rsr a0, EXCCAUSE
+ movi a3, 0
+ rsr a2, EXCSAVE_1
+ s32i a0, a1, PT_EXCCAUSE
+ s32i a3, a2, EXC_TABLE_FIXUP
+
+ /* All unrecoverable states are saved on stack, now, and a1 is valid,
+ * so we can allow exceptions and interrupts (*) again.
+ * Set PS(EXCM = 0, UM = 0, RING = 0, OWB = 0, WOE = 1, INTLEVEL = X)
+ *
+ * (*) We only allow interrupts if PS.INTLEVEL was not set to 1 before
+ * (interrupts disabled) and if this exception is not an interrupt.
+ */
+
+ rsr a3, PS
+ addi a0, a0, -4
+ movi a2, 1
+ extui a3, a3, 0, 1 # a3 = PS.INTLEVEL[0]
+ moveqz a3, a2, a0 # a3 = 1 iff interrupt exception
+ movi a2, PS_WOE_MASK
+ or a3, a3, a2
+ rsr a0, EXCCAUSE
+ xsr a3, PS
+
+ s32i a3, a1, PT_PS # save ps
+
+ /* Save LBEG, LEND */
+
+ rsr a2, LBEG
+ rsr a3, LEND
+ s32i a2, a1, PT_LBEG
+ s32i a3, a1, PT_LEND
+
+ /* Go to second-level dispatcher. Set up parameters to pass to the
+ * exception handler and call the exception handler.
+ */
+
+ movi a4, exc_table
+ mov a6, a1 # pass stack frame
+ mov a7, a0 # pass EXCCAUSE
+ addx4 a4, a0, a4
+ l32i a4, a4, EXC_TABLE_DEFAULT # load handler
+
+ /* Call the second-level handler */
+
+ callx4 a4
+
+ /* Jump here for exception exit */
+
+common_exception_return:
+
+ /* Jump if we are returning from kernel exceptions. */
+
+1: l32i a3, a1, PT_PS
+ _bbsi.l a3, PS_UM_SHIFT, 2f
+ j kernel_exception_exit
+
+ /* Specific to a user exception exit:
+ * We need to check some flags for signal handling and rescheduling,
+ * and have to restore WB and WS, extra states, and all registers
+ * in the register file that were in use in the user task.
+ */
+
+2: wsr a3, PS /* disable interrupts */
+
+ /* Check for signals (keep interrupts disabled while we read TI_FLAGS)
+ * Note: PS.INTLEVEL = 0, PS.EXCM = 1
+ */
+
+ GET_THREAD_INFO(a2,a1)
+ l32i a4, a2, TI_FLAGS
+
+ /* Enable interrupts again.
+ * Note: When we get here, we certainly have handled any interrupts.
+ * (Hint: There is only one user exception frame on stack)
+ */
+
+ movi a3, PS_WOE_MASK
+
+ _bbsi.l a4, TIF_NEED_RESCHED, 3f
+ _bbci.l a4, TIF_SIGPENDING, 4f
+
+#ifndef SIGNAL_HANDLING_IN_DOUBLE_EXCEPTION
+ l32i a4, a1, PT_DEPC
+ bgeui a4, VALID_DOUBLE_EXCEPTION_ADDRESS, 4f
+#endif
+
+ /* Reenable interrupts and call do_signal() */
+
+ wsr a3, PS
+ movi a4, do_signal # int do_signal(struct pt_regs*, sigset_t*)
+ mov a6, a1
+ movi a7, 0
+ callx4 a4
+ j 1b
+
+3: /* Reenable interrupts and reschedule */
+
+ wsr a3, PS
+ movi a4, schedule # void schedule (void)
+ callx4 a4
+ j 1b
+
+ /* Restore the state of the task and return from the exception. */
+
+
+ /* If we are returning from a user exception, and the process
+ * to run next has PT_SINGLESTEP set, we want to setup
+ * ICOUNT and ICOUNTLEVEL to step one instruction.
+ * PT_SINGLESTEP is set by sys_ptrace (ptrace.c)
+ */
+
+4: /* a2 holds GET_CURRENT(a2,a1) */
+
+ l32i a3, a2, TI_TASK
+ l32i a3, a3, TASK_PTRACE
+ bbci.l a3, PT_SINGLESTEP_BIT, 1f # jump if single-step flag is not set
+
+ movi a3, -2 # PT_SINGLESTEP flag is set,
+ movi a4, 1 # icountlevel of 1 means it won't
+ wsr a3, ICOUNT # start counting until after rfe
+ wsr a4, ICOUNTLEVEL # so setup icount & icountlevel.
+ isync
+
+1:
+
+#if XCHAL_EXTRA_SA_SIZE
+
+ /* For user exceptions, restore the extra state from the user's TCB. */
+
+ /* Note: a2 still contains GET_CURRENT(a2,a1) */
+ addi a2, a2, THREAD_CP_SAVE
+ xchal_extra_load_funcbody
+
+ /* We must assume that xchal_extra_store_funcbody destroys
+ * registers a2..a15. FIXME, this list can eventually be
+ * reduced once real register requirements of the macro are
+ * finalized. */
+
+#endif /* XCHAL_EXTRA_SA_SIZE */
+
+
+ /* Switch to the user thread WINDOWBASE. Save SP temporarily in DEPC */
+
+ l32i a2, a1, PT_WINDOWBASE
+ l32i a3, a1, PT_WINDOWSTART
+ wsr a1, DEPC # use DEPC as temp storage
+ wsr a3, WINDOWSTART # restore WINDOWSTART
+ ssr a2 # preserve user's WB in the SAR
+ wsr a2, WINDOWBASE # switch to user's saved WB
+ rsync
+ rsr a1, DEPC # restore stack pointer
+ l32i a2, a1, PT_WMASK # register frames saved (in bits 4...9)
+ rotw -1 # we restore a4..a7
+ _bltui a6, 16, 1f # only have to restore current window?
+
+ /* The working registers are a0 and a3. We are restoring to
+ * a4..a7. Be careful not to destroy what we have just restored.
+ * Note: wmask has the format YYYYM:
+ * Y: number of registers saved in groups of 4
+ * M: 4 bit mask of first 16 registers
+ */
+
+ mov a2, a6
+ mov a3, a5
+
+2: rotw -1 # a0..a3 become a4..a7
+ addi a3, a7, -4*4 # next iteration
+ addi a2, a6, -16 # decrementing Y in WMASK
+ l32i a4, a3, PT_AREG_END + 0
+ l32i a5, a3, PT_AREG_END + 4
+ l32i a6, a3, PT_AREG_END + 8
+ l32i a7, a3, PT_AREG_END + 12
+ _bgeui a2, 16, 2b
+
+ /* Clear unrestored registers (don't leak anything to user-land */
+
+1: rsr a0, WINDOWBASE
+ rsr a3, SAR
+ sub a3, a0, a3
+ beqz a3, 2f
+ extui a3, a3, 0, WBBITS
+
+1: rotw -1
+ addi a3, a7, -1
+ movi a4, 0
+ movi a5, 0
+ movi a6, 0
+ movi a7, 0
+ bgei a3, 1, 1b
+
+ /* We are back were we were when we started.
+ * Note: a2 still contains WMASK (if we've returned to the original
+ * frame where we had loaded a2), or at least the lower 4 bits
+ * (if we have restored WSBITS-1 frames).
+ */
+
+2: j common_exception_exit
+
+ /* This is the kernel exception exit.
+ * We avoided to do a MOVSP when we entered the exception, but we
+ * have to do it here.
+ */
+
+kernel_exception_exit:
+
+ /* Disable interrupts (a3 holds PT_PS) */
+
+ wsr a3, PS
+
+#ifdef PREEMPTIBLE_KERNEL
+
+#ifdef CONFIG_PREEMPT
+
+ /*
+ * Note: We've just returned from a call4, so we have
+ * at least 4 addt'l regs.
+ */
+
+ /* Check current_thread_info->preempt_count */
+
+ GET_THREAD_INFO(a2)
+ l32i a3, a2, TI_PREEMPT
+ bnez a3, 1f
+
+ l32i a2, a2, TI_FLAGS
+
+1:
+
+#endif
+
+#endif
+
+ /* Check if we have to do a movsp.
+ *
+ * We only have to do a movsp if the previous window-frame has
+ * been spilled to the *temporary* exception stack instead of the
+ * task's stack. This is the case if the corresponding bit in
+ * WINDOWSTART for the previous window-frame was set before
+ * (not spilled) but is zero now (spilled).
+ * If this bit is zero, all other bits except the one for the
+ * current window frame are also zero. So, we can use a simple test:
+ * 'and' WINDOWSTART and WINDOWSTART-1:
+ *
+ * (XXXXXX1[0]* - 1) AND XXXXXX1[0]* = XXXXXX0[0]*
+ *
+ * The result is zero only if one bit was set.
+ *
+ * (Note: We might have gone through several task switches before
+ * we come back to the current task, so WINDOWBASE might be
+ * different from the time the exception occurred.)
+ */
+
+ /* Test WINDOWSTART before and after the exception.
+ * We actually have WMASK, so we only have to test if it is 1 or not.
+ */
+
+ l32i a2, a1, PT_WMASK
+ _beqi a2, 1, common_exception_exit # Spilled before exception,jump
+
+ /* Test WINDOWSTART now. If spilled, do the movsp */
+
+ rsr a3, WINDOWSTART
+ addi a0, a3, -1
+ and a3, a3, a0
+ _bnez a3, common_exception_exit
+
+ /* Do a movsp (we returned from a call4, so we have at least a0..a7) */
+
+ addi a0, a1, -16
+ l32i a3, a0, 0
+ l32i a4, a0, 4
+ s32i a3, a1, PT_SIZE+0
+ s32i a4, a1, PT_SIZE+4
+ l32i a3, a0, 8
+ l32i a4, a0, 12
+ s32i a3, a1, PT_SIZE+8
+ s32i a4, a1, PT_SIZE+12
+
+ /* Common exception exit.
+ * We restore the special register and the current window frame, and
+ * return from the exception.
+ *
+ * Note: We expect a2 to hold PT_WMASK
+ */
+
+common_exception_exit:
+
+ _bbsi.l a2, 1, 1f
+ l32i a4, a1, PT_AREG4
+ l32i a5, a1, PT_AREG5
+ l32i a6, a1, PT_AREG6
+ l32i a7, a1, PT_AREG7
+ _bbsi.l a2, 2, 1f
+ l32i a8, a1, PT_AREG8
+ l32i a9, a1, PT_AREG9
+ l32i a10, a1, PT_AREG10
+ l32i a11, a1, PT_AREG11
+ _bbsi.l a2, 3, 1f
+ l32i a12, a1, PT_AREG12
+ l32i a13, a1, PT_AREG13
+ l32i a14, a1, PT_AREG14
+ l32i a15, a1, PT_AREG15
+
+ /* Restore PC, SAR */
+
+1: l32i a2, a1, PT_PC
+ l32i a3, a1, PT_SAR
+ wsr a2, EPC_1
+ wsr a3, SAR
+
+ /* Restore LBEG, LEND, LCOUNT */
+
+ l32i a2, a1, PT_LBEG
+ l32i a3, a1, PT_LEND
+ wsr a2, LBEG
+ l32i a2, a1, PT_LCOUNT
+ wsr a3, LEND
+ wsr a2, LCOUNT
+
+ /* Check if it was double exception. */
+
+ l32i a0, a1, PT_DEPC
+ l32i a3, a1, PT_AREG3
+ l32i a2, a1, PT_AREG2
+ _bgeui a0, VALID_DOUBLE_EXCEPTION_ADDRESS, 1f
+
+ /* Restore a0...a3 and return */
+
+ l32i a0, a1, PT_AREG0
+ l32i a1, a1, PT_AREG1
+ rfe
+
+1: wsr a0, DEPC
+ l32i a0, a1, PT_AREG0
+ l32i a1, a1, PT_AREG1
+ rfde
+
+/*
+ * Debug exception handler.
+ *
+ * Currently, we don't support KGDB, so only user application can be debugged.
+ *
+ * When we get here, a0 is trashed and saved to excsave[debuglevel]
+ */
+
+ENTRY(debug_exception)
+
+ rsr a0, EPS + XCHAL_DEBUGLEVEL
+ bbsi.l a0, PS_EXCM_SHIFT, 1f # exception mode
+
+ /* Set EPC_1 and EXCCAUSE */
+
+ wsr a2, DEPC # save a2 temporarily
+ rsr a2, EPC + XCHAL_DEBUGLEVEL
+ wsr a2, EPC_1
+
+ movi a2, EXCCAUSE_MAPPED_DEBUG
+ wsr a2, EXCCAUSE
+
+ /* Restore PS to the value before the debug exc but with PS.EXCM set.*/
+
+ movi a2, 1 << PS_EXCM_SHIFT
+ or a2, a0, a2
+ movi a0, debug_exception # restore a3, debug jump vector
+ wsr a2, PS
+ xsr a0, EXCSAVE + XCHAL_DEBUGLEVEL
+
+ /* Switch to kernel/user stack, restore jump vector, and save a0 */
+
+ bbsi.l a2, PS_UM_SHIFT, 2f # jump if user mode
+
+ addi a2, a1, -16-PT_SIZE # assume kernel stack
+ s32i a0, a2, PT_AREG0
+ movi a0, 0
+ s32i a1, a2, PT_AREG1
+ s32i a0, a2, PT_DEPC # mark it as a regular exception
+ xsr a0, DEPC
+ s32i a3, a2, PT_AREG3
+ s32i a0, a2, PT_AREG2
+ mov a1, a2
+ j _kernel_exception
+
+2: rsr a2, EXCSAVE_1
+ l32i a2, a2, EXC_TABLE_KSTK # load kernel stack pointer
+ s32i a0, a2, PT_AREG0
+ movi a0, 0
+ s32i a1, a2, PT_AREG1
+ s32i a0, a2, PT_DEPC
+ xsr a0, DEPC
+ s32i a3, a2, PT_AREG3
+ s32i a0, a2, PT_AREG2
+ mov a1, a2
+ j _user_exception
+
+ /* Debug exception while in exception mode. */
+1: j 1b // FIXME!!
+
+
+/*
+ * We get here in case of an unrecoverable exception.
+ * The only thing we can do is to be nice and print a panic message.
+ * We only produce a single stack frame for panic, so ???
+ *
+ *
+ * Entry conditions:
+ *
+ * - a0 contains the caller address; original value saved in excsave1.
+ * - the original a0 contains a valid return address (backtrace) or 0.
+ * - a2 contains a valid stackpointer
+ *
+ * Notes:
+ *
+ * - If the stack pointer could be invalid, the caller has to setup a
+ * dummy stack pointer (e.g. the stack of the init_task)
+ *
+ * - If the return address could be invalid, the caller has to set it
+ * to 0, so the backtrace would stop.
+ *
+ */
+ .align 4
+unrecoverable_text:
+ .ascii "Unrecoverable error in exception handler\0"
+
+ENTRY(unrecoverable_exception)
+
+ movi a0, 1
+ movi a1, 0
+
+ wsr a0, WINDOWSTART
+ wsr a1, WINDOWBASE
+ rsync
+
+ movi a1, PS_WOE_MASK | 1
+ wsr a1, PS
+ rsync
+
+ movi a1, init_task
+ movi a0, 0
+ addi a1, a1, PT_REGS_OFFSET
+
+ movi a4, panic
+ movi a6, unrecoverable_text
+
+ callx4 a4
+
+1: j 1b
+
+
+/* -------------------------- FAST EXCEPTION HANDLERS ----------------------- */
+
+/*
+ * Fast-handler for alloca exceptions
+ *
+ * The ALLOCA handler is entered when user code executes the MOVSP
+ * instruction and the caller's frame is not in the register file.
+ * In this case, the caller frame's a0..a3 are on the stack just
+ * below sp (a1), and this handler moves them.
+ *
+ * For "MOVSP <ar>,<as>" without destination register a1, this routine
+ * simply moves the value from <as> to <ar> without moving the save area.
+ *
+ * Entry condition:
+ *
+ * a0: trashed, original value saved on stack (PT_AREG0)
+ * a1: a1
+ * a2: new stack pointer, original in DEPC
+ * a3: dispatch table
+ * depc: a2, original value saved on stack (PT_DEPC)
+ * excsave_1: a3
+ *
+ * PT_DEPC >= VALID_DOUBLE_EXCEPTION_ADDRESS: double exception, DEPC
+ * < VALID_DOUBLE_EXCEPTION_ADDRESS: regular exception
+ */
+
+#if XCHAL_HAVE_BE
+#define _EXTUI_MOVSP_SRC(ar) extui ar, ar, 4, 4
+#define _EXTUI_MOVSP_DST(ar) extui ar, ar, 0, 4
+#else
+#define _EXTUI_MOVSP_SRC(ar) extui ar, ar, 0, 4
+#define _EXTUI_MOVSP_DST(ar) extui ar, ar, 4, 4
+#endif
+
+ENTRY(fast_alloca)
+
+ /* We shouldn't be in a double exception. */
+
+ l32i a0, a2, PT_DEPC
+ _bgeui a0, VALID_DOUBLE_EXCEPTION_ADDRESS, .Lunhandled_double
+
+ rsr a0, DEPC # get a2
+ s32i a4, a2, PT_AREG4 # save a4 and
+ s32i a0, a2, PT_AREG2 # a2 to stack
+
+ /* Exit critical section. */
+
+ movi a0, 0
+ s32i a0, a3, EXC_TABLE_FIXUP
+
+ /* Restore a3, excsave_1 */
+
+ xsr a3, EXCSAVE_1 # make sure excsave_1 is valid for dbl.
+ rsr a4, EPC_1 # get exception address
+ s32i a3, a2, PT_AREG3 # save a3 to stack
+
+#ifdef ALLOCA_EXCEPTION_IN_IRAM
+#error iram not supported
+#else
+ /* Note: l8ui not allowed in IRAM/IROM!! */
+ l8ui a0, a4, 1 # read as(src) from MOVSP instruction
+#endif
+ movi a3, .Lmovsp_src
+ _EXTUI_MOVSP_SRC(a0) # extract source register number
+ addx8 a3, a0, a3
+ jx a3
+
+.Lunhandled_double:
+ wsr a0, EXCSAVE_1
+ movi a0, unrecoverable_exception
+ callx0 a0
+
+ .align 8
+.Lmovsp_src:
+ l32i a3, a2, PT_AREG0; _j 1f; .align 8
+ mov a3, a1; _j 1f; .align 8
+ l32i a3, a2, PT_AREG2; _j 1f; .align 8
+ l32i a3, a2, PT_AREG3; _j 1f; .align 8
+ l32i a3, a2, PT_AREG4; _j 1f; .align 8
+ mov a3, a5; _j 1f; .align 8
+ mov a3, a6; _j 1f; .align 8
+ mov a3, a7; _j 1f; .align 8
+ mov a3, a8; _j 1f; .align 8
+ mov a3, a9; _j 1f; .align 8
+ mov a3, a10; _j 1f; .align 8
+ mov a3, a11; _j 1f; .align 8
+ mov a3, a12; _j 1f; .align 8
+ mov a3, a13; _j 1f; .align 8
+ mov a3, a14; _j 1f; .align 8
+ mov a3, a15; _j 1f; .align 8
+
+1:
+
+#ifdef ALLOCA_EXCEPTION_IN_IRAM
+#error iram not supported
+#else
+ l8ui a0, a4, 0 # read ar(dst) from MOVSP instruction
+#endif
+ addi a4, a4, 3 # step over movsp
+ _EXTUI_MOVSP_DST(a0) # extract destination register
+ wsr a4, EPC_1 # save new epc_1
+
+ _bnei a0, 1, 1f # no 'movsp a1, ax': jump
+
+ /* Move the save area. This implies the use of the L32E
+ * and S32E instructions, because this move must be done with
+ * the user's PS.RING privilege levels, not with ring 0
+ * (kernel's) privileges currently active with PS.EXCM
+ * set. Note that we have stil registered a fixup routine with the
+ * double exception vector in case a double exception occurs.
+ */
+
+ /* a0,a4:avail a1:old user stack a2:exc. stack a3:new user stack. */
+
+ l32e a0, a1, -16
+ l32e a4, a1, -12
+ s32e a0, a3, -16
+ s32e a4, a3, -12
+ l32e a0, a1, -8
+ l32e a4, a1, -4
+ s32e a0, a3, -8
+ s32e a4, a3, -4
+
+ /* Restore stack-pointer and all the other saved registers. */
+
+ mov a1, a3
+
+ l32i a4, a2, PT_AREG4
+ l32i a3, a2, PT_AREG3
+ l32i a0, a2, PT_AREG0
+ l32i a2, a2, PT_AREG2
+ rfe
+
+ /* MOVSP <at>,<as> was invoked with <at> != a1.
+ * Because the stack pointer is not being modified,
+ * we should be able to just modify the pointer
+ * without moving any save area.
+ * The processor only traps these occurrences if the
+ * caller window isn't live, so unfortunately we can't
+ * use this as an alternate trap mechanism.
+ * So we just do the move. This requires that we
+ * resolve the destination register, not just the source,
+ * so there's some extra work.
+ * (PERHAPS NOT REALLY NEEDED, BUT CLEANER...)
+ */
+
+ /* a0 dst-reg, a1 user-stack, a2 stack, a3 value of src reg. */
+
+1: movi a4, .Lmovsp_dst
+ addx8 a4, a0, a4
+ jx a4
+
+ .align 8
+.Lmovsp_dst:
+ s32i a3, a2, PT_AREG0; _j 1f; .align 8
+ mov a1, a3; _j 1f; .align 8
+ s32i a3, a2, PT_AREG2; _j 1f; .align 8
+ s32i a3, a2, PT_AREG3; _j 1f; .align 8
+ s32i a3, a2, PT_AREG4; _j 1f; .align 8
+ mov a5, a3; _j 1f; .align 8
+ mov a6, a3; _j 1f; .align 8
+ mov a7, a3; _j 1f; .align 8
+ mov a8, a3; _j 1f; .align 8
+ mov a9, a3; _j 1f; .align 8
+ mov a10, a3; _j 1f; .align 8
+ mov a11, a3; _j 1f; .align 8
+ mov a12, a3; _j 1f; .align 8
+ mov a13, a3; _j 1f; .align 8
+ mov a14, a3; _j 1f; .align 8
+ mov a15, a3; _j 1f; .align 8
+
+1: l32i a4, a2, PT_AREG4
+ l32i a3, a2, PT_AREG3
+ l32i a0, a2, PT_AREG0
+ l32i a2, a2, PT_AREG2
+ rfe
+
+
+/*
+ * fast system calls.
+ *
+ * WARNING: The kernel doesn't save the entire user context before
+ * handling a fast system call. These functions are small and short,
+ * usually offering some functionality not available to user tasks.
+ *
+ * BE CAREFUL TO PRESERVE THE USER'S CONTEXT.
+ *
+ * Entry condition:
+ *
+ * a0: trashed, original value saved on stack (PT_AREG0)
+ * a1: a1
+ * a2: new stack pointer, original in DEPC
+ * a3: dispatch table
+ * depc: a2, original value saved on stack (PT_DEPC)
+ * excsave_1: a3
+ */
+
+ENTRY(fast_syscall_kernel)
+
+ /* Skip syscall. */
+
+ rsr a0, EPC_1
+ addi a0, a0, 3
+ wsr a0, EPC_1
+
+ l32i a0, a2, PT_DEPC
+ bgeui a0, VALID_DOUBLE_EXCEPTION_ADDRESS, fast_syscall_unrecoverable
+
+ rsr a0, DEPC # get syscall-nr
+ _beqz a0, fast_syscall_spill_registers
+
+ addi a0, a0, -__NR_sysxtensa
+ _beqz a0, fast_syscall_sysxtensa
+
+ j kernel_exception
+
+
+ENTRY(fast_syscall_user)
+
+ /* Skip syscall. */
+
+ rsr a0, EPC_1
+ addi a0, a0, 3
+ wsr a0, EPC_1
+
+ l32i a0, a2, PT_DEPC
+ bgeui a0, VALID_DOUBLE_EXCEPTION_ADDRESS, fast_syscall_unrecoverable
+
+ rsr a0, DEPC # get syscall-nr
+ _beqz a0, fast_syscall_spill_registers
+
+ addi a0, a0, -__NR_sysxtensa
+ _beqz a0, fast_syscall_sysxtensa
+
+ j user_exception
+
+ENTRY(fast_syscall_unrecoverable)
+
+ /* Restore all states. */
+
+ l32i a0, a2, PT_AREG0 # restore a0
+ xsr a2, DEPC # restore a2, depc
+ rsr a3, EXCSAVE_1
+
+ wsr a0, EXCSAVE_1
+ movi a0, unrecoverable_exception
+ callx0 a0
+
+
+
+/*
+ * sysxtensa syscall handler
+ *
+ * int sysxtensa (XTENSA_ATOMIC_SET, ptr, val, unused);
+ * int sysxtensa (XTENSA_ATOMIC_ADD, ptr, val, unused);
+ * int sysxtensa (XTENSA_ATOMIC_EXG_ADD, ptr, val, unused);
+ * int sysxtensa (XTENSA_ATOMIC_CMP_SWP, ptr, oldval, newval);
+ * a2 a6 a3 a4 a5
+ *
+ * Entry condition:
+ *
+ * a0: trashed, original value saved on stack (PT_AREG0)
+ * a1: a1
+ * a2: new stack pointer, original in DEPC
+ * a3: dispatch table
+ * depc: a2, original value saved on stack (PT_DEPC)
+ * excsave_1: a3
+ *
+ * PT_DEPC >= VALID_DOUBLE_EXCEPTION_ADDRESS: double exception, DEPC
+ * < VALID_DOUBLE_EXCEPTION_ADDRESS: regular exception
+ *
+ * Note: we don't have to save a2; a2 holds the return value
+ *
+ * We use the two macros TRY and CATCH:
+ *
+ * TRY adds an entry to the __ex_table fixup table for the immediately
+ * following instruction.
+ *
+ * CATCH catches any exception that occurred at one of the preceeding TRY
+ * statements and continues from there
+ *
+ * Usage TRY l32i a0, a1, 0
+ * <other code>
+ * done: rfe
+ * CATCH <set return code>
+ * j done
+ */
+
+#define TRY \
+ .section __ex_table, "a"; \
+ .word 66f, 67f; \
+ .text; \
+66:
+
+#define CATCH \
+67:
+
+ENTRY(fast_syscall_sysxtensa)
+
+ _beqz a6, 1f
+ _blti a6, SYSXTENSA_COUNT, 2f
+
+1: j user_exception
+
+2: xsr a3, EXCSAVE_1 # restore a3, excsave1
+ s32i a7, a2, PT_AREG7
+
+ movi a7, 4 # sizeof(unsigned int)
+ verify_area a3, a7, a0, a2, .Leac
+
+ _beqi a6, SYSXTENSA_ATOMIC_SET, .Lset
+ _beqi a6, SYSXTENSA_ATOMIC_EXG_ADD, .Lexg
+ _beqi a6, SYSXTENSA_ATOMIC_ADD, .Ladd
+
+ /* Fall through for SYSXTENSA_ATOMIC_CMP_SWP */
+
+.Lswp: /* Atomic compare and swap */
+
+TRY l32i a7, a3, 0 # read old value
+ bne a7, a4, 1f # same as old value? jump
+ s32i a5, a3, 0 # different, modify value
+ movi a7, 1 # and return 1
+ j .Lret
+
+1: movi a7, 0 # same values: return 0
+ j .Lret
+
+.Ladd: /* Atomic add */
+.Lexg: /* Atomic (exchange) add */
+
+TRY l32i a7, a3, 0
+ add a4, a4, a7
+ s32i a4, a3, 0
+ j .Lret
+
+.Lset: /* Atomic set */
+
+TRY l32i a7, a3, 0 # read old value as return value
+ s32i a4, a3, 0 # write new value
+
+.Lret: mov a0, a2
+ mov a2, a7
+ l32i a7, a0, PT_AREG7
+ l32i a3, a0, PT_AREG3
+ l32i a0, a0, PT_AREG0
+ rfe
+
+CATCH
+.Leac: movi a7, -EFAULT
+ j .Lret
+
+
+
+/* fast_syscall_spill_registers.
+ *
+ * Entry condition:
+ *
+ * a0: trashed, original value saved on stack (PT_AREG0)
+ * a1: a1
+ * a2: new stack pointer, original in DEPC
+ * a3: dispatch table
+ * depc: a2, original value saved on stack (PT_DEPC)
+ * excsave_1: a3
+ *
+ * Note: We assume the stack pointer is EXC_TABLE_KSTK in the fixup handler.
+ * Note: We don't need to save a2 in depc (return value)
+ */
+
+ENTRY(fast_syscall_spill_registers)
+
+ /* Register a FIXUP handler (pass current wb as a parameter) */
+
+ movi a0, fast_syscall_spill_registers_fixup
+ s32i a0, a3, EXC_TABLE_FIXUP
+ rsr a0, WINDOWBASE
+ s32i a0, a3, EXC_TABLE_PARAM
+
+ /* Save a3 and SAR on stack. */
+
+ rsr a0, SAR
+ xsr a3, EXCSAVE_1 # restore a3 and excsave_1
+ s32i a0, a2, PT_AREG4 # store SAR to PT_AREG4
+ s32i a3, a2, PT_AREG3
+
+ /* The spill routine might clobber a7, a11, and a15. */
+
+ s32i a7, a2, PT_AREG5
+ s32i a11, a2, PT_AREG6
+ s32i a15, a2, PT_AREG7
+
+ call0 _spill_registers # destroys a3, DEPC, and SAR
+
+ /* Advance PC, restore registers and SAR, and return from exception. */
+
+ l32i a3, a2, PT_AREG4
+ l32i a0, a2, PT_AREG0
+ wsr a3, SAR
+ l32i a3, a2, PT_AREG3
+
+ /* Restore clobbered registers. */
+
+ l32i a7, a2, PT_AREG5
+ l32i a11, a2, PT_AREG6
+ l32i a15, a2, PT_AREG7
+
+ movi a2, 0
+ rfe
+
+/* Fixup handler.
+ *
+ * We get here if the spill routine causes an exception, e.g. tlb miss.
+ * We basically restore WINDOWBASE and WINDOWSTART to the condition when
+ * we entered the spill routine and jump to the user exception handler.
+ *
+ * a0: value of depc, original value in depc
+ * a2: trashed, original value in EXC_TABLE_DOUBLE_SAVE
+ * a3: exctable, original value in excsave1
+ */
+
+fast_syscall_spill_registers_fixup:
+
+ rsr a2, WINDOWBASE # get current windowbase (a2 is saved)
+ xsr a0, DEPC # restore depc and a0
+ ssl a2 # set shift (32 - WB)
+
+ /* We need to make sure the current registers (a0-a3) are preserved.
+ * To do this, we simply set the bit for the current window frame
+ * in WS, so that the exception handlers save them to the task stack.
+ */
+
+ rsr a3, EXCSAVE_1 # get spill-mask
+ slli a2, a3, 1 # shift left by one
+
+ slli a3, a2, 32-WSBITS
+ src a2, a2, a3 # a1 = xxwww1yyxxxwww1yy......
+ wsr a2, WINDOWSTART # set corrected windowstart
+
+ movi a3, exc_table
+ l32i a2, a3, EXC_TABLE_DOUBLE_SAVE # restore a2
+ l32i a3, a3, EXC_TABLE_PARAM # original WB (in user task)
+
+ /* Return to the original (user task) WINDOWBASE.
+ * We leave the following frame behind:
+ * a0, a1, a2 same
+ * a3: trashed (saved in excsave_1)
+ * depc: depc (we have to return to that address)
+ * excsave_1: a3
+ */
+
+ wsr a3, WINDOWBASE
+ rsync
+
+ /* We are now in the original frame when we entered _spill_registers:
+ * a0: return address
+ * a1: used, stack pointer
+ * a2: kernel stack pointer
+ * a3: available, saved in EXCSAVE_1
+ * depc: exception address
+ * excsave: a3
+ * Note: This frame might be the same as above.
+ */
+
+#ifdef SIGNAL_HANDLING_IN_DOUBLE_EXCEPTION
+ /* Restore registers we precautiously saved.
+ * We have the value of the 'right' a3
+ */
+
+ l32i a7, a2, PT_AREG5
+ l32i a11, a2, PT_AREG6
+ l32i a15, a2, PT_AREG7
+#endif
+
+ /* Setup stack pointer. */
+
+ addi a2, a2, -PT_USER_SIZE
+ s32i a0, a2, PT_AREG0
+
+ /* Make sure we return to this fixup handler. */
+
+ movi a3, fast_syscall_spill_registers_fixup_return
+ s32i a3, a2, PT_DEPC # setup depc
+
+ /* Jump to the exception handler. */
+
+ movi a3, exc_table
+ rsr a0, EXCCAUSE
+ addx4 a0, a0, a3 # find entry in table
+ l32i a0, a0, EXC_TABLE_FAST_USER # load handler
+ jx a0
+
+fast_syscall_spill_registers_fixup_return:
+
+ /* When we return here, all registers have been restored (a2: DEPC) */
+
+ wsr a2, DEPC # exception address
+
+ /* Restore fixup handler. */
+
+ xsr a3, EXCSAVE_1
+ movi a2, fast_syscall_spill_registers_fixup
+ s32i a2, a3, EXC_TABLE_FIXUP
+ rsr a2, WINDOWBASE
+ s32i a2, a3, EXC_TABLE_PARAM
+ l32i a2, a3, EXC_TABLE_KSTK
+
+#ifdef SIGNAL_HANDLING_IN_DOUBLE_EXCEPTION
+ /* Save registers again that might be clobbered. */
+
+ s32i a7, a2, PT_AREG5
+ s32i a11, a2, PT_AREG6
+ s32i a15, a2, PT_AREG7
+#endif
+
+ /* Load WB at the time the exception occurred. */
+
+ rsr a3, SAR # WB is still in SAR
+ neg a3, a3
+ wsr a3, WINDOWBASE
+ rsync
+
+ /* Restore a3 and return. */
+
+ movi a3, exc_table
+ xsr a3, EXCSAVE_1
+
+ rfde
+
+
+/*
+ * spill all registers.
+ *
+ * This is not a real function. The following conditions must be met:
+ *
+ * - must be called with call0.
+ * - uses DEPC, a3 and SAR.
+ * - the last 'valid' register of each frame are clobbered.
+ * - the caller must have registered a fixup handler
+ * (or be inside a critical section)
+ * - PS_EXCM must be set (PS_WOE cleared?)
+ */
+
+ENTRY(_spill_registers)
+
+ /*
+ * Rotate ws so that the current windowbase is at bit 0.
+ * Assume ws = xxxwww1yy (www1 current window frame).
+ * Rotate ws right so that a2 = yyxxxwww1.
+ */
+
+ wsr a2, DEPC # preserve a2
+ rsr a2, WINDOWBASE
+ rsr a3, WINDOWSTART
+ ssr a2 # holds WB
+ slli a2, a3, WSBITS
+ or a3, a3, a2 # a2 = xxxwww1yyxxxwww1yy
+ srl a3, a3
+
+ /* We are done if there are no more than the current register frame. */
+
+ extui a3, a3, 1, WSBITS-2 # a3 = 0yyxxxwww
+ movi a2, (1 << (WSBITS-1))
+ _beqz a3, .Lnospill # only one active frame? jump
+
+ /* We want 1 at the top, so that we return to the current windowbase */
+
+ or a3, a3, a2 # 1yyxxxwww
+
+ /* Skip empty frames - get 'oldest' WINDOWSTART-bit. */
+
+ wsr a3, WINDOWSTART # save shifted windowstart
+ neg a2, a3
+ and a3, a2, a3 # first bit set from right: 000010000
+
+ ffs_ws a2, a3 # a2: shifts to skip empty frames
+ movi a3, WSBITS
+ sub a2, a3, a2 # WSBITS-a2:number of 0-bits from right
+ ssr a2 # save in SAR for later.
+
+ rsr a3, WINDOWBASE
+ add a3, a3, a2
+ rsr a2, DEPC # restore a2
+ wsr a3, WINDOWBASE
+ rsync
+
+ rsr a3, WINDOWSTART
+ srl a3, a3 # shift windowstart
+
+ /* WB is now just one frame below the oldest frame in the register
+ window. WS is shifted so the oldest frame is in bit 0, thus, WB
+ and WS differ by one 4-register frame. */
+
+ /* Save frames. Depending what call was used (call4, call8, call12),
+ * we have to save 4,8. or 12 registers.
+ */
+
+ _bbsi.l a3, 1, .Lc4
+ _bbsi.l a3, 2, .Lc8
+
+ /* Special case: we have a call12-frame starting at a4. */
+
+ _bbci.l a3, 3, .Lc12 # bit 3 shouldn't be zero! (Jump to Lc12 first)
+
+ s32e a4, a1, -16 # a1 is valid with an empty spill area
+ l32e a4, a5, -12
+ s32e a8, a4, -48
+ mov a8, a4
+ l32e a4, a1, -16
+ j .Lc12c
+
+.Lloop: _bbsi.l a3, 1, .Lc4
+ _bbci.l a3, 2, .Lc12
+
+.Lc8: s32e a4, a13, -16
+ l32e a4, a5, -12
+ s32e a8, a4, -32
+ s32e a5, a13, -12
+ s32e a6, a13, -8
+ s32e a7, a13, -4
+ s32e a9, a4, -28
+ s32e a10, a4, -24
+ s32e a11, a4, -20
+
+ srli a11, a3, 2 # shift windowbase by 2
+ rotw 2
+ _bnei a3, 1, .Lloop
+
+.Lexit: /* Done. Do the final rotation, set WS, and return. */
+
+ rotw 1
+ rsr a3, WINDOWBASE
+ ssl a3
+ movi a3, 1
+ sll a3, a3
+ wsr a3, WINDOWSTART
+
+.Lnospill:
+ jx a0
+
+.Lc4: s32e a4, a9, -16
+ s32e a5, a9, -12
+ s32e a6, a9, -8
+ s32e a7, a9, -4
+
+ srli a7, a3, 1
+ rotw 1
+ _bnei a3, 1, .Lloop
+ j .Lexit
+
+.Lc12: _bbci.l a3, 3, .Linvalid_mask # bit 2 shouldn't be zero!
+
+ /* 12-register frame (call12) */
+
+ l32e a2, a5, -12
+ s32e a8, a2, -48
+ mov a8, a2
+
+.Lc12c: s32e a9, a8, -44
+ s32e a10, a8, -40
+ s32e a11, a8, -36
+ s32e a12, a8, -32
+ s32e a13, a8, -28
+ s32e a14, a8, -24
+ s32e a15, a8, -20
+ srli a15, a3, 3
+
+ /* The stack pointer for a4..a7 is out of reach, so we rotate the
+ * window, grab the stackpointer, and rotate back.
+ * Alternatively, we could also use the following approach, but that
+ * makes the fixup routine much more complicated:
+ * rotw 1
+ * s32e a0, a13, -16
+ * ...
+ * rotw 2
+ */
+
+ rotw 1
+ mov a5, a13
+ rotw -1
+
+ s32e a4, a9, -16
+ s32e a5, a9, -12
+ s32e a6, a9, -8
+ s32e a7, a9, -4
+
+ rotw 3
+
+ _beqi a3, 1, .Lexit
+ j .Lloop
+
+.Linvalid_mask:
+
+ /* We get here because of an unrecoverable error in the window
+ * registers. If we are in user space, we kill the application,
+ * however, this condition is unrecoverable in kernel space.
+ */
+
+ rsr a0, PS
+ _bbci.l a0, PS_UM_SHIFT, 1f
+
+ /* User space: Setup a dummy frame and kill application.
+ * Note: We assume EXC_TABLE_KSTK contains a valid stack pointer.
+ */
+
+ movi a0, 1
+ movi a1, 0
+
+ wsr a0, WINDOWSTART
+ wsr a1, WINDOWBASE
+ rsync
+
+ movi a0, 0
+
+ movi a3, exc_table
+ l32i a1, a3, EXC_TABLE_KSTK
+ wsr a3, EXCSAVE_1
+
+ movi a4, PS_WOE_MASK | 1
+ wsr a4, PS
+ rsync
+
+ movi a6, SIGSEGV
+ movi a4, do_exit
+ callx4 a4
+
+1: /* Kernel space: PANIC! */
+
+ wsr a0, EXCSAVE_1
+ movi a0, unrecoverable_exception
+ callx0 a0 # should not return
+1: j 1b
+
+/*
+ * We should never get here. Bail out!
+ */
+
+ENTRY(fast_second_level_miss_double_kernel)
+
+1: movi a0, unrecoverable_exception
+ callx0 a0 # should not return
+1: j 1b
+
+/* First-level entry handler for user, kernel, and double 2nd-level
+ * TLB miss exceptions. Note that for now, user and kernel miss
+ * exceptions share the same entry point and are handled identically.
+ *
+ * An old, less-efficient C version of this function used to exist.
+ * We include it below, interleaved as comments, for reference.
+ *
+ * Entry condition:
+ *
+ * a0: trashed, original value saved on stack (PT_AREG0)
+ * a1: a1
+ * a2: new stack pointer, original in DEPC
+ * a3: dispatch table
+ * depc: a2, original value saved on stack (PT_DEPC)
+ * excsave_1: a3
+ *
+ * PT_DEPC >= VALID_DOUBLE_EXCEPTION_ADDRESS: double exception, DEPC
+ * < VALID_DOUBLE_EXCEPTION_ADDRESS: regular exception
+ */
+
+ENTRY(fast_second_level_miss)
+
+ /* Save a1. Note: we don't expect a double exception. */
+
+ s32i a1, a2, PT_AREG1
+
+ /* We need to map the page of PTEs for the user task. Find
+ * the pointer to that page. Also, it's possible for tsk->mm
+ * to be NULL while tsk->active_mm is nonzero if we faulted on
+ * a vmalloc address. In that rare case, we must use
+ * active_mm instead to avoid a fault in this handler. See
+ *
+ * http://mail.nl.linux.org/linux-mm/2002-08/msg00258.html
+ * (or search Internet on "mm vs. active_mm")
+ *
+ * if (!mm)
+ * mm = tsk->active_mm;
+ * pgd = pgd_offset (mm, regs->excvaddr);
+ * pmd = pmd_offset (pgd, regs->excvaddr);
+ * pmdval = *pmd;
+ */
+
+ GET_CURRENT(a1,a2)
+ l32i a0, a1, TASK_MM # tsk->mm
+ beqz a0, 9f
+
+8: rsr a1, EXCVADDR # fault address
+ _PGD_OFFSET(a0, a1, a1)
+ l32i a0, a0, 0 # read pmdval
+ //beqi a0, _PAGE_USER, 2f
+ beqz a0, 2f
+
+ /* Read ptevaddr and convert to top of page-table page.
+ *
+ * vpnval = read_ptevaddr_register() & PAGE_MASK;
+ * vpnval += DTLB_WAY_PGTABLE;
+ * pteval = mk_pte (virt_to_page(pmd_val(pmdval)), PAGE_KERNEL);
+ * write_dtlb_entry (pteval, vpnval);
+ *
+ * The messy computation for 'pteval' above really simplifies
+ * into the following:
+ *
+ * pteval = ((pmdval - PAGE_OFFSET) & PAGE_MASK) | PAGE_KERNEL
+ */
+
+ movi a1, -PAGE_OFFSET
+ add a0, a0, a1 # pmdval - PAGE_OFFSET
+ extui a1, a0, 0, PAGE_SHIFT # ... & PAGE_MASK
+ xor a0, a0, a1
+
+
+ movi a1, PAGE_DIRECTORY
+ or a0, a0, a1 # ... | PAGE_DIRECTORY
+
+ rsr a1, PTEVADDR
+ srli a1, a1, PAGE_SHIFT
+ slli a1, a1, PAGE_SHIFT # ptevaddr & PAGE_MASK
+ addi a1, a1, DTLB_WAY_PGTABLE # ... + way_number
+
+ wdtlb a0, a1
+ dsync
+
+ /* Exit critical section. */
+
+ movi a0, 0
+ s32i a0, a3, EXC_TABLE_FIXUP
+
+ /* Restore the working registers, and return. */
+
+ l32i a0, a2, PT_AREG0
+ l32i a1, a2, PT_AREG1
+ l32i a2, a2, PT_DEPC
+ xsr a3, EXCSAVE_1
+
+ bgeui a2, VALID_DOUBLE_EXCEPTION_ADDRESS, 1f
+
+ /* Restore excsave1 and return. */
+
+ rsr a2, DEPC
+ rfe
+
+ /* Return from double exception. */
+
+1: xsr a2, DEPC
+ esync
+ rfde
+
+9: l32i a0, a1, TASK_ACTIVE_MM # unlikely case mm == 0
+ j 8b
+
+2: /* Invalid PGD, default exception handling */
+
+ rsr a1, DEPC
+ xsr a3, EXCSAVE_1
+ s32i a1, a2, PT_AREG2
+ s32i a3, a2, PT_AREG3
+ mov a1, a2
+
+ rsr a2, PS
+ bbsi.l a2, PS_UM_SHIFT, 1f
+ j _kernel_exception
+1: j _user_exception
+
+
+/*
+ * StoreProhibitedException
+ *
+ * Update the pte and invalidate the itlb mapping for this pte.
+ *
+ * Entry condition:
+ *
+ * a0: trashed, original value saved on stack (PT_AREG0)
+ * a1: a1
+ * a2: new stack pointer, original in DEPC
+ * a3: dispatch table
+ * depc: a2, original value saved on stack (PT_DEPC)
+ * excsave_1: a3
+ *
+ * PT_DEPC >= VALID_DOUBLE_EXCEPTION_ADDRESS: double exception, DEPC
+ * < VALID_DOUBLE_EXCEPTION_ADDRESS: regular exception
+ */
+
+ENTRY(fast_store_prohibited)
+
+ /* Save a1 and a4. */
+
+ s32i a1, a2, PT_AREG1
+ s32i a4, a2, PT_AREG4
+
+ GET_CURRENT(a1,a2)
+ l32i a0, a1, TASK_MM # tsk->mm
+ beqz a0, 9f
+
+8: rsr a1, EXCVADDR # fault address
+ _PGD_OFFSET(a0, a1, a4)
+ l32i a0, a0, 0
+ //beqi a0, _PAGE_USER, 2f # FIXME use _PAGE_INVALID
+ beqz a0, 2f
+
+ _PTE_OFFSET(a0, a1, a4)
+ l32i a4, a0, 0 # read pteval
+ movi a1, _PAGE_VALID | _PAGE_RW
+ bnall a4, a1, 2f
+
+ movi a1, _PAGE_ACCESSED | _PAGE_DIRTY | _PAGE_WRENABLE
+ or a4, a4, a1
+ rsr a1, EXCVADDR
+ s32i a4, a0, 0
+
+ /* We need to flush the cache if we have page coloring. */
+#if (DCACHE_WAY_SIZE > PAGE_SIZE) && XCHAL_DCACHE_IS_WRITEBACK
+ dhwb a0, 0
+#endif
+ pdtlb a0, a1
+ beqz a0, 1f
+ idtlb a0 // FIXME do we need this?
+ wdtlb a4, a0
+1:
+
+ /* Exit critical section. */
+
+ movi a0, 0
+ s32i a0, a3, EXC_TABLE_FIXUP
+
+ /* Restore the working registers, and return. */
+
+ l32i a4, a2, PT_AREG4
+ l32i a1, a2, PT_AREG1
+ l32i a0, a2, PT_AREG0
+ l32i a2, a2, PT_DEPC
+
+ /* Restore excsave1 and a3. */
+
+ xsr a3, EXCSAVE_1
+ bgeui a2, VALID_DOUBLE_EXCEPTION_ADDRESS, 1f
+
+ rsr a2, DEPC
+ rfe
+
+ /* Double exception. Restore FIXUP handler and return. */
+
+1: xsr a2, DEPC
+ esync
+ rfde
+
+9: l32i a0, a1, TASK_ACTIVE_MM # unlikely case mm == 0
+ j 8b
+
+2: /* If there was a problem, handle fault in C */
+
+ rsr a4, DEPC # still holds a2
+ xsr a3, EXCSAVE_1
+ s32i a4, a2, PT_AREG2
+ s32i a3, a2, PT_AREG3
+ l32i a4, a2, PT_AREG4
+ mov a1, a2
+
+ rsr a2, PS
+ bbsi.l a2, PS_UM_SHIFT, 1f
+ j _kernel_exception
+1: j _user_exception
+
+
+#if XCHAL_EXTRA_SA_SIZE
+
+#warning fast_coprocessor untested
+
+/*
+ * Entry condition:
+ *
+ * a0: trashed, original value saved on stack (PT_AREG0)
+ * a1: a1
+ * a2: new stack pointer, original in DEPC
+ * a3: dispatch table
+ * depc: a2, original value saved on stack (PT_DEPC)
+ * excsave_1: a3
+ *
+ * PT_DEPC >= VALID_DOUBLE_EXCEPTION_ADDRESS: double exception, DEPC
+ * < VALID_DOUBLE_EXCEPTION_ADDRESS: regular exception
+ */
+
+ENTRY(fast_coprocessor_double)
+ wsr a0, EXCSAVE_1
+ movi a0, unrecoverable_exception
+ callx0 a0
+
+ENTRY(fast_coprocessor)
+
+ /* Fatal if we are in a double exception. */
+
+ l32i a0, a2, PT_DEPC
+ _bgeui a0, VALID_DOUBLE_EXCEPTION_ADDRESS, fast_coprocessor_double
+
+ /* Save some registers a1, a3, a4, SAR */
+
+ xsr a3, EXCSAVE_1
+ s32i a3, a2, PT_AREG3
+ rsr a3, SAR
+ s32i a4, a2, PT_AREG4
+ s32i a1, a2, PT_AREG1
+ s32i a5, a1, PT_AREG5
+ s32i a3, a2, PT_SAR
+ mov a1, a2
+
+ /* Currently, the HAL macros only guarantee saving a0 and a1.
+ * These can and will be refined in the future, but for now,
+ * just save the remaining registers of a2...a15.
+ */
+ s32i a6, a1, PT_AREG6
+ s32i a7, a1, PT_AREG7
+ s32i a8, a1, PT_AREG8
+ s32i a9, a1, PT_AREG9
+ s32i a10, a1, PT_AREG10
+ s32i a11, a1, PT_AREG11
+ s32i a12, a1, PT_AREG12
+ s32i a13, a1, PT_AREG13
+ s32i a14, a1, PT_AREG14
+ s32i a15, a1, PT_AREG15
+
+ /* Find coprocessor number. Subtract first CP EXCCAUSE from EXCCAUSE */
+
+ rsr a0, EXCCAUSE
+ addi a3, a0, -XCHAL_EXCCAUSE_COPROCESSOR0_DISABLED
+
+ /* Set corresponding CPENABLE bit */
+
+ movi a4, 1
+ ssl a3 # SAR: 32 - coprocessor_number
+ rsr a5, CPENABLE
+ sll a4, a4
+ or a4, a5, a4
+ wsr a4, CPENABLE
+ rsync
+ movi a5, coprocessor_info # list of owner and offset into cp_save
+ addx8 a0, a4, a5 # entry for CP
+
+ bne a4, a5, .Lload # bit wasn't set before, cp not in use
+
+ /* Now compare the current task with the owner of the coprocessor.
+ * If they are the same, there is no reason to save or restore any
+ * coprocessor state. Having already enabled the coprocessor,
+ * branch ahead to return.
+ */
+ GET_CURRENT(a5,a1)
+ l32i a4, a0, COPROCESSOR_INFO_OWNER # a4: current owner for this CP
+ beq a4, a5, .Ldone
+
+ /* Find location to dump current coprocessor state:
+ * task_struct->task_cp_save_offset + coprocessor_offset[coprocessor]
+ *
+ * Note: a0 pointer to the entry in the coprocessor owner table,
+ * a3 coprocessor number,
+ * a4 current owner of coprocessor.
+ */
+ l32i a5, a0, COPROCESSOR_INFO_OFFSET
+ addi a2, a4, THREAD_CP_SAVE
+ add a2, a2, a5
+
+ /* Store current coprocessor states. (a5 still has CP number) */
+
+ xchal_cpi_store_funcbody
+
+ /* The macro might have destroyed a3 (coprocessor number), but
+ * SAR still has 32 - coprocessor_number!
+ */
+ movi a3, 32
+ rsr a4, SAR
+ sub a3, a3, a4
+
+.Lload: /* A new task now owns the corpocessors. Save its TCB pointer into
+ * the coprocessor owner table.
+ *
+ * Note: a0 pointer to the entry in the coprocessor owner table,
+ * a3 coprocessor number.
+ */
+ GET_CURRENT(a4,a1)
+ s32i a4, a0, 0
+
+ /* Find location from where to restore the current coprocessor state.*/
+
+ l32i a5, a0, COPROCESSOR_INFO_OFFSET
+ addi a2, a4, THREAD_CP_SAVE
+ add a2, a2, a4
+
+ xchal_cpi_load_funcbody
+
+ /* We must assume that the xchal_cpi_store_funcbody macro destroyed
+ * registers a2..a15.
+ */
+
+.Ldone: l32i a15, a1, PT_AREG15
+ l32i a14, a1, PT_AREG14
+ l32i a13, a1, PT_AREG13
+ l32i a12, a1, PT_AREG12
+ l32i a11, a1, PT_AREG11
+ l32i a10, a1, PT_AREG10
+ l32i a9, a1, PT_AREG9
+ l32i a8, a1, PT_AREG8
+ l32i a7, a1, PT_AREG7
+ l32i a6, a1, PT_AREG6
+ l32i a5, a1, PT_AREG5
+ l32i a4, a1, PT_AREG4
+ l32i a3, a1, PT_AREG3
+ l32i a2, a1, PT_AREG2
+ l32i a0, a1, PT_AREG0
+ l32i a1, a1, PT_AREG1
+
+ rfe
+
+#endif /* XCHAL_EXTRA_SA_SIZE */
+
+/*
+ * Task switch.
+ *
+ * struct task* _switch_to (struct task* prev, struct task* next)
+ * a2 a2 a3
+ */
+
+ENTRY(_switch_to)
+
+ entry a1, 16
+
+ mov a4, a3 # preserve a3
+
+ s32i a0, a2, THREAD_RA # save return address
+ s32i a1, a2, THREAD_SP # save stack pointer
+
+ /* Disable ints while we manipulate the stack pointer; spill regs. */
+
+ movi a5, PS_EXCM_MASK | LOCKLEVEL
+ xsr a5, PS
+ rsr a3, EXCSAVE_1
+ rsync
+ s32i a3, a3, EXC_TABLE_FIXUP /* enter critical section */
+
+ call0 _spill_registers
+
+ /* Set kernel stack (and leave critical section)
+ * Note: It's save to set it here. The stack will not be overwritten
+ * because the kernel stack will only be loaded again after
+ * we return from kernel space.
+ */
+
+ l32i a0, a4, TASK_THREAD_INFO
+ rsr a3, EXCSAVE_1 # exc_table
+ movi a1, 0
+ addi a0, a0, PT_REGS_OFFSET
+ s32i a1, a3, EXC_TABLE_FIXUP
+ s32i a0, a3, EXC_TABLE_KSTK
+
+ /* restore context of the task that 'next' addresses */
+
+ l32i a0, a4, THREAD_RA /* restore return address */
+ l32i a1, a4, THREAD_SP /* restore stack pointer */
+
+ wsr a5, PS
+ rsync
+
+ retw
+
+
+ENTRY(ret_from_fork)
+
+ /* void schedule_tail (struct task_struct *prev)
+ * Note: prev is still in a6 (return value from fake call4 frame)
+ */
+ movi a4, schedule_tail
+ callx4 a4
+
+ movi a4, do_syscall_trace
+ callx4 a4
+
+ j common_exception_return
+
+
+
+/*
+ * Table of syscalls
+ */
+
+.data
+.align 4
+.global sys_call_table
+sys_call_table:
+
+#define SYSCALL(call, narg) .word call
+#include "syscalls.h"
+
+/*
+ * Number of arguments of each syscall
+ */
+
+.global sys_narg_table
+sys_narg_table:
+
+#undef SYSCALL
+#define SYSCALL(call, narg) .byte narg
+#include "syscalls.h"
+
diff --git a/arch/xtensa/kernel/head.S b/arch/xtensa/kernel/head.S
new file mode 100644
index 000000000000..6e9b5225b8f6
--- /dev/null
+++ b/arch/xtensa/kernel/head.S
@@ -0,0 +1,237 @@
+/*
+ * arch/xtensa/kernel/head.S
+ *
+ * Xtensa Processor startup code.
+ *
+ * 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) 2001 - 2005 Tensilica Inc.
+ *
+ * Chris Zankel <chris@zankel.net>
+ * Marc Gauthier <marc@tensilica.com, marc@alumni.uwaterloo.ca>
+ * Joe Taylor <joe@tensilica.com, joetylr@yahoo.com>
+ * Kevin Chea
+ */
+
+#include <xtensa/cacheasm.h>
+#include <linux/config.h>
+#include <asm/processor.h>
+#include <asm/page.h>
+
+/*
+ * This module contains the entry code for kernel images. It performs the
+ * minimal setup needed to call the generic C routines.
+ *
+ * Prerequisites:
+ *
+ * - The kernel image has been loaded to the actual address where it was
+ * compiled to.
+ * - a2 contains either 0 or a pointer to a list of boot parameters.
+ * (see setup.c for more details)
+ *
+ */
+
+ .macro iterate from, to , cmd
+ .ifeq ((\to - \from) & ~0xfff)
+ \cmd \from
+ iterate "(\from+1)", \to, \cmd
+ .endif
+ .endm
+
+/*
+ * _start
+ *
+ * The bootloader passes a pointer to a list of boot parameters in a2.
+ */
+
+ /* The first bytes of the kernel image must be an instruction, so we
+ * manually allocate and define the literal constant we need for a jx
+ * instruction.
+ */
+
+ .section .head.text, "ax"
+ .globl _start
+_start: _j 2f
+ .align 4
+1: .word _startup
+2: l32r a0, 1b
+ jx a0
+
+ .text
+ .align 4
+_startup:
+
+ /* Disable interrupts and exceptions. */
+
+ movi a0, XCHAL_PS_EXCM_MASK
+ wsr a0, PS
+
+ /* Preserve the pointer to the boot parameter list in EXCSAVE_1 */
+
+ wsr a2, EXCSAVE_1
+
+ /* Start with a fresh windowbase and windowstart. */
+
+ movi a1, 1
+ movi a0, 0
+ wsr a1, WINDOWSTART
+ wsr a0, WINDOWBASE
+ rsync
+
+ /* Set a0 to 0 for the remaining initialization. */
+
+ movi a0, 0
+
+ /* Clear debugging registers. */
+
+#if XCHAL_HAVE_DEBUG
+ wsr a0, IBREAKENABLE
+ wsr a0, ICOUNT
+ movi a1, 15
+ wsr a0, ICOUNTLEVEL
+
+ .macro reset_dbreak num
+ wsr a0, DBREAKC + \num
+ .endm
+
+ iterate 0, XCHAL_NUM_IBREAK-1, reset_dbreak
+#endif
+
+ /* Clear CCOUNT (not really necessary, but nice) */
+
+ wsr a0, CCOUNT # not really necessary, but nice
+
+ /* Disable zero-loops. */
+
+#if XCHAL_HAVE_LOOPS
+ wsr a0, LCOUNT
+#endif
+
+ /* Disable all timers. */
+
+ .macro reset_timer num
+ wsr a0, CCOMPARE_0 + \num
+ .endm
+ iterate 0, XCHAL_NUM_TIMERS-1, reset_timer
+
+ /* Interrupt initialization. */
+
+ movi a2, XCHAL_INTTYPE_MASK_SOFTWARE | XCHAL_INTTYPE_MASK_EXTERN_EDGE
+ wsr a0, INTENABLE
+ wsr a2, INTCLEAR
+
+ /* Disable coprocessors. */
+
+#if XCHAL_CP_NUM > 0
+ wsr a0, CPENABLE
+#endif
+
+ /* Set PS.INTLEVEL=1, PS.WOE=0, kernel stack, PS.EXCM=0
+ *
+ * Note: PS.EXCM must be cleared before using any loop
+ * instructions; otherwise, they are silently disabled, and
+ * at most one iteration of the loop is executed.
+ */
+
+ movi a1, 1
+ wsr a1, PS
+ rsync
+
+ /* Initialize the caches.
+ * Does not include flushing writeback d-cache.
+ * a6, a7 are just working registers (clobbered).
+ */
+
+ icache_reset a2, a3
+ dcache_reset a2, a3
+
+ /* Unpack data sections
+ *
+ * The linker script used to build the Linux kernel image
+ * creates a table located at __boot_reloc_table_start
+ * that contans the information what data needs to be unpacked.
+ *
+ * Uses a2-a7.
+ */
+
+ movi a2, __boot_reloc_table_start
+ movi a3, __boot_reloc_table_end
+
+1: beq a2, a3, 3f # no more entries?
+ l32i a4, a2, 0 # start destination (in RAM)
+ l32i a5, a2, 4 # end desination (in RAM)
+ l32i a6, a2, 8 # start source (in ROM)
+ addi a2, a2, 12 # next entry
+ beq a4, a5, 1b # skip, empty entry
+ beq a4, a6, 1b # skip, source and dest. are the same
+
+2: l32i a7, a6, 0 # load word
+ addi a6, a6, 4
+ s32i a7, a4, 0 # store word
+ addi a4, a4, 4
+ bltu a4, a5, 2b
+ j 1b
+
+3:
+ /* All code and initialized data segments have been copied.
+ * Now clear the BSS segment.
+ */
+
+ movi a2, _bss_start # start of BSS
+ movi a3, _bss_end # end of BSS
+
+1: addi a2, a2, 4
+ s32i a0, a2, 0
+ blt a2, a3, 1b
+
+#if XCHAL_DCACHE_IS_WRITEBACK
+
+ /* After unpacking, flush the writeback cache to memory so the
+ * instructions/data are available.
+ */
+
+ dcache_writeback_all a2, a3
+#endif
+
+ /* Setup stack and enable window exceptions (keep irqs disabled) */
+
+ movi a1, init_thread_union
+ addi a1, a1, KERNEL_STACK_SIZE
+
+ movi a2, 0x00040001 # WOE=1, INTLEVEL=1, UM=0
+ wsr a2, PS # (enable reg-windows; progmode stack)
+ rsync
+
+ /* Set up EXCSAVE[DEBUGLEVEL] to point to the Debug Exception Handler.*/
+
+ movi a2, debug_exception
+ wsr a2, EXCSAVE + XCHAL_DEBUGLEVEL
+
+ /* Set up EXCSAVE[1] to point to the exc_table. */
+
+ movi a6, exc_table
+ xsr a6, EXCSAVE_1
+
+ /* init_arch kick-starts the linux kernel */
+
+ movi a4, init_arch
+ callx4 a4
+
+ movi a4, start_kernel
+ callx4 a4
+
+should_never_return:
+ j should_never_return
+
+ /* Define some common data structures here. We define them
+ * here in this assembly file due to their unusual alignment
+ * requirements.
+ */
+
+ .comm swapper_pg_dir,PAGE_SIZE,PAGE_SIZE
+ .comm empty_bad_page_table,PAGE_SIZE,PAGE_SIZE
+ .comm empty_bad_page,PAGE_SIZE,PAGE_SIZE
+ .comm empty_zero_page,PAGE_SIZE,PAGE_SIZE
+
diff --git a/arch/xtensa/kernel/irq.c b/arch/xtensa/kernel/irq.c
new file mode 100644
index 000000000000..4cbf6d91571f
--- /dev/null
+++ b/arch/xtensa/kernel/irq.c
@@ -0,0 +1,192 @@
+/*
+ * linux/arch/xtensa/kernel/irq.c
+ *
+ * Xtensa built-in interrupt controller and some generic functions copied
+ * from i386.
+ *
+ * Copyright (C) 2002 - 2005 Tensilica, Inc.
+ * Copyright (C) 1992, 1998 Linus Torvalds, Ingo Molnar
+ *
+ *
+ * Chris Zankel <chris@zankel.net>
+ * Kevin Chea
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/seq_file.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/kernel_stat.h>
+
+#include <asm/uaccess.h>
+#include <asm/platform.h>
+
+static void enable_xtensa_irq(unsigned int irq);
+static void disable_xtensa_irq(unsigned int irq);
+static void mask_and_ack_xtensa(unsigned int irq);
+static void end_xtensa_irq(unsigned int irq);
+
+static unsigned int cached_irq_mask;
+
+atomic_t irq_err_count;
+
+/*
+ * 'what should we do if we get a hw irq event on an illegal vector'.
+ * each architecture has to answer this themselves.
+ */
+void ack_bad_irq(unsigned int irq)
+{
+ printk("unexpected IRQ trap at vector %02x\n", irq);
+}
+
+/*
+ * do_IRQ handles all normal device IRQ's (the special
+ * SMP cross-CPU interrupts have their own specific
+ * handlers).
+ */
+
+unsigned int do_IRQ(int irq, struct pt_regs *regs)
+{
+ irq_enter();
+
+#ifdef CONFIG_DEBUG_STACKOVERFLOW
+ /* Debugging check for stack overflow: is there less than 1KB free? */
+ {
+ unsigned long sp;
+
+ __asm__ __volatile__ ("mov %0, a1\n" : "=a" (sp));
+ sp &= THREAD_SIZE - 1;
+
+ if (unlikely(sp < (sizeof(thread_info) + 1024)))
+ printk("Stack overflow in do_IRQ: %ld\n",
+ sp - sizeof(struct thread_info));
+ }
+#endif
+
+ __do_IRQ(irq, regs);
+
+ irq_exit();
+
+ return 1;
+}
+
+/*
+ * Generic, controller-independent functions:
+ */
+
+int show_interrupts(struct seq_file *p, void *v)
+{
+ int i = *(loff_t *) v, j;
+ struct irqaction * action;
+ unsigned long flags;
+
+ if (i == 0) {
+ seq_printf(p, " ");
+ for (j=0; j<NR_CPUS; j++)
+ if (cpu_online(j))
+ seq_printf(p, "CPU%d ",j);
+ seq_putc(p, '\n');
+ }
+
+ if (i < NR_IRQS) {
+ spin_lock_irqsave(&irq_desc[i].lock, flags);
+ action = irq_desc[i].action;
+ if (!action)
+ goto skip;
+ seq_printf(p, "%3d: ",i);
+#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]);
+#endif
+ seq_printf(p, " %14s", irq_desc[i].handler->typename);
+ seq_printf(p, " %s", action->name);
+
+ for (action=action->next; action; action = action->next)
+ seq_printf(p, ", %s", action->name);
+
+ seq_putc(p, '\n');
+skip:
+ spin_unlock_irqrestore(&irq_desc[i].lock, flags);
+ } else if (i == NR_IRQS) {
+ seq_printf(p, "NMI: ");
+ for (j = 0; j < NR_CPUS; j++)
+ if (cpu_online(j))
+ seq_printf(p, "%10u ", nmi_count(j));
+ seq_putc(p, '\n');
+ seq_printf(p, "ERR: %10u\n", atomic_read(&irq_err_count));
+ }
+ return 0;
+}
+/* shutdown is same as "disable" */
+#define shutdown_xtensa_irq disable_xtensa_irq
+
+static unsigned int startup_xtensa_irq(unsigned int irq)
+{
+ enable_xtensa_irq(irq);
+ return 0; /* never anything pending */
+}
+
+static struct hw_interrupt_type xtensa_irq_type = {
+ "Xtensa-IRQ",
+ startup_xtensa_irq,
+ shutdown_xtensa_irq,
+ enable_xtensa_irq,
+ disable_xtensa_irq,
+ mask_and_ack_xtensa,
+ end_xtensa_irq
+};
+
+static inline void mask_irq(unsigned int irq)
+{
+ cached_irq_mask &= ~(1 << irq);
+ set_sr (cached_irq_mask, INTENABLE);
+}
+
+static inline void unmask_irq(unsigned int irq)
+{
+ cached_irq_mask |= 1 << irq;
+ set_sr (cached_irq_mask, INTENABLE);
+}
+
+static void disable_xtensa_irq(unsigned int irq)
+{
+ unsigned long flags;
+ local_save_flags(flags);
+ mask_irq(irq);
+ local_irq_restore(flags);
+}
+
+static void enable_xtensa_irq(unsigned int irq)
+{
+ unsigned long flags;
+ local_save_flags(flags);
+ unmask_irq(irq);
+ local_irq_restore(flags);
+}
+
+static void mask_and_ack_xtensa(unsigned int irq)
+{
+ disable_xtensa_irq(irq);
+}
+
+static void end_xtensa_irq(unsigned int irq)
+{
+ enable_xtensa_irq(irq);
+}
+
+
+void __init init_IRQ(void)
+{
+ int i;
+
+ for (i=0; i < XTENSA_NR_IRQS; i++)
+ irq_desc[i].handler = &xtensa_irq_type;
+
+ cached_irq_mask = 0;
+
+ platform_init_irq();
+}
diff --git a/arch/xtensa/kernel/module.c b/arch/xtensa/kernel/module.c
new file mode 100644
index 000000000000..d1683cfa19a2
--- /dev/null
+++ b/arch/xtensa/kernel/module.c
@@ -0,0 +1,78 @@
+/*
+ * arch/xtensa/kernel/platform.c
+ *
+ * Module support.
+ *
+ * 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) 2001 - 2005 Tensilica Inc.
+ *
+ * Chris Zankel <chris@zankel.net>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleloader.h>
+#include <linux/elf.h>
+#include <linux/vmalloc.h>
+#include <linux/fs.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/cache.h>
+
+LIST_HEAD(module_buf_list);
+
+void *module_alloc(unsigned long size)
+{
+ panic("module_alloc not implemented");
+}
+
+void module_free(struct module *mod, void *module_region)
+{
+ panic("module_free not implemented");
+}
+
+int module_frob_arch_sections(Elf32_Ehdr *hdr,
+ Elf32_Shdr *sechdrs,
+ char *secstrings,
+ struct module *me)
+{
+ panic("module_frob_arch_sections not implemented");
+}
+
+int apply_relocate(Elf32_Shdr *sechdrs,
+ const char *strtab,
+ unsigned int symindex,
+ unsigned int relsec,
+ struct module *module)
+{
+ panic ("apply_relocate not implemented");
+}
+
+int apply_relocate_add(Elf32_Shdr *sechdrs,
+ const char *strtab,
+ unsigned int symindex,
+ unsigned int relsec,
+ struct module *module)
+{
+ panic("apply_relocate_add not implemented");
+}
+
+int module_finalize(const Elf_Ehdr *hdr,
+ const Elf_Shdr *sechdrs,
+ struct module *me)
+{
+ panic ("module_finalize not implemented");
+}
+
+void module_arch_cleanup(struct module *mod)
+{
+ panic("module_arch_cleanup not implemented");
+}
+
+struct bug_entry *module_find_bug(unsigned long bugaddr)
+{
+ panic("module_find_bug not implemented");
+}
diff --git a/arch/xtensa/kernel/pci-dma.c b/arch/xtensa/kernel/pci-dma.c
new file mode 100644
index 000000000000..84fde258cf85
--- /dev/null
+++ b/arch/xtensa/kernel/pci-dma.c
@@ -0,0 +1,73 @@
+/*
+ * arch/xtensa/pci-dma.c
+ *
+ * DMA coherent memory allocation.
+ *
+ * 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.
+ *
+ * Copyright (C) 2002 - 2005 Tensilica Inc.
+ *
+ * Based on version for i386.
+ *
+ * Chris Zankel <chris@zankel.net>
+ * Joe Taylor <joe@tensilica.com, joetylr@yahoo.com>
+ */
+
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/pci.h>
+#include <asm/io.h>
+#include <asm/cacheflush.h>
+
+/*
+ * Note: We assume that the full memory space is always mapped to 'kseg'
+ * Otherwise we have to use page attributes (not implemented).
+ */
+
+void *
+dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *handle, int gfp)
+{
+ void *ret;
+
+ /* ignore region speicifiers */
+ gfp &= ~(__GFP_DMA | __GFP_HIGHMEM);
+
+ if (dev == NULL || (*dev->dma_mask < 0xffffffff))
+ gfp |= GFP_DMA;
+ ret = (void *)__get_free_pages(gfp, get_order(size));
+
+ if (ret != NULL) {
+ memset(ret, 0, size);
+ *handle = virt_to_bus(ret);
+ }
+ return (void*) BYPASS_ADDR((unsigned long)ret);
+}
+
+void dma_free_coherent(struct device *hwdev, size_t size,
+ void *vaddr, dma_addr_t dma_handle)
+{
+ free_pages(CACHED_ADDR((unsigned long)vaddr), get_order(size));
+}
+
+
+void consistent_sync(void *vaddr, size_t size, int direction)
+{
+ switch (direction) {
+ case PCI_DMA_NONE:
+ BUG();
+ case PCI_DMA_FROMDEVICE: /* invalidate only */
+ __invalidate_dcache_range((unsigned long)vaddr,
+ (unsigned long)size);
+ break;
+
+ case PCI_DMA_TODEVICE: /* writeback only */
+ case PCI_DMA_BIDIRECTIONAL: /* writeback and invalidate */
+ __flush_invalidate_dcache_range((unsigned long)vaddr,
+ (unsigned long)size);
+ break;
+ }
+}
diff --git a/arch/xtensa/kernel/pci.c b/arch/xtensa/kernel/pci.c
new file mode 100644
index 000000000000..09887c96e9a1
--- /dev/null
+++ b/arch/xtensa/kernel/pci.c
@@ -0,0 +1,478 @@
+/*
+ * arch/xtensa/pcibios.c
+ *
+ * PCI bios-type initialisation for PCI machines
+ *
+ * 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.
+ *
+ * Copyright (C) 2001-2005 Tensilica Inc.
+ *
+ * Based largely on work from Cort (ppc/kernel/pci.c)
+ * IO functions copied from sparc.
+ *
+ * Chris Zankel <chris@zankel.net>
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/bootmem.h>
+
+#include <asm/pci-bridge.h>
+#include <asm/platform.h>
+
+#undef DEBUG
+
+#ifdef DEBUG
+#define DBG(x...) printk(x)
+#else
+#define DBG(x...)
+#endif
+
+/* PCI Controller */
+
+
+/*
+ * pcibios_alloc_controller
+ * pcibios_enable_device
+ * pcibios_fixups
+ * pcibios_align_resource
+ * pcibios_fixup_bus
+ * pcibios_setup
+ * pci_bus_add_device
+ * pci_mmap_page_range
+ */
+
+struct pci_controller* pci_ctrl_head;
+struct pci_controller** pci_ctrl_tail = &pci_ctrl_head;
+
+static int pci_bus_count;
+
+/*
+ * 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)
+{
+ struct pci_dev *dev = data;
+
+ if (res->flags & IORESOURCE_IO) {
+ unsigned long start = res->start;
+
+ if (size > 0x100) {
+ printk(KERN_ERR "PCI: I/O Region %s/%d too large"
+ " (%ld bytes)\n", pci_name(dev),
+ dev->resource - res, size);
+ }
+
+ if (start & 0x300) {
+ start = (start + 0x3ff) & ~0x3ff;
+ res->start = start;
+ }
+ }
+}
+
+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++) {
+ 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;
+}
+
+struct pci_controller * __init pcibios_alloc_controller(void)
+{
+ struct pci_controller *pci_ctrl;
+
+ pci_ctrl = (struct pci_controller *)alloc_bootmem(sizeof(*pci_ctrl));
+ memset(pci_ctrl, 0, sizeof(struct pci_controller));
+
+ *pci_ctrl_tail = pci_ctrl;
+ pci_ctrl_tail = &pci_ctrl->next;
+
+ return pci_ctrl;
+}
+
+static int __init pcibios_init(void)
+{
+ struct pci_controller *pci_ctrl;
+ struct pci_bus *bus;
+ int next_busno = 0, i;
+
+ printk("PCI: Probing PCI hardware\n");
+
+ /* Scan all of the recorded PCI controllers. */
+ for (pci_ctrl = pci_ctrl_head; pci_ctrl; pci_ctrl = pci_ctrl->next) {
+ pci_ctrl->last_busno = 0xff;
+ bus = pci_scan_bus(pci_ctrl->first_busno, pci_ctrl->ops,
+ pci_ctrl);
+ if (pci_ctrl->io_resource.flags) {
+ unsigned long offs;
+
+ offs = (unsigned long)pci_ctrl->io_space.base;
+ pci_ctrl->io_resource.start += offs;
+ pci_ctrl->io_resource.end += offs;
+ bus->resource[0] = &pci_ctrl->io_resource;
+ }
+ for (i = 0; i < 3; ++i)
+ if (pci_ctrl->mem_resources[i].flags)
+ bus->resource[i+1] =&pci_ctrl->mem_resources[i];
+ pci_ctrl->bus = bus;
+ pci_ctrl->last_busno = bus->subordinate;
+ if (next_busno <= pci_ctrl->last_busno)
+ next_busno = pci_ctrl->last_busno+1;
+ }
+ pci_bus_count = next_busno;
+
+ return platform_pcibios_fixup();
+}
+
+subsys_initcall(pcibios_init);
+
+void __init pcibios_fixup_bus(struct pci_bus *bus)
+{
+ struct pci_controller *pci_ctrl = bus->sysdata;
+ struct resource *res;
+ unsigned long io_offset;
+ int i;
+
+ io_offset = (unsigned long)pci_ctrl->io_space.base;
+ if (bus->parent == NULL) {
+ /* this is a host bridge - fill in its resources */
+ pci_ctrl->bus = bus;
+
+ bus->resource[0] = res = &pci_ctrl->io_resource;
+ if (!res->flags) {
+ if (io_offset)
+ printk (KERN_ERR "I/O resource not set for host"
+ " bridge %d\n", pci_ctrl->index);
+ res->start = 0;
+ res->end = IO_SPACE_LIMIT;
+ res->flags = IORESOURCE_IO;
+ }
+ res->start += io_offset;
+ res->end += io_offset;
+
+ for (i = 0; i < 3; i++) {
+ res = &pci_ctrl->mem_resources[i];
+ if (!res->flags) {
+ if (i > 0)
+ continue;
+ printk(KERN_ERR "Memory resource not set for "
+ "host bridge %d\n", pci_ctrl->index);
+ res->start = 0;
+ res->end = ~0U;
+ res->flags = IORESOURCE_MEM;
+ }
+ bus->resource[i+1] = res;
+ }
+ } else {
+ /* This is a subordinate bridge */
+ pci_read_bridge_bases(bus);
+
+ for (i = 0; i < 4; i++) {
+ if ((res = bus->resource[i]) == NULL || !res->flags)
+ continue;
+ if (io_offset && (res->flags & IORESOURCE_IO)) {
+ res->start += io_offset;
+ res->end += io_offset;
+ }
+ }
+ }
+}
+
+char __init *pcibios_setup(char *str)
+{
+ return str;
+}
+
+/* the next one is stolen from the alpha port... */
+
+void __init
+pcibios_update_irq(struct pci_dev *dev, int irq)
+{
+ pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq);
+}
+
+int pcibios_enable_device(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++) {
+ 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 (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;
+}
+
+#ifdef CONFIG_PROC_FS
+
+/*
+ * Return the index of the PCI controller for device pdev.
+ */
+
+int
+pci_controller_num(struct pci_dev *dev)
+{
+ struct pci_controller *pci_ctrl = (struct pci_controller*) dev->sysdata;
+ return pci_ctrl->index;
+}
+
+#endif /* CONFIG_PROC_FS */
+
+/*
+ * Platform support for /proc/bus/pci/X/Y mmap()s,
+ * modelled on the sparc64 implementation by Dave Miller.
+ * -- paulus.
+ */
+
+/*
+ * Adjust vm_pgoff of VMA such that it is the physical page offset
+ * corresponding to the 32-bit pci bus offset for DEV requested by the user.
+ *
+ * Basically, the user finds the base address for his device which he wishes
+ * to mmap. They read the 32-bit value from the config space base register,
+ * add whatever PAGE_SIZE multiple offset they wish, and feed this into the
+ * offset parameter of mmap on /proc/bus/pci/XXX for that device.
+ *
+ * Returns negative error code on failure, zero on success.
+ */
+static __inline__ int
+__pci_mmap_make_offset(struct pci_dev *dev, struct vm_area_struct *vma,
+ enum pci_mmap_state mmap_state)
+{
+ struct pci_controller *pci_ctrl = (struct pci_controller*) dev->sysdata;
+ unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
+ unsigned long io_offset = 0;
+ int i, res_bit;
+
+ if (pci_ctrl == 0)
+ return -EINVAL; /* should never happen */
+
+ /* If memory, add on the PCI bridge address offset */
+ if (mmap_state == pci_mmap_mem) {
+ res_bit = IORESOURCE_MEM;
+ } else {
+ io_offset = (unsigned long)pci_ctrl->io_space.base;
+ offset += io_offset;
+ res_bit = IORESOURCE_IO;
+ }
+
+ /*
+ * Check that the offset requested corresponds to one of the
+ * resources of the device.
+ */
+ for (i = 0; i <= PCI_ROM_RESOURCE; i++) {
+ struct resource *rp = &dev->resource[i];
+ int flags = rp->flags;
+
+ /* treat ROM as memory (should be already) */
+ if (i == PCI_ROM_RESOURCE)
+ flags |= IORESOURCE_MEM;
+
+ /* Active and same type? */
+ if ((flags & res_bit) == 0)
+ continue;
+
+ /* In the range of this resource? */
+ if (offset < (rp->start & PAGE_MASK) || offset > rp->end)
+ continue;
+
+ /* found it! construct the final physical address */
+ if (mmap_state == pci_mmap_io)
+ offset += pci_ctrl->io_space.start - io_offset;
+ vma->vm_pgoff = offset >> PAGE_SHIFT;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+/*
+ * Set vm_flags of VMA, as appropriate for this architecture, for a pci device
+ * mapping.
+ */
+static __inline__ void
+__pci_mmap_set_flags(struct pci_dev *dev, struct vm_area_struct *vma,
+ enum pci_mmap_state mmap_state)
+{
+ vma->vm_flags |= VM_SHM | VM_LOCKED | VM_IO;
+}
+
+/*
+ * Set vm_page_prot of VMA, as appropriate for this architecture, for a pci
+ * device mapping.
+ */
+static __inline__ void
+__pci_mmap_set_pgprot(struct pci_dev *dev, struct vm_area_struct *vma,
+ enum pci_mmap_state mmap_state, int write_combine)
+{
+ int prot = pgprot_val(vma->vm_page_prot);
+
+ /* Set to write-through */
+ prot &= ~_PAGE_NO_CACHE;
+#if 0
+ if (!write_combine)
+ prot |= _PAGE_WRITETHRU;
+#endif
+ vma->vm_page_prot = __pgprot(prot);
+}
+
+/*
+ * Perform the actual remap of the pages for a PCI device mapping, as
+ * appropriate for this architecture. The region in the process to map
+ * is described by vm_start and vm_end members of VMA, the base physical
+ * address is found in vm_pgoff.
+ * The pci device structure is provided so that architectures may make mapping
+ * decisions on a per-device or per-bus basis.
+ *
+ * Returns a negative error code on failure, zero on success.
+ */
+int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma,
+ enum pci_mmap_state mmap_state,
+ int write_combine)
+{
+ int ret;
+
+ ret = __pci_mmap_make_offset(dev, vma, mmap_state);
+ if (ret < 0)
+ return ret;
+
+ __pci_mmap_set_flags(dev, vma, mmap_state);
+ __pci_mmap_set_pgprot(dev, vma, mmap_state, write_combine);
+
+ ret = io_remap_page_range(vma, vma->vm_start, vma->vm_pgoff<<PAGE_SHIFT,
+ vma->vm_end - vma->vm_start, vma->vm_page_prot);
+
+ return ret;
+}
+
+/*
+ * This probably belongs here rather than ioport.c because
+ * we do not want this crud linked into SBus kernels.
+ * Also, think for a moment about likes of floppy.c that
+ * include architecture specific parts. They may want to redefine ins/outs.
+ *
+ * We do not use horroble macroses here because we want to
+ * advance pointer by sizeof(size).
+ */
+void outsb(unsigned long addr, const void *src, unsigned long count) {
+ while (count) {
+ count -= 1;
+ writeb(*(const char *)src, addr);
+ src += 1;
+ addr += 1;
+ }
+}
+
+void outsw(unsigned long addr, const void *src, unsigned long count) {
+ while (count) {
+ count -= 2;
+ writew(*(const short *)src, addr);
+ src += 2;
+ addr += 2;
+ }
+}
+
+void outsl(unsigned long addr, const void *src, unsigned long count) {
+ while (count) {
+ count -= 4;
+ writel(*(const long *)src, addr);
+ src += 4;
+ addr += 4;
+ }
+}
+
+void insb(unsigned long addr, void *dst, unsigned long count) {
+ while (count) {
+ count -= 1;
+ *(unsigned char *)dst = readb(addr);
+ dst += 1;
+ addr += 1;
+ }
+}
+
+void insw(unsigned long addr, void *dst, unsigned long count) {
+ while (count) {
+ count -= 2;
+ *(unsigned short *)dst = readw(addr);
+ dst += 2;
+ addr += 2;
+ }
+}
+
+void insl(unsigned long addr, void *dst, unsigned long count) {
+ while (count) {
+ count -= 4;
+ /*
+ * XXX I am sure we are in for an unaligned trap here.
+ */
+ *(unsigned long *)dst = readl(addr);
+ dst += 4;
+ addr += 4;
+ }
+}
+
+
+
diff --git a/arch/xtensa/kernel/platform.c b/arch/xtensa/kernel/platform.c
new file mode 100644
index 000000000000..cf1362784443
--- /dev/null
+++ b/arch/xtensa/kernel/platform.c
@@ -0,0 +1,49 @@
+/*
+ * arch/xtensa/kernel/platform.c
+ *
+ * Default platform functions.
+ *
+ * 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) 2005 Tensilica Inc.
+ *
+ * Chris Zankel <chris@zankel.net>
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/time.h>
+#include <asm/platform.h>
+#include <asm/timex.h>
+
+#define _F(r,f,a,b) \
+ r __platform_##f a b; \
+ r platform_##f a __attribute__((weak, alias("__platform_"#f)))
+
+/*
+ * Default functions that are used if no platform specific function is defined.
+ * (Please, refer to include/asm-xtensa/platform.h for more information)
+ */
+
+_F(void, setup, (char** cmd), { });
+_F(void, init_irq, (void), { });
+_F(void, restart, (void), { while(1); });
+_F(void, halt, (void), { while(1); });
+_F(void, power_off, (void), { while(1); });
+_F(void, idle, (void), { __asm__ __volatile__ ("waiti 0" ::: "memory"); });
+_F(void, heartbeat, (void), { });
+_F(int, pcibios_fixup, (void), { return 0; });
+_F(int, get_rtc_time, (time_t* t), { return 0; });
+_F(int, set_rtc_time, (time_t t), { return 0; });
+
+#if CONFIG_XTENSA_CALIBRATE_CCOUNT
+_F(void, calibrate_ccount, (void),
+{
+ printk ("ERROR: Cannot calibrate cpu frequency! Assuming 100MHz.\n");
+ ccount_per_jiffy = 100 * (1000000UL/HZ);
+});
+#endif
+
diff --git a/arch/xtensa/kernel/process.c b/arch/xtensa/kernel/process.c
new file mode 100644
index 000000000000..4099703b14be
--- /dev/null
+++ b/arch/xtensa/kernel/process.c
@@ -0,0 +1,482 @@
+// TODO verify coprocessor handling
+/*
+ * arch/xtensa/kernel/process.c
+ *
+ * Xtensa Processor version.
+ *
+ * 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) 2001 - 2005 Tensilica Inc.
+ *
+ * Joe Taylor <joe@tensilica.com, joetylr@yahoo.com>
+ * Chris Zankel <chris@zankel.net>
+ * Marc Gauthier <marc@tensilica.com, marc@alumni.uwaterloo.ca>
+ * Kevin Chea
+ */
+
+#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/elf.h>
+#include <linux/init.h>
+#include <linux/prctl.h>
+#include <linux/init_task.h>
+#include <linux/module.h>
+#include <linux/mqueue.h>
+
+#include <asm/pgtable.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/processor.h>
+#include <asm/platform.h>
+#include <asm/mmu.h>
+#include <asm/irq.h>
+#include <asm/atomic.h>
+#include <asm/offsets.h>
+#include <asm/coprocessor.h>
+
+extern void ret_from_fork(void);
+
+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);
+
+union thread_union init_thread_union
+ __attribute__((__section__(".data.init_task"))) =
+{ INIT_THREAD_INFO(init_task) };
+
+struct task_struct init_task = INIT_TASK(init_task);
+EXPORT_SYMBOL(init_task);
+
+struct task_struct *current_set[NR_CPUS] = {&init_task, };
+
+
+#if XCHAL_CP_NUM > 0
+
+/*
+ * Coprocessor ownership.
+ */
+
+coprocessor_info_t coprocessor_info[] = {
+ { 0, XTENSA_CPE_CP0_OFFSET },
+ { 0, XTENSA_CPE_CP1_OFFSET },
+ { 0, XTENSA_CPE_CP2_OFFSET },
+ { 0, XTENSA_CPE_CP3_OFFSET },
+ { 0, XTENSA_CPE_CP4_OFFSET },
+ { 0, XTENSA_CPE_CP5_OFFSET },
+ { 0, XTENSA_CPE_CP6_OFFSET },
+ { 0, XTENSA_CPE_CP7_OFFSET },
+};
+
+#endif
+
+/*
+ * Powermanagement idle function, if any is provided by the platform.
+ */
+
+void cpu_idle(void)
+{
+ local_irq_enable();
+
+ /* endless idle loop with no priority at all */
+ while (1) {
+ while (!need_resched())
+ platform_idle();
+ preempt_enable();
+ schedule();
+ }
+}
+
+/*
+ * Free current thread data structures etc..
+ */
+
+void exit_thread(void)
+{
+ release_coprocessors(current); /* Empty macro if no CPs are defined */
+}
+
+void flush_thread(void)
+{
+ release_coprocessors(current); /* Empty macro if no CPs are defined */
+}
+
+/*
+ * Copy thread.
+ *
+ * The stack layout for the new thread looks like this:
+ *
+ * +------------------------+ <- sp in childregs (= tos)
+ * | childregs |
+ * +------------------------+ <- thread.sp = sp in dummy-frame
+ * | dummy-frame | (saved in dummy-frame spill-area)
+ * +------------------------+
+ *
+ * We create a dummy frame to return to ret_from_fork:
+ * a0 points to ret_from_fork (simulating a call4)
+ * sp points to itself (thread.sp)
+ * a2, a3 are unused.
+ *
+ * Note: This is a pristine frame, so we don't need any spill region on top of
+ * childregs.
+ */
+
+int copy_thread(int nr, unsigned long clone_flags, unsigned long usp,
+ unsigned long unused,
+ struct task_struct * p, struct pt_regs * regs)
+{
+ struct pt_regs *childregs;
+ unsigned long tos;
+ int user_mode = user_mode(regs);
+
+ /* Set up new TSS. */
+ tos = (unsigned long)p->thread_info + THREAD_SIZE;
+ if (user_mode)
+ childregs = (struct pt_regs*)(tos - PT_USER_SIZE);
+ else
+ childregs = (struct pt_regs*)tos - 1;
+
+ *childregs = *regs;
+
+ /* Create a call4 dummy-frame: a0 = 0, a1 = childregs. */
+ *((int*)childregs - 3) = (unsigned long)childregs;
+ *((int*)childregs - 4) = 0;
+
+ childregs->areg[1] = tos;
+ childregs->areg[2] = 0;
+ p->set_child_tid = p->clear_child_tid = NULL;
+ p->thread.ra = MAKE_RA_FOR_CALL((unsigned long)ret_from_fork, 0x1);
+ p->thread.sp = (unsigned long)childregs;
+ if (user_mode(regs)) {
+
+ int len = childregs->wmask & ~0xf;
+ childregs->areg[1] = usp;
+ memcpy(&childregs->areg[XCHAL_NUM_AREGS - len/4],
+ &regs->areg[XCHAL_NUM_AREGS - len/4], len);
+
+ if (clone_flags & CLONE_SETTLS)
+ childregs->areg[2] = childregs->areg[6];
+
+ } else {
+ /* In kernel space, we start a new thread with a new stack. */
+ childregs->wmask = 1;
+ }
+ return 0;
+}
+
+
+/*
+ * Create a kernel thread
+ */
+
+int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
+{
+ long retval;
+ __asm__ __volatile__
+ ("mov a5, %4\n\t" /* preserve fn in a5 */
+ "mov a6, %3\n\t" /* preserve and setup arg in a6 */
+ "movi a2, %1\n\t" /* load __NR_clone for syscall*/
+ "mov a3, sp\n\t" /* sp check and sys_clone */
+ "mov a4, %5\n\t" /* load flags for syscall */
+ "syscall\n\t"
+ "beq a3, sp, 1f\n\t" /* branch if parent */
+ "callx4 a5\n\t" /* call fn */
+ "movi a2, %2\n\t" /* load __NR_exit for syscall */
+ "mov a3, a6\n\t" /* load fn return value */
+ "syscall\n"
+ "1:\n\t"
+ "mov %0, a2\n\t" /* parent returns zero */
+ :"=r" (retval)
+ :"i" (__NR_clone), "i" (__NR_exit),
+ "r" (arg), "r" (fn),
+ "r" (flags | CLONE_VM)
+ : "a2", "a3", "a4", "a5", "a6" );
+ return retval;
+}
+
+
+/*
+ * These bracket the sleeping functions..
+ */
+
+unsigned long get_wchan(struct task_struct *p)
+{
+ unsigned long sp, pc;
+ unsigned long stack_page = (unsigned long) p->thread_info;
+ int count = 0;
+
+ if (!p || p == current || p->state == TASK_RUNNING)
+ return 0;
+
+ sp = p->thread.sp;
+ pc = MAKE_PC_FROM_RA(p->thread.ra, p->thread.sp);
+
+ do {
+ if (sp < stack_page + sizeof(struct task_struct) ||
+ sp >= (stack_page + THREAD_SIZE) ||
+ pc == 0)
+ return 0;
+ if (!in_sched_functions(pc))
+ return pc;
+
+ /* Stack layout: sp-4: ra, sp-3: sp' */
+
+ pc = MAKE_PC_FROM_RA(*(unsigned long*)sp - 4, sp);
+ sp = *(unsigned long *)sp - 3;
+ } while (count++ < 16);
+ return 0;
+}
+
+/*
+ * do_copy_regs() gathers information from 'struct pt_regs' and
+ * 'current->thread.areg[]' to fill in the xtensa_gregset_t
+ * structure.
+ *
+ * xtensa_gregset_t and 'struct pt_regs' are vastly different formats
+ * of processor registers. Besides different ordering,
+ * xtensa_gregset_t contains non-live register information that
+ * 'struct pt_regs' does not. Exception handling (primarily) uses
+ * 'struct pt_regs'. Core files and ptrace use xtensa_gregset_t.
+ *
+ */
+
+void do_copy_regs (xtensa_gregset_t *elfregs, struct pt_regs *regs,
+ struct task_struct *tsk)
+{
+ int i, n, wb_offset;
+
+ elfregs->xchal_config_id0 = XCHAL_HW_CONFIGID0;
+ elfregs->xchal_config_id1 = XCHAL_HW_CONFIGID1;
+
+ __asm__ __volatile__ ("rsr %0, 176\n" : "=a" (i));
+ elfregs->cpux = i;
+ __asm__ __volatile__ ("rsr %0, 208\n" : "=a" (i));
+ elfregs->cpuy = i;
+
+ /* Note: PS.EXCM is not set while user task is running; its
+ * being set in regs->ps is for exception handling convenience.
+ */
+
+ elfregs->pc = regs->pc;
+ elfregs->ps = (regs->ps & ~XCHAL_PS_EXCM_MASK);
+ elfregs->exccause = regs->exccause;
+ elfregs->excvaddr = regs->excvaddr;
+ elfregs->windowbase = regs->windowbase;
+ elfregs->windowstart = regs->windowstart;
+ elfregs->lbeg = regs->lbeg;
+ elfregs->lend = regs->lend;
+ elfregs->lcount = regs->lcount;
+ elfregs->sar = regs->sar;
+ elfregs->syscall = regs->syscall;
+
+ /* Copy register file.
+ * The layout looks like this:
+ *
+ * | a0 ... a15 | Z ... Z | arX ... arY |
+ * current window unused saved frames
+ */
+
+ memset (elfregs->ar, 0, sizeof(elfregs->ar));
+
+ wb_offset = regs->windowbase * 4;
+ n = (regs->wmask&1)? 4 : (regs->wmask&2)? 8 : (regs->wmask&4)? 12 : 16;
+
+ for (i = 0; i < n; i++)
+ elfregs->ar[(wb_offset + i) % XCHAL_NUM_AREGS] = regs->areg[i];
+
+ n = (regs->wmask >> 4) * 4;
+
+ for (i = XCHAL_NUM_AREGS - n; n > 0; i++, n--)
+ elfregs->ar[(wb_offset + i) % XCHAL_NUM_AREGS] = regs->areg[i];
+}
+
+void xtensa_elf_core_copy_regs (xtensa_gregset_t *elfregs, struct pt_regs *regs)
+{
+ do_copy_regs ((xtensa_gregset_t *)elfregs, regs, current);
+}
+
+
+/* The inverse of do_copy_regs(). No error or sanity checking. */
+
+void do_restore_regs (xtensa_gregset_t *elfregs, struct pt_regs *regs,
+ struct task_struct *tsk)
+{
+ int i, n, wb_offset;
+
+ /* Note: PS.EXCM is not set while user task is running; it
+ * needs to be set in regs->ps is for exception handling convenience.
+ */
+
+ regs->pc = elfregs->pc;
+ regs->ps = (elfregs->ps | XCHAL_PS_EXCM_MASK);
+ regs->exccause = elfregs->exccause;
+ regs->excvaddr = elfregs->excvaddr;
+ regs->windowbase = elfregs->windowbase;
+ regs->windowstart = elfregs->windowstart;
+ regs->lbeg = elfregs->lbeg;
+ regs->lend = elfregs->lend;
+ regs->lcount = elfregs->lcount;
+ regs->sar = elfregs->sar;
+ regs->syscall = elfregs->syscall;
+
+ /* Clear everything. */
+
+ memset (regs->areg, 0, sizeof(regs->areg));
+
+ /* Copy regs from live window frame. */
+
+ wb_offset = regs->windowbase * 4;
+ n = (regs->wmask&1)? 4 : (regs->wmask&2)? 8 : (regs->wmask&4)? 12 : 16;
+
+ for (i = 0; i < n; i++)
+ regs->areg[(wb_offset+i) % XCHAL_NUM_AREGS] = elfregs->ar[i];
+
+ n = (regs->wmask >> 4) * 4;
+
+ for (i = XCHAL_NUM_AREGS - n; n > 0; i++, n--)
+ regs->areg[(wb_offset+i) % XCHAL_NUM_AREGS] = elfregs->ar[i];
+}
+
+/*
+ * do_save_fpregs() gathers information from 'struct pt_regs' and
+ * 'current->thread' to fill in the elf_fpregset_t structure.
+ *
+ * Core files and ptrace use elf_fpregset_t.
+ */
+
+void do_save_fpregs (elf_fpregset_t *fpregs, struct pt_regs *regs,
+ struct task_struct *tsk)
+{
+#if XCHAL_HAVE_CP
+
+ extern unsigned char _xtensa_reginfo_tables[];
+ extern unsigned _xtensa_reginfo_table_size;
+ int i;
+ unsigned long flags;
+
+ /* Before dumping coprocessor state from memory,
+ * ensure any live coprocessor contents for this
+ * task are first saved to memory:
+ */
+ local_irq_save(flags);
+
+ for (i = 0; i < XCHAL_CP_MAX; i++) {
+ if (tsk == coprocessor_info[i].owner) {
+ enable_coprocessor(i);
+ save_coprocessor_registers(
+ tsk->thread.cp_save+coprocessor_info[i].offset,i);
+ disable_coprocessor(i);
+ }
+ }
+
+ local_irq_restore(flags);
+
+ /* Now dump coprocessor & extra state: */
+ memcpy((unsigned char*)fpregs,
+ _xtensa_reginfo_tables, _xtensa_reginfo_table_size);
+ memcpy((unsigned char*)fpregs + _xtensa_reginfo_table_size,
+ tsk->thread.cp_save, XTENSA_CP_EXTRA_SIZE);
+#endif
+}
+
+/*
+ * The inverse of do_save_fpregs().
+ * Copies coprocessor and extra state from fpregs into regs and tsk->thread.
+ * Returns 0 on success, non-zero if layout doesn't match.
+ */
+
+int do_restore_fpregs (elf_fpregset_t *fpregs, struct pt_regs *regs,
+ struct task_struct *tsk)
+{
+#if XCHAL_HAVE_CP
+
+ extern unsigned char _xtensa_reginfo_tables[];
+ extern unsigned _xtensa_reginfo_table_size;
+ int i;
+ unsigned long flags;
+
+ /* Make sure save area layouts match.
+ * FIXME: in the future we could allow restoring from
+ * a different layout of the same registers, by comparing
+ * fpregs' table with _xtensa_reginfo_tables and matching
+ * entries and copying registers one at a time.
+ * Not too sure yet whether that's very useful.
+ */
+
+ if( memcmp((unsigned char*)fpregs,
+ _xtensa_reginfo_tables, _xtensa_reginfo_table_size) ) {
+ return -1;
+ }
+
+ /* Before restoring coprocessor state from memory,
+ * ensure any live coprocessor contents for this
+ * task are first invalidated.
+ */
+
+ local_irq_save(flags);
+
+ for (i = 0; i < XCHAL_CP_MAX; i++) {
+ if (tsk == coprocessor_info[i].owner) {
+ enable_coprocessor(i);
+ save_coprocessor_registers(
+ tsk->thread.cp_save+coprocessor_info[i].offset,i);
+ coprocessor_info[i].owner = 0;
+ disable_coprocessor(i);
+ }
+ }
+
+ local_irq_restore(flags);
+
+ /* Now restore coprocessor & extra state: */
+
+ memcpy(tsk->thread.cp_save,
+ (unsigned char*)fpregs + _xtensa_reginfo_table_size,
+ XTENSA_CP_EXTRA_SIZE);
+#endif
+ return 0;
+}
+/*
+ * Fill in the CP structure for a core dump for a particular task.
+ */
+
+int
+dump_task_fpu(struct pt_regs *regs, struct task_struct *task, elf_fpregset_t *r)
+{
+/* see asm/coprocessor.h for this magic number 16 */
+#if TOTAL_CPEXTRA_SIZE > 16
+ do_save_fpregs (r, regs, task);
+
+ /* For now, bit 16 means some extra state may be present: */
+// FIXME!! need to track to return more accurate mask
+ return 0x10000 | XCHAL_CP_MASK;
+#else
+ return 0; /* no coprocessors active on this processor */
+#endif
+}
+
+/*
+ * Fill in the CP structure for a core dump.
+ * This includes any FPU coprocessor.
+ * Here, we dump all coprocessors, and other ("extra") custom state.
+ *
+ * This function is called by elf_core_dump() in fs/binfmt_elf.c
+ * (in which case 'regs' comes from calls to do_coredump, see signals.c).
+ */
+int dump_fpu(struct pt_regs *regs, elf_fpregset_t *r)
+{
+ return dump_task_fpu(regs, current, r);
+}
diff --git a/arch/xtensa/kernel/ptrace.c b/arch/xtensa/kernel/ptrace.c
new file mode 100644
index 000000000000..2659efdd4e99
--- /dev/null
+++ b/arch/xtensa/kernel/ptrace.c
@@ -0,0 +1,408 @@
+// TODO some minor issues
+/*
+ * 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) 2001 - 2005 Tensilica Inc.
+ *
+ * Joe Taylor <joe@tensilica.com, joetylr@yahoo.com>
+ * Chris Zankel <chris@zankel.net>
+ * Scott Foehner<sfoehner@yahoo.com>,
+ * Kevin Chea
+ * Marc Gauthier<marc@tensilica.com> <marc@alumni.uwaterloo.ca>
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/errno.h>
+#include <linux/ptrace.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+#include <linux/security.h>
+#include <linux/signal.h>
+
+#include <asm/pgtable.h>
+#include <asm/page.h>
+#include <asm/system.h>
+#include <asm/uaccess.h>
+#include <asm/ptrace.h>
+#include <asm/elf.h>
+
+#define TEST_KERNEL // verify kernel operations FIXME: remove
+
+
+/*
+ * Called by kernel/ptrace.c when detaching..
+ *
+ * Make sure single step bits etc are not set.
+ */
+
+void ptrace_disable(struct task_struct *child)
+{
+ /* Nothing to do.. */
+}
+
+int sys_ptrace(long request, long pid, long addr, long data)
+{
+ struct task_struct *child;
+ int ret = -EPERM;
+
+ lock_kernel();
+
+#if 0
+ if ((int)request != 1)
+ printk("ptrace(r=%d,pid=%d,addr=%08lx,data=%08lx)\n",
+ (int) request, (int) pid, (unsigned long) addr,
+ (unsigned long) data);
+#endif
+
+ if (request == PTRACE_TRACEME) {
+
+ /* Are we already being traced? */
+
+ if (current->ptrace & PT_PTRACED)
+ goto out;
+
+ if ((ret = security_ptrace(current->parent, current)))
+ 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;
+
+ if (request == PTRACE_ATTACH) {
+ ret = ptrace_attach(child);
+ goto out_tsk;
+ }
+
+ if ((ret = ptrace_check_attach(child, request == PTRACE_KILL)) < 0)
+ goto out_tsk;
+
+ switch (request) {
+ case PTRACE_PEEKTEXT: /* read word at location addr. */
+ case PTRACE_PEEKDATA:
+ {
+ unsigned long tmp;
+ int copied;
+
+ copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
+ ret = -EIO;
+ if (copied != sizeof(tmp))
+ break;
+ ret = put_user(tmp,(unsigned long *) data);
+
+ goto out;
+ }
+
+ /* Read the word at location addr in the USER area. */
+
+ case PTRACE_PEEKUSR:
+ {
+ struct pt_regs *regs;
+ unsigned long tmp;
+
+ regs = xtensa_pt_regs(child);
+ tmp = 0; /* Default return value. */
+
+ switch(addr) {
+
+ case REG_AR_BASE ... REG_AR_BASE + XCHAL_NUM_AREGS - 1:
+ {
+ int ar = addr - REG_AR_BASE - regs->windowbase * 4;
+ ar &= (XCHAL_NUM_AREGS - 1);
+ if (ar < 16 && ar + (regs->wmask >> 4) * 4 >= 0)
+ tmp = regs->areg[ar];
+ else
+ ret = -EIO;
+ break;
+ }
+ case REG_A_BASE ... REG_A_BASE + 15:
+ tmp = regs->areg[addr - REG_A_BASE];
+ break;
+ case REG_PC:
+ tmp = regs->pc;
+ break;
+ case REG_PS:
+ /* Note: PS.EXCM is not set while user task is running;
+ * its being set in regs is for exception handling
+ * convenience. */
+ tmp = (regs->ps & ~XCHAL_PS_EXCM_MASK);
+ break;
+ case REG_WB:
+ tmp = regs->windowbase;
+ break;
+ case REG_WS:
+ tmp = regs->windowstart;
+ break;
+ case REG_LBEG:
+ tmp = regs->lbeg;
+ break;
+ case REG_LEND:
+ tmp = regs->lend;
+ break;
+ case REG_LCOUNT:
+ tmp = regs->lcount;
+ break;
+ case REG_SAR:
+ tmp = regs->sar;
+ break;
+ case REG_DEPC:
+ tmp = regs->depc;
+ break;
+ case REG_EXCCAUSE:
+ tmp = regs->exccause;
+ break;
+ case REG_EXCVADDR:
+ tmp = regs->excvaddr;
+ break;
+ case SYSCALL_NR:
+ tmp = regs->syscall;
+ break;
+ default:
+ tmp = 0;
+ ret = -EIO;
+ goto out;
+ }
+ ret = put_user(tmp, (unsigned long *) data);
+ goto out;
+ }
+
+ case PTRACE_POKETEXT: /* write the word at location addr. */
+ case PTRACE_POKEDATA:
+ if (access_process_vm(child, addr, &data, sizeof(data), 1)
+ == sizeof(data))
+ break;
+ ret = -EIO;
+ goto out;
+
+ case PTRACE_POKEUSR:
+ {
+ struct pt_regs *regs;
+ regs = xtensa_pt_regs(child);
+
+ switch (addr) {
+ case REG_AR_BASE ... REG_AR_BASE + XCHAL_NUM_AREGS - 1:
+ {
+ int ar = addr - REG_AR_BASE - regs->windowbase * 4;
+ if (ar < 16 && ar + (regs->wmask >> 4) * 4 >= 0)
+ regs->areg[ar & (XCHAL_NUM_AREGS - 1)] = data;
+ else
+ ret = -EIO;
+ break;
+ }
+ case REG_A_BASE ... REG_A_BASE + 15:
+ regs->areg[addr - REG_A_BASE] = data;
+ break;
+ case REG_PC:
+ regs->pc = data;
+ break;
+ case SYSCALL_NR:
+ regs->syscall = data;
+ break;
+#ifdef TEST_KERNEL
+ case REG_WB:
+ regs->windowbase = data;
+ break;
+ case REG_WS:
+ regs->windowstart = data;
+ break;
+#endif
+
+ default:
+ /* The rest are not allowed. */
+ ret = -EIO;
+ break;
+ }
+ break;
+ }
+
+ /* continue and stop at next (return from) syscall */
+ case PTRACE_SYSCALL:
+ case PTRACE_CONT: /* restart after signal. */
+ {
+ ret = -EIO;
+ if (!valid_signal(data))
+ 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;
+ /* Make sure the single step bit is not set. */
+ child->ptrace &= ~PT_SINGLESTEP;
+ 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->state == EXIT_ZOMBIE) /* already dead */
+ break;
+ child->exit_code = SIGKILL;
+ child->ptrace &= ~PT_SINGLESTEP;
+ wake_up_process(child);
+ break;
+
+ case PTRACE_SINGLESTEP:
+ ret = -EIO;
+ if (!valid_signal(data))
+ break;
+ clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
+ child->ptrace |= PT_SINGLESTEP;
+ child->exit_code = data;
+ wake_up_process(child);
+ ret = 0;
+ break;
+
+ case PTRACE_GETREGS:
+ {
+ /* 'data' points to user memory in which to write.
+ * Mainly due to the non-live register values, we
+ * reformat the register values into something more
+ * standard. For convenience, we use the handy
+ * elf_gregset_t format. */
+
+ xtensa_gregset_t format;
+ struct pt_regs *regs = xtensa_pt_regs(child);
+
+ do_copy_regs (&format, regs, child);
+
+ /* Now, copy to user space nice and easy... */
+ ret = 0;
+ if (copy_to_user((void *)data, &format, sizeof(elf_gregset_t)))
+ ret = -EFAULT;
+ break;
+ }
+
+ case PTRACE_SETREGS:
+ {
+ /* 'data' points to user memory that contains the new
+ * values in the elf_gregset_t format. */
+
+ xtensa_gregset_t format;
+ struct pt_regs *regs = xtensa_pt_regs(child);
+
+ if (copy_from_user(&format,(void *)data,sizeof(elf_gregset_t))){
+ ret = -EFAULT;
+ break;
+ }
+
+ /* FIXME: Perhaps we want some sanity checks on
+ * these user-space values? See ARM version. Are
+ * debuggers a security concern? */
+
+ do_restore_regs (&format, regs, child);
+
+ ret = 0;
+ break;
+ }
+
+ case PTRACE_GETFPREGS:
+ {
+ /* 'data' points to user memory in which to write.
+ * For convenience, we use the handy
+ * elf_fpregset_t format. */
+
+ elf_fpregset_t fpregs;
+ struct pt_regs *regs = xtensa_pt_regs(child);
+
+ do_save_fpregs (&fpregs, regs, child);
+
+ /* Now, copy to user space nice and easy... */
+ ret = 0;
+ if (copy_to_user((void *)data, &fpregs, sizeof(elf_fpregset_t)))
+ ret = -EFAULT;
+
+ break;
+ }
+
+ case PTRACE_SETFPREGS:
+ {
+ /* 'data' points to user memory that contains the new
+ * values in the elf_fpregset_t format.
+ */
+ elf_fpregset_t fpregs;
+ struct pt_regs *regs = xtensa_pt_regs(child);
+
+ ret = 0;
+ if (copy_from_user(&fpregs, (void *)data, sizeof(elf_fpregset_t))) {
+ ret = -EFAULT;
+ break;
+ }
+
+ if (do_restore_fpregs (&fpregs, regs, child))
+ ret = -EIO;
+ break;
+ }
+
+ case PTRACE_GETFPREGSIZE:
+ /* 'data' points to 'unsigned long' set to the size
+ * of elf_fpregset_t
+ */
+ ret = put_user(sizeof(elf_fpregset_t), (unsigned long *) data);
+ break;
+
+ case PTRACE_DETACH: /* detach a process that was attached. */
+ ret = ptrace_detach(child, data);
+ break;
+
+ default:
+ ret = ptrace_request(child, request, addr, data);
+ goto out;
+ }
+out_tsk:
+ put_task_struct(child);
+out:
+ unlock_kernel();
+ return ret;
+}
+
+void do_syscall_trace(void)
+{
+ if (!test_thread_flag(TIF_SYSCALL_TRACE))
+ return;
+
+ if (!(current->ptrace & PT_PTRACED))
+ return;
+
+ /*
+ * The 0x80 provides a way for the tracing parent to distinguish
+ * between a syscall stop and SIGTRAP delivery
+ */
+ ptrace_notify(SIGTRAP|((current->ptrace & PT_TRACESYSGOOD) ? 0x80 : 0));
+
+ /*
+ * this isn't the same as continuing with a signal, but it will do
+ * for normal use. strace only continues with a signal if the
+ * stopping signal is not SIGTRAP. -brl
+ */
+ if (current->exit_code) {
+ send_sig(current->exit_code, current, 1);
+ current->exit_code = 0;
+ }
+}
diff --git a/arch/xtensa/kernel/semaphore.c b/arch/xtensa/kernel/semaphore.c
new file mode 100644
index 000000000000..d40f4b1b75ac
--- /dev/null
+++ b/arch/xtensa/kernel/semaphore.c
@@ -0,0 +1,226 @@
+/*
+ * arch/xtensa/kernel/semaphore.c
+ *
+ * Generic semaphore code. Buyer beware. Do your own specific changes
+ * in <asm/semaphore-helper.h>
+ *
+ * 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) 2001 - 2005 Tensilica Inc.
+ *
+ * Joe Taylor <joe@tensilica.com, joetylr@yahoo.com>
+ * Chris Zankel <chris@zankel.net>
+ * Marc Gauthier<marc@tensilica.com, marc@alumni.uwaterloo.ca>
+ * Kevin Chea
+ */
+
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/init.h>
+#include <asm/semaphore.h>
+#include <asm/errno.h>
+
+/*
+ * These two _must_ execute atomically wrt each other.
+ */
+
+static __inline__ void wake_one_more(struct semaphore * sem)
+{
+ atomic_inc((atomic_t *)&sem->sleepers);
+}
+
+static __inline__ int waking_non_zero(struct semaphore *sem)
+{
+ unsigned long flags;
+ int ret = 0;
+
+ spin_lock_irqsave(&semaphore_wake_lock, flags);
+ if (sem->sleepers > 0) {
+ sem->sleepers--;
+ ret = 1;
+ }
+ spin_unlock_irqrestore(&semaphore_wake_lock, flags);
+ return ret;
+}
+
+/*
+ * waking_non_zero_interruptible:
+ * 1 got the lock
+ * 0 go to sleep
+ * -EINTR interrupted
+ *
+ * We must undo the sem->count down_interruptible() increment while we are
+ * protected by the spinlock in order to make atomic this atomic_inc() with the
+ * atomic_read() in wake_one_more(), otherwise we can race. -arca
+ */
+
+static __inline__ int waking_non_zero_interruptible(struct semaphore *sem,
+ struct task_struct *tsk)
+{
+ unsigned long flags;
+ int ret = 0;
+
+ spin_lock_irqsave(&semaphore_wake_lock, flags);
+ if (sem->sleepers > 0) {
+ sem->sleepers--;
+ ret = 1;
+ } else if (signal_pending(tsk)) {
+ atomic_inc(&sem->count);
+ ret = -EINTR;
+ }
+ spin_unlock_irqrestore(&semaphore_wake_lock, flags);
+ return ret;
+}
+
+/*
+ * waking_non_zero_trylock:
+ * 1 failed to lock
+ * 0 got the lock
+ *
+ * We must undo the sem->count down_trylock() increment while we are
+ * protected by the spinlock in order to make atomic this atomic_inc() with the
+ * atomic_read() in wake_one_more(), otherwise we can race. -arca
+ */
+
+static __inline__ int waking_non_zero_trylock(struct semaphore *sem)
+{
+ unsigned long flags;
+ int ret = 1;
+
+ spin_lock_irqsave(&semaphore_wake_lock, flags);
+ if (sem->sleepers <= 0)
+ atomic_inc(&sem->count);
+ else {
+ sem->sleepers--;
+ ret = 0;
+ }
+ spin_unlock_irqrestore(&semaphore_wake_lock, flags);
+ return ret;
+}
+
+spinlock_t semaphore_wake_lock;
+
+/*
+ * Semaphores are implemented using a two-way counter:
+ * The "count" variable is decremented for each process
+ * that tries to sleep, while the "waking" variable is
+ * incremented when the "up()" code goes to wake up waiting
+ * processes.
+ *
+ * Notably, the inline "up()" and "down()" functions can
+ * efficiently test if they need to do any extra work (up
+ * needs to do something only if count was negative before
+ * the increment operation.
+ *
+ * waking_non_zero() (from asm/semaphore.h) must execute
+ * atomically.
+ *
+ * When __up() is called, the count was negative before
+ * incrementing it, and we need to wake up somebody.
+ *
+ * This routine adds one to the count of processes that need to
+ * wake up and exit. ALL waiting processes actually wake up but
+ * only the one that gets to the "waking" field first will gate
+ * through and acquire the semaphore. The others will go back
+ * to sleep.
+ *
+ * Note that these functions are only called when there is
+ * contention on the lock, and as such all this is the
+ * "non-critical" part of the whole semaphore business. The
+ * critical part is the inline stuff in <asm/semaphore.h>
+ * where we want to avoid any extra jumps and calls.
+ */
+
+void __up(struct semaphore *sem)
+{
+ wake_one_more(sem);
+ wake_up(&sem->wait);
+}
+
+/*
+ * Perform the "down" function. Return zero for semaphore acquired,
+ * return negative for signalled out of the function.
+ *
+ * If called from __down, the return is ignored and the wait loop is
+ * not interruptible. This means that a task waiting on a semaphore
+ * using "down()" cannot be killed until someone does an "up()" on
+ * the semaphore.
+ *
+ * If called from __down_interruptible, the return value gets checked
+ * upon return. If the return value is negative then the task continues
+ * with the negative value in the return register (it can be tested by
+ * the caller).
+ *
+ * Either form may be used in conjunction with "up()".
+ *
+ */
+
+#define DOWN_VAR \
+ struct task_struct *tsk = current; \
+ wait_queue_t wait; \
+ init_waitqueue_entry(&wait, tsk);
+
+#define DOWN_HEAD(task_state) \
+ \
+ \
+ tsk->state = (task_state); \
+ add_wait_queue(&sem->wait, &wait); \
+ \
+ /* \
+ * Ok, we're set up. sem->count is known to be less than zero \
+ * so we must wait. \
+ * \
+ * We can let go the lock for purposes of waiting. \
+ * We re-acquire it after awaking so as to protect \
+ * all semaphore operations. \
+ * \
+ * If "up()" is called before we call waking_non_zero() then \
+ * we will catch it right away. If it is called later then \
+ * we will have to go through a wakeup cycle to catch it. \
+ * \
+ * Multiple waiters contend for the semaphore lock to see \
+ * who gets to gate through and who has to wait some more. \
+ */ \
+ for (;;) {
+
+#define DOWN_TAIL(task_state) \
+ tsk->state = (task_state); \
+ } \
+ tsk->state = TASK_RUNNING; \
+ remove_wait_queue(&sem->wait, &wait);
+
+void __sched __down(struct semaphore * sem)
+{
+ DOWN_VAR
+ DOWN_HEAD(TASK_UNINTERRUPTIBLE)
+ if (waking_non_zero(sem))
+ break;
+ schedule();
+ DOWN_TAIL(TASK_UNINTERRUPTIBLE)
+}
+
+int __sched __down_interruptible(struct semaphore * sem)
+{
+ int ret = 0;
+ DOWN_VAR
+ DOWN_HEAD(TASK_INTERRUPTIBLE)
+
+ ret = waking_non_zero_interruptible(sem, tsk);
+ if (ret)
+ {
+ if (ret == 1)
+ /* ret != 0 only if we get interrupted -arca */
+ ret = 0;
+ break;
+ }
+ schedule();
+ DOWN_TAIL(TASK_INTERRUPTIBLE)
+ return ret;
+}
+
+int __down_trylock(struct semaphore * sem)
+{
+ return waking_non_zero_trylock(sem);
+}
diff --git a/arch/xtensa/kernel/setup.c b/arch/xtensa/kernel/setup.c
new file mode 100644
index 000000000000..1f5bf5d624e4
--- /dev/null
+++ b/arch/xtensa/kernel/setup.c
@@ -0,0 +1,520 @@
+/*
+ * arch/xtensa/setup.c
+ *
+ * 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 Linus Torvalds
+ * Copyright (C) 2001 - 2005 Tensilica Inc.
+ *
+ * Chris Zankel <chris@zankel.net>
+ * Joe Taylor <joe@tensilica.com, joetylr@yahoo.com>
+ * Kevin Chea
+ * Marc Gauthier<marc@tensilica.com> <marc@alumni.uwaterloo.ca>
+ */
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/proc_fs.h>
+#include <linux/tty.h>
+#include <linux/bootmem.h>
+#include <linux/kernel.h>
+
+#if defined(CONFIG_VGA_CONSOLE) || defined(CONFIG_DUMMY_CONSOLE)
+# include <linux/console.h>
+#endif
+
+#ifdef CONFIG_RTC
+# include <linux/timex.h>
+#endif
+
+#ifdef CONFIG_PROC_FS
+# include <linux/seq_file.h>
+#endif
+
+#include <asm/system.h>
+#include <asm/bootparam.h>
+#include <asm/pgtable.h>
+#include <asm/processor.h>
+#include <asm/timex.h>
+#include <asm/platform.h>
+#include <asm/page.h>
+#include <asm/setup.h>
+
+#include <xtensa/config/system.h>
+
+#if defined(CONFIG_VGA_CONSOLE) || defined(CONFIG_DUMMY_CONSOLE)
+struct screen_info screen_info = { 0, 24, 0, 0, 0, 80, 0, 0, 0, 24, 1, 16};
+#endif
+
+#ifdef CONFIG_BLK_DEV_FD
+extern struct fd_ops no_fd_ops;
+struct fd_ops *fd_ops;
+#endif
+
+#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE)
+extern struct ide_ops no_ide_ops;
+struct ide_ops *ide_ops;
+#endif
+
+extern struct rtc_ops no_rtc_ops;
+struct rtc_ops *rtc_ops;
+
+#ifdef CONFIG_PC_KEYB
+extern struct kbd_ops no_kbd_ops;
+struct kbd_ops *kbd_ops;
+#endif
+
+#ifdef CONFIG_BLK_DEV_INITRD
+extern void *initrd_start;
+extern void *initrd_end;
+extern void *__initrd_start;
+extern void *__initrd_end;
+int initrd_is_mapped = 0;
+extern int initrd_below_start_ok;
+#endif
+
+unsigned char aux_device_present;
+extern unsigned long loops_per_jiffy;
+
+/* Command line specified as configuration option. */
+
+static char command_line[COMMAND_LINE_SIZE];
+
+#ifdef CONFIG_CMDLINE_BOOL
+static char default_command_line[COMMAND_LINE_SIZE] __initdata = CONFIG_CMDLINE;
+#endif
+
+sysmem_info_t __initdata sysmem;
+
+#ifdef CONFIG_BLK_DEV_INITRD
+int initrd_is_mapped;
+#endif
+
+extern void init_mmu(void);
+
+/*
+ * Boot parameter parsing.
+ *
+ * The Xtensa port uses a list of variable-sized tags to pass data to
+ * the kernel. The first tag must be a BP_TAG_FIRST tag for the list
+ * to be recognised. The list is terminated with a zero-sized
+ * BP_TAG_LAST tag.
+ */
+
+typedef struct tagtable {
+ u32 tag;
+ int (*parse)(const bp_tag_t*);
+} tagtable_t;
+
+#define __tagtable(tag, fn) static tagtable_t __tagtable_##fn \
+ __attribute__((unused, __section__(".taglist"))) = { tag, fn }
+
+/* parse current tag */
+
+static int __init parse_tag_mem(const bp_tag_t *tag)
+{
+ meminfo_t *mi = (meminfo_t*)(tag->data);
+
+ if (mi->type != MEMORY_TYPE_CONVENTIONAL)
+ return -1;
+
+ if (sysmem.nr_banks >= SYSMEM_BANKS_MAX) {
+ printk(KERN_WARNING
+ "Ignoring memory bank 0x%08lx size %ldKB\n",
+ (unsigned long)mi->start,
+ (unsigned long)mi->end - (unsigned long)mi->start);
+ return -EINVAL;
+ }
+ sysmem.bank[sysmem.nr_banks].type = mi->type;
+ sysmem.bank[sysmem.nr_banks].start = PAGE_ALIGN(mi->start);
+ sysmem.bank[sysmem.nr_banks].end = mi->end & PAGE_SIZE;
+ sysmem.nr_banks++;
+
+ return 0;
+}
+
+__tagtable(BP_TAG_MEMORY, parse_tag_mem);
+
+#ifdef CONFIG_BLK_DEV_INITRD
+
+static int __init parse_tag_initrd(const bp_tag_t* tag)
+{
+ meminfo_t* mi;
+ mi = (meminfo_t*)(tag->data);
+ initrd_start = (void*)(mi->start);
+ initrd_end = (void*)(mi->end);
+
+ return 0;
+}
+
+__tagtable(BP_TAG_INITRD, parse_tag_initrd);
+
+#endif /* CONFIG_BLK_DEV_INITRD */
+
+static int __init parse_tag_cmdline(const bp_tag_t* tag)
+{
+ strncpy(command_line, (char*)(tag->data), COMMAND_LINE_SIZE);
+ command_line[COMMAND_LINE_SIZE - 1] = '\0';
+ return 0;
+}
+
+__tagtable(BP_TAG_COMMAND_LINE, parse_tag_cmdline);
+
+static int __init parse_bootparam(const bp_tag_t* tag)
+{
+ extern tagtable_t __tagtable_begin, __tagtable_end;
+ tagtable_t *t;
+
+ /* Boot parameters must start with a BP_TAG_FIRST tag. */
+
+ if (tag->id != BP_TAG_FIRST) {
+ printk(KERN_WARNING "Invalid boot parameters!\n");
+ return 0;
+ }
+
+ tag = (bp_tag_t*)((unsigned long)tag + sizeof(bp_tag_t) + tag->size);
+
+ /* Parse all tags. */
+
+ while (tag != NULL && tag->id != BP_TAG_LAST) {
+ for (t = &__tagtable_begin; t < &__tagtable_end; t++) {
+ if (tag->id == t->tag) {
+ t->parse(tag);
+ break;
+ }
+ }
+ if (t == &__tagtable_end)
+ printk(KERN_WARNING "Ignoring tag "
+ "0x%08x\n", tag->id);
+ tag = (bp_tag_t*)((unsigned long)(tag + 1) + tag->size);
+ }
+
+ return 0;
+}
+
+/*
+ * Initialize architecture. (Early stage)
+ */
+
+void __init init_arch(bp_tag_t *bp_start)
+{
+
+#ifdef CONFIG_BLK_DEV_INITRD
+ initrd_start = &__initrd_start;
+ initrd_end = &__initrd_end;
+#endif
+
+ sysmem.nr_banks = 0;
+
+#ifdef CONFIG_CMDLINE_BOOL
+ strcpy(command_line, default_command_line);
+#endif
+
+ /* Parse boot parameters */
+
+ if (bp_start)
+ parse_bootparam(bp_start);
+
+ if (sysmem.nr_banks == 0) {
+ sysmem.nr_banks = 1;
+ sysmem.bank[0].start = PLATFORM_DEFAULT_MEM_START;
+ sysmem.bank[0].end = PLATFORM_DEFAULT_MEM_START
+ + PLATFORM_DEFAULT_MEM_SIZE;
+ }
+
+ /* Early hook for platforms */
+
+ platform_init(bp_start);
+
+ /* Initialize MMU. */
+
+ init_mmu();
+}
+
+/*
+ * Initialize system. Setup memory and reserve regions.
+ */
+
+extern char _end;
+extern char _stext;
+extern char _WindowVectors_text_start;
+extern char _WindowVectors_text_end;
+extern char _DebugInterruptVector_literal_start;
+extern char _DebugInterruptVector_text_end;
+extern char _KernelExceptionVector_literal_start;
+extern char _KernelExceptionVector_text_end;
+extern char _UserExceptionVector_literal_start;
+extern char _UserExceptionVector_text_end;
+extern char _DoubleExceptionVector_literal_start;
+extern char _DoubleExceptionVector_text_end;
+
+void __init setup_arch(char **cmdline_p)
+{
+ extern int mem_reserve(unsigned long, unsigned long, int);
+ extern void bootmem_init(void);
+
+ memcpy(saved_command_line, command_line, COMMAND_LINE_SIZE);
+ saved_command_line[COMMAND_LINE_SIZE-1] = '\0';
+ *cmdline_p = command_line;
+
+ /* Reserve some memory regions */
+
+#ifdef CONFIG_BLK_DEV_INITRD
+ if (initrd_start < initrd_end) {
+ initrd_is_mapped = mem_reserve(__pa(initrd_start),
+ __pa(initrd_end), 0);
+ initrd_below_start_ok = 1;
+ } else {
+ initrd_start = 0;
+ }
+#endif
+
+ mem_reserve(__pa(&_stext),__pa(&_end), 1);
+
+ mem_reserve(__pa(&_WindowVectors_text_start),
+ __pa(&_WindowVectors_text_end), 0);
+
+ mem_reserve(__pa(&_DebugInterruptVector_literal_start),
+ __pa(&_DebugInterruptVector_text_end), 0);
+
+ mem_reserve(__pa(&_KernelExceptionVector_literal_start),
+ __pa(&_KernelExceptionVector_text_end), 0);
+
+ mem_reserve(__pa(&_UserExceptionVector_literal_start),
+ __pa(&_UserExceptionVector_text_end), 0);
+
+ mem_reserve(__pa(&_DoubleExceptionVector_literal_start),
+ __pa(&_DoubleExceptionVector_text_end), 0);
+
+ bootmem_init();
+
+ platform_setup(cmdline_p);
+
+
+ paging_init();
+
+#ifdef CONFIG_VT
+# if defined(CONFIG_VGA_CONSOLE)
+ conswitchp = &vga_con;
+# elif defined(CONFIG_DUMMY_CONSOLE)
+ conswitchp = &dummy_con;
+# endif
+#endif
+
+#if CONFIG_PCI
+ platform_pcibios_init();
+#endif
+}
+
+void machine_restart(char * cmd)
+{
+ platform_restart();
+}
+
+void machine_halt(void)
+{
+ platform_halt();
+ while (1);
+}
+
+void machine_power_off(void)
+{
+ platform_power_off();
+ while (1);
+}
+#ifdef CONFIG_PROC_FS
+
+/*
+ * Display some core information through /proc/cpuinfo.
+ */
+
+static int
+c_show(struct seq_file *f, void *slot)
+{
+ /* high-level stuff */
+ seq_printf(f,"processor\t: 0\n"
+ "vendor_id\t: Tensilica\n"
+ "model\t\t: Xtensa " XCHAL_HW_RELEASE_NAME "\n"
+ "core ID\t\t: " XCHAL_CORE_ID "\n"
+ "build ID\t: 0x%x\n"
+ "byte order\t: %s\n"
+ "cpu MHz\t\t: %lu.%02lu\n"
+ "bogomips\t: %lu.%02lu\n",
+ XCHAL_BUILD_UNIQUE_ID,
+ XCHAL_HAVE_BE ? "big" : "little",
+ CCOUNT_PER_JIFFY/(1000000/HZ),
+ (CCOUNT_PER_JIFFY/(10000/HZ)) % 100,
+ loops_per_jiffy/(500000/HZ),
+ (loops_per_jiffy/(5000/HZ)) % 100);
+
+ seq_printf(f,"flags\t\t: "
+#if XCHAL_HAVE_NMI
+ "nmi "
+#endif
+#if XCHAL_HAVE_DEBUG
+ "debug "
+# if XCHAL_HAVE_OCD
+ "ocd "
+# endif
+#endif
+#if XCHAL_HAVE_DENSITY
+ "density "
+#endif
+#if XCHAL_HAVE_BOOLEANS
+ "boolean "
+#endif
+#if XCHAL_HAVE_LOOPS
+ "loop "
+#endif
+#if XCHAL_HAVE_NSA
+ "nsa "
+#endif
+#if XCHAL_HAVE_MINMAX
+ "minmax "
+#endif
+#if XCHAL_HAVE_SEXT
+ "sext "
+#endif
+#if XCHAL_HAVE_CLAMPS
+ "clamps "
+#endif
+#if XCHAL_HAVE_MAC16
+ "mac16 "
+#endif
+#if XCHAL_HAVE_MUL16
+ "mul16 "
+#endif
+#if XCHAL_HAVE_MUL32
+ "mul32 "
+#endif
+#if XCHAL_HAVE_MUL32_HIGH
+ "mul32h "
+#endif
+#if XCHAL_HAVE_FP
+ "fpu "
+#endif
+ "\n");
+
+ /* Registers. */
+ seq_printf(f,"physical aregs\t: %d\n"
+ "misc regs\t: %d\n"
+ "ibreak\t\t: %d\n"
+ "dbreak\t\t: %d\n",
+ XCHAL_NUM_AREGS,
+ XCHAL_NUM_MISC_REGS,
+ XCHAL_NUM_IBREAK,
+ XCHAL_NUM_DBREAK);
+
+
+ /* Interrupt. */
+ seq_printf(f,"num ints\t: %d\n"
+ "ext ints\t: %d\n"
+ "int levels\t: %d\n"
+ "timers\t\t: %d\n"
+ "debug level\t: %d\n",
+ XCHAL_NUM_INTERRUPTS,
+ XCHAL_NUM_EXTINTERRUPTS,
+ XCHAL_NUM_INTLEVELS,
+ XCHAL_NUM_TIMERS,
+ XCHAL_DEBUGLEVEL);
+
+ /* Coprocessors */
+#if XCHAL_HAVE_CP
+ seq_printf(f, "coprocessors\t: %d\n", XCHAL_CP_NUM);
+#else
+ seq_printf(f, "coprocessors\t: none\n");
+#endif
+
+ /* {I,D}{RAM,ROM} and XLMI */
+ seq_printf(f,"inst ROMs\t: %d\n"
+ "inst RAMs\t: %d\n"
+ "data ROMs\t: %d\n"
+ "data RAMs\t: %d\n"
+ "XLMI ports\t: %d\n",
+ XCHAL_NUM_IROM,
+ XCHAL_NUM_IRAM,
+ XCHAL_NUM_DROM,
+ XCHAL_NUM_DRAM,
+ XCHAL_NUM_XLMI);
+
+ /* Cache */
+ seq_printf(f,"icache line size: %d\n"
+ "icache ways\t: %d\n"
+ "icache size\t: %d\n"
+ "icache flags\t: "
+#if XCHAL_ICACHE_LINE_LOCKABLE
+ "lock"
+#endif
+ "\n"
+ "dcache line size: %d\n"
+ "dcache ways\t: %d\n"
+ "dcache size\t: %d\n"
+ "dcache flags\t: "
+#if XCHAL_DCACHE_IS_WRITEBACK
+ "writeback"
+#endif
+#if XCHAL_DCACHE_LINE_LOCKABLE
+ "lock"
+#endif
+ "\n",
+ XCHAL_ICACHE_LINESIZE,
+ XCHAL_ICACHE_WAYS,
+ XCHAL_ICACHE_SIZE,
+ XCHAL_DCACHE_LINESIZE,
+ XCHAL_DCACHE_WAYS,
+ XCHAL_DCACHE_SIZE);
+
+ /* MMU */
+ seq_printf(f,"ASID bits\t: %d\n"
+ "ASID invalid\t: %d\n"
+ "ASID kernel\t: %d\n"
+ "rings\t\t: %d\n"
+ "itlb ways\t: %d\n"
+ "itlb AR ways\t: %d\n"
+ "dtlb ways\t: %d\n"
+ "dtlb AR ways\t: %d\n",
+ XCHAL_MMU_ASID_BITS,
+ XCHAL_MMU_ASID_INVALID,
+ XCHAL_MMU_ASID_KERNEL,
+ XCHAL_MMU_RINGS,
+ XCHAL_ITLB_WAYS,
+ XCHAL_ITLB_ARF_WAYS,
+ XCHAL_DTLB_WAYS,
+ XCHAL_DTLB_ARF_WAYS);
+
+ return 0;
+}
+
+/*
+ * We show only CPU #0 info.
+ */
+static void *
+c_start(struct seq_file *f, loff_t *pos)
+{
+ return (void *) ((*pos == 0) ? (void *)1 : NULL);
+}
+
+static void *
+c_next(struct seq_file *f, void *v, loff_t *pos)
+{
+ return NULL;
+}
+
+static void
+c_stop(struct seq_file *f, void *v)
+{
+}
+
+struct seq_operations cpuinfo_op =
+{
+ start: c_start,
+ next: c_next,
+ stop: c_stop,
+ show: c_show
+};
+
+#endif /* CONFIG_PROC_FS */
+
diff --git a/arch/xtensa/kernel/signal.c b/arch/xtensa/kernel/signal.c
new file mode 100644
index 000000000000..df6e1e17b096
--- /dev/null
+++ b/arch/xtensa/kernel/signal.c
@@ -0,0 +1,713 @@
+// TODO coprocessor stuff
+/*
+ * linux/arch/xtensa/kernel/signal.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ * 1997-11-28 Modified for POSIX.1b signals by Richard Henderson
+ *
+ * Joe Taylor <joe@tensilica.com>
+ * Chris Zankel <chris@zankel.net>
+ *
+ *
+ *
+ */
+
+#include <xtensa/config/core.h>
+#include <xtensa/hal.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/errno.h>
+#include <linux/wait.h>
+#include <linux/ptrace.h>
+#include <linux/unistd.h>
+#include <linux/stddef.h>
+#include <linux/personality.h>
+#include <asm/ucontext.h>
+#include <asm/uaccess.h>
+#include <asm/pgtable.h>
+#include <asm/cacheflush.h>
+
+#define DEBUG_SIG 0
+
+#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
+
+asmlinkage long sys_wait4(pid_t pid,unsigned int * stat_addr, int options,
+ struct rusage * ru);
+asmlinkage int do_signal(struct pt_regs *regs, sigset_t *oldset);
+
+extern struct task_struct *coproc_owners[];
+
+
+/*
+ * Atomically swap in the new signal mask, and wait for a signal.
+ */
+
+int sys_sigsuspend(struct pt_regs *regs)
+{
+ old_sigset_t mask = (old_sigset_t) regs->areg[3];
+ sigset_t saveset;
+
+ mask &= _BLOCKABLE;
+ spin_lock_irq(&current->sighand->siglock);
+ saveset = current->blocked;
+ siginitset(&current->blocked, mask);
+ recalc_sigpending();
+ spin_unlock_irq(&current->sighand->siglock);
+
+ regs->areg[2] = -EINTR;
+ while (1) {
+ current->state = TASK_INTERRUPTIBLE;
+ schedule();
+ if (do_signal(regs, &saveset))
+ return -EINTR;
+ }
+}
+
+asmlinkage int
+sys_rt_sigsuspend(struct pt_regs *regs)
+{
+ sigset_t *unewset = (sigset_t *) regs->areg[4];
+ size_t sigsetsize = (size_t) regs->areg[3];
+ sigset_t saveset, newset;
+ /* XXX: Don't preclude handling different sized sigset_t's. */
+ if (sigsetsize != sizeof(sigset_t))
+ return -EINVAL;
+
+ if (copy_from_user(&newset, unewset, sizeof(newset)))
+ return -EFAULT;
+ sigdelsetmask(&newset, ~_BLOCKABLE);
+ spin_lock_irq(&current->sighand->siglock);
+ saveset = current->blocked;
+ current->blocked = newset;
+ recalc_sigpending();
+ spin_unlock_irq(&current->sighand->siglock);
+
+ regs->areg[2] = -EINTR;
+ while (1) {
+ current->state = TASK_INTERRUPTIBLE;
+ schedule();
+ if (do_signal(regs, &saveset))
+ return -EINTR;
+ }
+}
+
+asmlinkage int
+sys_sigaction(int sig, const struct old_sigaction *act,
+ struct old_sigaction *oact)
+{
+ struct k_sigaction new_ka, old_ka;
+ int ret;
+
+ if (act) {
+ old_sigset_t mask;
+ if (verify_area(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 (verify_area(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(struct pt_regs *regs)
+{
+ const stack_t *uss = (stack_t *) regs->areg[4];
+ stack_t *uoss = (stack_t *) regs->areg[3];
+
+ if (regs->depc > 64)
+ panic ("Double exception sys_sigreturn\n");
+
+
+ return do_sigaltstack(uss, uoss, regs->areg[1]);
+}
+
+
+/*
+ * Do a signal return; undo the signal stack.
+ */
+
+struct sigframe
+{
+ struct sigcontext sc;
+ struct _cpstate cpstate;
+ unsigned long extramask[_NSIG_WORDS-1];
+ unsigned char retcode[6];
+ unsigned int reserved[4]; /* Reserved area for chaining */
+ unsigned int window[4]; /* Window of 4 registers for initial context */
+};
+
+struct rt_sigframe
+{
+ struct siginfo info;
+ struct ucontext uc;
+ struct _cpstate cpstate;
+ unsigned char retcode[6];
+ unsigned int reserved[4]; /* Reserved area for chaining */
+ unsigned int window[4]; /* Window of 4 registers for initial context */
+};
+
+extern void release_all_cp (struct task_struct *);
+
+
+// FIXME restore_cpextra
+static inline int
+restore_cpextra (struct _cpstate *buf)
+{
+#if 0
+ /* The signal handler may have used coprocessors in which
+ * case they are still enabled. We disable them to force a
+ * reloading of the original task's CP state by the lazy
+ * context-switching mechanisms of CP exception handling.
+ * Also, we essentially discard any coprocessor state that the
+ * signal handler created. */
+
+ struct task_struct *tsk = current;
+ release_all_cp(tsk);
+ return __copy_from_user(tsk->thread.cpextra, buf, TOTAL_CPEXTRA_SIZE);
+#endif
+ return 0;
+}
+
+/* Note: We don't copy double exception 'tregs', we have to finish double exc. first before we return to signal handler! This dbl.exc.handler might cause another double exception, but I think we are fine as the situation is the same as if we had returned to the signal handerl and got an interrupt immediately...
+ */
+
+
+static int
+restore_sigcontext(struct pt_regs *regs, struct sigcontext *sc)
+{
+ struct thread_struct *thread;
+ unsigned int err = 0;
+ unsigned long ps;
+ struct _cpstate *buf;
+
+#define COPY(x) err |= __get_user(regs->x, &sc->sc_##x)
+ COPY(pc);
+ COPY(depc);
+ COPY(wmask);
+ COPY(lbeg);
+ COPY(lend);
+ COPY(lcount);
+ COPY(sar);
+ COPY(windowbase);
+ COPY(windowstart);
+#undef COPY
+
+ /* For PS, restore only PS.CALLINC.
+ * Assume that all other bits are either the same as for the signal
+ * handler, or the user mode value doesn't matter (e.g. PS.OWB).
+ */
+ err |= __get_user(ps, &sc->sc_ps);
+ regs->ps = (regs->ps & ~XCHAL_PS_CALLINC_MASK)
+ | (ps & XCHAL_PS_CALLINC_MASK);
+
+ /* Additional corruption checks */
+
+ if ((regs->windowbase >= (XCHAL_NUM_AREGS/4))
+ || ((regs->windowstart & ~((1<<(XCHAL_NUM_AREGS/4)) - 1)) != 0) )
+ err = 1;
+ if ((regs->lcount > 0)
+ && ((regs->lbeg > TASK_SIZE) || (regs->lend > TASK_SIZE)) )
+ err = 1;
+
+ /* Restore extended register state.
+ * See struct thread_struct in processor.h.
+ */
+ thread = &current->thread;
+
+ err |= __copy_from_user (regs->areg, sc->sc_areg, XCHAL_NUM_AREGS*4);
+ err |= __get_user(buf, &sc->sc_cpstate);
+ if (buf) {
+ if (verify_area(VERIFY_READ, buf, sizeof(*buf)))
+ goto badframe;
+ err |= restore_cpextra(buf);
+ }
+
+ regs->syscall = -1; /* disable syscall checks */
+ return err;
+
+badframe:
+ return 1;
+}
+
+static inline void
+flush_my_cpstate(struct task_struct *tsk)
+{
+ unsigned long flags;
+ local_irq_save(flags);
+
+#if 0 // FIXME
+ for (i = 0; i < XCHAL_CP_NUM; i++) {
+ if (tsk == coproc_owners[i]) {
+ xthal_validate_cp(i);
+ xthal_save_cpregs(tsk->thread.cpregs_ptr[i], i);
+
+ /* Invalidate and "disown" the cp to allow
+ * callers the chance to reset cp state in the
+ * task_struct. */
+
+ xthal_invalidate_cp(i);
+ coproc_owners[i] = 0;
+ }
+ }
+#endif
+ local_irq_restore(flags);
+}
+
+/* Return codes:
+ 0: nothing saved
+ 1: stuff to save, successful
+ -1: stuff to save, error happened
+*/
+static int
+save_cpextra (struct _cpstate *buf)
+{
+#if (XCHAL_EXTRA_SA_SIZE == 0) && (XCHAL_CP_NUM == 0)
+ return 0;
+#else
+
+ /* FIXME: If a task has never used a coprocessor, there is
+ * no need to save and restore anything. Tracking this
+ * information would allow us to optimize this section.
+ * Perhaps we can use current->used_math or (current->flags &
+ * PF_USEDFPU) or define a new field in the thread
+ * structure. */
+
+ /* We flush any live, task-owned cp state to the task_struct,
+ * then copy it all to the sigframe. Then we clear all
+ * cp/extra state in the task_struct, effectively
+ * clearing/resetting all cp/extra state for the signal
+ * handler (cp-exception handling will load these new values
+ * into the cp/extra registers.) This step is important for
+ * things like a floating-point cp, where the OS must reset
+ * the FCR to the default rounding mode. */
+
+ int err = 0;
+ struct task_struct *tsk = current;
+
+ flush_my_cpstate(tsk);
+ /* Note that we just copy everything: 'extra' and 'cp' state together.*/
+ err |= __copy_to_user(buf, tsk->thread.cp_save, XTENSA_CP_EXTRA_SIZE);
+ memset(tsk->thread.cp_save, 0, XTENSA_CP_EXTRA_SIZE);
+
+#if (XTENSA_CP_EXTRA_SIZE == 0)
+#error Sanity check on memset above, cpextra_size should not be zero.
+#endif
+
+ return err ? -1 : 1;
+#endif
+}
+
+static int
+setup_sigcontext(struct sigcontext *sc, struct _cpstate *cpstate,
+ struct pt_regs *regs, unsigned long mask)
+{
+ struct thread_struct *thread;
+ int err = 0;
+
+//printk("setup_sigcontext\n");
+#define COPY(x) err |= __put_user(regs->x, &sc->sc_##x)
+ COPY(pc);
+ COPY(ps);
+ COPY(depc);
+ COPY(wmask);
+ COPY(lbeg);
+ COPY(lend);
+ COPY(lcount);
+ COPY(sar);
+ COPY(windowbase);
+ COPY(windowstart);
+#undef COPY
+
+ /* Save extended register state.
+ * See struct thread_struct in processor.h.
+ */
+ thread = &current->thread;
+ err |= __copy_to_user (sc->sc_areg, regs->areg, XCHAL_NUM_AREGS * 4);
+ err |= save_cpextra(cpstate);
+ err |= __put_user(err ? NULL : cpstate, &sc->sc_cpstate);
+ /* non-iBCS2 extensions.. */
+ err |= __put_user(mask, &sc->oldmask);
+
+ return err;
+}
+
+asmlinkage int sys_sigreturn(struct pt_regs *regs)
+{
+ struct sigframe *frame = (struct sigframe *)regs->areg[1];
+ sigset_t set;
+ if (regs->depc > 64)
+ panic ("Double exception sys_sigreturn\n");
+
+ if (verify_area(VERIFY_READ, frame, sizeof(*frame)))
+ goto badframe;
+
+ if (__get_user(set.sig[0], &frame->sc.oldmask)
+ || (_NSIG_WORDS > 1
+ && __copy_from_user(&set.sig[1], &frame->extramask,
+ sizeof(frame->extramask))))
+ goto badframe;
+
+ sigdelsetmask(&set, ~_BLOCKABLE);
+
+ spin_lock_irq(&current->sighand->siglock);
+ current->blocked = set;
+ recalc_sigpending();
+ spin_unlock_irq(&current->sighand->siglock);
+
+ if (restore_sigcontext(regs, &frame->sc))
+ goto badframe;
+ return regs->areg[2];
+
+badframe:
+ force_sig(SIGSEGV, current);
+ return 0;
+}
+
+asmlinkage int sys_rt_sigreturn(struct pt_regs *regs)
+{
+ struct rt_sigframe *frame = (struct rt_sigframe *)regs->areg[1];
+ sigset_t set;
+ stack_t st;
+ int ret;
+ if (regs->depc > 64)
+ {
+ printk("!!!!!!! DEPC !!!!!!!\n");
+ return 0;
+ }
+
+ if (verify_area(VERIFY_READ, frame, sizeof(*frame)))
+ goto badframe;
+
+ if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set)))
+ goto badframe;
+
+ sigdelsetmask(&set, ~_BLOCKABLE);
+ spin_lock_irq(&current->sighand->siglock);
+ current->blocked = set;
+ recalc_sigpending();
+ spin_unlock_irq(&current->sighand->siglock);
+
+ if (restore_sigcontext(regs, &frame->uc.uc_mcontext))
+ goto badframe;
+ ret = regs->areg[2];
+
+ if (__copy_from_user(&st, &frame->uc.uc_stack, sizeof(st)))
+ goto badframe;
+ /* It is more difficult to avoid calling this function than to
+ call it and ignore errors. */
+ do_sigaltstack(&st, NULL, regs->areg[1]);
+
+ return ret;
+
+badframe:
+ force_sig(SIGSEGV, current);
+ return 0;
+}
+
+/*
+ * Set up a signal frame.
+ */
+
+/*
+ * Determine which stack to use..
+ */
+static inline void *
+get_sigframe(struct k_sigaction *ka, unsigned long sp, size_t frame_size)
+{
+ if ((ka->sa.sa_flags & SA_ONSTACK) != 0 && ! on_sig_stack(sp))
+ sp = current->sas_ss_sp + current->sas_ss_size;
+
+ return (void *)((sp - frame_size) & -16ul);
+}
+
+#define USE_SIGRETURN 0
+#define USE_RT_SIGRETURN 1
+
+static int
+gen_return_code(unsigned char *codemem, unsigned int use_rt_sigreturn)
+{
+ unsigned int retcall;
+ int err = 0;
+
+#if 0
+ /* Ignoring SA_RESTORER for now; it's supposed to be obsolete,
+ * and the xtensa glibc doesn't use it.
+ */
+ if (ka->sa.sa_flags & SA_RESTORER) {
+ regs->pr = (unsigned long) ka->sa.sa_restorer;
+ } else
+#endif /* 0 */
+ {
+
+#if (__NR_sigreturn > 255) || (__NR_rt_sigreturn > 255)
+
+/* The 12-bit immediate is really split up within the 24-bit MOVI
+ * instruction. As long as the above system call numbers fit within
+ * 8-bits, the following code works fine. See the Xtensa ISA for
+ * details.
+ */
+
+#error Generating the MOVI instruction below breaks!
+#endif
+
+ retcall = use_rt_sigreturn ? __NR_rt_sigreturn : __NR_sigreturn;
+
+#ifdef __XTENSA_EB__ /* Big Endian version */
+ /* Generate instruction: MOVI a2, retcall */
+ err |= __put_user(0x22, &codemem[0]);
+ err |= __put_user(0x0a, &codemem[1]);
+ err |= __put_user(retcall, &codemem[2]);
+ /* Generate instruction: SYSCALL */
+ err |= __put_user(0x00, &codemem[3]);
+ err |= __put_user(0x05, &codemem[4]);
+ err |= __put_user(0x00, &codemem[5]);
+
+#elif defined __XTENSA_EL__ /* Little Endian version */
+ /* Generate instruction: MOVI a2, retcall */
+ err |= __put_user(0x22, &codemem[0]);
+ err |= __put_user(0xa0, &codemem[1]);
+ err |= __put_user(retcall, &codemem[2]);
+ /* Generate instruction: SYSCALL */
+ err |= __put_user(0x00, &codemem[3]);
+ err |= __put_user(0x50, &codemem[4]);
+ err |= __put_user(0x00, &codemem[5]);
+#else
+#error Must use compiler for Xtensa processors.
+#endif
+ }
+
+ /* Flush generated code out of the data cache */
+
+ if (err == 0)
+ __flush_invalidate_cache_range((unsigned long)codemem, 6UL);
+
+ return err;
+}
+
+static void
+set_thread_state(struct pt_regs *regs, void *stack, unsigned char *retaddr,
+ void *handler, unsigned long arg1, void *arg2, void *arg3)
+{
+ /* Set up registers for signal handler */
+ start_thread(regs, (unsigned long) handler, (unsigned long) stack);
+
+ /* Set up a stack frame for a call4
+ * Note: PS.CALLINC is set to one by start_thread
+ */
+ regs->areg[4] = (((unsigned long) retaddr) & 0x3fffffff) | 0x40000000;
+ regs->areg[6] = arg1;
+ regs->areg[7] = (unsigned long) arg2;
+ regs->areg[8] = (unsigned long) arg3;
+}
+
+static void setup_frame(int sig, struct k_sigaction *ka,
+ sigset_t *set, struct pt_regs *regs)
+{
+ struct sigframe *frame;
+ int err = 0;
+ int signal;
+
+ frame = get_sigframe(ka, regs->areg[1], sizeof(*frame));
+ if (regs->depc > 64)
+ {
+ printk("!!!!!!! DEPC !!!!!!!\n");
+ return;
+ }
+
+
+ if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
+ goto give_sigsegv;
+
+ signal = current_thread_info()->exec_domain
+ && current_thread_info()->exec_domain->signal_invmap
+ && sig < 32
+ ? current_thread_info()->exec_domain->signal_invmap[sig]
+ : sig;
+
+ err |= setup_sigcontext(&frame->sc, &frame->cpstate, regs, set->sig[0]);
+
+ if (_NSIG_WORDS > 1) {
+ err |= __copy_to_user(frame->extramask, &set->sig[1],
+ sizeof(frame->extramask));
+ }
+
+ /* Create sys_sigreturn syscall in stack frame */
+ err |= gen_return_code(frame->retcode, USE_SIGRETURN);
+
+ if (err)
+ goto give_sigsegv;
+
+ /* Create signal handler execution context.
+ * Return context not modified until this point.
+ */
+ set_thread_state(regs, frame, frame->retcode,
+ ka->sa.sa_handler, signal, &frame->sc, NULL);
+
+ /* Set access mode to USER_DS. Nomenclature is outdated, but
+ * functionality is used in uaccess.h
+ */
+ set_fs(USER_DS);
+
+
+#if DEBUG_SIG
+ printk("SIG deliver (%s:%d): signal=%d sp=%p pc=%08x\n",
+ current->comm, current->pid, signal, frame, regs->pc);
+#endif
+
+ return;
+
+give_sigsegv:
+ if (sig == SIGSEGV)
+ ka->sa.sa_handler = SIG_DFL;
+ force_sig(SIGSEGV, current);
+}
+
+static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
+ sigset_t *set, struct pt_regs *regs)
+{
+ struct rt_sigframe *frame;
+ int err = 0;
+ int signal;
+
+ frame = get_sigframe(ka, regs->areg[1], sizeof(*frame));
+ if (regs->depc > 64)
+ panic ("Double exception sys_sigreturn\n");
+
+ if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
+ goto give_sigsegv;
+
+ signal = current_thread_info()->exec_domain
+ && current_thread_info()->exec_domain->signal_invmap
+ && sig < 32
+ ? current_thread_info()->exec_domain->signal_invmap[sig]
+ : sig;
+
+ err |= copy_siginfo_to_user(&frame->info, info);
+
+ /* Create the ucontext. */
+ err |= __put_user(0, &frame->uc.uc_flags);
+ err |= __put_user(0, &frame->uc.uc_link);
+ err |= __put_user((void *)current->sas_ss_sp,
+ &frame->uc.uc_stack.ss_sp);
+ err |= __put_user(sas_ss_flags(regs->areg[1]),
+ &frame->uc.uc_stack.ss_flags);
+ err |= __put_user(current->sas_ss_size, &frame->uc.uc_stack.ss_size);
+ err |= setup_sigcontext(&frame->uc.uc_mcontext, &frame->cpstate,
+ regs, set->sig[0]);
+ err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set));
+
+ /* Create sys_rt_sigreturn syscall in stack frame */
+ err |= gen_return_code(frame->retcode, USE_RT_SIGRETURN);
+
+ if (err)
+ goto give_sigsegv;
+
+ /* Create signal handler execution context.
+ * Return context not modified until this point.
+ */
+ set_thread_state(regs, frame, frame->retcode,
+ ka->sa.sa_handler, signal, &frame->info, &frame->uc);
+
+ /* Set access mode to USER_DS. Nomenclature is outdated, but
+ * functionality is used in uaccess.h
+ */
+ set_fs(USER_DS);
+
+#if DEBUG_SIG
+ printk("SIG rt deliver (%s:%d): signal=%d sp=%p pc=%08x\n",
+ current->comm, current->pid, signal, frame, regs->pc);
+#endif
+
+ return;
+
+give_sigsegv:
+ if (sig == SIGSEGV)
+ ka->sa.sa_handler = SIG_DFL;
+ force_sig(SIGSEGV, current);
+}
+
+
+
+/*
+ * Note that 'init' is a special process: it doesn't get signals it doesn't
+ * want to handle. Thus you cannot kill init even with a SIGKILL even by
+ * mistake.
+ *
+ * Note that we go through the signals twice: once to check the signals that
+ * the kernel can handle, and then we build all the user-level signal handling
+ * stack-frames in one go after that.
+ */
+int do_signal(struct pt_regs *regs, sigset_t *oldset)
+{
+ siginfo_t info;
+ int signr;
+ struct k_sigaction ka;
+
+ if (!oldset)
+ oldset = &current->blocked;
+
+ signr = get_signal_to_deliver(&info, &ka, regs, NULL);
+
+ /* Are we from a system call? */
+ if (regs->syscall >= 0) {
+ /* If so, check system call restarting.. */
+ switch (regs->areg[2]) {
+ case ERESTARTNOHAND:
+ case ERESTART_RESTARTBLOCK:
+ regs->areg[2] = -EINTR;
+ break;
+
+ case ERESTARTSYS:
+ if (!(ka.sa.sa_flags & SA_RESTART)) {
+ regs->areg[2] = -EINTR;
+ break;
+ }
+ /* fallthrough */
+ case ERESTARTNOINTR:
+ regs->areg[2] = regs->syscall;
+ regs->pc -= 3;
+ }
+ }
+
+ if (signr == 0)
+ return 0; /* no signals delivered */
+
+ /* Whee! Actually deliver the signal. */
+
+ /* Set up the stack frame */
+ if (ka.sa.sa_flags & SA_SIGINFO)
+ setup_rt_frame(signr, &ka, &info, oldset, regs);
+ else
+ setup_frame(signr, &ka, oldset, regs);
+
+ if (ka.sa.sa_flags & SA_ONESHOT)
+ ka.sa.sa_handler = SIG_DFL;
+
+ if (!(ka.sa.sa_flags & SA_NODEFER)) {
+ spin_lock_irq(&current->sighand->siglock);
+ sigorsets(&current->blocked, &current->blocked, &ka.sa.sa_mask);
+ sigaddset(&current->blocked, signr);
+ recalc_sigpending();
+ spin_unlock_irq(&current->sighand->siglock);
+ }
+ return 1;
+}
diff --git a/arch/xtensa/kernel/syscalls.c b/arch/xtensa/kernel/syscalls.c
new file mode 100644
index 000000000000..abc8ed6c7026
--- /dev/null
+++ b/arch/xtensa/kernel/syscalls.c
@@ -0,0 +1,418 @@
+/*
+ * arch/xtensa/kernel/syscall.c
+ *
+ * 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) 2001 - 2005 Tensilica Inc.
+ * Copyright (C) 2000 Silicon Graphics, Inc.
+ * Copyright (C) 1995 - 2000 by Ralf Baechle
+ *
+ * Joe Taylor <joe@tensilica.com, joetylr@yahoo.com>
+ * Marc Gauthier <marc@tensilica.com, marc@alumni.uwaterloo.ca>
+ * Chris Zankel <chris@zankel.net>
+ * Kevin Chea
+ *
+ */
+
+#define DEBUG 0
+
+#include <linux/config.h>
+#include <linux/linkage.h>
+#include <linux/mm.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+#include <linux/mman.h>
+#include <linux/sched.h>
+#include <linux/file.h>
+#include <linux/slab.h>
+#include <linux/utsname.h>
+#include <linux/unistd.h>
+#include <linux/stringify.h>
+#include <linux/syscalls.h>
+#include <linux/sem.h>
+#include <linux/msg.h>
+#include <linux/shm.h>
+#include <linux/errno.h>
+#include <asm/ptrace.h>
+#include <asm/signal.h>
+#include <asm/uaccess.h>
+#include <asm/hardirq.h>
+#include <asm/mman.h>
+#include <asm/shmparam.h>
+#include <asm/page.h>
+#include <asm/ipc.h>
+
+extern void do_syscall_trace(void);
+typedef int (*syscall_t)(void *a0,...);
+extern int (*do_syscalls)(struct pt_regs *regs, syscall_t fun,
+ int narg);
+extern syscall_t sys_call_table[];
+extern unsigned char sys_narg_table[];
+
+/*
+ * sys_pipe() is the normal C calling standard for creating a pipe. It's not
+ * the way unix traditional does this, though.
+ */
+
+int sys_pipe(int __user *userfds)
+{
+ int fd[2];
+ int error;
+
+ error = do_pipe(fd);
+ if (!error) {
+ if (copy_to_user(userfds, fd, 2 * sizeof(int)))
+ error = -EFAULT;
+ }
+ return error;
+}
+
+/*
+ * Common code for old and new mmaps.
+ */
+
+static inline long do_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;
+ }
+
+ 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;
+}
+
+unsigned long old_mmap(unsigned long addr, size_t len, int prot,
+ int flags, int fd, off_t offset)
+{
+ return do_mmap2(addr, len, prot, flags, fd, offset >> PAGE_SHIFT);
+}
+
+long sys_mmap2(unsigned long addr, unsigned long len, unsigned long prot,
+ unsigned long flags, unsigned long fd, unsigned long pgoff)
+{
+ return do_mmap2(addr, len, prot, flags, fd, pgoff);
+}
+
+int sys_fork(struct pt_regs *regs)
+{
+ return do_fork(SIGCHLD, regs->areg[1], regs, 0, NULL, NULL);
+}
+
+int sys_vfork(struct pt_regs *regs)
+{
+ return do_fork(CLONE_VFORK|CLONE_VM|SIGCHLD, regs->areg[1],
+ regs, 0, NULL, NULL);
+}
+
+int sys_clone(struct pt_regs *regs)
+{
+ unsigned long clone_flags;
+ unsigned long newsp;
+ int __user *parent_tidptr, *child_tidptr;
+ clone_flags = regs->areg[4];
+ newsp = regs->areg[3];
+ parent_tidptr = (int __user *)regs->areg[5];
+ child_tidptr = (int __user *)regs->areg[6];
+ if (!newsp)
+ newsp = regs->areg[1];
+ return do_fork(clone_flags,newsp,regs,0,parent_tidptr,child_tidptr);
+}
+
+/*
+ * sys_execve() executes a new program.
+ */
+
+int sys_execve(struct pt_regs *regs)
+{
+ int error;
+ char * filename;
+
+ filename = getname((char *) (long)regs->areg[5]);
+ error = PTR_ERR(filename);
+ if (IS_ERR(filename))
+ goto out;
+ error = do_execve(filename, (char **) (long)regs->areg[3],
+ (char **) (long)regs->areg[4], regs);
+ putname(filename);
+
+out:
+ return error;
+}
+
+int sys_uname(struct old_utsname * name)
+{
+ if (name && !copy_to_user(name, &system_utsname, sizeof (*name)))
+ return 0;
+ return -EFAULT;
+}
+
+int sys_olduname(struct oldold_utsname * name)
+{
+ int error;
+
+ if (!name)
+ return -EFAULT;
+ if (!access_ok(VERIFY_WRITE,name,sizeof(struct oldold_utsname)))
+ return -EFAULT;
+
+ error = __copy_to_user(&name->sysname,&system_utsname.sysname,__OLD_UTS_LEN);
+ error -= __put_user(0,name->sysname+__OLD_UTS_LEN);
+ error -= __copy_to_user(&name->nodename,&system_utsname.nodename,__OLD_UTS_LEN);
+ error -= __put_user(0,name->nodename+__OLD_UTS_LEN);
+ error -= __copy_to_user(&name->release,&system_utsname.release,__OLD_UTS_LEN);
+ error -= __put_user(0,name->release+__OLD_UTS_LEN);
+ error -= __copy_to_user(&name->version,&system_utsname.version,__OLD_UTS_LEN);
+ error -= __put_user(0,name->version+__OLD_UTS_LEN);
+ error -= __copy_to_user(&name->machine,&system_utsname.machine,__OLD_UTS_LEN);
+ error -= __put_user(0,name->machine+__OLD_UTS_LEN);
+
+ return error ? -EFAULT : 0;
+}
+
+
+/*
+ * Build the string table for the builtin "poor man's strace".
+ */
+
+#if DEBUG
+#define SYSCALL(fun, narg) #fun,
+static char *sfnames[] = {
+#include "syscalls.h"
+};
+#undef SYS
+#endif
+
+void system_call (struct pt_regs *regs)
+{
+ syscall_t syscall;
+ unsigned long parm0, parm1, parm2, parm3, parm4, parm5;
+ int nargs, res;
+ unsigned int syscallnr;
+ int ps;
+
+#if DEBUG
+ int i;
+ unsigned long parms[6];
+ char *sysname;
+#endif
+
+ regs->syscall = regs->areg[2];
+
+ do_syscall_trace();
+
+ /* Have to load after syscall_trace because strace
+ * sometimes changes regs->syscall.
+ */
+ syscallnr = regs->syscall;
+
+ parm0 = parm1 = parm2 = parm3 = parm4 = parm5 = 0;
+
+ /* Restore interrupt level to syscall invoker's.
+ * If this were in assembly, we wouldn't disable
+ * interrupts in the first place:
+ */
+ local_save_flags (ps);
+ local_irq_restore((ps & ~XCHAL_PS_INTLEVEL_MASK) |
+ (regs->ps & XCHAL_PS_INTLEVEL_MASK) );
+
+ if (syscallnr > __NR_Linux_syscalls) {
+ regs->areg[2] = -ENOSYS;
+ return;
+ }
+
+ syscall = sys_call_table[syscallnr];
+ nargs = sys_narg_table[syscallnr];
+
+ if (syscall == NULL) {
+ regs->areg[2] = -ENOSYS;
+ return;
+ }
+
+ /* There shouldn't be more than six arguments in the table! */
+
+ if (nargs > 6)
+ panic("Internal error - too many syscall arguments (%d)!\n",
+ nargs);
+
+ /* Linux takes system-call arguments in registers. The ABI
+ * and Xtensa software conventions require the system-call
+ * number in a2. If an argument exists in a2, we move it to
+ * the next available register. Note that for improved
+ * efficiency, we do NOT shift all parameters down one
+ * register to maintain the original order.
+ *
+ * At best case (zero arguments), we just write the syscall
+ * number to a2. At worst case (1 to 6 arguments), we move
+ * the argument in a2 to the next available register, then
+ * write the syscall number to a2.
+ *
+ * For clarity, the following truth table enumerates all
+ * possibilities.
+ *
+ * arguments syscall number arg0, arg1, arg2, arg3, arg4, arg5
+ * --------- -------------- ----------------------------------
+ * 0 a2
+ * 1 a2 a3
+ * 2 a2 a4, a3
+ * 3 a2 a5, a3, a4
+ * 4 a2 a6, a3, a4, a5
+ * 5 a2 a7, a3, a4, a5, a6
+ * 6 a2 a8, a3, a4, a5, a6, a7
+ */
+ if (nargs) {
+ parm0 = regs->areg[nargs+2];
+ parm1 = regs->areg[3];
+ parm2 = regs->areg[4];
+ parm3 = regs->areg[5];
+ parm4 = regs->areg[6];
+ parm5 = regs->areg[7];
+ } else /* nargs == 0 */
+ parm0 = (unsigned long) regs;
+
+#if DEBUG
+ parms[0] = parm0;
+ parms[1] = parm1;
+ parms[2] = parm2;
+ parms[3] = parm3;
+ parms[4] = parm4;
+ parms[5] = parm5;
+
+ sysname = sfnames[syscallnr];
+ if (strncmp(sysname, "sys_", 4) == 0)
+ sysname = sysname + 4;
+
+ printk("\017SYSCALL:I:%x:%d:%s %s(", regs->pc, current->pid,
+ current->comm, sysname);
+ for (i = 0; i < nargs; i++)
+ printk((i>0) ? ", %#lx" : "%#lx", parms[i]);
+ printk(")\n");
+#endif
+
+ res = syscall((void *)parm0, parm1, parm2, parm3, parm4, parm5);
+
+#if DEBUG
+ printk("\017SYSCALL:O:%d:%s %s(",current->pid, current->comm, sysname);
+ for (i = 0; i < nargs; i++)
+ printk((i>0) ? ", %#lx" : "%#lx", parms[i]);
+ if (res < 4096)
+ printk(") = %d\n", res);
+ else
+ printk(") = %#x\n", res);
+#endif /* DEBUG */
+
+ regs->areg[2] = res;
+ do_syscall_trace();
+}
+
+/*
+ * sys_ipc() is the de-multiplexer for the SysV IPC calls..
+ *
+ * This is really horribly ugly.
+ */
+
+int sys_ipc (uint call, int first, int second,
+ int third, void __user *ptr, long fifth)
+{
+ int version, ret;
+
+ version = call >> 16; /* hack for backward compatibility */
+ call &= 0xffff;
+ ret = -ENOSYS;
+
+ switch (call) {
+ case SEMOP:
+ ret = sys_semtimedop (first, (struct sembuf __user *)ptr,
+ second, NULL);
+ break;
+
+ case SEMTIMEDOP:
+ ret = sys_semtimedop (first, (struct sembuf __user *)ptr,
+ second, (const struct timespec *) fifth);
+ break;
+
+ case SEMGET:
+ ret = sys_semget (first, second, third);
+ break;
+
+ case SEMCTL: {
+ union semun fourth;
+
+ if (ptr && !get_user(fourth.__pad, (void *__user *) ptr))
+ ret = sys_semctl (first, second, third, fourth);
+ break;
+ }
+
+ case MSGSND:
+ ret = sys_msgsnd (first, (struct msgbuf __user*) ptr,
+ second, third);
+ break;
+
+ case MSGRCV:
+ switch (version) {
+ case 0: {
+ struct ipc_kludge tmp;
+
+ if (ptr && !copy_from_user(&tmp,
+ (struct ipc_kludge *) ptr,
+ sizeof (tmp)))
+ ret = sys_msgrcv (first, tmp.msgp, second,
+ tmp.msgtyp, third);
+ break;
+ }
+
+ default:
+ ret = sys_msgrcv (first, (struct msgbuf __user *) ptr,
+ second, 0, third);
+ break;
+ }
+ break;
+
+ case MSGGET:
+ ret = sys_msgget ((key_t) first, second);
+ break;
+
+ case MSGCTL:
+ ret = sys_msgctl (first, second, (struct msqid_ds __user*) ptr);
+ break;
+
+ case SHMAT: {
+ ulong raddr;
+ ret = do_shmat (first, (char __user *) ptr, second, &raddr);
+
+ if (!ret)
+ ret = put_user (raddr, (ulong __user *) third);
+
+ break;
+ }
+
+ case SHMDT:
+ ret = sys_shmdt ((char __user *)ptr);
+ break;
+
+ case SHMGET:
+ ret = sys_shmget (first, second, third);
+ break;
+
+ case SHMCTL:
+ ret = sys_shmctl (first, second, (struct shmid_ds __user*) ptr);
+ break;
+ }
+ return ret;
+}
+
diff --git a/arch/xtensa/kernel/syscalls.h b/arch/xtensa/kernel/syscalls.h
new file mode 100644
index 000000000000..5b3f75f50feb
--- /dev/null
+++ b/arch/xtensa/kernel/syscalls.h
@@ -0,0 +1,248 @@
+/*
+ * arch/xtensa/kernel/syscalls.h
+ *
+ * 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, 1996, 1997, 1998 by Ralf Baechle
+ * Copyright (C) 2001 - 2005 Tensilica Inc.
+ *
+ * Changes by Joe Taylor <joe@tensilica.com>
+ */
+
+/*
+ * This file is being included twice - once to build a list of all
+ * syscalls and once to build a table of how many arguments each syscall
+ * accepts. Syscalls that receive a pointer to the saved registers are
+ * marked as having zero arguments.
+ *
+ * The binary compatibility calls are in a separate list.
+ *
+ * Entry '0' used to be system_call. It's removed to disable indirect
+ * system calls for now so user tasks can't recurse. See mips'
+ * sys_syscall for a comparable example.
+ */
+
+SYSCALL(0, 0) /* 00 */
+
+SYSCALL(sys_exit, 1)
+SYSCALL(sys_fork, 0)
+SYSCALL(sys_read, 3)
+SYSCALL(sys_write, 3)
+SYSCALL(sys_open, 3) /* 05 */
+SYSCALL(sys_close, 1)
+SYSCALL(sys_waitpid, 3)
+SYSCALL(sys_creat, 2)
+SYSCALL(sys_link, 2)
+SYSCALL(sys_unlink, 1) /* 10 */
+SYSCALL(sys_execve, 0)
+SYSCALL(sys_chdir, 1)
+SYSCALL(sys_time, 1)
+SYSCALL(sys_mknod, 3)
+SYSCALL(sys_chmod, 2) /* 15 */
+SYSCALL(sys_lchown, 3)
+SYSCALL(sys_ni_syscall, 0)
+SYSCALL(sys_stat, 2)
+SYSCALL(sys_lseek, 3)
+SYSCALL(sys_getpid, 0) /* 20 */
+SYSCALL(sys_mount, 5)
+SYSCALL(sys_oldumount, 1)
+SYSCALL(sys_setuid, 1)
+SYSCALL(sys_getuid, 0)
+SYSCALL(sys_stime, 1) /* 25 */
+SYSCALL(sys_ptrace, 4)
+SYSCALL(sys_alarm, 1)
+SYSCALL(sys_fstat, 2)
+SYSCALL(sys_pause, 0)
+SYSCALL(sys_utime, 2) /* 30 */
+SYSCALL(sys_ni_syscall, 0)
+SYSCALL(sys_ni_syscall, 0)
+SYSCALL(sys_access, 2)
+SYSCALL(sys_nice, 1)
+SYSCALL(sys_ni_syscall, 0) /* 35 */
+SYSCALL(sys_sync, 0)
+SYSCALL(sys_kill, 2)
+SYSCALL(sys_rename, 2)
+SYSCALL(sys_mkdir, 2)
+SYSCALL(sys_rmdir, 1) /* 40 */
+SYSCALL(sys_dup, 1)
+SYSCALL(sys_pipe, 1)
+SYSCALL(sys_times, 1)
+SYSCALL(sys_ni_syscall, 0)
+SYSCALL(sys_brk, 1) /* 45 */
+SYSCALL(sys_setgid, 1)
+SYSCALL(sys_getgid, 0)
+SYSCALL(sys_ni_syscall, 0) /* was signal(2) */
+SYSCALL(sys_geteuid, 0)
+SYSCALL(sys_getegid, 0) /* 50 */
+SYSCALL(sys_acct, 1)
+SYSCALL(sys_umount, 2)
+SYSCALL(sys_ni_syscall, 0)
+SYSCALL(sys_ioctl, 3)
+SYSCALL(sys_fcntl, 3) /* 55 */
+SYSCALL(sys_ni_syscall, 2)
+SYSCALL(sys_setpgid, 2)
+SYSCALL(sys_ni_syscall, 0)
+SYSCALL(sys_olduname, 1)
+SYSCALL(sys_umask, 1) /* 60 */
+SYSCALL(sys_chroot, 1)
+SYSCALL(sys_ustat, 2)
+SYSCALL(sys_dup2, 2)
+SYSCALL(sys_getppid, 0)
+SYSCALL(sys_getpgrp, 0) /* 65 */
+SYSCALL(sys_setsid, 0)
+SYSCALL(sys_sigaction, 3)
+SYSCALL(sys_sgetmask, 0)
+SYSCALL(sys_ssetmask, 1)
+SYSCALL(sys_setreuid, 2) /* 70 */
+SYSCALL(sys_setregid, 2)
+SYSCALL(sys_sigsuspend, 0)
+SYSCALL(sys_sigpending, 1)
+SYSCALL(sys_sethostname, 2)
+SYSCALL(sys_setrlimit, 2) /* 75 */
+SYSCALL(sys_getrlimit, 2)
+SYSCALL(sys_getrusage, 2)
+SYSCALL(sys_gettimeofday, 2)
+SYSCALL(sys_settimeofday, 2)
+SYSCALL(sys_getgroups, 2) /* 80 */
+SYSCALL(sys_setgroups, 2)
+SYSCALL(sys_ni_syscall, 0) /* old_select */
+SYSCALL(sys_symlink, 2)
+SYSCALL(sys_lstat, 2)
+SYSCALL(sys_readlink, 3) /* 85 */
+SYSCALL(sys_uselib, 1)
+SYSCALL(sys_swapon, 2)
+SYSCALL(sys_reboot, 3)
+SYSCALL(old_readdir, 3)
+SYSCALL(old_mmap, 6) /* 90 */
+SYSCALL(sys_munmap, 2)
+SYSCALL(sys_truncate, 2)
+SYSCALL(sys_ftruncate, 2)
+SYSCALL(sys_fchmod, 2)
+SYSCALL(sys_fchown, 3) /* 95 */
+SYSCALL(sys_getpriority, 2)
+SYSCALL(sys_setpriority, 3)
+SYSCALL(sys_ni_syscall, 0)
+SYSCALL(sys_statfs, 2)
+SYSCALL(sys_fstatfs, 2) /* 100 */
+SYSCALL(sys_ni_syscall, 3)
+SYSCALL(sys_socketcall, 2)
+SYSCALL(sys_syslog, 3)
+SYSCALL(sys_setitimer, 3)
+SYSCALL(sys_getitimer, 2) /* 105 */
+SYSCALL(sys_newstat, 2)
+SYSCALL(sys_newlstat, 2)
+SYSCALL(sys_newfstat, 2)
+SYSCALL(sys_uname, 1)
+SYSCALL(sys_ni_syscall, 0) /* 110 */
+SYSCALL(sys_vhangup, 0)
+SYSCALL(sys_ni_syscall, 0) /* was sys_idle() */
+SYSCALL(sys_ni_syscall, 0)
+SYSCALL(sys_wait4, 4)
+SYSCALL(sys_swapoff, 1) /* 115 */
+SYSCALL(sys_sysinfo, 1)
+SYSCALL(sys_ipc, 5) /* 6 really, but glibc uses only 5) */
+SYSCALL(sys_fsync, 1)
+SYSCALL(sys_sigreturn, 0)
+SYSCALL(sys_clone, 0) /* 120 */
+SYSCALL(sys_setdomainname, 2)
+SYSCALL(sys_newuname, 1)
+SYSCALL(sys_ni_syscall, 0) /* sys_modify_ldt */
+SYSCALL(sys_adjtimex, 1)
+SYSCALL(sys_mprotect, 3) /* 125 */
+SYSCALL(sys_sigprocmask, 3)
+SYSCALL(sys_ni_syscall, 2) /* old sys_create_module */
+SYSCALL(sys_init_module, 2)
+SYSCALL(sys_delete_module, 1)
+SYSCALL(sys_ni_syscall, 1) /* old sys_get_kernel_sysm */ /* 130 */
+SYSCALL(sys_quotactl, 0)
+SYSCALL(sys_getpgid, 1)
+SYSCALL(sys_fchdir, 1)
+SYSCALL(sys_bdflush, 2)
+SYSCALL(sys_sysfs, 3) /* 135 */
+SYSCALL(sys_personality, 1)
+SYSCALL(sys_ni_syscall, 0) /* for afs_syscall */
+SYSCALL(sys_setfsuid, 1)
+SYSCALL(sys_setfsgid, 1)
+SYSCALL(sys_llseek, 5) /* 140 */
+SYSCALL(sys_getdents, 3)
+SYSCALL(sys_select, 5)
+SYSCALL(sys_flock, 2)
+SYSCALL(sys_msync, 3)
+SYSCALL(sys_readv, 3) /* 145 */
+SYSCALL(sys_writev, 3)
+SYSCALL(sys_ni_syscall, 3)
+SYSCALL(sys_ni_syscall, 3)
+SYSCALL(sys_ni_syscall, 4) /* handled in fast syscall handler. */
+SYSCALL(sys_ni_syscall, 0) /* 150 */
+SYSCALL(sys_getsid, 1)
+SYSCALL(sys_fdatasync, 1)
+SYSCALL(sys_sysctl, 1)
+SYSCALL(sys_mlock, 2)
+SYSCALL(sys_munlock, 2) /* 155 */
+SYSCALL(sys_mlockall, 1)
+SYSCALL(sys_munlockall, 0)
+SYSCALL(sys_sched_setparam,2)
+SYSCALL(sys_sched_getparam,2)
+SYSCALL(sys_sched_setscheduler,3) /* 160 */
+SYSCALL(sys_sched_getscheduler,1)
+SYSCALL(sys_sched_yield,0)
+SYSCALL(sys_sched_get_priority_max,1)
+SYSCALL(sys_sched_get_priority_min,1)
+SYSCALL(sys_sched_rr_get_interval,2) /* 165 */
+SYSCALL(sys_nanosleep,2)
+SYSCALL(sys_mremap,4)
+SYSCALL(sys_accept, 3)
+SYSCALL(sys_bind, 3)
+SYSCALL(sys_connect, 3) /* 170 */
+SYSCALL(sys_getpeername, 3)
+SYSCALL(sys_getsockname, 3)
+SYSCALL(sys_getsockopt, 5)
+SYSCALL(sys_listen, 2)
+SYSCALL(sys_recv, 4) /* 175 */
+SYSCALL(sys_recvfrom, 6)
+SYSCALL(sys_recvmsg, 3)
+SYSCALL(sys_send, 4)
+SYSCALL(sys_sendmsg, 3)
+SYSCALL(sys_sendto, 6) /* 180 */
+SYSCALL(sys_setsockopt, 5)
+SYSCALL(sys_shutdown, 2)
+SYSCALL(sys_socket, 3)
+SYSCALL(sys_socketpair, 4)
+SYSCALL(sys_setresuid, 3) /* 185 */
+SYSCALL(sys_getresuid, 3)
+SYSCALL(sys_ni_syscall, 5) /* old sys_query_module */
+SYSCALL(sys_poll, 3)
+SYSCALL(sys_nfsservctl, 3)
+SYSCALL(sys_setresgid, 3) /* 190 */
+SYSCALL(sys_getresgid, 3)
+SYSCALL(sys_prctl, 5)
+SYSCALL(sys_rt_sigreturn, 0)
+SYSCALL(sys_rt_sigaction, 4)
+SYSCALL(sys_rt_sigprocmask, 4) /* 195 */
+SYSCALL(sys_rt_sigpending, 2)
+SYSCALL(sys_rt_sigtimedwait, 4)
+SYSCALL(sys_rt_sigqueueinfo, 3)
+SYSCALL(sys_rt_sigsuspend, 0)
+SYSCALL(sys_pread64, 5) /* 200 */
+SYSCALL(sys_pwrite64, 5)
+SYSCALL(sys_chown, 3)
+SYSCALL(sys_getcwd, 2)
+SYSCALL(sys_capget, 2)
+SYSCALL(sys_capset, 2) /* 205 */
+SYSCALL(sys_sigaltstack, 0)
+SYSCALL(sys_sendfile, 4)
+SYSCALL(sys_ni_syscall, 0)
+SYSCALL(sys_ni_syscall, 0)
+SYSCALL(sys_mmap2, 6) /* 210 */
+SYSCALL(sys_truncate64, 2)
+SYSCALL(sys_ftruncate64, 2)
+SYSCALL(sys_stat64, 2)
+SYSCALL(sys_lstat64, 2)
+SYSCALL(sys_fstat64, 2) /* 215 */
+SYSCALL(sys_pivot_root, 2)
+SYSCALL(sys_mincore, 3)
+SYSCALL(sys_madvise, 3)
+SYSCALL(sys_getdents64, 3)
+SYSCALL(sys_vfork, 0) /* 220 */
diff --git a/arch/xtensa/kernel/time.c b/arch/xtensa/kernel/time.c
new file mode 100644
index 000000000000..e07287db5a40
--- /dev/null
+++ b/arch/xtensa/kernel/time.c
@@ -0,0 +1,227 @@
+/*
+ * arch/xtensa/kernel/time.c
+ *
+ * Timer and clock support.
+ *
+ * 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) 2005 Tensilica Inc.
+ *
+ * Chris Zankel <chris@zankel.net>
+ */
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/time.h>
+#include <linux/timex.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/profile.h>
+#include <linux/delay.h>
+
+#include <asm/timex.h>
+#include <asm/platform.h>
+
+
+extern volatile unsigned long wall_jiffies;
+
+u64 jiffies_64 = INITIAL_JIFFIES;
+EXPORT_SYMBOL(jiffies_64);
+
+spinlock_t rtc_lock = SPIN_LOCK_UNLOCKED;
+EXPORT_SYMBOL(rtc_lock);
+
+
+#ifdef CONFIG_XTENSA_CALIBRATE_CCOUNT
+unsigned long ccount_per_jiffy; /* per 1/HZ */
+unsigned long ccount_nsec; /* nsec per ccount increment */
+#endif
+
+unsigned int last_ccount_stamp;
+static long last_rtc_update = 0;
+
+/*
+ * Scheduler clock - returns current tim in nanosec units.
+ */
+
+unsigned long long sched_clock(void)
+{
+ return (unsigned long long)jiffies * (1000000000 / HZ);
+}
+
+static irqreturn_t timer_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+static struct irqaction timer_irqaction = {
+ .handler = timer_interrupt,
+ .flags = SA_INTERRUPT,
+ .name = "timer",
+};
+
+void __init time_init(void)
+{
+ time_t sec_o, sec_n = 0;
+
+ /* The platform must provide a function to calibrate the processor
+ * speed for the CALIBRATE.
+ */
+
+#if CONFIG_XTENSA_CALIBRATE_CCOUNT
+ printk("Calibrating CPU frequency ");
+ platform_calibrate_ccount();
+ printk("%d.%02d MHz\n", (int)ccount_per_jiffy/(1000000/HZ),
+ (int)(ccount_per_jiffy/(10000/HZ))%100);
+#endif
+
+ /* Set time from RTC (if provided) */
+
+ if (platform_get_rtc_time(&sec_o) == 0)
+ while (platform_get_rtc_time(&sec_n))
+ if (sec_o != sec_n)
+ break;
+
+ xtime.tv_nsec = 0;
+ last_rtc_update = xtime.tv_sec = sec_n;
+ last_ccount_stamp = get_ccount();
+
+ set_normalized_timespec(&wall_to_monotonic,
+ -xtime.tv_sec, -xtime.tv_nsec);
+
+ /* Initialize the linux timer interrupt. */
+
+ setup_irq(LINUX_TIMER_INT, &timer_irqaction);
+ set_linux_timer(get_ccount() + CCOUNT_PER_JIFFY);
+}
+
+
+int do_settimeofday(struct timespec *tv)
+{
+ time_t wtm_sec, sec = tv->tv_sec;
+ long wtm_nsec, nsec = tv->tv_nsec;
+ unsigned long ccount;
+
+ 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!
+ */
+ ccount = get_ccount();
+ nsec -= (ccount - last_ccount_stamp) * CCOUNT_NSEC;
+ nsec -= (jiffies - wall_jiffies) * CCOUNT_PER_JIFFY * CCOUNT_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);
+ return 0;
+}
+
+EXPORT_SYMBOL(do_settimeofday);
+
+
+void do_gettimeofday(struct timeval *tv)
+{
+ unsigned long flags;
+ unsigned long sec, usec, delta, lost, seq;
+
+ do {
+ seq = read_seqbegin_irqsave(&xtime_lock, flags);
+
+ delta = get_ccount() - last_ccount_stamp;
+ sec = xtime.tv_sec;
+ usec = (xtime.tv_nsec / NSEC_PER_USEC);
+
+ lost = jiffies - wall_jiffies;
+
+ } while (read_seqretry_irqrestore(&xtime_lock, seq, flags));
+
+ usec += lost * (1000000UL/HZ) + (delta * CCOUNT_NSEC) / NSEC_PER_USEC;
+ for (; usec >= 1000000; sec++, usec -= 1000000)
+ ;
+
+ tv->tv_sec = sec;
+ tv->tv_usec = usec;
+}
+
+EXPORT_SYMBOL(do_gettimeofday);
+
+/*
+ * The timer interrupt is called HZ times per second.
+ */
+
+irqreturn_t timer_interrupt (int irq, void *dev_id, struct pt_regs *regs)
+{
+
+ unsigned long next;
+
+ next = get_linux_timer();
+
+again:
+ while ((signed long)(get_ccount() - next) > 0) {
+
+ profile_tick(CPU_PROFILING, regs);
+#ifndef CONFIG_SMP
+ update_process_times(user_mode(regs));
+#endif
+
+ write_seqlock(&xtime_lock);
+
+ last_ccount_stamp = next;
+ next += CCOUNT_PER_JIFFY;
+ do_timer (regs); /* Linux handler in kernel/timer.c */
+
+ if ((time_status & STA_UNSYNC) == 0 &&
+ xtime.tv_sec - last_rtc_update >= 659 &&
+ abs((xtime.tv_nsec/1000)-(1000000-1000000/HZ))<5000000/HZ &&
+ jiffies - wall_jiffies == 1) {
+
+ if (platform_set_rtc_time(xtime.tv_sec+1) == 0)
+ last_rtc_update = xtime.tv_sec+1;
+ else
+ /* Do it again in 60 s */
+ last_rtc_update += 60;
+ }
+ write_sequnlock(&xtime_lock);
+ }
+
+ /* NOTE: writing CCOMPAREn clears the interrupt. */
+
+ set_linux_timer (next);
+
+ /* Make sure we didn't miss any tick... */
+
+ if ((signed long)(get_ccount() - next) > 0)
+ goto again;
+
+ /* Allow platform to do something usefull (Wdog). */
+
+ platform_heartbeat();
+
+ return IRQ_HANDLED;
+}
+
+#ifndef CONFIG_GENERIC_CALIBRATE_DELAY
+void __devinit calibrate_delay(void)
+{
+ loops_per_jiffy = CCOUNT_PER_JIFFY;
+ printk("Calibrating delay loop (skipped)... "
+ "%lu.%02lu BogoMIPS preset\n",
+ loops_per_jiffy/(1000000/HZ),
+ (loops_per_jiffy/(10000/HZ)) % 100);
+}
+#endif
+
diff --git a/arch/xtensa/kernel/traps.c b/arch/xtensa/kernel/traps.c
new file mode 100644
index 000000000000..804246e743b1
--- /dev/null
+++ b/arch/xtensa/kernel/traps.c
@@ -0,0 +1,498 @@
+/*
+ * arch/xtensa/kernel/traps.c
+ *
+ * Exception handling.
+ *
+ * Derived from code with the following copyrights:
+ * Copyright (C) 1994 - 1999 by Ralf Baechle
+ * Modified for R3000 by Paul M. Antoine, 1995, 1996
+ * Complete output from die() by Ulf Carlsson, 1998
+ * Copyright (C) 1999 Silicon Graphics, Inc.
+ *
+ * Essentially rewritten for the Xtensa architecture port.
+ *
+ * Copyright (C) 2001 - 2005 Tensilica Inc.
+ *
+ * Joe Taylor <joe@tensilica.com, joetylr@yahoo.com>
+ * Chris Zankel <chris@zankel.net>
+ * Marc Gauthier<marc@tensilica.com, marc@alumni.uwaterloo.ca>
+ * Kevin Chea
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/stringify.h>
+#include <linux/kallsyms.h>
+
+#include <asm/ptrace.h>
+#include <asm/timex.h>
+#include <asm/uaccess.h>
+#include <asm/pgtable.h>
+#include <asm/processor.h>
+
+#ifdef CONFIG_KGDB
+extern int gdb_enter;
+extern int return_from_debug_flag;
+#endif
+
+/*
+ * Machine specific interrupt handlers
+ */
+
+extern void kernel_exception(void);
+extern void user_exception(void);
+
+extern void fast_syscall_kernel(void);
+extern void fast_syscall_user(void);
+extern void fast_alloca(void);
+extern void fast_unaligned(void);
+extern void fast_second_level_miss(void);
+extern void fast_store_prohibited(void);
+extern void fast_coprocessor(void);
+
+extern void do_illegal_instruction (struct pt_regs*);
+extern void do_interrupt (struct pt_regs*);
+extern void do_unaligned_user (struct pt_regs*);
+extern void do_multihit (struct pt_regs*, unsigned long);
+extern void do_page_fault (struct pt_regs*, unsigned long);
+extern void do_debug (struct pt_regs*);
+extern void system_call (struct pt_regs*);
+
+/*
+ * The vector table must be preceded by a save area (which
+ * implies it must be in RAM, unless one places RAM immediately
+ * before a ROM and puts the vector at the start of the ROM (!))
+ */
+
+#define KRNL 0x01
+#define USER 0x02
+
+#define COPROCESSOR(x) \
+{ XCHAL_EXCCAUSE_COPROCESSOR ## x ## _DISABLED, USER, fast_coprocessor }
+
+typedef struct {
+ int cause;
+ int fast;
+ void* handler;
+} dispatch_init_table_t;
+
+dispatch_init_table_t __init dispatch_init_table[] = {
+
+{ XCHAL_EXCCAUSE_ILLEGAL_INSTRUCTION, 0, do_illegal_instruction},
+{ XCHAL_EXCCAUSE_SYSTEM_CALL, KRNL, fast_syscall_kernel },
+{ XCHAL_EXCCAUSE_SYSTEM_CALL, USER, fast_syscall_user },
+{ XCHAL_EXCCAUSE_SYSTEM_CALL, 0, system_call },
+/* XCHAL_EXCCAUSE_INSTRUCTION_FETCH unhandled */
+/* XCHAL_EXCCAUSE_LOAD_STORE_ERROR unhandled*/
+{ XCHAL_EXCCAUSE_LEVEL1_INTERRUPT, 0, do_interrupt },
+{ XCHAL_EXCCAUSE_ALLOCA, USER|KRNL, fast_alloca },
+/* XCHAL_EXCCAUSE_INTEGER_DIVIDE_BY_ZERO unhandled */
+/* XCHAL_EXCCAUSE_PRIVILEGED unhandled */
+#if XCHAL_UNALIGNED_LOAD_EXCEPTION || XCHAL_UNALIGNED_STORE_EXCEPTION
+#ifdef CONFIG_UNALIGNED_USER
+{ XCHAL_EXCCAUSE_UNALIGNED, USER, fast_unaligned },
+#else
+{ XCHAL_EXCCAUSE_UNALIGNED, 0, do_unaligned_user },
+#endif
+{ XCHAL_EXCCAUSE_UNALIGNED, KRNL, fast_unaligned },
+#endif
+{ XCHAL_EXCCAUSE_ITLB_MISS, 0, do_page_fault },
+{ XCHAL_EXCCAUSE_ITLB_MISS, USER|KRNL, fast_second_level_miss},
+{ XCHAL_EXCCAUSE_ITLB_MULTIHIT, 0, do_multihit },
+{ XCHAL_EXCCAUSE_ITLB_PRIVILEGE, 0, do_page_fault },
+/* XCHAL_EXCCAUSE_SIZE_RESTRICTION unhandled */
+{ XCHAL_EXCCAUSE_FETCH_CACHE_ATTRIBUTE, 0, do_page_fault },
+{ XCHAL_EXCCAUSE_DTLB_MISS, USER|KRNL, fast_second_level_miss},
+{ XCHAL_EXCCAUSE_DTLB_MISS, 0, do_page_fault },
+{ XCHAL_EXCCAUSE_DTLB_MULTIHIT, 0, do_multihit },
+{ XCHAL_EXCCAUSE_DTLB_PRIVILEGE, 0, do_page_fault },
+/* XCHAL_EXCCAUSE_DTLB_SIZE_RESTRICTION unhandled */
+{ XCHAL_EXCCAUSE_STORE_CACHE_ATTRIBUTE, USER|KRNL, fast_store_prohibited },
+{ XCHAL_EXCCAUSE_STORE_CACHE_ATTRIBUTE, 0, do_page_fault },
+{ XCHAL_EXCCAUSE_LOAD_CACHE_ATTRIBUTE, 0, do_page_fault },
+/* XCCHAL_EXCCAUSE_FLOATING_POINT unhandled */
+#if (XCHAL_CP_MASK & 1)
+COPROCESSOR(0),
+#endif
+#if (XCHAL_CP_MASK & 2)
+COPROCESSOR(1),
+#endif
+#if (XCHAL_CP_MASK & 4)
+COPROCESSOR(2),
+#endif
+#if (XCHAL_CP_MASK & 8)
+COPROCESSOR(3),
+#endif
+#if (XCHAL_CP_MASK & 16)
+COPROCESSOR(4),
+#endif
+#if (XCHAL_CP_MASK & 32)
+COPROCESSOR(5),
+#endif
+#if (XCHAL_CP_MASK & 64)
+COPROCESSOR(6),
+#endif
+#if (XCHAL_CP_MASK & 128)
+COPROCESSOR(7),
+#endif
+{ EXCCAUSE_MAPPED_DEBUG, 0, do_debug },
+{ -1, -1, 0 }
+
+};
+
+/* The exception table <exc_table> serves two functions:
+ * 1. it contains three dispatch tables (fast_user, fast_kernel, default-c)
+ * 2. it is a temporary memory buffer for the exception handlers.
+ */
+
+unsigned long exc_table[EXC_TABLE_SIZE/4];
+
+void die(const char*, struct pt_regs*, long);
+
+static inline void
+__die_if_kernel(const char *str, struct pt_regs *regs, long err)
+{
+ if (!user_mode(regs))
+ die(str, regs, err);
+}
+
+/*
+ * Unhandled Exceptions. Kill user task or panic if in kernel space.
+ */
+
+void do_unhandled(struct pt_regs *regs, unsigned long exccause)
+{
+ __die_if_kernel("Caught unhandled exception - should not happen",
+ regs, SIGKILL);
+
+ /* If in user mode, send SIGILL signal to current process */
+ printk("Caught unhandled exception in '%s' "
+ "(pid = %d, pc = %#010lx) - should not happen\n"
+ "\tEXCCAUSE is %ld\n",
+ current->comm, current->pid, regs->pc, exccause);
+ force_sig(SIGILL, current);
+}
+
+/*
+ * Multi-hit exception. This if fatal!
+ */
+
+void do_multihit(struct pt_regs *regs, unsigned long exccause)
+{
+ die("Caught multihit exception", regs, SIGKILL);
+}
+
+/*
+ * Level-1 interrupt.
+ * We currently have no priority encoding.
+ */
+
+unsigned long ignored_level1_interrupts;
+extern void do_IRQ(int, struct pt_regs *);
+
+void do_interrupt (struct pt_regs *regs)
+{
+ unsigned long intread = get_sr (INTREAD);
+ unsigned long intenable = get_sr (INTENABLE);
+ int i, mask;
+
+ /* Handle all interrupts (no priorities).
+ * (Clear the interrupt before processing, in case it's
+ * edge-triggered or software-generated)
+ */
+
+ for (i=0, mask = 1; i < XCHAL_NUM_INTERRUPTS; i++, mask <<= 1) {
+ if (mask & (intread & intenable)) {
+ set_sr (mask, INTCLEAR);
+ do_IRQ (i,regs);
+ }
+ }
+}
+
+/*
+ * Illegal instruction. Fatal if in kernel space.
+ */
+
+void
+do_illegal_instruction(struct pt_regs *regs)
+{
+ __die_if_kernel("Illegal instruction in kernel", regs, SIGKILL);
+
+ /* If in user mode, send SIGILL signal to current process. */
+
+ printk("Illegal Instruction in '%s' (pid = %d, pc = %#010lx)\n",
+ current->comm, current->pid, regs->pc);
+ force_sig(SIGILL, current);
+}
+
+
+/*
+ * Handle unaligned memory accesses from user space. Kill task.
+ *
+ * If CONFIG_UNALIGNED_USER is not set, we don't allow unaligned memory
+ * accesses causes from user space.
+ */
+
+#if XCHAL_UNALIGNED_LOAD_EXCEPTION || XCHAL_UNALIGNED_STORE_EXCEPTION
+#ifndef CONFIG_UNALIGNED_USER
+void
+do_unaligned_user (struct pt_regs *regs)
+{
+ siginfo_t info;
+
+ __die_if_kernel("Unhandled unaligned exception in kernel",
+ regs, SIGKILL);
+
+ current->thread.bad_vaddr = regs->excvaddr;
+ current->thread.error_code = -3;
+ printk("Unaligned memory access to %08lx in '%s' "
+ "(pid = %d, pc = %#010lx)\n",
+ regs->excvaddr, current->comm, current->pid, regs->pc);
+ info.si_signo = SIGBUS;
+ info.si_errno = 0;
+ info.si_code = BUS_ADRALN;
+ info.si_addr = (void *) regs->excvaddr;
+ force_sig_info(SIGSEGV, &info, current);
+
+}
+#endif
+#endif
+
+void
+do_debug(struct pt_regs *regs)
+{
+#ifdef CONFIG_KGDB
+ /* If remote debugging is configured AND enabled, we give control to
+ * kgdb. Otherwise, we fall through, perhaps giving control to the
+ * native debugger.
+ */
+
+ if (gdb_enter) {
+ extern void gdb_handle_exception(struct pt_regs *);
+ gdb_handle_exception(regs);
+ return_from_debug_flag = 1;
+ return;
+ }
+#endif
+
+ __die_if_kernel("Breakpoint in kernel", regs, SIGKILL);
+
+ /* If in user mode, send SIGTRAP signal to current process */
+
+ force_sig(SIGTRAP, current);
+}
+
+
+/*
+ * Initialize dispatch tables.
+ *
+ * The exception vectors are stored compressed the __init section in the
+ * dispatch_init_table. This function initializes the following three tables
+ * from that compressed table:
+ * - fast user first dispatch table for user exceptions
+ * - fast kernel first dispatch table for kernel exceptions
+ * - default C-handler C-handler called by the default fast handler.
+ *
+ * See vectors.S for more details.
+ */
+
+#define set_handler(idx,handler) (exc_table[idx] = (unsigned long) (handler))
+
+void trap_init(void)
+{
+ int i;
+
+ /* Setup default vectors. */
+
+ for(i = 0; i < 64; i++) {
+ set_handler(EXC_TABLE_FAST_USER/4 + i, user_exception);
+ set_handler(EXC_TABLE_FAST_KERNEL/4 + i, kernel_exception);
+ set_handler(EXC_TABLE_DEFAULT/4 + i, do_unhandled);
+ }
+
+ /* Setup specific handlers. */
+
+ for(i = 0; dispatch_init_table[i].cause >= 0; i++) {
+
+ int fast = dispatch_init_table[i].fast;
+ int cause = dispatch_init_table[i].cause;
+ void *handler = dispatch_init_table[i].handler;
+
+ if (fast == 0)
+ set_handler (EXC_TABLE_DEFAULT/4 + cause, handler);
+ if (fast && fast & USER)
+ set_handler (EXC_TABLE_FAST_USER/4 + cause, handler);
+ if (fast && fast & KRNL)
+ set_handler (EXC_TABLE_FAST_KERNEL/4 + cause, handler);
+ }
+
+ /* Initialize EXCSAVE_1 to hold the address of the exception table. */
+
+ i = (unsigned long)exc_table;
+ __asm__ __volatile__("wsr %0, "__stringify(EXCSAVE_1)"\n" : : "a" (i));
+}
+
+/*
+ * This function dumps the current valid window frame and other base registers.
+ */
+
+void show_regs(struct pt_regs * regs)
+{
+ int i, wmask;
+
+ wmask = regs->wmask & ~1;
+
+ for (i = 0; i < 32; i++) {
+ if (wmask & (1 << (i / 4)))
+ break;
+ if ((i % 8) == 0)
+ printk ("\n" KERN_INFO "a%02d: ", i);
+ printk("%08lx ", regs->areg[i]);
+ }
+ printk("\n");
+
+ printk("pc: %08lx, ps: %08lx, depc: %08lx, excvaddr: %08lx\n",
+ regs->pc, regs->ps, regs->depc, regs->excvaddr);
+ printk("lbeg: %08lx, lend: %08lx lcount: %08lx, sar: %08lx\n",
+ regs->lbeg, regs->lend, regs->lcount, regs->sar);
+ if (user_mode(regs))
+ printk("wb: %08lx, ws: %08lx, wmask: %08lx, syscall: %ld\n",
+ regs->windowbase, regs->windowstart, regs->wmask,
+ regs->syscall);
+}
+
+void show_trace(struct task_struct *task, unsigned long *sp)
+{
+ unsigned long a0, a1, pc;
+ unsigned long sp_start, sp_end;
+
+ a1 = (unsigned long)sp;
+
+ if (a1 == 0)
+ __asm__ __volatile__ ("mov %0, a1\n" : "=a"(a1));
+
+
+ sp_start = a1 & ~(THREAD_SIZE-1);
+ sp_end = sp_start + THREAD_SIZE;
+
+ printk("Call Trace:");
+#ifdef CONFIG_KALLSYMS
+ printk("\n");
+#endif
+ spill_registers();
+
+ while (a1 > sp_start && a1 < sp_end) {
+ sp = (unsigned long*)a1;
+
+ a0 = *(sp - 4);
+ a1 = *(sp - 3);
+
+ if (a1 <= (unsigned long) sp)
+ break;
+
+ pc = MAKE_PC_FROM_RA(a0, a1);
+
+ if (kernel_text_address(pc)) {
+ printk(" [<%08lx>] ", pc);
+ print_symbol("%s\n", pc);
+ }
+ }
+ printk("\n");
+}
+
+/*
+ * This routine abuses get_user()/put_user() to reference pointers
+ * with at least a bit of error checking ...
+ */
+
+static int kstack_depth_to_print = 24;
+
+void show_stack(struct task_struct *task, unsigned long *sp)
+{
+ int i = 0;
+ unsigned long *stack;
+
+ if (sp == 0)
+ __asm__ __volatile__ ("mov %0, a1\n" : "=a"(sp));
+
+ stack = sp;
+
+ printk("\nStack: ");
+
+ for (i = 0; i < kstack_depth_to_print; i++) {
+ if (kstack_end(sp))
+ break;
+ if (i && ((i % 8) == 0))
+ printk("\n ");
+ printk("%08lx ", *sp++);
+ }
+ printk("\n");
+ show_trace(task, stack);
+}
+
+void dump_stack(void)
+{
+ show_stack(current, NULL);
+}
+
+EXPORT_SYMBOL(dump_stack);
+
+
+void show_code(unsigned int *pc)
+{
+ long i;
+
+ printk("\nCode:");
+
+ for(i = -3 ; i < 6 ; i++) {
+ unsigned long insn;
+ if (__get_user(insn, pc + i)) {
+ printk(" (Bad address in pc)\n");
+ break;
+ }
+ printk("%c%08lx%c",(i?' ':'<'),insn,(i?' ':'>'));
+ }
+}
+
+spinlock_t die_lock = SPIN_LOCK_UNLOCKED;
+
+void die(const char * str, struct pt_regs * regs, long err)
+{
+ static int die_counter;
+ int nl = 0;
+
+ console_verbose();
+ spin_lock_irq(&die_lock);
+
+ printk("%s: sig: %ld [#%d]\n", str, err, ++die_counter);
+#ifdef CONFIG_PREEMPT
+ printk("PREEMPT ");
+ nl = 1;
+#endif
+ if (nl)
+ printk("\n");
+ show_regs(regs);
+ if (!user_mode(regs))
+ show_stack(NULL, (unsigned long*)regs->areg[1]);
+
+ spin_unlock_irq(&die_lock);
+
+ if (in_interrupt())
+ panic("Fatal exception in interrupt");
+
+ if (panic_on_oops) {
+ printk(KERN_EMERG "Fatal exception: panic in 5 seconds\n");
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(5 * HZ);
+ panic("Fatal exception");
+ }
+ do_exit(err);
+}
+
+
diff --git a/arch/xtensa/kernel/vectors.S b/arch/xtensa/kernel/vectors.S
new file mode 100644
index 000000000000..81808f0c6742
--- /dev/null
+++ b/arch/xtensa/kernel/vectors.S
@@ -0,0 +1,464 @@
+/*
+ * arch/xtensa/kernel/vectors.S
+ *
+ * This file contains all exception vectors (user, kernel, and double),
+ * as well as the window vectors (overflow and underflow), and the debug
+ * vector. These are the primary vectors executed by the processor if an
+ * exception occurs.
+ *
+ * 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) 2005 Tensilica, Inc.
+ *
+ * Chris Zankel <chris@zankel.net>
+ *
+ */
+
+/*
+ * We use a two-level table approach. The user and kernel exception vectors
+ * use a first-level dispatch table to dispatch the exception to a registered
+ * fast handler or the default handler, if no fast handler was registered.
+ * The default handler sets up a C-stack and dispatches the exception to a
+ * registerd C handler in the second-level dispatch table.
+ *
+ * Fast handler entry condition:
+ *
+ * a0: trashed, original value saved on stack (PT_AREG0)
+ * a1: a1
+ * a2: new stack pointer, original value in depc
+ * a3: dispatch table
+ * depc: a2, original value saved on stack (PT_DEPC)
+ * excsave_1: a3
+ *
+ * The value for PT_DEPC saved to stack also functions as a boolean to
+ * indicate that the exception is either a double or a regular exception:
+ *
+ * PT_DEPC >= VALID_DOUBLE_EXCEPTION_ADDRESS: double exception
+ * < VALID_DOUBLE_EXCEPTION_ADDRESS: regular exception
+ *
+ * Note: Neither the kernel nor the user exception handler generate literals.
+ *
+ */
+
+#include <linux/linkage.h>
+#include <asm/ptrace.h>
+#include <asm/ptrace.h>
+#include <asm/current.h>
+#include <asm/offsets.h>
+#include <asm/pgtable.h>
+#include <asm/processor.h>
+#include <asm/page.h>
+#include <asm/thread_info.h>
+#include <asm/processor.h>
+
+
+/*
+ * User exception vector. (Exceptions with PS.UM == 1, PS.EXCM == 0)
+ *
+ * We get here when an exception occurred while we were in userland.
+ * We switch to the kernel stack and jump to the first level handler
+ * associated to the exception cause.
+ *
+ * Note: the saved kernel stack pointer (EXC_TABLE_KSTK) is already
+ * decremented by PT_USER_SIZE.
+ */
+
+ .section .UserExceptionVector.text, "ax"
+
+ENTRY(_UserExceptionVector)
+
+ xsr a3, EXCSAVE_1 # save a3 and get dispatch table
+ wsr a2, DEPC # save a2
+ l32i a2, a3, EXC_TABLE_KSTK # load kernel stack to a2
+ s32i a0, a2, PT_AREG0 # save a0 to ESF
+ rsr a0, EXCCAUSE # retrieve exception cause
+ s32i a0, a2, PT_DEPC # mark it as a regular exception
+ addx4 a0, a0, a3 # find entry in table
+ l32i a0, a0, EXC_TABLE_FAST_USER # load handler
+ jx a0
+
+/*
+ * Kernel exception vector. (Exceptions with PS.UM == 0, PS.EXCM == 0)
+ *
+ * We get this exception when we were already in kernel space.
+ * We decrement the current stack pointer (kernel) by PT_SIZE and
+ * jump to the first-level handler associated with the exception cause.
+ *
+ * Note: we need to preserve space for the spill region.
+ */
+
+ .section .KernelExceptionVector.text, "ax"
+
+ENTRY(_KernelExceptionVector)
+
+ xsr a3, EXCSAVE_1 # save a3, and get dispatch table
+ wsr a2, DEPC # save a2
+ addi a2, a1, -16-PT_SIZE # adjust stack pointer
+ s32i a0, a2, PT_AREG0 # save a0 to ESF
+ rsr a0, EXCCAUSE # retrieve exception cause
+ s32i a0, a2, PT_DEPC # mark it as a regular exception
+ addx4 a0, a0, a3 # find entry in table
+ l32i a0, a0, EXC_TABLE_FAST_KERNEL # load handler address
+ jx a0
+
+
+/*
+ * Double exception vector (Exceptions with PS.EXCM == 1)
+ * We get this exception when another exception occurs while were are
+ * already in an exception, such as window overflow/underflow exception,
+ * or 'expected' exceptions, for example memory exception when we were trying
+ * to read data from an invalid address in user space.
+ *
+ * Note that this vector is never invoked for level-1 interrupts, because such
+ * interrupts are disabled (masked) when PS.EXCM is set.
+ *
+ * We decode the exception and take the appropriate action. However, the
+ * double exception vector is much more careful, because a lot more error
+ * cases go through the double exception vector than through the user and
+ * kernel exception vectors.
+ *
+ * Occasionally, the kernel expects a double exception to occur. This usually
+ * happens when accessing user-space memory with the user's permissions
+ * (l32e/s32e instructions). The kernel state, though, is not always suitable
+ * for immediate transfer of control to handle_double, where "normal" exception
+ * processing occurs. Also in kernel mode, TLB misses can occur if accessing
+ * vmalloc memory, possibly requiring repair in a double exception handler.
+ *
+ * The variable at TABLE_FIXUP offset from the pointer in EXCSAVE_1 doubles as
+ * a boolean variable and a pointer to a fixup routine. If the variable
+ * EXC_TABLE_FIXUP is non-zero, this handler jumps to that address. A value of
+ * zero indicates to use the default kernel/user exception handler.
+ * There is only one exception, when the value is identical to the exc_table
+ * label, the kernel is in trouble. This mechanism is used to protect critical
+ * sections, mainly when the handler writes to the stack to assert the stack
+ * pointer is valid. Once the fixup/default handler leaves that area, the
+ * EXC_TABLE_FIXUP variable is reset to the fixup handler or zero.
+ *
+ * Procedures wishing to use this mechanism should set EXC_TABLE_FIXUP to the
+ * nonzero address of a fixup routine before it could cause a double exception
+ * and reset it before it returns.
+ *
+ * Some other things to take care of when a fast exception handler doesn't
+ * specify a particular fixup handler but wants to use the default handlers:
+ *
+ * - The original stack pointer (in a1) must not be modified. The fast
+ * exception handler should only use a2 as the stack pointer.
+ *
+ * - If the fast handler manipulates the stack pointer (in a2), it has to
+ * register a valid fixup handler and cannot use the default handlers.
+ *
+ * - The handler can use any other generic register from a3 to a15, but it
+ * must save the content of these registers to stack (PT_AREG3...PT_AREGx)
+ *
+ * - These registers must be saved before a double exception can occur.
+ *
+ * - If we ever implement handling signals while in double exceptions, the
+ * number of registers a fast handler has saved (excluding a0 and a1) must
+ * be written to PT_AREG1. (1 if only a3 is used, 2 for a3 and a4, etc. )
+ *
+ * The fixup handlers are special handlers:
+ *
+ * - Fixup entry conditions differ from regular exceptions:
+ *
+ * a0: DEPC
+ * a1: a1
+ * a2: trashed, original value in EXC_TABLE_DOUBLE_A2
+ * a3: exctable
+ * depc: a0
+ * excsave_1: a3
+ *
+ * - When the kernel enters the fixup handler, it still assumes it is in a
+ * critical section, so EXC_TABLE_FIXUP variable is set to exc_table.
+ * The fixup handler, therefore, has to re-register itself as the fixup
+ * handler before it returns from the double exception.
+ *
+ * - Fixup handler can share the same exception frame with the fast handler.
+ * The kernel stack pointer is not changed when entering the fixup handler.
+ *
+ * - Fixup handlers can jump to the default kernel and user exception
+ * handlers. Before it jumps, though, it has to setup a exception frame
+ * on stack. Because the default handler resets the register fixup handler
+ * the fixup handler must make sure that the default handler returns to
+ * it instead of the exception address, so it can re-register itself as
+ * the fixup handler.
+ *
+ * In case of a critical condition where the kernel cannot recover, we jump
+ * to unrecoverable_exception with the following entry conditions.
+ * All registers a0...a15 are unchanged from the last exception, except:
+ *
+ * a0: last address before we jumped to the unrecoverable_exception.
+ * excsave_1: a0
+ *
+ *
+ * See the handle_alloca_user and spill_registers routines for example clients.
+ *
+ * FIXME: Note: we currently don't allow signal handling coming from a double
+ * exception, so the item markt with (*) is not required.
+ */
+
+ .section .DoubleExceptionVector.text, "ax"
+ .begin literal_prefix .DoubleExceptionVector
+
+ENTRY(_DoubleExceptionVector)
+
+ /* Deliberately destroy excsave (don't assume it's value was valid). */
+
+ wsr a3, EXCSAVE_1 # save a3
+
+ /* Check for kernel double exception (usually fatal). */
+
+ rsr a3, PS
+ _bbci.l a3, PS_UM_SHIFT, .Lksp
+
+ /* Check if we are currently handling a window exception. */
+ /* Note: We don't need to indicate that we enter a critical section. */
+
+ xsr a0, DEPC # get DEPC, save a0
+
+ movi a3, XCHAL_WINDOW_VECTORS_VADDR
+ _bltu a0, a3, .Lfixup
+ addi a3, a3, XSHAL_WINDOW_VECTORS_SIZE
+ _bgeu a0, a3, .Lfixup
+
+ /* Window overflow/underflow exception. Get stack pointer. */
+
+ mov a3, a2
+ movi a2, exc_table
+ l32i a2, a2, EXC_TABLE_KSTK
+
+ /* Check for overflow/underflow exception, jump if overflow. */
+
+ _bbci.l a0, 6, .Lovfl
+
+ /* a0: depc, a1: a1, a2: kstk, a3: a2, depc: a0, excsave: a3 */
+
+ /* Restart window underflow exception.
+ * We return to the instruction in user space that caused the window
+ * underflow exception. Therefore, we change window base to the value
+ * before we entered the window underflow exception and prepare the
+ * registers to return as if we were coming from a regular exception
+ * by changing depc (in a0).
+ * Note: We can trash the current window frame (a0...a3) and depc!
+ */
+
+ wsr a2, DEPC # save stack pointer temporarily
+ rsr a0, PS
+ extui a0, a0, XCHAL_PS_OWB_SHIFT, XCHAL_PS_OWB_BITS
+ wsr a0, WINDOWBASE
+ rsync
+
+ /* We are now in the previous window frame. Save registers again. */
+
+ xsr a2, DEPC # save a2 and get stack pointer
+ s32i a0, a2, PT_AREG0
+
+ wsr a3, EXCSAVE_1 # save a3
+ movi a3, exc_table
+
+ rsr a0, EXCCAUSE
+ s32i a0, a2, PT_DEPC # mark it as a regular exception
+ addx4 a0, a0, a3
+ l32i a0, a0, EXC_TABLE_FAST_USER
+ jx a0
+
+.Lfixup:/* Check for a fixup handler or if we were in a critical section. */
+
+ /* a0: depc, a1: a1, a2: a2, a3: trashed, depc: a0, excsave1: a3 */
+
+ movi a3, exc_table
+ s32i a2, a3, EXC_TABLE_DOUBLE_SAVE # temporary variable
+
+ /* Enter critical section. */
+
+ l32i a2, a3, EXC_TABLE_FIXUP
+ s32i a3, a3, EXC_TABLE_FIXUP
+ beq a2, a3, .Lunrecoverable_fixup # critical!
+ beqz a2, .Ldflt # no handler was registered
+
+ /* a0: depc, a1: a1, a2: trash, a3: exctable, depc: a0, excsave: a3 */
+
+ jx a2
+
+.Ldflt: /* Get stack pointer. */
+
+ l32i a3, a3, EXC_TABLE_DOUBLE_SAVE
+ addi a2, a3, -PT_USER_SIZE
+
+.Lovfl: /* Jump to default handlers. */
+
+ /* a0: depc, a1: a1, a2: kstk, a3: a2, depc: a0, excsave: a3 */
+
+ xsr a3, DEPC
+ s32i a0, a2, PT_DEPC
+ s32i a3, a2, PT_AREG0
+
+ /* a0: avail, a1: a1, a2: kstk, a3: avail, depc: a2, excsave: a3 */
+
+ movi a3, exc_table
+ rsr a0, EXCCAUSE
+ addx4 a0, a0, a3
+ l32i a0, a0, EXC_TABLE_FAST_USER
+ jx a0
+
+ /*
+ * We only allow the ITLB miss exception if we are in kernel space.
+ * All other exceptions are unexpected and thus unrecoverable!
+ */
+
+ .extern fast_second_level_miss_double_kernel
+
+.Lksp: /* a0: a0, a1: a1, a2: a2, a3: trashed, depc: depc, excsave: a3 */
+
+ rsr a3, EXCCAUSE
+ beqi a3, XCHAL_EXCCAUSE_ITLB_MISS, 1f
+ addi a3, a3, -XCHAL_EXCCAUSE_DTLB_MISS
+ bnez a3, .Lunrecoverable
+1: movi a3, fast_second_level_miss_double_kernel
+ jx a3
+
+ /* Critical! We can't handle this situation. PANIC! */
+
+ .extern unrecoverable_exception
+
+.Lunrecoverable_fixup:
+ l32i a2, a3, EXC_TABLE_DOUBLE_SAVE
+ xsr a0, DEPC
+
+.Lunrecoverable:
+ rsr a3, EXCSAVE_1
+ wsr a0, EXCSAVE_1
+ movi a0, unrecoverable_exception
+ callx0 a0
+
+ .end literal_prefix
+
+
+/*
+ * Debug interrupt vector
+ *
+ * There is not much space here, so simply jump to another handler.
+ * EXCSAVE[DEBUGLEVEL] has been set to that handler.
+ */
+
+ .section .DebugInterruptVector.text, "ax"
+
+ENTRY(_DebugInterruptVector)
+ xsr a0, EXCSAVE + XCHAL_DEBUGLEVEL
+ jx a0
+
+
+
+/* Window overflow and underflow handlers.
+ * The handlers must be 64 bytes apart, first starting with the underflow
+ * handlers underflow-4 to underflow-12, then the overflow handlers
+ * overflow-4 to overflow-12.
+ *
+ * Note: We rerun the underflow handlers if we hit an exception, so
+ * we try to access any page that would cause a page fault early.
+ */
+
+ .section .WindowVectors.text, "ax"
+
+
+/* 4-Register Window Overflow Vector (Handler) */
+
+ .align 64
+.global _WindowOverflow4
+_WindowOverflow4:
+ s32e a0, a5, -16
+ s32e a1, a5, -12
+ s32e a2, a5, -8
+ s32e a3, a5, -4
+ rfwo
+
+
+/* 4-Register Window Underflow Vector (Handler) */
+
+ .align 64
+.global _WindowUnderflow4
+_WindowUnderflow4:
+ l32e a0, a5, -16
+ l32e a1, a5, -12
+ l32e a2, a5, -8
+ l32e a3, a5, -4
+ rfwu
+
+
+/* 8-Register Window Overflow Vector (Handler) */
+
+ .align 64
+.global _WindowOverflow8
+_WindowOverflow8:
+ s32e a0, a9, -16
+ l32e a0, a1, -12
+ s32e a2, a9, -8
+ s32e a1, a9, -12
+ s32e a3, a9, -4
+ s32e a4, a0, -32
+ s32e a5, a0, -28
+ s32e a6, a0, -24
+ s32e a7, a0, -20
+ rfwo
+
+/* 8-Register Window Underflow Vector (Handler) */
+
+ .align 64
+.global _WindowUnderflow8
+_WindowUnderflow8:
+ l32e a1, a9, -12
+ l32e a0, a9, -16
+ l32e a7, a1, -12
+ l32e a2, a9, -8
+ l32e a4, a7, -32
+ l32e a3, a9, -4
+ l32e a5, a7, -28
+ l32e a6, a7, -24
+ l32e a7, a7, -20
+ rfwu
+
+
+/* 12-Register Window Overflow Vector (Handler) */
+
+ .align 64
+.global _WindowOverflow12
+_WindowOverflow12:
+ s32e a0, a13, -16
+ l32e a0, a1, -12
+ s32e a1, a13, -12
+ s32e a2, a13, -8
+ s32e a3, a13, -4
+ s32e a4, a0, -48
+ s32e a5, a0, -44
+ s32e a6, a0, -40
+ s32e a7, a0, -36
+ s32e a8, a0, -32
+ s32e a9, a0, -28
+ s32e a10, a0, -24
+ s32e a11, a0, -20
+ rfwo
+
+/* 12-Register Window Underflow Vector (Handler) */
+
+ .align 64
+.global _WindowUnderflow12
+_WindowUnderflow12:
+ l32e a1, a13, -12
+ l32e a0, a13, -16
+ l32e a11, a1, -12
+ l32e a2, a13, -8
+ l32e a4, a11, -48
+ l32e a8, a11, -32
+ l32e a3, a13, -4
+ l32e a5, a11, -44
+ l32e a6, a11, -40
+ l32e a7, a11, -36
+ l32e a9, a11, -28
+ l32e a10, a11, -24
+ l32e a11, a11, -20
+ rfwu
+
+ .text
+
+
diff --git a/arch/xtensa/kernel/vmlinux.lds.S b/arch/xtensa/kernel/vmlinux.lds.S
new file mode 100644
index 000000000000..476b2b53cd01
--- /dev/null
+++ b/arch/xtensa/kernel/vmlinux.lds.S
@@ -0,0 +1,341 @@
+/*
+ * arch/xtensa/kernel/vmlinux.lds.S
+ *
+ * Xtensa linker script
+ *
+ * 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) 2001 - 2005 Tensilica Inc.
+ *
+ * Chris Zankel <chris@zankel.net>
+ * Marc Gauthier <marc@tensilica.com, marc@alumni.uwaterloo.ca>
+ * Joe Taylor <joe@tensilica.com, joetylr@yahoo.com>
+ */
+
+#include <asm-generic/vmlinux.lds.h>
+
+#include <linux/config.h>
+#define _NOCLANGUAGE
+#include <xtensa/config/core.h>
+#include <xtensa/config/system.h>
+OUTPUT_ARCH(xtensa)
+ENTRY(_start)
+
+#if XCHAL_MEMORY_ORDER == XTHAL_BIGENDIAN
+jiffies = jiffies_64 + 4;
+#else
+jiffies = jiffies_64;
+#endif
+
+#define KERNELOFFSET 0x1000
+
+/* Note: In the following macros, it would be nice to specify only the
+ vector name and section kind and construct "sym" and "section" using
+ CPP concatenation, but that does not work reliably. Concatenating a
+ string with "." produces an invalid token. CPP will not print a
+ warning because it thinks this is an assembly file, but it leaves
+ them as multiple tokens and there may or may not be whitespace
+ between them. */
+
+/* Macro for a relocation entry */
+
+#define RELOCATE_ENTRY(sym, section) \
+ LONG(sym ## _start); \
+ LONG(sym ## _end); \
+ LONG(LOADADDR(section))
+
+/* Macro to define a section for a vector.
+ *
+ * Use of the MIN function catches the types of errors illustrated in
+ * the following example:
+ *
+ * Assume the section .DoubleExceptionVector.literal is completely
+ * full. Then a programmer adds code to .DoubleExceptionVector.text
+ * that produces another literal. The final literal position will
+ * overlay onto the first word of the adjacent code section
+ * .DoubleExceptionVector.text. (In practice, the literals will
+ * overwrite the code, and the first few instructions will be
+ * garbage.)
+ */
+
+#define SECTION_VECTOR(sym, section, addr, max_prevsec_size, prevsec) \
+ section addr : AT((MIN(LOADADDR(prevsec) + max_prevsec_size, \
+ LOADADDR(prevsec) + SIZEOF(prevsec)) + 3) & ~ 3) \
+ { \
+ . = ALIGN(4); \
+ sym ## _start = ABSOLUTE(.); \
+ *(section) \
+ sym ## _end = ABSOLUTE(.); \
+ }
+
+/*
+ * Mapping of input sections to output sections when linking.
+ */
+
+SECTIONS
+{
+ . = XCHAL_KSEG_CACHED_VADDR + KERNELOFFSET;
+ /* .text section */
+
+ _text = .;
+ _stext = .;
+ _ftext = .;
+
+ .text :
+ {
+ /* The .head.text section must be the first section! */
+ *(.head.text)
+ *(.literal .text)
+ *(.srom.text)
+ VMLINUX_SYMBOL(__sched_text_start) = .;
+ *(.sched.text.literal .sched.text)
+ VMLINUX_SYMBOL(__sched_text_end) = .;
+ VMLINUX_SYMBOL(__lock_text_start) = .;
+ *(.spinlock.text.literal .spinlock.text)
+ VMLINUX_SYMBOL(__lock_text_end) = .;
+
+ }
+ _etext = .;
+
+ . = ALIGN(16);
+
+ RODATA
+
+ /* Relocation table */
+
+ . = ALIGN(16);
+ __boot_reloc_table_start = ABSOLUTE(.);
+
+ __relocate : {
+
+ RELOCATE_ENTRY(_WindowVectors_text,
+ .WindowVectors.text);
+#if 0
+ RELOCATE_ENTRY(_KernelExceptionVector_literal,
+ .KernelExceptionVector.literal);
+#endif
+ RELOCATE_ENTRY(_KernelExceptionVector_text,
+ .KernelExceptionVector.text);
+#if 0
+ RELOCATE_ENTRY(_UserExceptionVector_literal,
+ .UserExceptionVector.literal);
+#endif
+ RELOCATE_ENTRY(_UserExceptionVector_text,
+ .UserExceptionVector.text);
+ RELOCATE_ENTRY(_DoubleExceptionVector_literal,
+ .DoubleExceptionVector.literal);
+ RELOCATE_ENTRY(_DoubleExceptionVector_text,
+ .DoubleExceptionVector.text);
+ }
+ __boot_reloc_table_end = ABSOLUTE(.) ;
+
+ .fixup : { *(.fixup) }
+
+ . = ALIGN(16);
+
+ __ex_table : {
+ __start___ex_table = .;
+ *(__ex_table)
+ __stop___ex_table = .;
+ }
+
+ /* Data section */
+
+ . = ALIGN(XCHAL_ICACHE_LINESIZE);
+ _fdata = .;
+ .data :
+ {
+ *(.data) CONSTRUCTORS
+ . = ALIGN(XCHAL_ICACHE_LINESIZE);
+ *(.data.cacheline_aligned)
+ }
+
+ _edata = .;
+
+ /* The initial task */
+ . = ALIGN(8192);
+ .data.init_task : { *(.data.init_task) }
+
+ /* Initialization code and data: */
+
+ . = ALIGN(1<<XCHAL_MMU_MIN_PTE_PAGE_SIZE);
+ __init_begin = .;
+ .init.text : {
+ _sinittext = .;
+ *(.init.text.literal) *(.init.text)
+ _einittext = .;
+ }
+
+ .init.data :
+ {
+ *(.init.data)
+ . = ALIGN(0x4);
+ __tagtable_begin = .;
+ *(.taglist)
+ __tagtable_end = .;
+ }
+
+ . = ALIGN(XCHAL_ICACHE_LINESIZE);
+
+ __setup_start = .;
+ .init.setup : { *(.init.setup) }
+ __setup_end = .;
+
+ __initcall_start = .;
+ .initcall.init : {
+ *(.initcall1.init)
+ *(.initcall2.init)
+ *(.initcall3.init)
+ *(.initcall4.init)
+ *(.initcall5.init)
+ *(.initcall6.init)
+ *(.initcall7.init)
+ }
+ __initcall_end = .;
+
+ __con_initcall_start = .;
+ .con_initcall.init : { *(.con_initcall.init) }
+ __con_initcall_end = .;
+
+ SECURITY_INIT
+
+ . = ALIGN(4);
+
+ __start___ftr_fixup = .;
+ __ftr_fixup : { *(__ftr_fixup) }
+ __stop___ftr_fixup = .;
+
+ . = ALIGN(32);
+ __per_cpu_start = .;
+ .data.percpu : { *(.data.percpu) }
+ __per_cpu_end = .;
+
+ . = ALIGN(4096);
+ __initramfs_start =.;
+ .init.ramfs : { *(.init.ramfs) }
+ __initramfs_end = .;
+
+ /* We need this dummy segment here */
+
+ . = ALIGN(4);
+ .dummy : { LONG(0) }
+
+ /* The vectors are relocated to the real position at startup time */
+
+ SECTION_VECTOR (_WindowVectors_text,
+ .WindowVectors.text,
+ XCHAL_WINDOW_VECTORS_VADDR, 4,
+ .dummy)
+ SECTION_VECTOR (_DebugInterruptVector_literal,
+ .DebugInterruptVector.literal,
+ XCHAL_INTLEVEL_VECTOR_VADDR(XCHAL_DEBUGLEVEL) - 4,
+ SIZEOF(.WindowVectors.text),
+ .WindowVectors.text)
+ SECTION_VECTOR (_DebugInterruptVector_text,
+ .DebugInterruptVector.text,
+ XCHAL_INTLEVEL_VECTOR_VADDR(XCHAL_DEBUGLEVEL),
+ 4,
+ .DebugInterruptVector.literal)
+ SECTION_VECTOR (_KernelExceptionVector_literal,
+ .KernelExceptionVector.literal,
+ XCHAL_KERNELEXC_VECTOR_VADDR - 4,
+ SIZEOF(.DebugInterruptVector.text),
+ .DebugInterruptVector.text)
+ SECTION_VECTOR (_KernelExceptionVector_text,
+ .KernelExceptionVector.text,
+ XCHAL_KERNELEXC_VECTOR_VADDR,
+ 4,
+ .KernelExceptionVector.literal)
+ SECTION_VECTOR (_UserExceptionVector_literal,
+ .UserExceptionVector.literal,
+ XCHAL_USEREXC_VECTOR_VADDR - 4,
+ SIZEOF(.KernelExceptionVector.text),
+ .KernelExceptionVector.text)
+ SECTION_VECTOR (_UserExceptionVector_text,
+ .UserExceptionVector.text,
+ XCHAL_USEREXC_VECTOR_VADDR,
+ 4,
+ .UserExceptionVector.literal)
+ SECTION_VECTOR (_DoubleExceptionVector_literal,
+ .DoubleExceptionVector.literal,
+ XCHAL_DOUBLEEXC_VECTOR_VADDR - 16,
+ SIZEOF(.UserExceptionVector.text),
+ .UserExceptionVector.text)
+ SECTION_VECTOR (_DoubleExceptionVector_text,
+ .DoubleExceptionVector.text,
+ XCHAL_DOUBLEEXC_VECTOR_VADDR,
+ 32,
+ .DoubleExceptionVector.literal)
+
+ . = (LOADADDR( .DoubleExceptionVector.text ) + SIZEOF( .DoubleExceptionVector.text ) + 3) & ~ 3;
+ . = ALIGN(1<<XCHAL_MMU_MIN_PTE_PAGE_SIZE);
+
+ __init_end = .;
+
+ . = ALIGN(8192);
+
+ /* BSS section */
+ _bss_start = .;
+ .sbss : { *(.sbss) *(.scommon) }
+ .bss : { *(COMMON) *(.bss) }
+ _bss_end = .;
+ _end = .;
+
+ /* only used by the boot loader */
+
+ . = ALIGN(0x10);
+ .bootstrap : { *(.bootstrap.literal .bootstrap.text .bootstrap.data) }
+
+ . = ALIGN(0x1000);
+ __initrd_start = .;
+ .initrd : { *(.initrd) }
+ __initrd_end = .;
+
+ .ResetVector.text XCHAL_RESET_VECTOR_VADDR :
+ {
+ *(.ResetVector.text)
+ }
+
+
+ /* Sections to be discarded */
+ /DISCARD/ :
+ {
+ *(.text.exit)
+ *(.text.exit.literal)
+ *(.data.exit)
+ *(.exitcall.exit)
+ }
+
+
+ .debug 0 : { *(.debug) }
+ .line 0 : { *(.line) }
+ .debug_srcinfo 0 : { *(.debug_srcinfo) }
+ .debug_sfnames 0 : { *(.debug_sfnames) }
+ .debug_aranges 0 : { *(.debug_aranges) }
+ .debug_pubnames 0 : { *(.debug_pubnames) }
+ .debug_info 0 : { *(.debug_info) }
+ .debug_abbrev 0 : { *(.debug_abbrev) }
+ .debug_line 0 : { *(.debug_line) }
+ .debug_frame 0 : { *(.debug_frame) }
+ .debug_str 0 : { *(.debug_str) }
+ .debug_loc 0 : { *(.debug_loc) }
+ .debug_macinfo 0 : { *(.debug_macinfo) }
+ .debug_weaknames 0 : { *(.debug_weaknames) }
+ .debug_funcnames 0 : { *(.debug_funcnames) }
+ .debug_typenames 0 : { *(.debug_typenames) }
+ .debug_varnames 0 : { *(.debug_varnames) }
+
+ .xt.insn 0 :
+ {
+ *(.xt.insn)
+ *(.gnu.linkonce.x*)
+ }
+
+ .xt.lit 0 :
+ {
+ *(.xt.lit)
+ *(.gnu.linkonce.p*)
+ }
+}
diff --git a/arch/xtensa/kernel/xtensa_ksyms.c b/arch/xtensa/kernel/xtensa_ksyms.c
new file mode 100644
index 000000000000..efae56a51475
--- /dev/null
+++ b/arch/xtensa/kernel/xtensa_ksyms.c
@@ -0,0 +1,123 @@
+/*
+ * arch/xtensa/kernel/xtensa_ksyms.c
+ *
+ * Export Xtensa-specific functions for loadable modules.
+ *
+ * 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) 2001 - 2005 Tensilica Inc.
+ *
+ * Joe Taylor <joe@tensilica.com>
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <asm/irq.h>
+#include <linux/in6.h>
+#include <linux/pci.h>
+#include <linux/ide.h>
+
+#include <asm/uaccess.h>
+#include <asm/checksum.h>
+#include <asm/dma.h>
+#include <asm/io.h>
+#include <asm/page.h>
+#include <asm/pgalloc.h>
+#include <asm/semaphore.h>
+#ifdef CONFIG_BLK_DEV_FD
+#include <asm/floppy.h>
+#endif
+#ifdef CONFIG_NET
+#include <net/checksum.h>
+#endif /* CONFIG_NET */
+
+
+/*
+ * String functions
+ */
+EXPORT_SYMBOL(memcmp);
+EXPORT_SYMBOL(memset);
+EXPORT_SYMBOL(memcpy);
+EXPORT_SYMBOL(memmove);
+EXPORT_SYMBOL(memchr);
+EXPORT_SYMBOL(strcat);
+EXPORT_SYMBOL(strchr);
+EXPORT_SYMBOL(strlen);
+EXPORT_SYMBOL(strpbrk);
+EXPORT_SYMBOL(strncat);
+EXPORT_SYMBOL(strnlen);
+EXPORT_SYMBOL(strrchr);
+EXPORT_SYMBOL(strstr);
+
+EXPORT_SYMBOL(enable_irq);
+EXPORT_SYMBOL(disable_irq);
+EXPORT_SYMBOL(kernel_thread);
+
+/*
+ * gcc internal math functions
+ */
+extern long long __ashrdi3(long long, int);
+extern long long __ashldi3(long long, int);
+extern long long __lshrdi3(long long, int);
+extern int __divsi3(int, int);
+extern int __modsi3(int, int);
+extern long long __muldi3(long long, long long);
+extern int __mulsi3(int, int);
+extern unsigned int __udivsi3(unsigned int, unsigned int);
+extern unsigned int __umodsi3(unsigned int, unsigned int);
+extern unsigned long long __umoddi3(unsigned long long, unsigned long long);
+extern unsigned long long __udivdi3(unsigned long long, unsigned long long);
+
+EXPORT_SYMBOL(__ashldi3);
+EXPORT_SYMBOL(__ashrdi3);
+EXPORT_SYMBOL(__lshrdi3);
+EXPORT_SYMBOL(__divsi3);
+EXPORT_SYMBOL(__modsi3);
+EXPORT_SYMBOL(__muldi3);
+EXPORT_SYMBOL(__mulsi3);
+EXPORT_SYMBOL(__udivsi3);
+EXPORT_SYMBOL(__umodsi3);
+EXPORT_SYMBOL(__udivdi3);
+EXPORT_SYMBOL(__umoddi3);
+
+/*
+ * Semaphore operations
+ */
+EXPORT_SYMBOL(__down);
+EXPORT_SYMBOL(__down_interruptible);
+EXPORT_SYMBOL(__down_trylock);
+EXPORT_SYMBOL(__up);
+
+#ifdef CONFIG_NET
+/*
+ * Networking support
+ */
+EXPORT_SYMBOL(csum_partial_copy_generic);
+#endif /* CONFIG_NET */
+
+/*
+ * Architecture-specific symbols
+ */
+EXPORT_SYMBOL(__xtensa_copy_user);
+
+/*
+ * Kernel hacking ...
+ */
+
+#if defined(CONFIG_VGA_CONSOLE) || defined(CONFIG_DUMMY_CONSOLE)
+// FIXME EXPORT_SYMBOL(screen_info);
+#endif
+
+EXPORT_SYMBOL(get_wchan);
+
+EXPORT_SYMBOL(outsb);
+EXPORT_SYMBOL(outsw);
+EXPORT_SYMBOL(outsl);
+EXPORT_SYMBOL(insb);
+EXPORT_SYMBOL(insw);
+EXPORT_SYMBOL(insl);
diff --git a/arch/xtensa/lib/Makefile b/arch/xtensa/lib/Makefile
new file mode 100644
index 000000000000..ed935b58e8a4
--- /dev/null
+++ b/arch/xtensa/lib/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for Xtensa-specific library files.
+#
+
+lib-y += memcopy.o memset.o checksum.o strcasecmp.o \
+ usercopy.o strncpy_user.o strnlen_user.o
+lib-$(CONFIG_PCI) += pci-auto.o
diff --git a/arch/xtensa/lib/checksum.S b/arch/xtensa/lib/checksum.S
new file mode 100644
index 000000000000..e2d64dfd530c
--- /dev/null
+++ b/arch/xtensa/lib/checksum.S
@@ -0,0 +1,410 @@
+/*
+ * 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
+ *
+ * Xtensa version: Copyright (C) 2001 Tensilica, Inc. by Kevin Chea
+ * Optimized by Joe Taylor
+ *
+ * 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/errno.h>
+#include <linux/linkage.h>
+#define _ASMLANGUAGE
+#include <xtensa/config/core.h>
+
+/*
+ * computes a partial checksum, e.g. for TCP/UDP fragments
+ */
+
+/*
+ * unsigned int csum_partial(const unsigned char *buf, int len,
+ * unsigned int sum);
+ * a2 = buf
+ * a3 = len
+ * a4 = sum
+ *
+ * This function assumes 2- or 4-byte alignment. Other alignments will fail!
+ */
+
+/* ONES_ADD converts twos-complement math to ones-complement. */
+#define ONES_ADD(sum, val) \
+ add sum, sum, val ; \
+ bgeu sum, val, 99f ; \
+ addi sum, sum, 1 ; \
+99: ;
+
+.text
+ENTRY(csum_partial)
+ /*
+ * Experiments with Ethernet and SLIP connections show that buf
+ * is aligned on either a 2-byte or 4-byte boundary.
+ */
+ entry sp, 32
+ extui a5, a2, 0, 2
+ bnez a5, 8f /* branch if 2-byte aligned */
+ /* Fall-through on common case, 4-byte alignment */
+1:
+ srli a5, a3, 5 /* 32-byte chunks */
+#if XCHAL_HAVE_LOOPS
+ loopgtz a5, 2f
+#else
+ beqz a5, 2f
+ slli a5, a5, 5
+ add a5, a5, a2 /* a5 = end of last 32-byte chunk */
+.Loop1:
+#endif
+ l32i a6, a2, 0
+ l32i a7, a2, 4
+ ONES_ADD(a4, a6)
+ ONES_ADD(a4, a7)
+ l32i a6, a2, 8
+ l32i a7, a2, 12
+ ONES_ADD(a4, a6)
+ ONES_ADD(a4, a7)
+ l32i a6, a2, 16
+ l32i a7, a2, 20
+ ONES_ADD(a4, a6)
+ ONES_ADD(a4, a7)
+ l32i a6, a2, 24
+ l32i a7, a2, 28
+ ONES_ADD(a4, a6)
+ ONES_ADD(a4, a7)
+ addi a2, a2, 4*8
+#if !XCHAL_HAVE_LOOPS
+ blt a2, a5, .Loop1
+#endif
+2:
+ extui a5, a3, 2, 3 /* remaining 4-byte chunks */
+#if XCHAL_HAVE_LOOPS
+ loopgtz a5, 3f
+#else
+ beqz a5, 3f
+ slli a5, a5, 2
+ add a5, a5, a2 /* a5 = end of last 4-byte chunk */
+.Loop2:
+#endif
+ l32i a6, a2, 0
+ ONES_ADD(a4, a6)
+ addi a2, a2, 4
+#if !XCHAL_HAVE_LOOPS
+ blt a2, a5, .Loop2
+#endif
+3:
+ _bbci.l a3, 1, 5f /* remaining 2-byte chunk */
+ l16ui a6, a2, 0
+ ONES_ADD(a4, a6)
+ addi a2, a2, 2
+5:
+ _bbci.l a3, 0, 7f /* remaining 1-byte chunk */
+6: l8ui a6, a2, 0
+#ifdef __XTENSA_EB__
+ slli a6, a6, 8 /* load byte into bits 8..15 */
+#endif
+ ONES_ADD(a4, a6)
+7:
+ mov a2, a4
+ retw
+
+ /* uncommon case, buf is 2-byte aligned */
+8:
+ beqz a3, 7b /* branch if len == 0 */
+ beqi a3, 1, 6b /* branch if len == 1 */
+
+ extui a5, a2, 0, 1
+ bnez a5, 8f /* branch if 1-byte aligned */
+
+ l16ui a6, a2, 0 /* common case, len >= 2 */
+ ONES_ADD(a4, a6)
+ addi a2, a2, 2 /* adjust buf */
+ addi a3, a3, -2 /* adjust len */
+ j 1b /* now buf is 4-byte aligned */
+
+ /* case: odd-byte aligned, len > 1
+ * This case is dog slow, so don't give us an odd address.
+ * (I don't think this ever happens, but just in case.)
+ */
+8:
+ srli a5, a3, 2 /* 4-byte chunks */
+#if XCHAL_HAVE_LOOPS
+ loopgtz a5, 2f
+#else
+ beqz a5, 2f
+ slli a5, a5, 2
+ add a5, a5, a2 /* a5 = end of last 4-byte chunk */
+.Loop3:
+#endif
+ l8ui a6, a2, 0 /* bits 24..31 */
+ l16ui a7, a2, 1 /* bits 8..23 */
+ l8ui a8, a2, 3 /* bits 0.. 8 */
+#ifdef __XTENSA_EB__
+ slli a6, a6, 24
+#else
+ slli a8, a8, 24
+#endif
+ slli a7, a7, 8
+ or a7, a7, a6
+ or a7, a7, a8
+ ONES_ADD(a4, a7)
+ addi a2, a2, 4
+#if !XCHAL_HAVE_LOOPS
+ blt a2, a5, .Loop3
+#endif
+2:
+ _bbci.l a3, 1, 3f /* remaining 2-byte chunk, still odd addr */
+ l8ui a6, a2, 0
+ l8ui a7, a2, 1
+#ifdef __XTENSA_EB__
+ slli a6, a6, 8
+#else
+ slli a7, a7, 8
+#endif
+ or a7, a7, a6
+ ONES_ADD(a4, a7)
+ addi a2, a2, 2
+3:
+ j 5b /* branch to handle the remaining byte */
+
+
+
+/*
+ * Copy from ds while checksumming, otherwise like csum_partial
+ *
+ * The macros SRC and DST specify the type of access for the instruction.
+ * thus we can call a custom exception handler for each access type.
+ */
+
+#define SRC(y...) \
+ 9999: y; \
+ .section __ex_table, "a"; \
+ .long 9999b, 6001f ; \
+ .previous
+
+#define DST(y...) \
+ 9999: y; \
+ .section __ex_table, "a"; \
+ .long 9999b, 6002f ; \
+ .previous
+
+/*
+unsigned int csum_partial_copy_generic (const char *src, char *dst, int len,
+ int sum, int *src_err_ptr, int *dst_err_ptr)
+ a2 = src
+ a3 = dst
+ a4 = len
+ a5 = sum
+ a6 = src_err_ptr
+ a7 = dst_err_ptr
+ a8 = temp
+ a9 = temp
+ a10 = temp
+ a11 = original len for exception handling
+ a12 = original dst for exception handling
+
+ This function is optimized for 4-byte aligned addresses. Other
+ alignments work, but not nearly as efficiently.
+ */
+
+ENTRY(csum_partial_copy_generic)
+ entry sp, 32
+ mov a12, a3
+ mov a11, a4
+ or a10, a2, a3
+
+ /* We optimize the following alignment tests for the 4-byte
+ aligned case. Two bbsi.l instructions might seem more optimal
+ (commented out below). However, both labels 5: and 3: are out
+ of the imm8 range, so the assembler relaxes them into
+ equivalent bbci.l, j combinations, which is actually
+ slower. */
+
+ extui a9, a10, 0, 2
+ beqz a9, 1f /* branch if both are 4-byte aligned */
+ bbsi.l a10, 0, 5f /* branch if one address is odd */
+ j 3f /* one address is 2-byte aligned */
+
+/* _bbsi.l a10, 0, 5f */ /* branch if odd address */
+/* _bbsi.l a10, 1, 3f */ /* branch if 2-byte-aligned address */
+
+1:
+ /* src and dst are both 4-byte aligned */
+ srli a10, a4, 5 /* 32-byte chunks */
+#if XCHAL_HAVE_LOOPS
+ loopgtz a10, 2f
+#else
+ beqz a10, 2f
+ slli a10, a10, 5
+ add a10, a10, a2 /* a10 = end of last 32-byte src chunk */
+.Loop5:
+#endif
+SRC( l32i a9, a2, 0 )
+SRC( l32i a8, a2, 4 )
+DST( s32i a9, a3, 0 )
+DST( s32i a8, a3, 4 )
+ ONES_ADD(a5, a9)
+ ONES_ADD(a5, a8)
+SRC( l32i a9, a2, 8 )
+SRC( l32i a8, a2, 12 )
+DST( s32i a9, a3, 8 )
+DST( s32i a8, a3, 12 )
+ ONES_ADD(a5, a9)
+ ONES_ADD(a5, a8)
+SRC( l32i a9, a2, 16 )
+SRC( l32i a8, a2, 20 )
+DST( s32i a9, a3, 16 )
+DST( s32i a8, a3, 20 )
+ ONES_ADD(a5, a9)
+ ONES_ADD(a5, a8)
+SRC( l32i a9, a2, 24 )
+SRC( l32i a8, a2, 28 )
+DST( s32i a9, a3, 24 )
+DST( s32i a8, a3, 28 )
+ ONES_ADD(a5, a9)
+ ONES_ADD(a5, a8)
+ addi a2, a2, 32
+ addi a3, a3, 32
+#if !XCHAL_HAVE_LOOPS
+ blt a2, a10, .Loop5
+#endif
+2:
+ extui a10, a4, 2, 3 /* remaining 4-byte chunks */
+ extui a4, a4, 0, 2 /* reset len for general-case, 2-byte chunks */
+#if XCHAL_HAVE_LOOPS
+ loopgtz a10, 3f
+#else
+ beqz a10, 3f
+ slli a10, a10, 2
+ add a10, a10, a2 /* a10 = end of last 4-byte src chunk */
+.Loop6:
+#endif
+SRC( l32i a9, a2, 0 )
+DST( s32i a9, a3, 0 )
+ ONES_ADD(a5, a9)
+ addi a2, a2, 4
+ addi a3, a3, 4
+#if !XCHAL_HAVE_LOOPS
+ blt a2, a10, .Loop6
+#endif
+3:
+ /*
+ Control comes to here in two cases: (1) It may fall through
+ to here from the 4-byte alignment case to process, at most,
+ one 2-byte chunk. (2) It branches to here from above if
+ either src or dst is 2-byte aligned, and we process all bytes
+ here, except for perhaps a trailing odd byte. It's
+ inefficient, so align your addresses to 4-byte boundaries.
+
+ a2 = src
+ a3 = dst
+ a4 = len
+ a5 = sum
+ */
+ srli a10, a4, 1 /* 2-byte chunks */
+#if XCHAL_HAVE_LOOPS
+ loopgtz a10, 4f
+#else
+ beqz a10, 4f
+ slli a10, a10, 1
+ add a10, a10, a2 /* a10 = end of last 2-byte src chunk */
+.Loop7:
+#endif
+SRC( l16ui a9, a2, 0 )
+DST( s16i a9, a3, 0 )
+ ONES_ADD(a5, a9)
+ addi a2, a2, 2
+ addi a3, a3, 2
+#if !XCHAL_HAVE_LOOPS
+ blt a2, a10, .Loop7
+#endif
+4:
+ /* This section processes a possible trailing odd byte. */
+ _bbci.l a4, 0, 8f /* 1-byte chunk */
+SRC( l8ui a9, a2, 0 )
+DST( s8i a9, a3, 0 )
+#ifdef __XTENSA_EB__
+ slli a9, a9, 8 /* shift byte to bits 8..15 */
+#endif
+ ONES_ADD(a5, a9)
+8:
+ mov a2, a5
+ retw
+
+5:
+ /* Control branch to here when either src or dst is odd. We
+ process all bytes using 8-bit accesses. Grossly inefficient,
+ so don't feed us an odd address. */
+
+ srli a10, a4, 1 /* handle in pairs for 16-bit csum */
+#if XCHAL_HAVE_LOOPS
+ loopgtz a10, 6f
+#else
+ beqz a10, 6f
+ slli a10, a10, 1
+ add a10, a10, a2 /* a10 = end of last odd-aligned, 2-byte src chunk */
+.Loop8:
+#endif
+SRC( l8ui a9, a2, 0 )
+SRC( l8ui a8, a2, 1 )
+DST( s8i a9, a3, 0 )
+DST( s8i a8, a3, 1 )
+#ifdef __XTENSA_EB__
+ slli a9, a9, 8 /* combine into a single 16-bit value */
+#else /* for checksum computation */
+ slli a8, a8, 8
+#endif
+ or a9, a9, a8
+ ONES_ADD(a5, a9)
+ addi a2, a2, 2
+ addi a3, a3, 2
+#if !XCHAL_HAVE_LOOPS
+ blt a2, a10, .Loop8
+#endif
+6:
+ j 4b /* process the possible trailing odd byte */
+
+
+# Exception handler:
+.section .fixup, "ax"
+/*
+ a6 = src_err_ptr
+ a7 = dst_err_ptr
+ a11 = original len for exception handling
+ a12 = original dst for exception handling
+*/
+
+6001:
+ _movi a2, -EFAULT
+ s32i a2, a6, 0 /* src_err_ptr */
+
+ # clear the complete destination - computing the rest
+ # is too much work
+ movi a2, 0
+#if XCHAL_HAVE_LOOPS
+ loopgtz a11, 2f
+#else
+ beqz a11, 2f
+ add a11, a11, a12 /* a11 = ending address */
+.Leloop:
+#endif
+ s8i a2, a12, 0
+ addi a12, a12, 1
+#if !XCHAL_HAVE_LOOPS
+ blt a12, a11, .Leloop
+#endif
+2:
+ retw
+
+6002:
+ movi a2, -EFAULT
+ s32i a2, a7, 0 /* dst_err_ptr */
+ movi a2, 0
+ retw
+
+.previous
+
diff --git a/arch/xtensa/lib/memcopy.S b/arch/xtensa/lib/memcopy.S
new file mode 100644
index 000000000000..e8f6d7eb7222
--- /dev/null
+++ b/arch/xtensa/lib/memcopy.S
@@ -0,0 +1,315 @@
+/*
+ * arch/xtensa/lib/hal/memcopy.S -- Core HAL library functions
+ * xthal_memcpy and xthal_bcopy
+ *
+ * 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) 2002 - 2005 Tensilica Inc.
+ */
+
+#include <xtensa/coreasm.h>
+
+ .macro src_b r, w0, w1
+#ifdef __XTENSA_EB__
+ src \r, \w0, \w1
+#else
+ src \r, \w1, \w0
+#endif
+ .endm
+
+ .macro ssa8 r
+#ifdef __XTENSA_EB__
+ ssa8b \r
+#else
+ ssa8l \r
+#endif
+ .endm
+
+
+/*
+ * void *memcpy(void *dst, const void *src, size_t len);
+ * void *memmove(void *dst, const void *src, size_t len);
+ * void *bcopy(const void *src, void *dst, size_t len);
+ *
+ * This function is intended to do the same thing as the standard
+ * library function memcpy() (or bcopy()) for most cases.
+ * However, where the source and/or destination references
+ * an instruction RAM or ROM or a data RAM or ROM, that
+ * source and/or destination will always be accessed with
+ * 32-bit load and store instructions (as required for these
+ * types of devices).
+ *
+ * !!!!!!! XTFIXME:
+ * !!!!!!! Handling of IRAM/IROM has not yet
+ * !!!!!!! been implemented.
+ *
+ * The bcopy version is provided here to avoid the overhead
+ * of an extra call, for callers that require this convention.
+ *
+ * The (general case) algorithm is as follows:
+ * If destination is unaligned, align it by conditionally
+ * copying 1 and 2 bytes.
+ * If source is aligned,
+ * do 16 bytes with a loop, and then finish up with
+ * 8, 4, 2, and 1 byte copies conditional on the length;
+ * else (if source is unaligned),
+ * do the same, but use SRC to align the source data.
+ * This code tries to use fall-through branches for the common
+ * case of aligned source and destination and multiple
+ * of 4 (or 8) length.
+ *
+ * Register use:
+ * a0/ return address
+ * a1/ stack pointer
+ * a2/ return value
+ * a3/ src
+ * a4/ length
+ * a5/ dst
+ * a6/ tmp
+ * a7/ tmp
+ * a8/ tmp
+ * a9/ tmp
+ * a10/ tmp
+ * a11/ tmp
+ */
+
+ .text
+ .align 4
+ .global bcopy
+ .type bcopy,@function
+bcopy:
+ entry sp, 16 # minimal stack frame
+ # a2=src, a3=dst, a4=len
+ mov a5, a3 # copy dst so that a2 is return value
+ mov a3, a2
+ mov a2, a5
+ j .Lcommon # go to common code for memcpy+bcopy
+
+
+/*
+ * Byte by byte copy
+ */
+ .align 4
+ .byte 0 # 1 mod 4 alignment for LOOPNEZ
+ # (0 mod 4 alignment for LBEG)
+.Lbytecopy:
+#if XCHAL_HAVE_LOOPS
+ loopnez a4, .Lbytecopydone
+#else /* !XCHAL_HAVE_LOOPS */
+ beqz a4, .Lbytecopydone
+ add a7, a3, a4 # a7 = end address for source
+#endif /* !XCHAL_HAVE_LOOPS */
+.Lnextbyte:
+ l8ui a6, a3, 0
+ addi a3, a3, 1
+ s8i a6, a5, 0
+ addi a5, a5, 1
+#if !XCHAL_HAVE_LOOPS
+ blt a3, a7, .Lnextbyte
+#endif /* !XCHAL_HAVE_LOOPS */
+.Lbytecopydone:
+ retw
+
+/*
+ * Destination is unaligned
+ */
+
+ .align 4
+.Ldst1mod2: # dst is only byte aligned
+ _bltui a4, 7, .Lbytecopy # do short copies byte by byte
+
+ # copy 1 byte
+ l8ui a6, a3, 0
+ addi a3, a3, 1
+ addi a4, a4, -1
+ s8i a6, a5, 0
+ addi a5, a5, 1
+ _bbci.l a5, 1, .Ldstaligned # if dst is now aligned, then
+ # return to main algorithm
+.Ldst2mod4: # dst 16-bit aligned
+ # copy 2 bytes
+ _bltui a4, 6, .Lbytecopy # do short copies byte by byte
+ l8ui a6, a3, 0
+ l8ui a7, a3, 1
+ addi a3, a3, 2
+ addi a4, a4, -2
+ s8i a6, a5, 0
+ s8i a7, a5, 1
+ addi a5, a5, 2
+ j .Ldstaligned # dst is now aligned, return to main algorithm
+
+ .align 4
+ .global memcpy
+ .type memcpy,@function
+memcpy:
+ .global memmove
+ .type memmove,@function
+memmove:
+
+ entry sp, 16 # minimal stack frame
+ # a2/ dst, a3/ src, a4/ len
+ mov a5, a2 # copy dst so that a2 is return value
+.Lcommon:
+ _bbsi.l a2, 0, .Ldst1mod2 # if dst is 1 mod 2
+ _bbsi.l a2, 1, .Ldst2mod4 # if dst is 2 mod 4
+.Ldstaligned: # return here from .Ldst?mod? once dst is aligned
+ srli a7, a4, 4 # number of loop iterations with 16B
+ # per iteration
+ movi a8, 3 # if source is not aligned,
+ _bany a3, a8, .Lsrcunaligned # then use shifting copy
+ /*
+ * Destination and source are word-aligned, use word copy.
+ */
+ # copy 16 bytes per iteration for word-aligned dst and word-aligned src
+#if XCHAL_HAVE_LOOPS
+ loopnez a7, .Loop1done
+#else /* !XCHAL_HAVE_LOOPS */
+ beqz a7, .Loop1done
+ slli a8, a7, 4
+ add a8, a8, a3 # a8 = end of last 16B source chunk
+#endif /* !XCHAL_HAVE_LOOPS */
+.Loop1:
+ l32i a6, a3, 0
+ l32i a7, a3, 4
+ s32i a6, a5, 0
+ l32i a6, a3, 8
+ s32i a7, a5, 4
+ l32i a7, a3, 12
+ s32i a6, a5, 8
+ addi a3, a3, 16
+ s32i a7, a5, 12
+ addi a5, a5, 16
+#if !XCHAL_HAVE_LOOPS
+ blt a3, a8, .Loop1
+#endif /* !XCHAL_HAVE_LOOPS */
+.Loop1done:
+ bbci.l a4, 3, .L2
+ # copy 8 bytes
+ l32i a6, a3, 0
+ l32i a7, a3, 4
+ addi a3, a3, 8
+ s32i a6, a5, 0
+ s32i a7, a5, 4
+ addi a5, a5, 8
+.L2:
+ bbsi.l a4, 2, .L3
+ bbsi.l a4, 1, .L4
+ bbsi.l a4, 0, .L5
+ retw
+.L3:
+ # copy 4 bytes
+ l32i a6, a3, 0
+ addi a3, a3, 4
+ s32i a6, a5, 0
+ addi a5, a5, 4
+ bbsi.l a4, 1, .L4
+ bbsi.l a4, 0, .L5
+ retw
+.L4:
+ # copy 2 bytes
+ l16ui a6, a3, 0
+ addi a3, a3, 2
+ s16i a6, a5, 0
+ addi a5, a5, 2
+ bbsi.l a4, 0, .L5
+ retw
+.L5:
+ # copy 1 byte
+ l8ui a6, a3, 0
+ s8i a6, a5, 0
+ retw
+
+/*
+ * Destination is aligned, Source is unaligned
+ */
+
+ .align 4
+.Lsrcunaligned:
+ _beqz a4, .Ldone # avoid loading anything for zero-length copies
+ # copy 16 bytes per iteration for word-aligned dst and unaligned src
+ ssa8 a3 # set shift amount from byte offset
+#define SIM_CHECKS_ALIGNMENT 1 /* set to 1 when running on ISS (simulator) with the
+ lint or ferret client, or 0 to save a few cycles */
+#if XCHAL_UNALIGNED_LOAD_EXCEPTION || SIM_CHECKS_ALIGNMENT
+ and a11, a3, a8 # save unalignment offset for below
+ sub a3, a3, a11 # align a3
+#endif
+ l32i a6, a3, 0 # load first word
+#if XCHAL_HAVE_LOOPS
+ loopnez a7, .Loop2done
+#else /* !XCHAL_HAVE_LOOPS */
+ beqz a7, .Loop2done
+ slli a10, a7, 4
+ add a10, a10, a3 # a10 = end of last 16B source chunk
+#endif /* !XCHAL_HAVE_LOOPS */
+.Loop2:
+ l32i a7, a3, 4
+ l32i a8, a3, 8
+ src_b a6, a6, a7
+ s32i a6, a5, 0
+ l32i a9, a3, 12
+ src_b a7, a7, a8
+ s32i a7, a5, 4
+ l32i a6, a3, 16
+ src_b a8, a8, a9
+ s32i a8, a5, 8
+ addi a3, a3, 16
+ src_b a9, a9, a6
+ s32i a9, a5, 12
+ addi a5, a5, 16
+#if !XCHAL_HAVE_LOOPS
+ blt a3, a10, .Loop2
+#endif /* !XCHAL_HAVE_LOOPS */
+.Loop2done:
+ bbci.l a4, 3, .L12
+ # copy 8 bytes
+ l32i a7, a3, 4
+ l32i a8, a3, 8
+ src_b a6, a6, a7
+ s32i a6, a5, 0
+ addi a3, a3, 8
+ src_b a7, a7, a8
+ s32i a7, a5, 4
+ addi a5, a5, 8
+ mov a6, a8
+.L12:
+ bbci.l a4, 2, .L13
+ # copy 4 bytes
+ l32i a7, a3, 4
+ addi a3, a3, 4
+ src_b a6, a6, a7
+ s32i a6, a5, 0
+ addi a5, a5, 4
+ mov a6, a7
+.L13:
+#if XCHAL_UNALIGNED_LOAD_EXCEPTION || SIM_CHECKS_ALIGNMENT
+ add a3, a3, a11 # readjust a3 with correct misalignment
+#endif
+ bbsi.l a4, 1, .L14
+ bbsi.l a4, 0, .L15
+.Ldone: retw
+.L14:
+ # copy 2 bytes
+ l8ui a6, a3, 0
+ l8ui a7, a3, 1
+ addi a3, a3, 2
+ s8i a6, a5, 0
+ s8i a7, a5, 1
+ addi a5, a5, 2
+ bbsi.l a4, 0, .L15
+ retw
+.L15:
+ # copy 1 byte
+ l8ui a6, a3, 0
+ s8i a6, a5, 0
+ retw
+
+/*
+ * Local Variables:
+ * mode:fundamental
+ * comment-start: "# "
+ * comment-start-skip: "# *"
+ * End:
+ */
diff --git a/arch/xtensa/lib/memset.S b/arch/xtensa/lib/memset.S
new file mode 100644
index 000000000000..4de25134bc62
--- /dev/null
+++ b/arch/xtensa/lib/memset.S
@@ -0,0 +1,160 @@
+/*
+ * arch/xtensa/lib/memset.S
+ *
+ * ANSI C standard library function memset
+ * (Well, almost. .fixup code might return zero.)
+ *
+ * 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) 2002 Tensilica Inc.
+ */
+
+#include <xtensa/coreasm.h>
+
+/*
+ * void *memset(void *dst, int c, size_t length)
+ *
+ * The algorithm is as follows:
+ * Create a word with c in all byte positions
+ * If the destination is aligned,
+ * do 16B chucks with a loop, and then finish up with
+ * 8B, 4B, 2B, and 1B stores conditional on the length.
+ * If destination is unaligned, align it by conditionally
+ * setting 1B and 2B and then go to aligned case.
+ * This code tries to use fall-through branches for the common
+ * case of an aligned destination (except for the branches to
+ * the alignment labels).
+ */
+
+/* Load or store instructions that may cause exceptions use the EX macro. */
+
+#define EX(insn,reg1,reg2,offset,handler) \
+9: insn reg1, reg2, offset; \
+ .section __ex_table, "a"; \
+ .word 9b, handler; \
+ .previous
+
+
+.text
+.align 4
+.global memset
+.type memset,@function
+memset:
+ entry sp, 16 # minimal stack frame
+ # a2/ dst, a3/ c, a4/ length
+ extui a3, a3, 0, 8 # mask to just 8 bits
+ slli a7, a3, 8 # duplicate character in all bytes of word
+ or a3, a3, a7 # ...
+ slli a7, a3, 16 # ...
+ or a3, a3, a7 # ...
+ mov a5, a2 # copy dst so that a2 is return value
+ movi a6, 3 # for alignment tests
+ bany a2, a6, .Ldstunaligned # if dst is unaligned
+.L0: # return here from .Ldstunaligned when dst is aligned
+ srli a7, a4, 4 # number of loop iterations with 16B
+ # per iteration
+ bnez a4, .Laligned
+ retw
+
+/*
+ * Destination is word-aligned.
+ */
+ # set 16 bytes per iteration for word-aligned dst
+ .align 4 # 1 mod 4 alignment for LOOPNEZ
+ .byte 0 # (0 mod 4 alignment for LBEG)
+.Laligned:
+#if XCHAL_HAVE_LOOPS
+ loopnez a7, .Loop1done
+#else /* !XCHAL_HAVE_LOOPS */
+ beqz a7, .Loop1done
+ slli a6, a7, 4
+ add a6, a6, a5 # a6 = end of last 16B chunk
+#endif /* !XCHAL_HAVE_LOOPS */
+.Loop1:
+ EX(s32i, a3, a5, 0, memset_fixup)
+ EX(s32i, a3, a5, 4, memset_fixup)
+ EX(s32i, a3, a5, 8, memset_fixup)
+ EX(s32i, a3, a5, 12, memset_fixup)
+ addi a5, a5, 16
+#if !XCHAL_HAVE_LOOPS
+ blt a5, a6, .Loop1
+#endif /* !XCHAL_HAVE_LOOPS */
+.Loop1done:
+ bbci.l a4, 3, .L2
+ # set 8 bytes
+ EX(s32i, a3, a5, 0, memset_fixup)
+ EX(s32i, a3, a5, 4, memset_fixup)
+ addi a5, a5, 8
+.L2:
+ bbci.l a4, 2, .L3
+ # set 4 bytes
+ EX(s32i, a3, a5, 0, memset_fixup)
+ addi a5, a5, 4
+.L3:
+ bbci.l a4, 1, .L4
+ # set 2 bytes
+ EX(s16i, a3, a5, 0, memset_fixup)
+ addi a5, a5, 2
+.L4:
+ bbci.l a4, 0, .L5
+ # set 1 byte
+ EX(s8i, a3, a5, 0, memset_fixup)
+.L5:
+.Lret1:
+ retw
+
+/*
+ * Destination is unaligned
+ */
+
+.Ldstunaligned:
+ bltui a4, 8, .Lbyteset # do short copies byte by byte
+ bbci.l a5, 0, .L20 # branch if dst alignment half-aligned
+ # dst is only byte aligned
+ # set 1 byte
+ EX(s8i, a3, a5, 0, memset_fixup)
+ addi a5, a5, 1
+ addi a4, a4, -1
+ # now retest if dst aligned
+ bbci.l a5, 1, .L0 # if now aligned, return to main algorithm
+.L20:
+ # dst half-aligned
+ # set 2 bytes
+ EX(s16i, a3, a5, 0, memset_fixup)
+ addi a5, a5, 2
+ addi a4, a4, -2
+ j .L0 # dst is now aligned, return to main algorithm
+
+/*
+ * Byte by byte set
+ */
+ .align 4
+ .byte 0 # 1 mod 4 alignment for LOOPNEZ
+ # (0 mod 4 alignment for LBEG)
+.Lbyteset:
+#if XCHAL_HAVE_LOOPS
+ loopnez a4, .Lbytesetdone
+#else /* !XCHAL_HAVE_LOOPS */
+ beqz a4, .Lbytesetdone
+ add a6, a5, a4 # a6 = ending address
+#endif /* !XCHAL_HAVE_LOOPS */
+.Lbyteloop:
+ EX(s8i, a3, a5, 0, memset_fixup)
+ addi a5, a5, 1
+#if !XCHAL_HAVE_LOOPS
+ blt a5, a6, .Lbyteloop
+#endif /* !XCHAL_HAVE_LOOPS */
+.Lbytesetdone:
+ retw
+
+
+ .section .fixup, "ax"
+ .align 4
+
+/* We return zero if a failure occurred. */
+
+memset_fixup:
+ movi a2, 0
+ retw
diff --git a/arch/xtensa/lib/pci-auto.c b/arch/xtensa/lib/pci-auto.c
new file mode 100644
index 000000000000..90c790f6123b
--- /dev/null
+++ b/arch/xtensa/lib/pci-auto.c
@@ -0,0 +1,352 @@
+/*
+ * arch/xtensa/kernel/pci-auto.c
+ *
+ * PCI autoconfiguration library
+ *
+ * Copyright (C) 2001 - 2005 Tensilica Inc.
+ *
+ * Chris Zankel <zankel@tensilica.com, cez@zankel.net>
+ *
+ * Based on work from Matt Porter <mporter@mvista.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/kernel.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+
+#include <asm/pci-bridge.h>
+
+
+/*
+ *
+ * Setting up a PCI
+ *
+ * pci_ctrl->first_busno = <first bus number (0)>
+ * pci_ctrl->last_busno = <last bus number (0xff)>
+ * pci_ctrl->ops = <PCI config operations>
+ * pci_ctrl->map_irq = <function to return the interrupt number for a device>
+ *
+ * pci_ctrl->io_space.start = <IO space start address (PCI view)>
+ * pci_ctrl->io_space.end = <IO space end address (PCI view)>
+ * pci_ctrl->io_space.base = <IO space offset: address 0 from CPU space>
+ * pci_ctrl->mem_space.start = <MEM space start address (PCI view)>
+ * pci_ctrl->mem_space.end = <MEM space end address (PCI view)>
+ * pci_ctrl->mem_space.base = <MEM space offset: address 0 from CPU space>
+ *
+ * pcibios_init_resource(&pci_ctrl->io_resource, <IO space start>,
+ * <IO space end>, IORESOURCE_IO, "PCI host bridge");
+ * pcibios_init_resource(&pci_ctrl->mem_resources[0], <MEM space start>,
+ * <MEM space end>, IORESOURCE_MEM, "PCI host bridge");
+ *
+ * pci_ctrl->last_busno = pciauto_bus_scan(pci_ctrl,pci_ctrl->first_busno);
+ *
+ * int __init pciauto_bus_scan(struct pci_controller *pci_ctrl, int current_bus)
+ *
+ */
+
+
+/* define DEBUG to print some debugging messages. */
+
+#undef DEBUG
+
+#ifdef DEBUG
+# define DBG(x...) printk(x)
+#else
+# define DBG(x...)
+#endif
+
+static int pciauto_upper_iospc;
+static int pciauto_upper_memspc;
+
+static struct pci_dev pciauto_dev;
+static struct pci_bus pciauto_bus;
+
+/*
+ * Helper functions
+ */
+
+/* Initialize the bars of a PCI device. */
+
+static void __init
+pciauto_setup_bars(struct pci_dev *dev, int bar_limit)
+{
+ int bar_size;
+ int bar, bar_nr;
+ int *upper_limit;
+ int found_mem64 = 0;
+
+ for (bar = PCI_BASE_ADDRESS_0, bar_nr = 0;
+ bar <= bar_limit;
+ bar+=4, bar_nr++)
+ {
+ /* Tickle the BAR and get the size */
+ pci_write_config_dword(dev, bar, 0xffffffff);
+ pci_read_config_dword(dev, bar, &bar_size);
+
+ /* If BAR is not implemented go to the next BAR */
+ if (!bar_size)
+ continue;
+
+ /* Check the BAR type and set our address mask */
+ if (bar_size & PCI_BASE_ADDRESS_SPACE_IO)
+ {
+ bar_size &= PCI_BASE_ADDRESS_IO_MASK;
+ upper_limit = &pciauto_upper_iospc;
+ DBG("PCI Autoconfig: BAR %d, I/O, ", bar_nr);
+ }
+ else
+ {
+ if ((bar_size & PCI_BASE_ADDRESS_MEM_TYPE_MASK) ==
+ PCI_BASE_ADDRESS_MEM_TYPE_64)
+ found_mem64 = 1;
+
+ bar_size &= PCI_BASE_ADDRESS_MEM_MASK;
+ upper_limit = &pciauto_upper_memspc;
+ DBG("PCI Autoconfig: BAR %d, Mem, ", bar_nr);
+ }
+
+ /* Allocate a base address (bar_size is negative!) */
+ *upper_limit = (*upper_limit + bar_size) & bar_size;
+
+ /* Write it out and update our limit */
+ pci_write_config_dword(dev, bar, *upper_limit);
+
+ /*
+ * If we are a 64-bit decoder then increment to the
+ * upper 32 bits of the bar and force it to locate
+ * in the lower 4GB of memory.
+ */
+
+ if (found_mem64)
+ pci_write_config_dword(dev, (bar+=4), 0x00000000);
+
+ DBG("size=0x%x, address=0x%x\n", ~bar_size + 1, *upper_limit);
+ }
+}
+
+/* Initialize the interrupt number. */
+
+static void __init
+pciauto_setup_irq(struct pci_controller* pci_ctrl,struct pci_dev *dev,int devfn)
+{
+ u8 pin;
+ int irq = 0;
+
+ pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin);
+
+ /* Fix illegal pin numbers. */
+
+ if (pin == 0 || pin > 4)
+ pin = 1;
+
+ if (pci_ctrl->map_irq)
+ irq = pci_ctrl->map_irq(dev, PCI_SLOT(devfn), pin);
+
+ if (irq == -1)
+ irq = 0;
+
+ DBG("PCI Autoconfig: Interrupt %d, pin %d\n", irq, pin);
+
+ pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq);
+}
+
+
+static void __init
+pciauto_prescan_setup_bridge(struct pci_dev *dev, int current_bus,
+ int sub_bus, int *iosave, int *memsave)
+{
+ /* Configure bus number registers */
+ pci_write_config_byte(dev, PCI_PRIMARY_BUS, current_bus);
+ pci_write_config_byte(dev, PCI_SECONDARY_BUS, sub_bus + 1);
+ pci_write_config_byte(dev, PCI_SUBORDINATE_BUS, 0xff);
+
+ /* Round memory allocator to 1MB boundary */
+ pciauto_upper_memspc &= ~(0x100000 - 1);
+ *memsave = pciauto_upper_memspc;
+
+ /* Round I/O allocator to 4KB boundary */
+ pciauto_upper_iospc &= ~(0x1000 - 1);
+ *iosave = pciauto_upper_iospc;
+
+ /* Set up memory and I/O filter limits, assume 32-bit I/O space */
+ pci_write_config_word(dev, PCI_MEMORY_LIMIT,
+ ((pciauto_upper_memspc - 1) & 0xfff00000) >> 16);
+ pci_write_config_byte(dev, PCI_IO_LIMIT,
+ ((pciauto_upper_iospc - 1) & 0x0000f000) >> 8);
+ pci_write_config_word(dev, PCI_IO_LIMIT_UPPER16,
+ ((pciauto_upper_iospc - 1) & 0xffff0000) >> 16);
+}
+
+static void __init
+pciauto_postscan_setup_bridge(struct pci_dev *dev, int current_bus, int sub_bus,
+ int *iosave, int *memsave)
+{
+ int cmdstat;
+
+ /* Configure bus number registers */
+ pci_write_config_byte(dev, PCI_SUBORDINATE_BUS, sub_bus);
+
+ /*
+ * Round memory allocator to 1MB boundary.
+ * If no space used, allocate minimum.
+ */
+ pciauto_upper_memspc &= ~(0x100000 - 1);
+ if (*memsave == pciauto_upper_memspc)
+ pciauto_upper_memspc -= 0x00100000;
+
+ pci_write_config_word(dev, PCI_MEMORY_BASE, pciauto_upper_memspc >> 16);
+
+ /* Allocate 1MB for pre-fretch */
+ pci_write_config_word(dev, PCI_PREF_MEMORY_LIMIT,
+ ((pciauto_upper_memspc - 1) & 0xfff00000) >> 16);
+
+ pciauto_upper_memspc -= 0x100000;
+
+ pci_write_config_word(dev, PCI_PREF_MEMORY_BASE,
+ pciauto_upper_memspc >> 16);
+
+ /* Round I/O allocator to 4KB boundary */
+ pciauto_upper_iospc &= ~(0x1000 - 1);
+ if (*iosave == pciauto_upper_iospc)
+ pciauto_upper_iospc -= 0x1000;
+
+ pci_write_config_byte(dev, PCI_IO_BASE,
+ (pciauto_upper_iospc & 0x0000f000) >> 8);
+ pci_write_config_word(dev, PCI_IO_BASE_UPPER16,
+ pciauto_upper_iospc >> 16);
+
+ /* Enable memory and I/O accesses, enable bus master */
+ pci_read_config_dword(dev, PCI_COMMAND, &cmdstat);
+ pci_write_config_dword(dev, PCI_COMMAND,
+ cmdstat |
+ PCI_COMMAND_IO |
+ PCI_COMMAND_MEMORY |
+ PCI_COMMAND_MASTER);
+}
+
+/*
+ * Scan the current PCI bus.
+ */
+
+
+int __init pciauto_bus_scan(struct pci_controller *pci_ctrl, int current_bus)
+{
+ int sub_bus, pci_devfn, pci_class, cmdstat, found_multi=0;
+ unsigned short vid;
+ unsigned char header_type;
+ struct pci_dev *dev = &pciauto_dev;
+
+ pciauto_dev.bus = &pciauto_bus;
+ pciauto_dev.sysdata = pci_ctrl;
+ pciauto_bus.ops = pci_ctrl->ops;
+
+ /*
+ * Fetch our I/O and memory space upper boundaries used
+ * to allocated base addresses on this pci_controller.
+ */
+
+ if (current_bus == pci_ctrl->first_busno)
+ {
+ pciauto_upper_iospc = pci_ctrl->io_resource.end + 1;
+ pciauto_upper_memspc = pci_ctrl->mem_resources[0].end + 1;
+ }
+
+ sub_bus = current_bus;
+
+ for (pci_devfn = 0; pci_devfn < 0xff; pci_devfn++)
+ {
+ /* Skip our host bridge */
+ if ((current_bus == pci_ctrl->first_busno) && (pci_devfn == 0))
+ continue;
+
+ if (PCI_FUNC(pci_devfn) && !found_multi)
+ continue;
+
+ pciauto_bus.number = current_bus;
+ pciauto_dev.devfn = pci_devfn;
+
+ /* If config space read fails from this device, move on */
+ if (pci_read_config_byte(dev, PCI_HEADER_TYPE, &header_type))
+ continue;
+
+ if (!PCI_FUNC(pci_devfn))
+ found_multi = header_type & 0x80;
+ pci_read_config_word(dev, PCI_VENDOR_ID, &vid);
+
+ if (vid == 0xffff || vid == 0x0000) {
+ found_multi = 0;
+ continue;
+ }
+
+ pci_read_config_dword(dev, PCI_CLASS_REVISION, &pci_class);
+
+ if ((pci_class >> 16) == PCI_CLASS_BRIDGE_PCI) {
+
+ int iosave, memsave;
+
+ DBG("PCI Autoconfig: Found P2P bridge, device %d\n",
+ PCI_SLOT(pci_devfn));
+
+ /* Allocate PCI I/O and/or memory space */
+ pciauto_setup_bars(dev, PCI_BASE_ADDRESS_1);
+
+ pciauto_prescan_setup_bridge(dev, current_bus, sub_bus,
+ &iosave, &memsave);
+ sub_bus = pciauto_bus_scan(pci_ctrl, sub_bus+1);
+ pciauto_postscan_setup_bridge(dev, current_bus, sub_bus,
+ &iosave, &memsave);
+ pciauto_bus.number = current_bus;
+
+ continue;
+
+ }
+
+
+#if 0
+ /* Skip legacy mode IDE controller */
+
+ if ((pci_class >> 16) == PCI_CLASS_STORAGE_IDE) {
+
+ unsigned char prg_iface;
+ pci_read_config_byte(dev, PCI_CLASS_PROG, &prg_iface);
+
+ if (!(prg_iface & PCIAUTO_IDE_MODE_MASK)) {
+ DBG("PCI Autoconfig: Skipping legacy mode "
+ "IDE controller\n");
+ continue;
+ }
+ }
+#endif
+
+ /*
+ * Found a peripheral, enable some standard
+ * settings
+ */
+
+ pci_read_config_dword(dev, PCI_COMMAND, &cmdstat);
+ pci_write_config_dword(dev, PCI_COMMAND,
+ cmdstat |
+ PCI_COMMAND_IO |
+ PCI_COMMAND_MEMORY |
+ PCI_COMMAND_MASTER);
+ pci_write_config_byte(dev, PCI_LATENCY_TIMER, 0x80);
+
+ /* Allocate PCI I/O and/or memory space */
+ DBG("PCI Autoconfig: Found Bus %d, Device %d, Function %d\n",
+ current_bus, PCI_SLOT(pci_devfn), PCI_FUNC(pci_devfn) );
+
+ pciauto_setup_bars(dev, PCI_BASE_ADDRESS_5);
+ pciauto_setup_irq(pci_ctrl, dev, pci_devfn);
+ }
+ return sub_bus;
+}
+
+
+
+
+
diff --git a/arch/xtensa/lib/strcasecmp.c b/arch/xtensa/lib/strcasecmp.c
new file mode 100644
index 000000000000..165b2d6effa5
--- /dev/null
+++ b/arch/xtensa/lib/strcasecmp.c
@@ -0,0 +1,32 @@
+/*
+ * linux/arch/xtensa/lib/strcasecmp.c
+ *
+ * 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) 2002 Tensilica Inc.
+ */
+
+#include <linux/string.h>
+
+
+/* We handle nothing here except the C locale. Since this is used in
+ only one place, on strings known to contain only 7 bit ASCII, this
+ is ok. */
+
+int strcasecmp(const char *a, const char *b)
+{
+ int ca, cb;
+
+ do {
+ ca = *a++ & 0xff;
+ cb = *b++ & 0xff;
+ if (ca >= 'A' && ca <= 'Z')
+ ca += 'a' - 'A';
+ if (cb >= 'A' && cb <= 'Z')
+ cb += 'a' - 'A';
+ } while (ca == cb && ca != '\0');
+
+ return ca - cb;
+}
diff --git a/arch/xtensa/lib/strncpy_user.S b/arch/xtensa/lib/strncpy_user.S
new file mode 100644
index 000000000000..71d55df43893
--- /dev/null
+++ b/arch/xtensa/lib/strncpy_user.S
@@ -0,0 +1,224 @@
+/*
+ * arch/xtensa/lib/strncpy_user.S
+ *
+ * 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.
+ *
+ * Returns: -EFAULT if exception before terminator, N if the entire
+ * buffer filled, else strlen.
+ *
+ * Copyright (C) 2002 Tensilica Inc.
+ */
+
+#include <xtensa/coreasm.h>
+#include <linux/errno.h>
+
+/* Load or store instructions that may cause exceptions use the EX macro. */
+
+#define EX(insn,reg1,reg2,offset,handler) \
+9: insn reg1, reg2, offset; \
+ .section __ex_table, "a"; \
+ .word 9b, handler; \
+ .previous
+
+/*
+ * char *__strncpy_user(char *dst, const char *src, size_t len)
+ */
+.text
+.begin literal
+.align 4
+.Lmask0:
+ .byte 0xff, 0x00, 0x00, 0x00
+.Lmask1:
+ .byte 0x00, 0xff, 0x00, 0x00
+.Lmask2:
+ .byte 0x00, 0x00, 0xff, 0x00
+.Lmask3:
+ .byte 0x00, 0x00, 0x00, 0xff
+.end literal
+
+# Register use
+# a0/ return address
+# a1/ stack pointer
+# a2/ return value
+# a3/ src
+# a4/ len
+# a5/ mask0
+# a6/ mask1
+# a7/ mask2
+# a8/ mask3
+# a9/ tmp
+# a10/ tmp
+# a11/ dst
+# a12/ tmp
+
+.align 4
+.global __strncpy_user
+.type __strncpy_user,@function
+__strncpy_user:
+ entry sp, 16 # minimal stack frame
+ # a2/ dst, a3/ src, a4/ len
+ mov a11, a2 # leave dst in return value register
+ beqz a4, .Lret # if len is zero
+ l32r a5, .Lmask0 # mask for byte 0
+ l32r a6, .Lmask1 # mask for byte 1
+ l32r a7, .Lmask2 # mask for byte 2
+ l32r a8, .Lmask3 # mask for byte 3
+ bbsi.l a3, 0, .Lsrc1mod2 # if only 8-bit aligned
+ bbsi.l a3, 1, .Lsrc2mod4 # if only 16-bit aligned
+.Lsrcaligned: # return here when src is word-aligned
+ srli a12, a4, 2 # number of loop iterations with 4B per loop
+ movi a9, 3
+ bnone a11, a9, .Laligned
+ j .Ldstunaligned
+
+.Lsrc1mod2: # src address is odd
+ EX(l8ui, a9, a3, 0, fixup_l) # get byte 0
+ addi a3, a3, 1 # advance src pointer
+ EX(s8i, a9, a11, 0, fixup_s) # store byte 0
+ beqz a9, .Lret # if byte 0 is zero
+ addi a11, a11, 1 # advance dst pointer
+ addi a4, a4, -1 # decrement len
+ beqz a4, .Lret # if len is zero
+ bbci.l a3, 1, .Lsrcaligned # if src is now word-aligned
+
+.Lsrc2mod4: # src address is 2 mod 4
+ EX(l8ui, a9, a3, 0, fixup_l) # get byte 0
+ /* 1-cycle interlock */
+ EX(s8i, a9, a11, 0, fixup_s) # store byte 0
+ beqz a9, .Lret # if byte 0 is zero
+ addi a11, a11, 1 # advance dst pointer
+ addi a4, a4, -1 # decrement len
+ beqz a4, .Lret # if len is zero
+ EX(l8ui, a9, a3, 1, fixup_l) # get byte 0
+ addi a3, a3, 2 # advance src pointer
+ EX(s8i, a9, a11, 0, fixup_s) # store byte 0
+ beqz a9, .Lret # if byte 0 is zero
+ addi a11, a11, 1 # advance dst pointer
+ addi a4, a4, -1 # decrement len
+ bnez a4, .Lsrcaligned # if len is nonzero
+.Lret:
+ sub a2, a11, a2 # compute strlen
+ retw
+
+/*
+ * dst is word-aligned, src is word-aligned
+ */
+ .align 4 # 1 mod 4 alignment for LOOPNEZ
+ .byte 0 # (0 mod 4 alignment for LBEG)
+.Laligned:
+#if XCHAL_HAVE_LOOPS
+ loopnez a12, .Loop1done
+#else
+ beqz a12, .Loop1done
+ slli a12, a12, 2
+ add a12, a12, a11 # a12 = end of last 4B chunck
+#endif
+.Loop1:
+ EX(l32i, a9, a3, 0, fixup_l) # get word from src
+ addi a3, a3, 4 # advance src pointer
+ bnone a9, a5, .Lz0 # if byte 0 is zero
+ bnone a9, a6, .Lz1 # if byte 1 is zero
+ bnone a9, a7, .Lz2 # if byte 2 is zero
+ EX(s32i, a9, a11, 0, fixup_s) # store word to dst
+ bnone a9, a8, .Lz3 # if byte 3 is zero
+ addi a11, a11, 4 # advance dst pointer
+#if !XCHAL_HAVE_LOOPS
+ blt a11, a12, .Loop1
+#endif
+
+.Loop1done:
+ bbci.l a4, 1, .L100
+ # copy 2 bytes
+ EX(l16ui, a9, a3, 0, fixup_l)
+ addi a3, a3, 2 # advance src pointer
+#ifdef __XTENSA_EB__
+ bnone a9, a7, .Lz0 # if byte 2 is zero
+ bnone a9, a8, .Lz1 # if byte 3 is zero
+#else
+ bnone a9, a5, .Lz0 # if byte 0 is zero
+ bnone a9, a6, .Lz1 # if byte 1 is zero
+#endif
+ EX(s16i, a9, a11, 0, fixup_s)
+ addi a11, a11, 2 # advance dst pointer
+.L100:
+ bbci.l a4, 0, .Lret
+ EX(l8ui, a9, a3, 0, fixup_l)
+ /* slot */
+ EX(s8i, a9, a11, 0, fixup_s)
+ beqz a9, .Lret # if byte is zero
+ addi a11, a11, 1-3 # advance dst ptr 1, but also cancel
+ # the effect of adding 3 in .Lz3 code
+ /* fall thru to .Lz3 and "retw" */
+
+.Lz3: # byte 3 is zero
+ addi a11, a11, 3 # advance dst pointer
+ sub a2, a11, a2 # compute strlen
+ retw
+.Lz0: # byte 0 is zero
+#ifdef __XTENSA_EB__
+ movi a9, 0
+#endif /* __XTENSA_EB__ */
+ EX(s8i, a9, a11, 0, fixup_s)
+ sub a2, a11, a2 # compute strlen
+ retw
+.Lz1: # byte 1 is zero
+#ifdef __XTENSA_EB__
+ extui a9, a9, 16, 16
+#endif /* __XTENSA_EB__ */
+ EX(s16i, a9, a11, 0, fixup_s)
+ addi a11, a11, 1 # advance dst pointer
+ sub a2, a11, a2 # compute strlen
+ retw
+.Lz2: # byte 2 is zero
+#ifdef __XTENSA_EB__
+ extui a9, a9, 16, 16
+#endif /* __XTENSA_EB__ */
+ EX(s16i, a9, a11, 0, fixup_s)
+ movi a9, 0
+ EX(s8i, a9, a11, 2, fixup_s)
+ addi a11, a11, 2 # advance dst pointer
+ sub a2, a11, a2 # compute strlen
+ retw
+
+ .align 4 # 1 mod 4 alignment for LOOPNEZ
+ .byte 0 # (0 mod 4 alignment for LBEG)
+.Ldstunaligned:
+/*
+ * for now just use byte copy loop
+ */
+#if XCHAL_HAVE_LOOPS
+ loopnez a4, .Lunalignedend
+#else
+ beqz a4, .Lunalignedend
+ add a12, a11, a4 # a12 = ending address
+#endif /* XCHAL_HAVE_LOOPS */
+.Lnextbyte:
+ EX(l8ui, a9, a3, 0, fixup_l)
+ addi a3, a3, 1
+ EX(s8i, a9, a11, 0, fixup_s)
+ beqz a9, .Lunalignedend
+ addi a11, a11, 1
+#if !XCHAL_HAVE_LOOPS
+ blt a11, a12, .Lnextbyte
+#endif
+
+.Lunalignedend:
+ sub a2, a11, a2 # compute strlen
+ retw
+
+
+ .section .fixup, "ax"
+ .align 4
+
+ /* For now, just return -EFAULT. Future implementations might
+ * like to clear remaining kernel space, like the fixup
+ * implementation in memset(). Thus, we differentiate between
+ * load/store fixups. */
+
+fixup_s:
+fixup_l:
+ movi a2, -EFAULT
+ retw
+
diff --git a/arch/xtensa/lib/strnlen_user.S b/arch/xtensa/lib/strnlen_user.S
new file mode 100644
index 000000000000..cdff4d670f3b
--- /dev/null
+++ b/arch/xtensa/lib/strnlen_user.S
@@ -0,0 +1,147 @@
+/*
+ * arch/xtensa/lib/strnlen_user.S
+ *
+ * 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.
+ *
+ * Returns strnlen, including trailing zero terminator.
+ * Zero indicates error.
+ *
+ * Copyright (C) 2002 Tensilica Inc.
+ */
+
+#include <xtensa/coreasm.h>
+
+/* Load or store instructions that may cause exceptions use the EX macro. */
+
+#define EX(insn,reg1,reg2,offset,handler) \
+9: insn reg1, reg2, offset; \
+ .section __ex_table, "a"; \
+ .word 9b, handler; \
+ .previous
+
+/*
+ * size_t __strnlen_user(const char *s, size_t len)
+ */
+.text
+.begin literal
+.align 4
+.Lmask0:
+ .byte 0xff, 0x00, 0x00, 0x00
+.Lmask1:
+ .byte 0x00, 0xff, 0x00, 0x00
+.Lmask2:
+ .byte 0x00, 0x00, 0xff, 0x00
+.Lmask3:
+ .byte 0x00, 0x00, 0x00, 0xff
+.end literal
+
+# Register use:
+# a2/ src
+# a3/ len
+# a4/ tmp
+# a5/ mask0
+# a6/ mask1
+# a7/ mask2
+# a8/ mask3
+# a9/ tmp
+# a10/ tmp
+
+.align 4
+.global __strnlen_user
+.type __strnlen_user,@function
+__strnlen_user:
+ entry sp, 16 # minimal stack frame
+ # a2/ s, a3/ len
+ addi a4, a2, -4 # because we overincrement at the end;
+ # we compensate with load offsets of 4
+ l32r a5, .Lmask0 # mask for byte 0
+ l32r a6, .Lmask1 # mask for byte 1
+ l32r a7, .Lmask2 # mask for byte 2
+ l32r a8, .Lmask3 # mask for byte 3
+ bbsi.l a2, 0, .L1mod2 # if only 8-bit aligned
+ bbsi.l a2, 1, .L2mod4 # if only 16-bit aligned
+
+/*
+ * String is word-aligned.
+ */
+.Laligned:
+ srli a10, a3, 2 # number of loop iterations with 4B per loop
+#if XCHAL_HAVE_LOOPS
+ loopnez a10, .Ldone
+#else
+ beqz a10, .Ldone
+ slli a10, a10, 2
+ add a10, a10, a4 # a10 = end of last 4B chunk
+#endif /* XCHAL_HAVE_LOOPS */
+.Loop:
+ EX(l32i, a9, a4, 4, lenfixup) # get next word of string
+ addi a4, a4, 4 # advance string pointer
+ bnone a9, a5, .Lz0 # if byte 0 is zero
+ bnone a9, a6, .Lz1 # if byte 1 is zero
+ bnone a9, a7, .Lz2 # if byte 2 is zero
+ bnone a9, a8, .Lz3 # if byte 3 is zero
+#if !XCHAL_HAVE_LOOPS
+ blt a4, a10, .Loop
+#endif
+
+.Ldone:
+ EX(l32i, a9, a4, 4, lenfixup) # load 4 bytes for remaining checks
+
+ bbci.l a3, 1, .L100
+ # check two more bytes (bytes 0, 1 of word)
+ addi a4, a4, 2 # advance string pointer
+ bnone a9, a5, .Lz0 # if byte 0 is zero
+ bnone a9, a6, .Lz1 # if byte 1 is zero
+.L100:
+ bbci.l a3, 0, .L101
+ # check one more byte (byte 2 of word)
+ # Actually, we don't need to check. Zero or nonzero, we'll add one.
+ # Do not add an extra one for the NULL terminator since we have
+ # exhausted the original len parameter.
+ addi a4, a4, 1 # advance string pointer
+.L101:
+ sub a2, a4, a2 # compute length
+ retw
+
+# NOTE that in several places below, we point to the byte just after
+# the zero byte in order to include the NULL terminator in the count.
+
+.Lz3: # byte 3 is zero
+ addi a4, a4, 3 # point to zero byte
+.Lz0: # byte 0 is zero
+ addi a4, a4, 1 # point just beyond zero byte
+ sub a2, a4, a2 # subtract to get length
+ retw
+.Lz1: # byte 1 is zero
+ addi a4, a4, 1+1 # point just beyond zero byte
+ sub a2, a4, a2 # subtract to get length
+ retw
+.Lz2: # byte 2 is zero
+ addi a4, a4, 2+1 # point just beyond zero byte
+ sub a2, a4, a2 # subtract to get length
+ retw
+
+.L1mod2: # address is odd
+ EX(l8ui, a9, a4, 4, lenfixup) # get byte 0
+ addi a4, a4, 1 # advance string pointer
+ beqz a9, .Lz3 # if byte 0 is zero
+ bbci.l a4, 1, .Laligned # if string pointer is now word-aligned
+
+.L2mod4: # address is 2 mod 4
+ addi a4, a4, 2 # advance ptr for aligned access
+ EX(l32i, a9, a4, 0, lenfixup) # get word with first two bytes of string
+ bnone a9, a7, .Lz2 # if byte 2 (of word, not string) is zero
+ bany a9, a8, .Laligned # if byte 3 (of word, not string) is nonzero
+ # byte 3 is zero
+ addi a4, a4, 3+1 # point just beyond zero byte
+ sub a2, a4, a2 # subtract to get length
+ retw
+
+ .section .fixup, "ax"
+ .align 4
+lenfixup:
+ movi a2, 0
+ retw
+
diff --git a/arch/xtensa/lib/usercopy.S b/arch/xtensa/lib/usercopy.S
new file mode 100644
index 000000000000..265db2693cbd
--- /dev/null
+++ b/arch/xtensa/lib/usercopy.S
@@ -0,0 +1,321 @@
+/*
+ * arch/xtensa/lib/usercopy.S
+ *
+ * Copy to/from user space (derived from arch/xtensa/lib/hal/memcopy.S)
+ *
+ * DO NOT COMBINE this function with <arch/xtensa/lib/hal/memcopy.S>.
+ * It needs to remain separate and distinct. The hal files are part
+ * of the the Xtensa link-time HAL, and those files may differ per
+ * processor configuration. Patching the kernel for another
+ * processor configuration includes replacing the hal files, and we
+ * could loose the special functionality for accessing user-space
+ * memory during such a patch. We sacrifice a little code space here
+ * in favor to simplify code maintenance.
+ *
+ * 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) 2002 Tensilica Inc.
+ */
+
+
+/*
+ * size_t __xtensa_copy_user (void *dst, const void *src, size_t len);
+ *
+ * The returned value is the number of bytes not copied. Implies zero
+ * is success.
+ *
+ * The general case algorithm is as follows:
+ * If the destination and source are both aligned,
+ * do 16B chunks with a loop, and then finish up with
+ * 8B, 4B, 2B, and 1B copies conditional on the length.
+ * If destination is aligned and source unaligned,
+ * do the same, but use SRC to align the source data.
+ * If destination is unaligned, align it by conditionally
+ * copying 1B and 2B and then retest.
+ * This code tries to use fall-through braches for the common
+ * case of aligned destinations (except for the branches to
+ * the alignment label).
+ *
+ * Register use:
+ * a0/ return address
+ * a1/ stack pointer
+ * a2/ return value
+ * a3/ src
+ * a4/ length
+ * a5/ dst
+ * a6/ tmp
+ * a7/ tmp
+ * a8/ tmp
+ * a9/ tmp
+ * a10/ tmp
+ * a11/ original length
+ */
+
+#include <xtensa/coreasm.h>
+
+#ifdef __XTENSA_EB__
+#define ALIGN(R, W0, W1) src R, W0, W1
+#define SSA8(R) ssa8b R
+#else
+#define ALIGN(R, W0, W1) src R, W1, W0
+#define SSA8(R) ssa8l R
+#endif
+
+/* Load or store instructions that may cause exceptions use the EX macro. */
+
+#define EX(insn,reg1,reg2,offset,handler) \
+9: insn reg1, reg2, offset; \
+ .section __ex_table, "a"; \
+ .word 9b, handler; \
+ .previous
+
+
+ .text
+ .align 4
+ .global __xtensa_copy_user
+ .type __xtensa_copy_user,@function
+__xtensa_copy_user:
+ entry sp, 16 # minimal stack frame
+ # a2/ dst, a3/ src, a4/ len
+ mov a5, a2 # copy dst so that a2 is return value
+ mov a11, a4 # preserve original len for error case
+.Lcommon:
+ bbsi.l a2, 0, .Ldst1mod2 # if dst is 1 mod 2
+ bbsi.l a2, 1, .Ldst2mod4 # if dst is 2 mod 4
+.Ldstaligned: # return here from .Ldstunaligned when dst is aligned
+ srli a7, a4, 4 # number of loop iterations with 16B
+ # per iteration
+ movi a8, 3 # if source is also aligned,
+ bnone a3, a8, .Laligned # then use word copy
+ SSA8( a3) # set shift amount from byte offset
+ bnez a4, .Lsrcunaligned
+ movi a2, 0 # return success for len==0
+ retw
+
+/*
+ * Destination is unaligned
+ */
+
+.Ldst1mod2: # dst is only byte aligned
+ bltui a4, 7, .Lbytecopy # do short copies byte by byte
+
+ # copy 1 byte
+ EX(l8ui, a6, a3, 0, l_fixup)
+ addi a3, a3, 1
+ EX(s8i, a6, a5, 0, s_fixup)
+ addi a5, a5, 1
+ addi a4, a4, -1
+ bbci.l a5, 1, .Ldstaligned # if dst is now aligned, then
+ # return to main algorithm
+.Ldst2mod4: # dst 16-bit aligned
+ # copy 2 bytes
+ bltui a4, 6, .Lbytecopy # do short copies byte by byte
+ EX(l8ui, a6, a3, 0, l_fixup)
+ EX(l8ui, a7, a3, 1, l_fixup)
+ addi a3, a3, 2
+ EX(s8i, a6, a5, 0, s_fixup)
+ EX(s8i, a7, a5, 1, s_fixup)
+ addi a5, a5, 2
+ addi a4, a4, -2
+ j .Ldstaligned # dst is now aligned, return to main algorithm
+
+/*
+ * Byte by byte copy
+ */
+ .align 4
+ .byte 0 # 1 mod 4 alignment for LOOPNEZ
+ # (0 mod 4 alignment for LBEG)
+.Lbytecopy:
+#if XCHAL_HAVE_LOOPS
+ loopnez a4, .Lbytecopydone
+#else /* !XCHAL_HAVE_LOOPS */
+ beqz a4, .Lbytecopydone
+ add a7, a3, a4 # a7 = end address for source
+#endif /* !XCHAL_HAVE_LOOPS */
+.Lnextbyte:
+ EX(l8ui, a6, a3, 0, l_fixup)
+ addi a3, a3, 1
+ EX(s8i, a6, a5, 0, s_fixup)
+ addi a5, a5, 1
+#if !XCHAL_HAVE_LOOPS
+ blt a3, a7, .Lnextbyte
+#endif /* !XCHAL_HAVE_LOOPS */
+.Lbytecopydone:
+ movi a2, 0 # return success for len bytes copied
+ retw
+
+/*
+ * Destination and source are word-aligned.
+ */
+ # copy 16 bytes per iteration for word-aligned dst and word-aligned src
+ .align 4 # 1 mod 4 alignment for LOOPNEZ
+ .byte 0 # (0 mod 4 alignment for LBEG)
+.Laligned:
+#if XCHAL_HAVE_LOOPS
+ loopnez a7, .Loop1done
+#else /* !XCHAL_HAVE_LOOPS */
+ beqz a7, .Loop1done
+ slli a8, a7, 4
+ add a8, a8, a3 # a8 = end of last 16B source chunk
+#endif /* !XCHAL_HAVE_LOOPS */
+.Loop1:
+ EX(l32i, a6, a3, 0, l_fixup)
+ EX(l32i, a7, a3, 4, l_fixup)
+ EX(s32i, a6, a5, 0, s_fixup)
+ EX(l32i, a6, a3, 8, l_fixup)
+ EX(s32i, a7, a5, 4, s_fixup)
+ EX(l32i, a7, a3, 12, l_fixup)
+ EX(s32i, a6, a5, 8, s_fixup)
+ addi a3, a3, 16
+ EX(s32i, a7, a5, 12, s_fixup)
+ addi a5, a5, 16
+#if !XCHAL_HAVE_LOOPS
+ blt a3, a8, .Loop1
+#endif /* !XCHAL_HAVE_LOOPS */
+.Loop1done:
+ bbci.l a4, 3, .L2
+ # copy 8 bytes
+ EX(l32i, a6, a3, 0, l_fixup)
+ EX(l32i, a7, a3, 4, l_fixup)
+ addi a3, a3, 8
+ EX(s32i, a6, a5, 0, s_fixup)
+ EX(s32i, a7, a5, 4, s_fixup)
+ addi a5, a5, 8
+.L2:
+ bbci.l a4, 2, .L3
+ # copy 4 bytes
+ EX(l32i, a6, a3, 0, l_fixup)
+ addi a3, a3, 4
+ EX(s32i, a6, a5, 0, s_fixup)
+ addi a5, a5, 4
+.L3:
+ bbci.l a4, 1, .L4
+ # copy 2 bytes
+ EX(l16ui, a6, a3, 0, l_fixup)
+ addi a3, a3, 2
+ EX(s16i, a6, a5, 0, s_fixup)
+ addi a5, a5, 2
+.L4:
+ bbci.l a4, 0, .L5
+ # copy 1 byte
+ EX(l8ui, a6, a3, 0, l_fixup)
+ EX(s8i, a6, a5, 0, s_fixup)
+.L5:
+ movi a2, 0 # return success for len bytes copied
+ retw
+
+/*
+ * Destination is aligned, Source is unaligned
+ */
+
+ .align 4
+ .byte 0 # 1 mod 4 alignement for LOOPNEZ
+ # (0 mod 4 alignment for LBEG)
+.Lsrcunaligned:
+ # copy 16 bytes per iteration for word-aligned dst and unaligned src
+ and a10, a3, a8 # save unalignment offset for below
+ sub a3, a3, a10 # align a3 (to avoid sim warnings only; not needed for hardware)
+ EX(l32i, a6, a3, 0, l_fixup) # load first word
+#if XCHAL_HAVE_LOOPS
+ loopnez a7, .Loop2done
+#else /* !XCHAL_HAVE_LOOPS */
+ beqz a7, .Loop2done
+ slli a10, a7, 4
+ add a10, a10, a3 # a10 = end of last 16B source chunk
+#endif /* !XCHAL_HAVE_LOOPS */
+.Loop2:
+ EX(l32i, a7, a3, 4, l_fixup)
+ EX(l32i, a8, a3, 8, l_fixup)
+ ALIGN( a6, a6, a7)
+ EX(s32i, a6, a5, 0, s_fixup)
+ EX(l32i, a9, a3, 12, l_fixup)
+ ALIGN( a7, a7, a8)
+ EX(s32i, a7, a5, 4, s_fixup)
+ EX(l32i, a6, a3, 16, l_fixup)
+ ALIGN( a8, a8, a9)
+ EX(s32i, a8, a5, 8, s_fixup)
+ addi a3, a3, 16
+ ALIGN( a9, a9, a6)
+ EX(s32i, a9, a5, 12, s_fixup)
+ addi a5, a5, 16
+#if !XCHAL_HAVE_LOOPS
+ blt a3, a10, .Loop2
+#endif /* !XCHAL_HAVE_LOOPS */
+.Loop2done:
+ bbci.l a4, 3, .L12
+ # copy 8 bytes
+ EX(l32i, a7, a3, 4, l_fixup)
+ EX(l32i, a8, a3, 8, l_fixup)
+ ALIGN( a6, a6, a7)
+ EX(s32i, a6, a5, 0, s_fixup)
+ addi a3, a3, 8
+ ALIGN( a7, a7, a8)
+ EX(s32i, a7, a5, 4, s_fixup)
+ addi a5, a5, 8
+ mov a6, a8
+.L12:
+ bbci.l a4, 2, .L13
+ # copy 4 bytes
+ EX(l32i, a7, a3, 4, l_fixup)
+ addi a3, a3, 4
+ ALIGN( a6, a6, a7)
+ EX(s32i, a6, a5, 0, s_fixup)
+ addi a5, a5, 4
+ mov a6, a7
+.L13:
+ add a3, a3, a10 # readjust a3 with correct misalignment
+ bbci.l a4, 1, .L14
+ # copy 2 bytes
+ EX(l8ui, a6, a3, 0, l_fixup)
+ EX(l8ui, a7, a3, 1, l_fixup)
+ addi a3, a3, 2
+ EX(s8i, a6, a5, 0, s_fixup)
+ EX(s8i, a7, a5, 1, s_fixup)
+ addi a5, a5, 2
+.L14:
+ bbci.l a4, 0, .L15
+ # copy 1 byte
+ EX(l8ui, a6, a3, 0, l_fixup)
+ EX(s8i, a6, a5, 0, s_fixup)
+.L15:
+ movi a2, 0 # return success for len bytes copied
+ retw
+
+
+ .section .fixup, "ax"
+ .align 4
+
+/* a2 = original dst; a5 = current dst; a11= original len
+ * bytes_copied = a5 - a2
+ * retval = bytes_not_copied = original len - bytes_copied
+ * retval = a11 - (a5 - a2)
+ *
+ * Clearing the remaining pieces of kernel memory plugs security
+ * holes. This functionality is the equivalent of the *_zeroing
+ * functions that some architectures provide.
+ */
+
+.Lmemset:
+ .word memset
+
+s_fixup:
+ sub a2, a5, a2 /* a2 <-- bytes copied */
+ sub a2, a11, a2 /* a2 <-- bytes not copied */
+ retw
+
+l_fixup:
+ sub a2, a5, a2 /* a2 <-- bytes copied */
+ sub a2, a11, a2 /* a2 <-- bytes not copied == return value */
+
+ /* void *memset(void *s, int c, size_t n); */
+ mov a6, a5 /* s */
+ movi a7, 0 /* c */
+ mov a8, a2 /* n */
+ l32r a4, .Lmemset
+ callx4 a4
+ /* Ignore memset return value in a6. */
+ /* a2 still contains bytes not copied. */
+ retw
+
diff --git a/arch/xtensa/mm/Makefile b/arch/xtensa/mm/Makefile
new file mode 100644
index 000000000000..a5aed5932d7b
--- /dev/null
+++ b/arch/xtensa/mm/Makefile
@@ -0,0 +1,13 @@
+#
+# Makefile for the Linux/Xtensa-specific parts of the memory manager.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definition is now in the main makefile...
+
+obj-y := init.o fault.o tlb.o misc.o
+obj-m :=
+obj-n :=
+obj- :=
diff --git a/arch/xtensa/mm/fault.c b/arch/xtensa/mm/fault.c
new file mode 100644
index 000000000000..a945a33e85a1
--- /dev/null
+++ b/arch/xtensa/mm/fault.c
@@ -0,0 +1,241 @@
+// TODO VM_EXEC flag work-around, cache aliasing
+/*
+ * arch/xtensa/mm/fault.c
+ *
+ * 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) 2001 - 2005 Tensilica Inc.
+ *
+ * Chris Zankel <chris@zankel.net>
+ * Joe Taylor <joe@tensilica.com, joetylr@yahoo.com>
+ */
+
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <asm/mmu_context.h>
+#include <asm/cacheflush.h>
+#include <asm/hardirq.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <asm/pgalloc.h>
+
+unsigned long asid_cache = ASID_FIRST_VERSION;
+void bad_page_fault(struct pt_regs*, unsigned long, int);
+
+/*
+ * This routine handles page faults. It determines the address,
+ * and the problem, and then passes it off to one of the appropriate
+ * routines.
+ *
+ * Note: does not handle Miss and MultiHit.
+ */
+
+void do_page_fault(struct pt_regs *regs)
+{
+ struct vm_area_struct * vma;
+ struct mm_struct *mm = current->mm;
+ unsigned int exccause = regs->exccause;
+ unsigned int address = regs->excvaddr;
+ siginfo_t info;
+
+ int is_write, is_exec;
+
+ info.si_code = SEGV_MAPERR;
+
+ /* We fault-in kernel-space virtual memory on-demand. The
+ * 'reference' page table is init_mm.pgd.
+ */
+ if (address >= TASK_SIZE && !user_mode(regs))
+ goto vmalloc_fault;
+
+ /* If we're in an interrupt or have no user
+ * context, we must not take the fault..
+ */
+ if (in_atomic() || !mm) {
+ bad_page_fault(regs, address, SIGSEGV);
+ return;
+ }
+
+ is_write = (exccause == XCHAL_EXCCAUSE_STORE_CACHE_ATTRIBUTE) ? 1 : 0;
+ is_exec = (exccause == XCHAL_EXCCAUSE_ITLB_PRIVILEGE ||
+ exccause == XCHAL_EXCCAUSE_ITLB_MISS ||
+ exccause == XCHAL_EXCCAUSE_FETCH_CACHE_ATTRIBUTE) ? 1 : 0;
+
+#if 0
+ printk("[%s:%d:%08x:%d:%08x:%s%s]\n", current->comm, current->pid,
+ address, exccause, regs->pc, is_write? "w":"", is_exec? "x":"");
+#endif
+
+ down_read(&mm->mmap_sem);
+ vma = find_vma(mm, address);
+
+ if (!vma)
+ goto bad_area;
+ if (vma->vm_start <= address)
+ goto good_area;
+ if (!(vma->vm_flags & VM_GROWSDOWN))
+ goto bad_area;
+ if (expand_stack(vma, address))
+ 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;
+
+ if (is_write) {
+ if (!(vma->vm_flags & VM_WRITE))
+ goto bad_area;
+ } else if (is_exec) {
+ if (!(vma->vm_flags & VM_EXEC))
+ goto bad_area;
+ } else /* Allow read even from write-only pages. */
+ if (!(vma->vm_flags & (VM_READ | VM_WRITE)))
+ goto bad_area;
+
+ /* If for any reason at all we couldn't handle the fault,
+ * make sure we exit gracefully rather than endlessly redo
+ * the fault.
+ */
+survive:
+ switch (handle_mm_fault(mm, vma, address, is_write)) {
+ case VM_FAULT_MINOR:
+ current->min_flt++;
+ break;
+ case VM_FAULT_MAJOR:
+ current->maj_flt++;
+ break;
+ case VM_FAULT_SIGBUS:
+ goto do_sigbus;
+ case VM_FAULT_OOM:
+ goto out_of_memory;
+ default:
+ BUG();
+ }
+
+ 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);
+ if (user_mode(regs)) {
+ current->thread.bad_vaddr = address;
+ current->thread.error_code = is_write;
+ info.si_signo = SIGSEGV;
+ info.si_errno = 0;
+ /* info.si_code has been set above */
+ info.si_addr = (void *) address;
+ force_sig_info(SIGSEGV, &info, current);
+ return;
+ }
+ bad_page_fault(regs, address, SIGSEGV);
+ return;
+
+
+ /* 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);
+ if (current->pid == 1) {
+ yield();
+ down_read(&mm->mmap_sem);
+ goto survive;
+ }
+ printk("VM: killing process %s\n", current->comm);
+ if (user_mode(regs))
+ do_exit(SIGKILL);
+ bad_page_fault(regs, address, SIGKILL);
+ return;
+
+do_sigbus:
+ up_read(&mm->mmap_sem);
+
+ /* Send a sigbus, regardless of whether we were in kernel
+ * or user mode.
+ */
+ current->thread.bad_vaddr = address;
+ info.si_code = SIGBUS;
+ info.si_errno = 0;
+ info.si_code = BUS_ADRERR;
+ info.si_addr = (void *) address;
+ force_sig_info(SIGBUS, &info, current);
+
+ /* Kernel mode? Handle exceptions or die */
+ if (!user_mode(regs))
+ bad_page_fault(regs, address, SIGBUS);
+
+vmalloc_fault:
+ {
+ /* Synchronize this task's top level page-table
+ * with the 'reference' page table.
+ */
+ struct mm_struct *act_mm = current->active_mm;
+ int index = pgd_index(address);
+ pgd_t *pgd, *pgd_k;
+ pmd_t *pmd, *pmd_k;
+ pte_t *pte_k;
+
+ if (act_mm == NULL)
+ goto bad_page_fault;
+
+ pgd = act_mm->pgd + index;
+ pgd_k = init_mm.pgd + index;
+
+ if (!pgd_present(*pgd_k))
+ goto bad_page_fault;
+
+ pgd_val(*pgd) = pgd_val(*pgd_k);
+
+ pmd = pmd_offset(pgd, address);
+ pmd_k = pmd_offset(pgd_k, address);
+ if (!pmd_present(*pmd) || !pmd_present(*pmd_k))
+ goto bad_page_fault;
+
+ pmd_val(*pmd) = pmd_val(*pmd_k);
+ pte_k = pte_offset_kernel(pmd_k, address);
+
+ if (!pte_present(*pte_k))
+ goto bad_page_fault;
+ return;
+ }
+bad_page_fault:
+ bad_page_fault(regs, address, SIGKILL);
+ return;
+}
+
+
+void
+bad_page_fault(struct pt_regs *regs, unsigned long address, int sig)
+{
+ extern void die(const char*, struct pt_regs*, long);
+ const struct exception_table_entry *entry;
+
+ /* Are we prepared to handle this kernel fault? */
+ if ((entry = search_exception_tables(regs->pc)) != NULL) {
+#if 1
+ printk(KERN_DEBUG "%s: Exception at pc=%#010lx (%lx)\n",
+ current->comm, regs->pc, entry->fixup);
+#endif
+ current->thread.bad_uaddr = address;
+ regs->pc = entry->fixup;
+ return;
+ }
+
+ /* Oops. The kernel tried to access some bad page. We'll have to
+ * terminate things with extreme prejudice.
+ */
+ printk(KERN_ALERT "Unable to handle kernel paging request at virtual "
+ "address %08lx\n pc = %08lx, ra = %08lx\n",
+ address, regs->pc, regs->areg[0]);
+ die("Oops", regs, sig);
+ do_exit(sig);
+}
+
diff --git a/arch/xtensa/mm/init.c b/arch/xtensa/mm/init.c
new file mode 100644
index 000000000000..56aace84aaeb
--- /dev/null
+++ b/arch/xtensa/mm/init.c
@@ -0,0 +1,551 @@
+/*
+ * arch/xtensa/mm/init.c
+ *
+ * Derived from MIPS, PPC.
+ *
+ * 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) 2001 - 2005 Tensilica Inc.
+ *
+ * Chris Zankel <chris@zankel.net>
+ * Joe Taylor <joe@tensilica.com, joetylr@yahoo.com>
+ * Marc Gauthier
+ * Kevin Chea
+ */
+
+#include <linux/config.h>
+#include <linux/init.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/bootmem.h>
+#include <linux/swap.h>
+
+#include <asm/pgtable.h>
+#include <asm/bootparam.h>
+#include <asm/mmu_context.h>
+#include <asm/tlb.h>
+#include <asm/tlbflush.h>
+#include <asm/page.h>
+#include <asm/pgalloc.h>
+#include <asm/pgtable.h>
+
+
+#define DEBUG 0
+
+DEFINE_PER_CPU(struct mmu_gather, mmu_gathers);
+//static DEFINE_SPINLOCK(tlb_lock);
+
+/*
+ * This flag is used to indicate that the page was mapped and modified in
+ * kernel space, so the cache is probably dirty at that address.
+ * If cache aliasing is enabled and the page color mismatches, update_mmu_cache
+ * synchronizes the caches if this bit is set.
+ */
+
+#define PG_cache_clean PG_arch_1
+
+/* References to section boundaries */
+
+extern char _ftext, _etext, _fdata, _edata, _rodata_end;
+extern char __init_begin, __init_end;
+
+/*
+ * mem_reserve(start, end, must_exist)
+ *
+ * Reserve some memory from the memory pool.
+ *
+ * Parameters:
+ * start Start of region,
+ * end End of region,
+ * must_exist Must exist in memory pool.
+ *
+ * Returns:
+ * 0 (memory area couldn't be mapped)
+ * -1 (success)
+ */
+
+int __init mem_reserve(unsigned long start, unsigned long end, int must_exist)
+{
+ int i;
+
+ if (start == end)
+ return 0;
+
+ start = start & PAGE_MASK;
+ end = PAGE_ALIGN(end);
+
+ for (i = 0; i < sysmem.nr_banks; i++)
+ if (start < sysmem.bank[i].end
+ && end >= sysmem.bank[i].start)
+ break;
+
+ if (i == sysmem.nr_banks) {
+ if (must_exist)
+ printk (KERN_WARNING "mem_reserve: [0x%0lx, 0x%0lx) "
+ "not in any region!\n", start, end);
+ return 0;
+ }
+
+ if (start > sysmem.bank[i].start) {
+ if (end < sysmem.bank[i].end) {
+ /* split entry */
+ if (sysmem.nr_banks >= SYSMEM_BANKS_MAX)
+ panic("meminfo overflow\n");
+ sysmem.bank[sysmem.nr_banks].start = end;
+ sysmem.bank[sysmem.nr_banks].end = sysmem.bank[i].end;
+ sysmem.nr_banks++;
+ }
+ sysmem.bank[i].end = start;
+ } else {
+ if (end < sysmem.bank[i].end)
+ sysmem.bank[i].start = end;
+ else {
+ /* remove entry */
+ sysmem.nr_banks--;
+ sysmem.bank[i].start = sysmem.bank[sysmem.nr_banks].start;
+ sysmem.bank[i].end = sysmem.bank[sysmem.nr_banks].end;
+ }
+ }
+ return -1;
+}
+
+
+/*
+ * Initialize the bootmem system and give it all the memory we have available.
+ */
+
+void __init bootmem_init(void)
+{
+ unsigned long pfn;
+ unsigned long bootmap_start, bootmap_size;
+ int i;
+
+ max_low_pfn = max_pfn = 0;
+ min_low_pfn = ~0;
+
+ for (i=0; i < sysmem.nr_banks; i++) {
+ pfn = PAGE_ALIGN(sysmem.bank[i].start) >> PAGE_SHIFT;
+ if (pfn < min_low_pfn)
+ min_low_pfn = pfn;
+ pfn = PAGE_ALIGN(sysmem.bank[i].end - 1) >> PAGE_SHIFT;
+ if (pfn > max_pfn)
+ max_pfn = pfn;
+ }
+
+ if (min_low_pfn > max_pfn)
+ panic("No memory found!\n");
+
+ max_low_pfn = max_pfn < MAX_LOW_MEMORY >> PAGE_SHIFT ?
+ max_pfn : MAX_LOW_MEMORY >> PAGE_SHIFT;
+
+ /* Find an area to use for the bootmem bitmap. */
+
+ bootmap_size = bootmem_bootmap_pages(max_low_pfn) << PAGE_SHIFT;
+ bootmap_start = ~0;
+
+ for (i=0; i<sysmem.nr_banks; i++)
+ if (sysmem.bank[i].end - sysmem.bank[i].start >= bootmap_size) {
+ bootmap_start = sysmem.bank[i].start;
+ break;
+ }
+
+ if (bootmap_start == ~0UL)
+ panic("Cannot find %ld bytes for bootmap\n", bootmap_size);
+
+ /* Reserve the bootmem bitmap area */
+
+ mem_reserve(bootmap_start, bootmap_start + bootmap_size, 1);
+ bootmap_size = init_bootmem_node(NODE_DATA(0), min_low_pfn,
+ bootmap_start >> PAGE_SHIFT,
+ max_low_pfn);
+
+ /* Add all remaining memory pieces into the bootmem map */
+
+ for (i=0; i<sysmem.nr_banks; i++)
+ free_bootmem(sysmem.bank[i].start,
+ sysmem.bank[i].end - sysmem.bank[i].start);
+
+}
+
+
+void __init paging_init(void)
+{
+ unsigned long zones_size[MAX_NR_ZONES];
+ int i;
+
+ /* All pages are DMA-able, so we put them all in the DMA zone. */
+
+ zones_size[ZONE_DMA] = max_low_pfn;
+ for (i = 1; i < MAX_NR_ZONES; i++)
+ zones_size[i] = 0;
+
+#ifdef CONFIG_HIGHMEM
+ zones_size[ZONE_HIGHMEM] = max_pfn - max_low_pfn;
+#endif
+
+ /* Initialize the kernel's page tables. */
+
+ memset(swapper_pg_dir, 0, PAGE_SIZE);
+
+ free_area_init(zones_size);
+}
+
+/*
+ * Flush the mmu and reset associated register to default values.
+ */
+
+void __init init_mmu (void)
+{
+ /* Writing zeros to the <t>TLBCFG special registers ensure
+ * that valid values exist in the register. For existing
+ * PGSZID<w> fields, zero selects the first element of the
+ * page-size array. For nonexistant PGSZID<w> fields, zero is
+ * the best value to write. Also, when changing PGSZID<w>
+ * fields, the corresponding TLB must be flushed.
+ */
+ set_itlbcfg_register (0);
+ set_dtlbcfg_register (0);
+ flush_tlb_all ();
+
+ /* Set rasid register to a known value. */
+
+ set_rasid_register (ASID_ALL_RESERVED);
+
+ /* Set PTEVADDR special register to the start of the page
+ * table, which is in kernel mappable space (ie. not
+ * statically mapped). This register's value is undefined on
+ * reset.
+ */
+ set_ptevaddr_register (PGTABLE_START);
+}
+
+/*
+ * Initialize memory pages.
+ */
+
+void __init mem_init(void)
+{
+ unsigned long codesize, reservedpages, datasize, initsize;
+ unsigned long highmemsize, tmp, ram;
+
+ max_mapnr = num_physpages = max_low_pfn;
+ high_memory = (void *) __va(max_mapnr << PAGE_SHIFT);
+ highmemsize = 0;
+
+#if CONFIG_HIGHMEM
+#error HIGHGMEM not implemented in init.c
+#endif
+
+ totalram_pages += free_all_bootmem();
+
+ reservedpages = ram = 0;
+ for (tmp = 0; tmp < max_low_pfn; tmp++) {
+ ram++;
+ if (PageReserved(mem_map+tmp))
+ reservedpages++;
+ }
+
+ codesize = (unsigned long) &_etext - (unsigned long) &_ftext;
+ datasize = (unsigned long) &_edata - (unsigned long) &_fdata;
+ initsize = (unsigned long) &__init_end - (unsigned long) &__init_begin;
+
+ printk("Memory: %luk/%luk available (%ldk kernel code, %ldk reserved, "
+ "%ldk data, %ldk init %ldk highmem)\n",
+ (unsigned long) nr_free_pages() << (PAGE_SHIFT-10),
+ ram << (PAGE_SHIFT-10),
+ codesize >> 10,
+ reservedpages << (PAGE_SHIFT-10),
+ datasize >> 10,
+ initsize >> 10,
+ highmemsize >> 10);
+}
+
+void
+free_reserved_mem(void *start, void *end)
+{
+ for (; start < end; start += PAGE_SIZE) {
+ ClearPageReserved(virt_to_page(start));
+ set_page_count(virt_to_page(start), 1);
+ free_page((unsigned long)start);
+ totalram_pages++;
+ }
+}
+
+#ifdef CONFIG_BLK_DEV_INITRD
+extern int initrd_is_mapped;
+
+void free_initrd_mem(unsigned long start, unsigned long end)
+{
+ if (initrd_is_mapped) {
+ free_reserved_mem((void*)start, (void*)end);
+ printk ("Freeing initrd memory: %ldk freed\n",(end-start)>>10);
+ }
+}
+#endif
+
+void free_initmem(void)
+{
+ free_reserved_mem(&__init_begin, &__init_end);
+ printk("Freeing unused kernel memory: %dk freed\n",
+ (&__init_end - &__init_begin) >> 10);
+}
+
+void show_mem(void)
+{
+ int i, free = 0, total = 0, reserved = 0;
+ int shared = 0, cached = 0;
+
+ printk("Mem-info:\n");
+ show_free_areas();
+ printk("Free swap: %6ldkB\n", nr_swap_pages<<(PAGE_SHIFT-10));
+ i = max_mapnr;
+ while (i-- > 0) {
+ total++;
+ if (PageReserved(mem_map+i))
+ reserved++;
+ else if (PageSwapCache(mem_map+i))
+ cached++;
+ else if (!page_count(mem_map + i))
+ free++;
+ else
+ shared += page_count(mem_map + i) - 1;
+ }
+ printk("%d pages of RAM\n", total);
+ printk("%d reserved pages\n", reserved);
+ printk("%d pages shared\n", shared);
+ printk("%d pages swap cached\n",cached);
+ printk("%d free pages\n", free);
+}
+
+/* ------------------------------------------------------------------------- */
+
+#if (DCACHE_WAY_SIZE > PAGE_SIZE)
+
+/*
+ * With cache aliasing, the page color of the page in kernel space and user
+ * space might mismatch. We temporarily map the page to a different virtual
+ * address with the same color and clear the page there.
+ */
+
+void clear_user_page(void *kaddr, unsigned long vaddr, struct page* page)
+{
+
+ /* There shouldn't be any entries for this page. */
+
+ __flush_invalidate_dcache_page_phys(__pa(page_address(page)));
+
+ if (!PAGE_COLOR_EQ(vaddr, kaddr)) {
+ unsigned long v, p;
+
+ /* Temporarily map page to DTLB_WAY_DCACHE_ALIAS0. */
+
+ spin_lock(&tlb_lock);
+
+ p = (unsigned long)pte_val((mk_pte(page,PAGE_KERNEL)));
+ kaddr = (void*)PAGE_COLOR_MAP0(vaddr);
+ v = (unsigned long)kaddr | DTLB_WAY_DCACHE_ALIAS0;
+ __asm__ __volatile__("wdtlb %0,%1; dsync" : :"a" (p), "a" (v));
+
+ clear_page(kaddr);
+
+ spin_unlock(&tlb_lock);
+ } else {
+ clear_page(kaddr);
+ }
+
+ /* We need to make sure that i$ and d$ are coherent. */
+
+ clear_bit(PG_cache_clean, &page->flags);
+}
+
+/*
+ * With cache aliasing, we have to make sure that the page color of the page
+ * in kernel space matches that of the virtual user address before we read
+ * the page. If the page color differ, we create a temporary DTLB entry with
+ * the corrent page color and use this 'temporary' address as the source.
+ * We then use the same approach as in clear_user_page and copy the data
+ * to the kernel space and clear the PG_cache_clean bit to synchronize caches
+ * later.
+ *
+ * Note:
+ * Instead of using another 'way' for the temporary DTLB entry, we could
+ * probably use the same entry that points to the kernel address (after
+ * saving the original value and restoring it when we are done).
+ */
+
+void copy_user_page(void* to, void* from, unsigned long vaddr,
+ struct page* to_page)
+{
+ /* There shouldn't be any entries for the new page. */
+
+ __flush_invalidate_dcache_page_phys(__pa(page_address(to_page)));
+
+ spin_lock(&tlb_lock);
+
+ if (!PAGE_COLOR_EQ(vaddr, from)) {
+ unsigned long v, p, t;
+
+ __asm__ __volatile__ ("pdtlb %1,%2; rdtlb1 %0,%1"
+ : "=a"(p), "=a"(t) : "a"(from));
+ from = (void*)PAGE_COLOR_MAP0(vaddr);
+ v = (unsigned long)from | DTLB_WAY_DCACHE_ALIAS0;
+ __asm__ __volatile__ ("wdtlb %0,%1; dsync" ::"a" (p), "a" (v));
+ }
+
+ if (!PAGE_COLOR_EQ(vaddr, to)) {
+ unsigned long v, p;
+
+ p = (unsigned long)pte_val((mk_pte(to_page,PAGE_KERNEL)));
+ to = (void*)PAGE_COLOR_MAP1(vaddr);
+ v = (unsigned long)to | DTLB_WAY_DCACHE_ALIAS1;
+ __asm__ __volatile__ ("wdtlb %0,%1; dsync" ::"a" (p), "a" (v));
+ }
+ copy_page(to, from);
+
+ spin_unlock(&tlb_lock);
+
+ /* We need to make sure that i$ and d$ are coherent. */
+
+ clear_bit(PG_cache_clean, &to_page->flags);
+}
+
+
+
+/*
+ * Any time the kernel writes to a user page cache page, or it is about to
+ * read from a page cache page this routine is called.
+ *
+ * Note:
+ * The kernel currently only provides one architecture bit in the page
+ * flags that we use for I$/D$ coherency. Maybe, in future, we can
+ * use a sepearte bit for deferred dcache aliasing:
+ * If the page is not mapped yet, we only need to set a flag,
+ * if mapped, we need to invalidate the page.
+ */
+// FIXME: we probably need this for WB caches not only for Page Coloring..
+
+void flush_dcache_page(struct page *page)
+{
+ unsigned long addr = __pa(page_address(page));
+ struct address_space *mapping = page_mapping(page);
+
+ __flush_invalidate_dcache_page_phys(addr);
+
+ if (!test_bit(PG_cache_clean, &page->flags))
+ return;
+
+ /* If this page hasn't been mapped, yet, handle I$/D$ coherency later.*/
+#if 0
+ if (mapping && !mapping_mapped(mapping))
+ clear_bit(PG_cache_clean, &page->flags);
+ else
+#endif
+ __invalidate_icache_page_phys(addr);
+}
+
+void flush_cache_range(struct vm_area_struct* vma, unsigned long s,
+ unsigned long e)
+{
+ __flush_invalidate_cache_all();
+}
+
+void flush_cache_page(struct vm_area_struct* vma, unsigned long address,
+ unsigned long pfn)
+{
+ struct page *page = pfn_to_page(pfn);
+
+ /* Remove any entry for the old mapping. */
+
+ if (current->active_mm == vma->vm_mm) {
+ unsigned long addr = __pa(page_address(page));
+ __flush_invalidate_dcache_page_phys(addr);
+ if ((vma->vm_flags & VM_EXEC) != 0)
+ __invalidate_icache_page_phys(addr);
+ } else {
+ BUG();
+ }
+}
+
+#endif /* (DCACHE_WAY_SIZE > PAGE_SIZE) */
+
+
+pte_t* pte_alloc_one_kernel (struct mm_struct* mm, unsigned long addr)
+{
+ pte_t* pte = (pte_t*)__get_free_pages(GFP_KERNEL|__GFP_REPEAT, 0);
+ if (likely(pte)) {
+ pte_t* ptep = (pte_t*)(pte_val(*pte) + PAGE_OFFSET);
+ int i;
+ for (i = 0; i < 1024; i++, ptep++)
+ pte_clear(mm, addr, ptep);
+ }
+ return pte;
+}
+
+struct page* pte_alloc_one(struct mm_struct *mm, unsigned long addr)
+{
+ struct page *page;
+
+ page = alloc_pages(GFP_KERNEL | __GFP_REPEAT, 0);
+
+ if (likely(page)) {
+ pte_t* ptep = kmap_atomic(page, KM_USER0);
+ int i;
+
+ for (i = 0; i < 1024; i++, ptep++)
+ pte_clear(mm, addr, ptep);
+
+ kunmap_atomic(ptep, KM_USER0);
+ }
+ return page;
+}
+
+
+/*
+ * Handle D$/I$ coherency.
+ *
+ * Note:
+ * We only have one architecture bit for the page flags, so we cannot handle
+ * cache aliasing, yet.
+ */
+
+void
+update_mmu_cache(struct vm_area_struct * vma, unsigned long addr, pte_t pte)
+{
+ unsigned long pfn = pte_pfn(pte);
+ struct page *page;
+ unsigned long vaddr = addr & PAGE_MASK;
+
+ if (!pfn_valid(pfn))
+ return;
+
+ page = pfn_to_page(pfn);
+
+ invalidate_itlb_mapping(addr);
+ invalidate_dtlb_mapping(addr);
+
+ /* We have a new mapping. Use it. */
+
+ write_dtlb_entry(pte, dtlb_probe(addr));
+
+ /* If the processor can execute from this page, synchronize D$/I$. */
+
+ if ((vma->vm_flags & VM_EXEC) != 0) {
+
+ write_itlb_entry(pte, itlb_probe(addr));
+
+ /* Synchronize caches, if not clean. */
+
+ if (!test_and_set_bit(PG_cache_clean, &page->flags)) {
+ __flush_dcache_page(vaddr);
+ __invalidate_icache_page(vaddr);
+ }
+ }
+}
+
diff --git a/arch/xtensa/mm/misc.S b/arch/xtensa/mm/misc.S
new file mode 100644
index 000000000000..327c0f17187c
--- /dev/null
+++ b/arch/xtensa/mm/misc.S
@@ -0,0 +1,374 @@
+/*
+ * arch/xtensa/mm/misc.S
+ *
+ * Miscellaneous assembly functions.
+ *
+ * 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) 2001 - 2005 Tensilica Inc.
+ *
+ * Chris Zankel <chris@zankel.net>
+ */
+
+/* Note: we might want to implement some of the loops as zero-overhead-loops,
+ * where applicable and if supported by the processor.
+ */
+
+#include <linux/linkage.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+
+#include <xtensa/cacheasm.h>
+#include <xtensa/cacheattrasm.h>
+
+/* clear_page (page) */
+
+ENTRY(clear_page)
+ entry a1, 16
+ addi a4, a2, PAGE_SIZE
+ movi a3, 0
+
+1: s32i a3, a2, 0
+ s32i a3, a2, 4
+ s32i a3, a2, 8
+ s32i a3, a2, 12
+ s32i a3, a2, 16
+ s32i a3, a2, 20
+ s32i a3, a2, 24
+ s32i a3, a2, 28
+ addi a2, a2, 32
+ blt a2, a4, 1b
+
+ retw
+
+/*
+ * copy_page (void *to, void *from)
+ * a2 a3
+ */
+
+ENTRY(copy_page)
+ entry a1, 16
+ addi a4, a2, PAGE_SIZE
+
+1: l32i a5, a3, 0
+ l32i a6, a3, 4
+ l32i a7, a3, 8
+ s32i a5, a2, 0
+ s32i a6, a2, 4
+ s32i a7, a2, 8
+ l32i a5, a3, 12
+ l32i a6, a3, 16
+ l32i a7, a3, 20
+ s32i a5, a2, 12
+ s32i a6, a2, 16
+ s32i a7, a2, 20
+ l32i a5, a3, 24
+ l32i a6, a3, 28
+ s32i a5, a2, 24
+ s32i a6, a2, 28
+ addi a2, a2, 32
+ addi a3, a3, 32
+ blt a2, a4, 1b
+
+ retw
+
+
+/*
+ * void __flush_invalidate_cache_all(void)
+ */
+
+ENTRY(__flush_invalidate_cache_all)
+ entry sp, 16
+ dcache_writeback_inv_all a2, a3
+ icache_invalidate_all a2, a3
+ retw
+
+/*
+ * void __invalidate_icache_all(void)
+ */
+
+ENTRY(__invalidate_icache_all)
+ entry sp, 16
+ icache_invalidate_all a2, a3
+ retw
+
+/*
+ * void __flush_invalidate_dcache_all(void)
+ */
+
+ENTRY(__flush_invalidate_dcache_all)
+ entry sp, 16
+ dcache_writeback_inv_all a2, a3
+ retw
+
+
+/*
+ * void __flush_invalidate_cache_range(ulong start, ulong size)
+ */
+
+ENTRY(__flush_invalidate_cache_range)
+ entry sp, 16
+ mov a4, a2
+ mov a5, a3
+ dcache_writeback_inv_region a4, a5, a6
+ icache_invalidate_region a2, a3, a4
+ retw
+
+/*
+ * void __invalidate_icache_page(ulong start)
+ */
+
+ENTRY(__invalidate_icache_page)
+ entry sp, 16
+ movi a3, PAGE_SIZE
+ icache_invalidate_region a2, a3, a4
+ retw
+
+/*
+ * void __invalidate_dcache_page(ulong start)
+ */
+
+ENTRY(__invalidate_dcache_page)
+ entry sp, 16
+ movi a3, PAGE_SIZE
+ dcache_invalidate_region a2, a3, a4
+ retw
+
+/*
+ * void __invalidate_icache_range(ulong start, ulong size)
+ */
+
+ENTRY(__invalidate_icache_range)
+ entry sp, 16
+ icache_invalidate_region a2, a3, a4
+ retw
+
+/*
+ * void __invalidate_dcache_range(ulong start, ulong size)
+ */
+
+ENTRY(__invalidate_dcache_range)
+ entry sp, 16
+ dcache_invalidate_region a2, a3, a4
+ retw
+
+/*
+ * void __flush_dcache_page(ulong start)
+ */
+
+ENTRY(__flush_dcache_page)
+ entry sp, 16
+ movi a3, PAGE_SIZE
+ dcache_writeback_region a2, a3, a4
+ retw
+
+/*
+ * void __flush_invalidate_dcache_page(ulong start)
+ */
+
+ENTRY(__flush_invalidate_dcache_page)
+ entry sp, 16
+ movi a3, PAGE_SIZE
+ dcache_writeback_inv_region a2, a3, a4
+ retw
+
+/*
+ * void __flush_invalidate_dcache_range(ulong start, ulong size)
+ */
+
+ENTRY(__flush_invalidate_dcache_range)
+ entry sp, 16
+ dcache_writeback_inv_region a2, a3, a4
+ retw
+
+/*
+ * void __invalidate_dcache_all(void)
+ */
+
+ENTRY(__invalidate_dcache_all)
+ entry sp, 16
+ dcache_invalidate_all a2, a3
+ retw
+
+/*
+ * void __flush_invalidate_dcache_page_phys(ulong start)
+ */
+
+ENTRY(__flush_invalidate_dcache_page_phys)
+ entry sp, 16
+
+ movi a3, XCHAL_DCACHE_SIZE
+ movi a4, PAGE_MASK | 1
+ addi a2, a2, 1
+
+1: addi a3, a3, -XCHAL_DCACHE_LINESIZE
+
+ ldct a6, a3
+ dsync
+ and a6, a6, a4
+ beq a6, a2, 2f
+ bgeui a3, 2, 1b
+ retw
+
+2: diwbi a3, 0
+ bgeui a3, 2, 1b
+ retw
+
+ENTRY(check_dcache_low0)
+ entry sp, 16
+
+ movi a3, XCHAL_DCACHE_SIZE / 4
+ movi a4, PAGE_MASK | 1
+ addi a2, a2, 1
+
+1: addi a3, a3, -XCHAL_DCACHE_LINESIZE
+
+ ldct a6, a3
+ dsync
+ and a6, a6, a4
+ beq a6, a2, 2f
+ bgeui a3, 2, 1b
+ retw
+
+2: j 2b
+
+ENTRY(check_dcache_high0)
+ entry sp, 16
+
+ movi a5, XCHAL_DCACHE_SIZE / 4
+ movi a3, XCHAL_DCACHE_SIZE / 2
+ movi a4, PAGE_MASK | 1
+ addi a2, a2, 1
+
+1: addi a3, a3, -XCHAL_DCACHE_LINESIZE
+ addi a5, a5, -XCHAL_DCACHE_LINESIZE
+
+ ldct a6, a3
+ dsync
+ and a6, a6, a4
+ beq a6, a2, 2f
+ bgeui a5, 2, 1b
+ retw
+
+2: j 2b
+
+ENTRY(check_dcache_low1)
+ entry sp, 16
+
+ movi a5, XCHAL_DCACHE_SIZE / 4
+ movi a3, XCHAL_DCACHE_SIZE * 3 / 4
+ movi a4, PAGE_MASK | 1
+ addi a2, a2, 1
+
+1: addi a3, a3, -XCHAL_DCACHE_LINESIZE
+ addi a5, a5, -XCHAL_DCACHE_LINESIZE
+
+ ldct a6, a3
+ dsync
+ and a6, a6, a4
+ beq a6, a2, 2f
+ bgeui a5, 2, 1b
+ retw
+
+2: j 2b
+
+ENTRY(check_dcache_high1)
+ entry sp, 16
+
+ movi a5, XCHAL_DCACHE_SIZE / 4
+ movi a3, XCHAL_DCACHE_SIZE
+ movi a4, PAGE_MASK | 1
+ addi a2, a2, 1
+
+1: addi a3, a3, -XCHAL_DCACHE_LINESIZE
+ addi a5, a5, -XCHAL_DCACHE_LINESIZE
+
+ ldct a6, a3
+ dsync
+ and a6, a6, a4
+ beq a6, a2, 2f
+ bgeui a5, 2, 1b
+ retw
+
+2: j 2b
+
+
+/*
+ * void __invalidate_icache_page_phys(ulong start)
+ */
+
+ENTRY(__invalidate_icache_page_phys)
+ entry sp, 16
+
+ movi a3, XCHAL_ICACHE_SIZE
+ movi a4, PAGE_MASK | 1
+ addi a2, a2, 1
+
+1: addi a3, a3, -XCHAL_ICACHE_LINESIZE
+
+ lict a6, a3
+ isync
+ and a6, a6, a4
+ beq a6, a2, 2f
+ bgeui a3, 2, 1b
+ retw
+
+2: iii a3, 0
+ bgeui a3, 2, 1b
+ retw
+
+
+#if 0
+
+ movi a3, XCHAL_DCACHE_WAYS - 1
+ movi a4, PAGE_SIZE
+
+1: mov a5, a2
+ add a6, a2, a4
+
+2: diwbi a5, 0
+ diwbi a5, XCHAL_DCACHE_LINESIZE
+ diwbi a5, XCHAL_DCACHE_LINESIZE * 2
+ diwbi a5, XCHAL_DCACHE_LINESIZE * 3
+
+ addi a5, a5, XCHAL_DCACHE_LINESIZE * 4
+ blt a5, a6, 2b
+
+ addi a3, a3, -1
+ addi a2, a2, XCHAL_DCACHE_SIZE / XCHAL_DCACHE_WAYS
+ bgez a3, 1b
+
+ retw
+
+ENTRY(__invalidate_icache_page_index)
+ entry sp, 16
+
+ movi a3, XCHAL_ICACHE_WAYS - 1
+ movi a4, PAGE_SIZE
+
+1: mov a5, a2
+ add a6, a2, a4
+
+2: iii a5, 0
+ iii a5, XCHAL_ICACHE_LINESIZE
+ iii a5, XCHAL_ICACHE_LINESIZE * 2
+ iii a5, XCHAL_ICACHE_LINESIZE * 3
+
+ addi a5, a5, XCHAL_ICACHE_LINESIZE * 4
+ blt a5, a6, 2b
+
+ addi a3, a3, -1
+ addi a2, a2, XCHAL_ICACHE_SIZE / XCHAL_ICACHE_WAYS
+ bgez a3, 2b
+
+ retw
+
+#endif
+
+
+
+
+
+
diff --git a/arch/xtensa/mm/pgtable.c b/arch/xtensa/mm/pgtable.c
new file mode 100644
index 000000000000..e5e119c820e4
--- /dev/null
+++ b/arch/xtensa/mm/pgtable.c
@@ -0,0 +1,76 @@
+/*
+ * arch/xtensa/mm/fault.c
+ *
+ * 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) 2001 - 2005 Tensilica Inc.
+ *
+ * Chris Zankel <chris@zankel.net>
+ */
+
+#if (DCACHE_SIZE > PAGE_SIZE)
+
+pte_t* pte_alloc_one_kernel(struct mm_struct *mm, unsigned long address)
+{
+ pte_t *pte, p;
+ int color = ADDR_COLOR(address);
+ int i;
+
+ p = (pte_t*) __get_free_pages(GFP_KERNEL|__GFP_REPEAT, COLOR_ORDER);
+
+ if (likely(p)) {
+ struct page *page;
+
+ for (i = 0; i < COLOR_SIZE; i++, p++) {
+ page = virt_to_page(pte);
+
+ set_page_count(page, 1);
+ ClearPageCompound(page);
+
+ if (ADDR_COLOR(p) == color)
+ pte = p;
+ else
+ free_page(p);
+ }
+ clear_page(pte);
+ }
+ return pte;
+}
+
+#ifdef PROFILING
+
+int mask;
+int hit;
+int flush;
+
+#endif
+
+struct page* pte_alloc_one(struct mm_struct *mm, unsigned long address)
+{
+ struct page *page, p;
+ int color = ADDR_COLOR(address);
+
+ p = alloc_pages(GFP_KERNEL | __GFP_REPEAT, PTE_ORDER);
+
+ if (likely(p)) {
+ for (i = 0; i < PAGE_ORDER; i++) {
+ set_page_count(p, 1);
+ ClearPageCompound(p);
+
+ if (PADDR_COLOR(page_address(pg)) == color)
+ page = p;
+ else
+ free_page(p);
+ }
+ clear_highpage(page);
+ }
+
+ return page;
+}
+
+#endif
+
+
+
diff --git a/arch/xtensa/mm/tlb.c b/arch/xtensa/mm/tlb.c
new file mode 100644
index 000000000000..d3bd3bfc3b3b
--- /dev/null
+++ b/arch/xtensa/mm/tlb.c
@@ -0,0 +1,545 @@
+/*
+ * arch/xtensa/mm/mmu.c
+ *
+ * Logic that manipulates the Xtensa MMU. Derived from MIPS.
+ *
+ * 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) 2001 - 2003 Tensilica Inc.
+ *
+ * Joe Taylor
+ * Chris Zankel <chris@zankel.net>
+ * Marc Gauthier
+ */
+
+#include <linux/mm.h>
+#include <asm/processor.h>
+#include <asm/mmu_context.h>
+#include <asm/tlbflush.h>
+#include <asm/system.h>
+#include <asm/cacheflush.h>
+
+
+static inline void __flush_itlb_all (void)
+{
+ int way, index;
+
+ for (way = 0; way < XCHAL_ITLB_ARF_WAYS; way++) {
+ for (index = 0; index < ITLB_ENTRIES_PER_ARF_WAY; index++) {
+ int entry = way + (index << PAGE_SHIFT);
+ invalidate_itlb_entry_no_isync (entry);
+ }
+ }
+ asm volatile ("isync\n");
+}
+
+static inline void __flush_dtlb_all (void)
+{
+ int way, index;
+
+ for (way = 0; way < XCHAL_DTLB_ARF_WAYS; way++) {
+ for (index = 0; index < DTLB_ENTRIES_PER_ARF_WAY; index++) {
+ int entry = way + (index << PAGE_SHIFT);
+ invalidate_dtlb_entry_no_isync (entry);
+ }
+ }
+ asm volatile ("isync\n");
+}
+
+
+void flush_tlb_all (void)
+{
+ __flush_itlb_all();
+ __flush_dtlb_all();
+}
+
+/* If mm is current, we simply assign the current task a new ASID, thus,
+ * invalidating all previous tlb entries. If mm is someone else's user mapping,
+ * wie invalidate the context, thus, when that user mapping is swapped in,
+ * a new context will be assigned to it.
+ */
+
+void flush_tlb_mm(struct mm_struct *mm)
+{
+#if 0
+ printk("[tlbmm<%lx>]\n", (unsigned long)mm->context);
+#endif
+
+ if (mm == current->active_mm) {
+ int flags;
+ local_save_flags(flags);
+ get_new_mmu_context(mm, asid_cache);
+ set_rasid_register(ASID_INSERT(mm->context));
+ local_irq_restore(flags);
+ }
+ else
+ mm->context = 0;
+}
+
+void flush_tlb_range (struct vm_area_struct *vma,
+ unsigned long start, unsigned long end)
+{
+ struct mm_struct *mm = vma->vm_mm;
+ unsigned long flags;
+
+ if (mm->context == NO_CONTEXT)
+ return;
+
+#if 0
+ printk("[tlbrange<%02lx,%08lx,%08lx>]\n",
+ (unsigned long)mm->context, start, end);
+#endif
+ local_save_flags(flags);
+
+ if (end-start + (PAGE_SIZE-1) <= SMALLEST_NTLB_ENTRIES << PAGE_SHIFT) {
+ int oldpid = get_rasid_register();
+ set_rasid_register (ASID_INSERT(mm->context));
+ start &= PAGE_MASK;
+ if (vma->vm_flags & VM_EXEC)
+ while(start < end) {
+ invalidate_itlb_mapping(start);
+ invalidate_dtlb_mapping(start);
+ start += PAGE_SIZE;
+ }
+ else
+ while(start < end) {
+ invalidate_dtlb_mapping(start);
+ start += PAGE_SIZE;
+ }
+
+ set_rasid_register(oldpid);
+ } else {
+ get_new_mmu_context(mm, asid_cache);
+ if (mm == current->active_mm)
+ set_rasid_register(ASID_INSERT(mm->context));
+ }
+ local_irq_restore(flags);
+}
+
+void flush_tlb_page (struct vm_area_struct *vma, unsigned long page)
+{
+ struct mm_struct* mm = vma->vm_mm;
+ unsigned long flags;
+ int oldpid;
+#if 0
+ printk("[tlbpage<%02lx,%08lx>]\n",
+ (unsigned long)mm->context, page);
+#endif
+
+ if(mm->context == NO_CONTEXT)
+ return;
+
+ local_save_flags(flags);
+
+ oldpid = get_rasid_register();
+
+ if (vma->vm_flags & VM_EXEC)
+ invalidate_itlb_mapping(page);
+ invalidate_dtlb_mapping(page);
+
+ set_rasid_register(oldpid);
+
+ local_irq_restore(flags);
+
+#if 0
+ flush_tlb_all();
+ return;
+#endif
+}
+
+
+#ifdef DEBUG_TLB
+
+#define USE_ITLB 0
+#define USE_DTLB 1
+
+struct way_config_t {
+ int indicies;
+ int indicies_log2;
+ int pgsz_log2;
+ int arf;
+};
+
+static struct way_config_t itlb[XCHAL_ITLB_WAYS] =
+{
+ { XCHAL_ITLB_SET(XCHAL_ITLB_WAY0_SET, ENTRIES),
+ XCHAL_ITLB_SET(XCHAL_ITLB_WAY0_SET, ENTRIES_LOG2),
+ XCHAL_ITLB_SET(XCHAL_ITLB_WAY0_SET, PAGESZ_LOG2_MIN),
+ XCHAL_ITLB_SET(XCHAL_ITLB_WAY0_SET, ARF)
+ },
+ { XCHAL_ITLB_SET(XCHAL_ITLB_WAY1_SET, ENTRIES),
+ XCHAL_ITLB_SET(XCHAL_ITLB_WAY1_SET, ENTRIES_LOG2),
+ XCHAL_ITLB_SET(XCHAL_ITLB_WAY1_SET, PAGESZ_LOG2_MIN),
+ XCHAL_ITLB_SET(XCHAL_ITLB_WAY1_SET, ARF)
+ },
+ { XCHAL_ITLB_SET(XCHAL_ITLB_WAY2_SET, ENTRIES),
+ XCHAL_ITLB_SET(XCHAL_ITLB_WAY2_SET, ENTRIES_LOG2),
+ XCHAL_ITLB_SET(XCHAL_ITLB_WAY2_SET, PAGESZ_LOG2_MIN),
+ XCHAL_ITLB_SET(XCHAL_ITLB_WAY2_SET, ARF)
+ },
+ { XCHAL_ITLB_SET(XCHAL_ITLB_WAY3_SET, ENTRIES),
+ XCHAL_ITLB_SET(XCHAL_ITLB_WAY3_SET, ENTRIES_LOG2),
+ XCHAL_ITLB_SET(XCHAL_ITLB_WAY3_SET, PAGESZ_LOG2_MIN),
+ XCHAL_ITLB_SET(XCHAL_ITLB_WAY3_SET, ARF)
+ },
+ { XCHAL_ITLB_SET(XCHAL_ITLB_WAY4_SET, ENTRIES),
+ XCHAL_ITLB_SET(XCHAL_ITLB_WAY4_SET, ENTRIES_LOG2),
+ XCHAL_ITLB_SET(XCHAL_ITLB_WAY4_SET, PAGESZ_LOG2_MIN),
+ XCHAL_ITLB_SET(XCHAL_ITLB_WAY4_SET, ARF)
+ },
+ { XCHAL_ITLB_SET(XCHAL_ITLB_WAY5_SET, ENTRIES),
+ XCHAL_ITLB_SET(XCHAL_ITLB_WAY5_SET, ENTRIES_LOG2),
+ XCHAL_ITLB_SET(XCHAL_ITLB_WAY5_SET, PAGESZ_LOG2_MIN),
+ XCHAL_ITLB_SET(XCHAL_ITLB_WAY5_SET, ARF)
+ },
+ { XCHAL_ITLB_SET(XCHAL_ITLB_WAY6_SET, ENTRIES),
+ XCHAL_ITLB_SET(XCHAL_ITLB_WAY6_SET, ENTRIES_LOG2),
+ XCHAL_ITLB_SET(XCHAL_ITLB_WAY6_SET, PAGESZ_LOG2_MIN),
+ XCHAL_ITLB_SET(XCHAL_ITLB_WAY6_SET, ARF)
+ }
+};
+
+static struct way_config_t dtlb[XCHAL_DTLB_WAYS] =
+{
+ { XCHAL_DTLB_SET(XCHAL_DTLB_WAY0_SET, ENTRIES),
+ XCHAL_DTLB_SET(XCHAL_DTLB_WAY0_SET, ENTRIES_LOG2),
+ XCHAL_DTLB_SET(XCHAL_DTLB_WAY0_SET, PAGESZ_LOG2_MIN),
+ XCHAL_DTLB_SET(XCHAL_DTLB_WAY0_SET, ARF)
+ },
+ { XCHAL_DTLB_SET(XCHAL_DTLB_WAY1_SET, ENTRIES),
+ XCHAL_DTLB_SET(XCHAL_DTLB_WAY1_SET, ENTRIES_LOG2),
+ XCHAL_DTLB_SET(XCHAL_DTLB_WAY1_SET, PAGESZ_LOG2_MIN),
+ XCHAL_DTLB_SET(XCHAL_DTLB_WAY1_SET, ARF)
+ },
+ { XCHAL_DTLB_SET(XCHAL_DTLB_WAY2_SET, ENTRIES),
+ XCHAL_DTLB_SET(XCHAL_DTLB_WAY2_SET, ENTRIES_LOG2),
+ XCHAL_DTLB_SET(XCHAL_DTLB_WAY2_SET, PAGESZ_LOG2_MIN),
+ XCHAL_DTLB_SET(XCHAL_DTLB_WAY2_SET, ARF)
+ },
+ { XCHAL_DTLB_SET(XCHAL_DTLB_WAY3_SET, ENTRIES),
+ XCHAL_DTLB_SET(XCHAL_DTLB_WAY3_SET, ENTRIES_LOG2),
+ XCHAL_DTLB_SET(XCHAL_DTLB_WAY3_SET, PAGESZ_LOG2_MIN),
+ XCHAL_DTLB_SET(XCHAL_DTLB_WAY3_SET, ARF)
+ },
+ { XCHAL_DTLB_SET(XCHAL_DTLB_WAY4_SET, ENTRIES),
+ XCHAL_DTLB_SET(XCHAL_DTLB_WAY4_SET, ENTRIES_LOG2),
+ XCHAL_DTLB_SET(XCHAL_DTLB_WAY4_SET, PAGESZ_LOG2_MIN),
+ XCHAL_DTLB_SET(XCHAL_DTLB_WAY4_SET, ARF)
+ },
+ { XCHAL_DTLB_SET(XCHAL_DTLB_WAY5_SET, ENTRIES),
+ XCHAL_DTLB_SET(XCHAL_DTLB_WAY5_SET, ENTRIES_LOG2),
+ XCHAL_DTLB_SET(XCHAL_DTLB_WAY5_SET, PAGESZ_LOG2_MIN),
+ XCHAL_DTLB_SET(XCHAL_DTLB_WAY5_SET, ARF)
+ },
+ { XCHAL_DTLB_SET(XCHAL_DTLB_WAY6_SET, ENTRIES),
+ XCHAL_DTLB_SET(XCHAL_DTLB_WAY6_SET, ENTRIES_LOG2),
+ XCHAL_DTLB_SET(XCHAL_DTLB_WAY6_SET, PAGESZ_LOG2_MIN),
+ XCHAL_DTLB_SET(XCHAL_DTLB_WAY6_SET, ARF)
+ },
+ { XCHAL_DTLB_SET(XCHAL_DTLB_WAY7_SET, ENTRIES),
+ XCHAL_DTLB_SET(XCHAL_DTLB_WAY7_SET, ENTRIES_LOG2),
+ XCHAL_DTLB_SET(XCHAL_DTLB_WAY7_SET, PAGESZ_LOG2_MIN),
+ XCHAL_DTLB_SET(XCHAL_DTLB_WAY7_SET, ARF)
+ },
+ { XCHAL_DTLB_SET(XCHAL_DTLB_WAY8_SET, ENTRIES),
+ XCHAL_DTLB_SET(XCHAL_DTLB_WAY8_SET, ENTRIES_LOG2),
+ XCHAL_DTLB_SET(XCHAL_DTLB_WAY8_SET, PAGESZ_LOG2_MIN),
+ XCHAL_DTLB_SET(XCHAL_DTLB_WAY8_SET, ARF)
+ },
+ { XCHAL_DTLB_SET(XCHAL_DTLB_WAY9_SET, ENTRIES),
+ XCHAL_DTLB_SET(XCHAL_DTLB_WAY9_SET, ENTRIES_LOG2),
+ XCHAL_DTLB_SET(XCHAL_DTLB_WAY9_SET, PAGESZ_LOG2_MIN),
+ XCHAL_DTLB_SET(XCHAL_DTLB_WAY9_SET, ARF)
+ }
+};
+
+/* Total number of entries: */
+#define ITLB_TOTAL_ENTRIES \
+ XCHAL_ITLB_SET(XCHAL_ITLB_WAY0_SET, ENTRIES) + \
+ XCHAL_ITLB_SET(XCHAL_ITLB_WAY1_SET, ENTRIES) + \
+ XCHAL_ITLB_SET(XCHAL_ITLB_WAY2_SET, ENTRIES) + \
+ XCHAL_ITLB_SET(XCHAL_ITLB_WAY3_SET, ENTRIES) + \
+ XCHAL_ITLB_SET(XCHAL_ITLB_WAY4_SET, ENTRIES) + \
+ XCHAL_ITLB_SET(XCHAL_ITLB_WAY5_SET, ENTRIES) + \
+ XCHAL_ITLB_SET(XCHAL_ITLB_WAY6_SET, ENTRIES)
+#define DTLB_TOTAL_ENTRIES \
+ XCHAL_DTLB_SET(XCHAL_DTLB_WAY0_SET, ENTRIES) + \
+ XCHAL_DTLB_SET(XCHAL_DTLB_WAY1_SET, ENTRIES) + \
+ XCHAL_DTLB_SET(XCHAL_DTLB_WAY2_SET, ENTRIES) + \
+ XCHAL_DTLB_SET(XCHAL_DTLB_WAY3_SET, ENTRIES) + \
+ XCHAL_DTLB_SET(XCHAL_DTLB_WAY4_SET, ENTRIES) + \
+ XCHAL_DTLB_SET(XCHAL_DTLB_WAY5_SET, ENTRIES) + \
+ XCHAL_DTLB_SET(XCHAL_DTLB_WAY6_SET, ENTRIES) + \
+ XCHAL_DTLB_SET(XCHAL_DTLB_WAY7_SET, ENTRIES) + \
+ XCHAL_DTLB_SET(XCHAL_DTLB_WAY8_SET, ENTRIES) + \
+ XCHAL_DTLB_SET(XCHAL_DTLB_WAY9_SET, ENTRIES)
+
+
+typedef struct {
+ unsigned va;
+ unsigned pa;
+ unsigned char asid;
+ unsigned char ca;
+ unsigned char way;
+ unsigned char index;
+ unsigned char pgsz_log2; /* 0 .. 32 */
+ unsigned char type; /* 0=ITLB 1=DTLB */
+} tlb_dump_entry_t;
+
+/* Return -1 if a precedes b, +1 if a follows b, 0 if same: */
+int cmp_tlb_dump_info( tlb_dump_entry_t *a, tlb_dump_entry_t *b )
+{
+ if (a->asid < b->asid) return -1;
+ if (a->asid > b->asid) return 1;
+ if (a->va < b->va) return -1;
+ if (a->va > b->va) return 1;
+ if (a->pa < b->pa) return -1;
+ if (a->pa > b->pa) return 1;
+ if (a->ca < b->ca) return -1;
+ if (a->ca > b->ca) return 1;
+ if (a->way < b->way) return -1;
+ if (a->way > b->way) return 1;
+ if (a->index < b->index) return -1;
+ if (a->index > b->index) return 1;
+ return 0;
+}
+
+void sort_tlb_dump_info( tlb_dump_entry_t *t, int n )
+{
+ int i, j;
+ /* Simple O(n*n) sort: */
+ for (i = 0; i < n-1; i++)
+ for (j = i+1; j < n; j++)
+ if (cmp_tlb_dump_info(t+i, t+j) > 0) {
+ tlb_dump_entry_t tmp = t[i];
+ t[i] = t[j];
+ t[j] = tmp;
+ }
+}
+
+
+static tlb_dump_entry_t itlb_dump_info[ITLB_TOTAL_ENTRIES];
+static tlb_dump_entry_t dtlb_dump_info[DTLB_TOTAL_ENTRIES];
+
+
+static inline char *way_type (int type)
+{
+ return type ? "autorefill" : "non-autorefill";
+}
+
+void print_entry (struct way_config_t *way_info,
+ unsigned int way,
+ unsigned int index,
+ unsigned int virtual,
+ unsigned int translation)
+{
+ char valid_chr;
+ unsigned int va, pa, asid, ca;
+
+ va = virtual &
+ ~((1 << (way_info->pgsz_log2 + way_info->indicies_log2)) - 1);
+ asid = virtual & ((1 << XCHAL_MMU_ASID_BITS) - 1);
+ pa = translation & ~((1 << way_info->pgsz_log2) - 1);
+ ca = translation & ((1 << XCHAL_MMU_CA_BITS) - 1);
+ valid_chr = asid ? 'V' : 'I';
+
+ /* Compute and incorporate the effect of the index bits on the
+ * va. It's more useful for kernel debugging, since we always
+ * want to know the effective va anyway. */
+
+ va += index << way_info->pgsz_log2;
+
+ printk ("\t[%d,%d] (%c) vpn 0x%.8x ppn 0x%.8x asid 0x%.2x am 0x%x\n",
+ way, index, valid_chr, va, pa, asid, ca);
+}
+
+void print_itlb_entry (struct way_config_t *way_info, int way, int index)
+{
+ print_entry (way_info, way, index,
+ read_itlb_virtual (way + (index << way_info->pgsz_log2)),
+ read_itlb_translation (way + (index << way_info->pgsz_log2)));
+}
+
+void print_dtlb_entry (struct way_config_t *way_info, int way, int index)
+{
+ print_entry (way_info, way, index,
+ read_dtlb_virtual (way + (index << way_info->pgsz_log2)),
+ read_dtlb_translation (way + (index << way_info->pgsz_log2)));
+}
+
+void dump_itlb (void)
+{
+ int way, index;
+
+ printk ("\nITLB: ways = %d\n", XCHAL_ITLB_WAYS);
+
+ for (way = 0; way < XCHAL_ITLB_WAYS; way++) {
+ printk ("\nWay: %d, Entries: %d, MinPageSize: %d, Type: %s\n",
+ way, itlb[way].indicies,
+ itlb[way].pgsz_log2, way_type(itlb[way].arf));
+ for (index = 0; index < itlb[way].indicies; index++) {
+ print_itlb_entry(&itlb[way], way, index);
+ }
+ }
+}
+
+void dump_dtlb (void)
+{
+ int way, index;
+
+ printk ("\nDTLB: ways = %d\n", XCHAL_DTLB_WAYS);
+
+ for (way = 0; way < XCHAL_DTLB_WAYS; way++) {
+ printk ("\nWay: %d, Entries: %d, MinPageSize: %d, Type: %s\n",
+ way, dtlb[way].indicies,
+ dtlb[way].pgsz_log2, way_type(dtlb[way].arf));
+ for (index = 0; index < dtlb[way].indicies; index++) {
+ print_dtlb_entry(&dtlb[way], way, index);
+ }
+ }
+}
+
+void dump_tlb (tlb_dump_entry_t *tinfo, struct way_config_t *config,
+ int entries, int ways, int type, int show_invalid)
+{
+ tlb_dump_entry_t *e = tinfo;
+ int way, i;
+
+ /* Gather all info: */
+ for (way = 0; way < ways; way++) {
+ struct way_config_t *cfg = config + way;
+ for (i = 0; i < cfg->indicies; i++) {
+ unsigned wayindex = way + (i << cfg->pgsz_log2);
+ unsigned vv = (type ? read_dtlb_virtual (wayindex)
+ : read_itlb_virtual (wayindex));
+ unsigned pp = (type ? read_dtlb_translation (wayindex)
+ : read_itlb_translation (wayindex));
+
+ /* Compute and incorporate the effect of the index bits on the
+ * va. It's more useful for kernel debugging, since we always
+ * want to know the effective va anyway. */
+
+ e->va = (vv & ~((1 << (cfg->pgsz_log2 + cfg->indicies_log2)) - 1));
+ e->va += (i << cfg->pgsz_log2);
+ e->pa = (pp & ~((1 << cfg->pgsz_log2) - 1));
+ e->asid = (vv & ((1 << XCHAL_MMU_ASID_BITS) - 1));
+ e->ca = (pp & ((1 << XCHAL_MMU_CA_BITS) - 1));
+ e->way = way;
+ e->index = i;
+ e->pgsz_log2 = cfg->pgsz_log2;
+ e->type = type;
+ e++;
+ }
+ }
+#if 1
+ /* Sort by ASID and VADDR: */
+ sort_tlb_dump_info (tinfo, entries);
+#endif
+
+ /* Display all sorted info: */
+ printk ("\n%cTLB dump:\n", (type ? 'D' : 'I'));
+ for (e = tinfo, i = 0; i < entries; i++, e++) {
+#if 0
+ if (e->asid == 0 && !show_invalid)
+ continue;
+#endif
+ printk ("%c way=%d i=%d ASID=%02X V=%08X -> P=%08X CA=%X (%d %cB)\n",
+ (e->type ? 'D' : 'I'), e->way, e->index,
+ e->asid, e->va, e->pa, e->ca,
+ (1 << (e->pgsz_log2 % 10)),
+ " kMG"[e->pgsz_log2 / 10]
+ );
+ }
+}
+
+void dump_tlbs2 (int showinv)
+{
+ dump_tlb (itlb_dump_info, itlb, ITLB_TOTAL_ENTRIES, XCHAL_ITLB_WAYS, 0, showinv);
+ dump_tlb (dtlb_dump_info, dtlb, DTLB_TOTAL_ENTRIES, XCHAL_DTLB_WAYS, 1, showinv);
+}
+
+void dump_all_tlbs (void)
+{
+ dump_tlbs2 (1);
+}
+
+void dump_valid_tlbs (void)
+{
+ dump_tlbs2 (0);
+}
+
+
+void dump_tlbs (void)
+{
+ dump_itlb();
+ dump_dtlb();
+}
+
+void dump_cache_tag(int dcache, int idx)
+{
+ int w, i, s, e;
+ unsigned long tag, index;
+ unsigned long num_lines, num_ways, cache_size, line_size;
+
+ num_ways = dcache ? XCHAL_DCACHE_WAYS : XCHAL_ICACHE_WAYS;
+ cache_size = dcache ? XCHAL_DCACHE_SIZE : XCHAL_ICACHE_SIZE;
+ line_size = dcache ? XCHAL_DCACHE_LINESIZE : XCHAL_ICACHE_LINESIZE;
+
+ num_lines = cache_size / num_ways;
+
+ s = 0; e = num_lines;
+
+ if (idx >= 0)
+ e = (s = idx * line_size) + 1;
+
+ for (i = s; i < e; i+= line_size) {
+ printk("\nline %#08x:", i);
+ for (w = 0; w < num_ways; w++) {
+ index = w * num_lines + i;
+ if (dcache)
+ __asm__ __volatile__("ldct %0, %1\n\t"
+ : "=a"(tag) : "a"(index));
+ else
+ __asm__ __volatile__("lict %0, %1\n\t"
+ : "=a"(tag) : "a"(index));
+
+ printk(" %#010lx", tag);
+ }
+ }
+ printk ("\n");
+}
+
+void dump_icache(int index)
+{
+ unsigned long data, addr;
+ int w, i;
+
+ const unsigned long num_ways = XCHAL_ICACHE_WAYS;
+ const unsigned long cache_size = XCHAL_ICACHE_SIZE;
+ const unsigned long line_size = XCHAL_ICACHE_LINESIZE;
+ const unsigned long num_lines = cache_size / num_ways / line_size;
+
+ for (w = 0; w < num_ways; w++) {
+ printk ("\nWay %d", w);
+
+ for (i = 0; i < line_size; i+= 4) {
+ addr = w * num_lines + index * line_size + i;
+ __asm__ __volatile__("licw %0, %1\n\t"
+ : "=a"(data) : "a"(addr));
+ printk(" %#010lx", data);
+ }
+ }
+ printk ("\n");
+}
+
+void dump_cache_tags(void)
+{
+ printk("Instruction cache\n");
+ dump_cache_tag(0, -1);
+ printk("Data cache\n");
+ dump_cache_tag(1, -1);
+}
+
+#endif
diff --git a/arch/xtensa/platform-iss/Makefile b/arch/xtensa/platform-iss/Makefile
new file mode 100644
index 000000000000..5b394e9620e5
--- /dev/null
+++ b/arch/xtensa/platform-iss/Makefile
@@ -0,0 +1,13 @@
+# $Id: Makefile,v 1.1.1.1 2002/08/28 16:10:14 aroll Exp $
+#
+# Makefile for the Xtensa Instruction Set Simulator (ISS)
+# "prom monitor" library routines under Linux.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are in the main makefile...
+
+obj-y = io.o console.o setup.o network.o
+
diff --git a/arch/xtensa/platform-iss/console.c b/arch/xtensa/platform-iss/console.c
new file mode 100644
index 000000000000..4fbddf92a921
--- /dev/null
+++ b/arch/xtensa/platform-iss/console.c
@@ -0,0 +1,300 @@
+/*
+ * arch/xtensa/platform-iss/console.c
+ *
+ * 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) 2001-2005 Tensilica Inc.
+ * Authors Christian Zankel, Joe Taylor
+ */
+
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/console.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/major.h>
+#include <linux/param.h>
+#include <linux/serial.h>
+#include <linux/serialP.h>
+#include <linux/console.h>
+
+#include <asm/uaccess.h>
+#include <asm/irq.h>
+
+#include <xtensa/simcall.h>
+
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+
+#ifdef SERIAL_INLINE
+#define _INLINE_ inline
+#endif
+
+#define SERIAL_MAX_NUM_LINES 1
+#define SERIAL_TIMER_VALUE (20 * HZ)
+
+static struct tty_driver *serial_driver;
+static struct timer_list serial_timer;
+
+static DEFINE_SPINLOCK(timer_lock);
+
+int errno;
+
+static int __simc (int a, int b, int c, int d, int e, int f)
+{
+ int ret;
+ __asm__ __volatile__ ("simcall\n"
+ "mov %0, a2\n"
+ "mov %1, a3\n" : "=a" (ret), "=a" (errno)
+ : : "a2", "a3");
+ return ret;
+}
+
+static char *serial_version = "0.1";
+static char *serial_name = "ISS serial driver";
+
+/*
+ * This routine is called whenever a serial port is opened. It
+ * enables interrupts for a serial port, linking in its async structure into
+ * the IRQ chain. It also performs the serial-specific
+ * initialization for the tty structure.
+ */
+
+static void rs_poll(unsigned long);
+
+static int rs_open(struct tty_struct *tty, struct file * filp)
+{
+ int line = tty->index;
+
+ if ((line < 0) || (line >= SERIAL_MAX_NUM_LINES))
+ return -ENODEV;
+
+ spin_lock(&timer_lock);
+
+ if (tty->count == 1) {
+ init_timer(&serial_timer);
+ serial_timer.data = (unsigned long) tty;
+ serial_timer.function = rs_poll;
+ mod_timer(&serial_timer, jiffies + SERIAL_TIMER_VALUE);
+ }
+ spin_unlock(&timer_lock);
+
+ return 0;
+}
+
+
+/*
+ * ------------------------------------------------------------
+ * iss_serial_close()
+ *
+ * This routine is called when the serial port gets closed. First, we
+ * wait for the last remaining data to be sent. Then, we unlink its
+ * async structure from the interrupt chain if necessary, and we free
+ * that IRQ if nothing is left in the chain.
+ * ------------------------------------------------------------
+ */
+static void rs_close(struct tty_struct *tty, struct file * filp)
+{
+ spin_lock(&timer_lock);
+ if (tty->count == 1)
+ del_timer_sync(&serial_timer);
+ spin_unlock(&timer_lock);
+}
+
+
+static int rs_write(struct tty_struct * tty,
+ const unsigned char *buf, int count)
+{
+ /* see drivers/char/serialX.c to reference original version */
+
+ __simc (SYS_write, 1, (unsigned long)buf, count, 0, 0);
+ return count;
+}
+
+static void rs_poll(unsigned long priv)
+{
+ struct tty_struct* tty = (struct tty_struct*) priv;
+
+ struct timeval tv = { .tv_sec = 0, .tv_usec = 0 };
+ int i = 0;
+ unsigned char c;
+
+ spin_lock(&timer_lock);
+
+ while (__simc(SYS_select_one, 0, XTISS_SELECT_ONE_READ, (int)&tv,0,0)){
+ __simc (SYS_read, 0, (unsigned long)&c, 1, 0, 0);
+ tty->flip.count++;
+ *tty->flip.char_buf_ptr++ = c;
+ *tty->flip.flag_buf_ptr++ = TTY_NORMAL;
+ i++;
+ }
+
+ if (i)
+ tty_flip_buffer_push(tty);
+
+
+ mod_timer(&serial_timer, jiffies + SERIAL_TIMER_VALUE);
+ spin_unlock(&timer_lock);
+}
+
+
+static void rs_put_char(struct tty_struct *tty, unsigned char ch)
+{
+ char buf[2];
+
+ if (!tty)
+ return;
+
+ buf[0] = ch;
+ buf[1] = '\0'; /* Is this NULL necessary? */
+ __simc (SYS_write, 1, (unsigned long) buf, 1, 0, 0);
+}
+
+static void rs_flush_chars(struct tty_struct *tty)
+{
+}
+
+static int rs_write_room(struct tty_struct *tty)
+{
+ /* Let's say iss can always accept 2K characters.. */
+ return 2 * 1024;
+}
+
+static int rs_chars_in_buffer(struct tty_struct *tty)
+{
+ /* the iss doesn't buffer characters */
+ return 0;
+}
+
+static void rs_hangup(struct tty_struct *tty)
+{
+ /* Stub, once again.. */
+}
+
+static void rs_wait_until_sent(struct tty_struct *tty, int timeout)
+{
+ /* Stub, once again.. */
+}
+
+static int rs_read_proc(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ int len = 0;
+ off_t begin = 0;
+
+ len += sprintf(page, "serinfo:1.0 driver:%s\n", serial_version);
+ *eof = 1;
+
+ if (off >= len + begin)
+ return 0;
+
+ *start = page + (off - begin);
+ return ((count < begin + len - off) ? count : begin + len - off);
+}
+
+
+static struct tty_operations serial_ops = {
+ .open = rs_open,
+ .close = rs_close,
+ .write = rs_write,
+ .put_char = rs_put_char,
+ .flush_chars = rs_flush_chars,
+ .write_room = rs_write_room,
+ .chars_in_buffer = rs_chars_in_buffer,
+ .hangup = rs_hangup,
+ .wait_until_sent = rs_wait_until_sent,
+ .read_proc = rs_read_proc
+};
+
+int __init rs_init(void)
+{
+ serial_driver = alloc_tty_driver(1);
+
+ printk ("%s %s\n", serial_name, serial_version);
+
+ /* Initialize the tty_driver structure */
+
+ serial_driver->owner = THIS_MODULE;
+ serial_driver->driver_name = "iss_serial";
+ serial_driver->name = "ttyS";
+ serial_driver->major = TTY_MAJOR;
+ serial_driver->minor_start = 64;
+ serial_driver->type = TTY_DRIVER_TYPE_SERIAL;
+ serial_driver->subtype = SERIAL_TYPE_NORMAL;
+ serial_driver->init_termios = tty_std_termios;
+ serial_driver->init_termios.c_cflag =
+ B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+ serial_driver->flags = TTY_DRIVER_REAL_RAW;
+
+ tty_set_operations(serial_driver, &serial_ops);
+
+ if (tty_register_driver(serial_driver))
+ panic("Couldn't register serial driver\n");
+ return 0;
+}
+
+
+static __exit void rs_exit(void)
+{
+ int error;
+
+ if ((error = tty_unregister_driver(serial_driver)))
+ printk("ISS_SERIAL: failed to unregister serial driver (%d)\n",
+ error);
+ put_tty_driver(serial_driver);
+}
+
+
+/* We use `late_initcall' instead of just `__initcall' as a workaround for
+ * the fact that (1) simcons_tty_init can't be called before tty_init,
+ * (2) tty_init is called via `module_init', (3) if statically linked,
+ * module_init == device_init, and (4) there's no ordering of init lists.
+ * We can do this easily because simcons is always statically linked, but
+ * other tty drivers that depend on tty_init and which must use
+ * `module_init' to declare their init routines are likely to be broken.
+ */
+
+late_initcall(rs_init);
+
+
+#ifdef CONFIG_SERIAL_CONSOLE
+
+static void iss_console_write(struct console *co, const char *s, unsigned count)
+{
+ int len = strlen(s);
+
+ if (s != 0 && *s != 0)
+ __simc (SYS_write, 1, (unsigned long)s,
+ count < len ? count : len,0,0);
+}
+
+static struct tty_driver* iss_console_device(struct console *c, int *index)
+{
+ *index = c->index;
+ return serial_driver;
+}
+
+
+static struct console sercons = {
+ .name = "ttyS",
+ .write = iss_console_write,
+ .device = iss_console_device,
+ .flags = CON_PRINTBUFFER,
+ .index = -1
+};
+
+static int __init iss_console_init(void)
+{
+ register_console(&sercons);
+ return 0;
+}
+
+console_initcall(iss_console_init);
+
+#endif /* CONFIG_SERIAL_CONSOLE */
+
diff --git a/arch/xtensa/platform-iss/io.c b/arch/xtensa/platform-iss/io.c
new file mode 100644
index 000000000000..5b161a5cb65f
--- /dev/null
+++ b/arch/xtensa/platform-iss/io.c
@@ -0,0 +1,32 @@
+/* This file isn't really needed right now. */
+
+#if 0
+
+#include <asm/io.h>
+#include <xtensa/simcall.h>
+
+extern int __simc ();
+
+
+char iss_serial_getc()
+{
+ char c;
+ __simc( SYS_read, 0, &c, 1 );
+ return c;
+}
+
+void iss_serial_putc( char c )
+{
+ __simc( SYS_write, 1, &c, 1 );
+}
+
+void iss_serial_puts( char *s )
+{
+ if( s != 0 && *s != 0 )
+ __simc( SYS_write, 1, s, strlen(s) );
+}
+
+/*#error Need I/O ports to specific hardware!*/
+
+#endif
+
diff --git a/arch/xtensa/platform-iss/network.c b/arch/xtensa/platform-iss/network.c
new file mode 100644
index 000000000000..498d7dced1f4
--- /dev/null
+++ b/arch/xtensa/platform-iss/network.c
@@ -0,0 +1,855 @@
+/*
+ *
+ * arch/xtensa/platform-iss/network.c
+ *
+ * Platform specific initialization.
+ *
+ * Authors: Chris Zankel <chris@zankel.net>
+ * Based on work form the UML team.
+ *
+ * Copyright 2005 Tensilica Inc.
+ *
+ * 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/list.h>
+#include <linux/irq.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/timer.h>
+#include <linux/if_ether.h>
+#include <linux/inetdevice.h>
+#include <linux/init.h>
+#include <linux/if_tun.h>
+#include <linux/etherdevice.h>
+#include <linux/interrupt.h>
+#include <linux/ioctl.h>
+#include <linux/bootmem.h>
+#include <linux/ethtool.h>
+#include <linux/rtnetlink.h>
+#include <linux/timer.h>
+
+#include <xtensa/simcall.h>
+
+#define DRIVER_NAME "iss-netdev"
+#define ETH_MAX_PACKET 1500
+#define ETH_HEADER_OTHER 14
+#define ISS_NET_TIMER_VALUE (2 * HZ)
+
+
+static DEFINE_SPINLOCK(opened_lock);
+static LIST_HEAD(opened);
+
+static DEFINE_SPINLOCK(devices_lock);
+static LIST_HEAD(devices);
+
+/* ------------------------------------------------------------------------- */
+
+/* We currently only support the TUNTAP transport protocol. */
+
+#define TRANSPORT_TUNTAP_NAME "tuntap"
+#define TRANSPORT_TUNTAP_MTU ETH_MAX_PACKET
+
+struct tuntap_info {
+ char dev_name[IFNAMSIZ];
+ int fixed_config;
+ unsigned char gw[ETH_ALEN];
+ int fd;
+};
+
+/* ------------------------------------------------------------------------- */
+
+
+/* This structure contains out private information for the driver. */
+
+struct iss_net_private {
+
+ struct list_head device_list;
+ struct list_head opened_list;
+
+ spinlock_t lock;
+ struct net_device *dev;
+ struct platform_device pdev;
+ struct timer_list tl;
+ struct net_device_stats stats;
+
+ struct timer_list timer;
+ unsigned int timer_val;
+
+ int index;
+ int mtu;
+
+ unsigned char mac[ETH_ALEN];
+ int have_mac;
+
+ struct {
+ union {
+ struct tuntap_info tuntap;
+ } info;
+
+ int (*open)(struct iss_net_private *lp);
+ void (*close)(struct iss_net_private *lp);
+ int (*read)(struct iss_net_private *lp, struct sk_buff **skb);
+ int (*write)(struct iss_net_private *lp, struct sk_buff **skb);
+ unsigned short (*protocol)(struct sk_buff *skb);
+ int (*poll)(struct iss_net_private *lp);
+ } tp;
+
+};
+
+/* ======================= ISS SIMCALL INTERFACE =========================== */
+
+/* Note: __simc must _not_ be declared inline! */
+
+static int errno;
+
+static int __simc (int a, int b, int c, int d, int e, int f)
+{
+ int ret;
+ __asm__ __volatile__ ("simcall\n"
+ "mov %0, a2\n"
+ "mov %1, a3\n" : "=a" (ret), "=a" (errno)
+ : : "a2", "a3");
+ return ret;
+}
+
+static int inline simc_open(char *file, int flags, int mode)
+{
+ return __simc(SYS_open, (int) file, flags, mode, 0, 0);
+}
+
+static int inline simc_close(int fd)
+{
+ return __simc(SYS_close, fd, 0, 0, 0, 0);
+}
+
+static int inline simc_ioctl(int fd, int request, void *arg)
+{
+ return __simc(SYS_ioctl, fd, request, (int) arg, 0, 0);
+}
+
+static int inline simc_read(int fd, void *buf, size_t count)
+{
+ return __simc(SYS_read, fd, (int) buf, count, 0, 0);
+}
+
+static int inline simc_write(int fd, void *buf, size_t count)
+{
+ return __simc(SYS_write, fd, (int) buf, count, 0, 0);
+}
+
+static int inline simc_poll(int fd)
+{
+ struct timeval tv = { .tv_sec = 0, .tv_usec = 0 };
+
+ return __simc(SYS_select_one, fd, XTISS_SELECT_ONE_READ, (int)&tv,0,0);
+}
+
+/* ================================ HELPERS ================================ */
+
+
+static char *split_if_spec(char *str, ...)
+{
+ char **arg, *end;
+ va_list ap;
+
+ va_start(ap, str);
+ while ((arg = va_arg(ap, char**)) != NULL) {
+ if (*str == '\0')
+ return NULL;
+ end = strchr(str, ',');
+ if (end != str)
+ *arg = str;
+ if (end == NULL)
+ return NULL;
+ *end ++ = '\0';
+ str = end;
+ }
+ va_end(ap);
+ return str;
+}
+
+
+#if 0
+/* Adjust SKB. */
+
+struct sk_buff *ether_adjust_skb(struct sk_buff *skb, int extra)
+{
+ if ((skb != NULL) && (skb_tailroom(skb) < extra)) {
+ struct sk_buff *skb2;
+
+ skb2 = skb_copy_expand(skb, 0, extra, GFP_ATOMIC);
+ dev_kfree_skb(skb);
+ skb = skb2;
+ }
+ if (skb != NULL)
+ skb_put(skb, extra);
+
+ return skb;
+}
+#endif
+
+/* Return the IP address as a string for a given device. */
+
+static void dev_ip_addr(void *d, char *buf, char *bin_buf)
+{
+ struct net_device *dev = d;
+ struct in_device *ip = dev->ip_ptr;
+ struct in_ifaddr *in;
+ u32 addr;
+
+ if ((ip == NULL) || ((in = ip->ifa_list) == NULL)) {
+ printk(KERN_WARNING "Device not assigned an IP address!\n");
+ return;
+ }
+
+ addr = in->ifa_address;
+ sprintf(buf, "%d.%d.%d.%d", addr & 0xff, (addr >> 8) & 0xff,
+ (addr >> 16) & 0xff, addr >> 24);
+
+ if (bin_buf) {
+ bin_buf[0] = addr & 0xff;
+ bin_buf[1] = (addr >> 8) & 0xff;
+ bin_buf[2] = (addr >> 16) & 0xff;
+ bin_buf[3] = addr >> 24;
+ }
+}
+
+/* Set Ethernet address of the specified device. */
+
+static void inline set_ether_mac(void *d, unsigned char *addr)
+{
+ struct net_device *dev = d;
+ memcpy(dev->dev_addr, addr, ETH_ALEN);
+}
+
+
+/* ======================= TUNTAP TRANSPORT INTERFACE ====================== */
+
+static int tuntap_open(struct iss_net_private *lp)
+{
+ struct ifreq ifr;
+ char *dev_name = lp->tp.info.tuntap.dev_name;
+ int err = -EINVAL;
+ int fd;
+
+ /* We currently only support a fixed configuration. */
+
+ if (!lp->tp.info.tuntap.fixed_config)
+ return -EINVAL;
+
+ if ((fd = simc_open("/dev/net/tun", 02, 0)) < 0) { /* O_RDWR */
+ printk("Failed to open /dev/net/tun, returned %d "
+ "(errno = %d)\n", fd, errno);
+ return fd;
+ }
+
+ memset(&ifr, 0, sizeof ifr);
+ ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
+ strlcpy(ifr.ifr_name, dev_name, sizeof ifr.ifr_name - 1);
+
+ if ((err = simc_ioctl(fd, TUNSETIFF, (void*) &ifr)) < 0) {
+ printk("Failed to set interface, returned %d "
+ "(errno = %d)\n", err, errno);
+ simc_close(fd);
+ return err;
+ }
+
+ lp->tp.info.tuntap.fd = fd;
+ return err;
+}
+
+static void tuntap_close(struct iss_net_private *lp)
+{
+#if 0
+ if (lp->tp.info.tuntap.fixed_config)
+ iter_addresses(lp->tp.info.tuntap.dev, close_addr, lp->host.dev_name);
+#endif
+ simc_close(lp->tp.info.tuntap.fd);
+ lp->tp.info.tuntap.fd = -1;
+}
+
+static int tuntap_read (struct iss_net_private *lp, struct sk_buff **skb)
+{
+#if 0
+ *skb = ether_adjust_skb(*skb, ETH_HEADER_OTHER);
+ if (*skb == NULL)
+ return -ENOMEM;
+#endif
+
+ return simc_read(lp->tp.info.tuntap.fd,
+ (*skb)->data, (*skb)->dev->mtu + ETH_HEADER_OTHER);
+}
+
+static int tuntap_write (struct iss_net_private *lp, struct sk_buff **skb)
+{
+ return simc_write(lp->tp.info.tuntap.fd, (*skb)->data, (*skb)->len);
+}
+
+unsigned short tuntap_protocol(struct sk_buff *skb)
+{
+ return eth_type_trans(skb, skb->dev);
+}
+
+static int tuntap_poll(struct iss_net_private *lp)
+{
+ return simc_poll(lp->tp.info.tuntap.fd);
+}
+
+/*
+ * Currently only a device name is supported.
+ * ethX=tuntap[,[mac address][,[device name]]]
+ */
+
+static int tuntap_probe(struct iss_net_private *lp, int index, char *init)
+{
+ const int len = strlen(TRANSPORT_TUNTAP_NAME);
+ char *dev_name = NULL, *mac_str = NULL, *rem = NULL;
+
+ /* Transport should be 'tuntap': ethX=tuntap,mac,dev_name */
+
+ if (strncmp(init, TRANSPORT_TUNTAP_NAME, len))
+ return 0;
+
+ if (*(init += strlen(TRANSPORT_TUNTAP_NAME)) == ',') {
+ if ((rem=split_if_spec(init+1, &mac_str, &dev_name)) != NULL) {
+ printk("Extra garbage on specification : '%s'\n", rem);
+ return 0;
+ }
+ } else if (*init != '\0') {
+ printk("Invalid argument: %s. Skipping device!\n", init);
+ return 0;
+ }
+
+ if (dev_name) {
+ strncpy(lp->tp.info.tuntap.dev_name, dev_name,
+ sizeof lp->tp.info.tuntap.dev_name);
+ lp->tp.info.tuntap.fixed_config = 1;
+ } else
+ strcpy(lp->tp.info.tuntap.dev_name, TRANSPORT_TUNTAP_NAME);
+
+
+#if 0
+ if (setup_etheraddr(mac_str, lp->mac))
+ lp->have_mac = 1;
+#endif
+ lp->mtu = TRANSPORT_TUNTAP_MTU;
+
+ //lp->info.tuntap.gate_addr = gate_addr;
+
+ lp->tp.info.tuntap.fd = -1;
+
+ lp->tp.open = tuntap_open;
+ lp->tp.close = tuntap_close;
+ lp->tp.read = tuntap_read;
+ lp->tp.write = tuntap_write;
+ lp->tp.protocol = tuntap_protocol;
+ lp->tp.poll = tuntap_poll;
+
+ printk("TUN/TAP backend - ");
+#if 0
+ if (lp->host.gate_addr != NULL)
+ printk("IP = %s", lp->host.gate_addr);
+#endif
+ printk("\n");
+
+ return 1;
+}
+
+/* ================================ ISS NET ================================ */
+
+static int iss_net_rx(struct net_device *dev)
+{
+ struct iss_net_private *lp = dev->priv;
+ int pkt_len;
+ struct sk_buff *skb;
+
+ /* Check if there is any new data. */
+
+ if (lp->tp.poll(lp) == 0)
+ return 0;
+
+ /* Try to allocate memory, if it fails, try again next round. */
+
+ if ((skb = dev_alloc_skb(dev->mtu + 2 + ETH_HEADER_OTHER)) == NULL) {
+ lp->stats.rx_dropped++;
+ return 0;
+ }
+
+ skb_reserve(skb, 2);
+
+ /* Setup skb */
+
+ skb->dev = dev;
+ skb->mac.raw = skb->data;
+ pkt_len = lp->tp.read(lp, &skb);
+ skb_put(skb, pkt_len);
+
+ if (pkt_len > 0) {
+ skb_trim(skb, pkt_len);
+ skb->protocol = lp->tp.protocol(skb);
+ // netif_rx(skb);
+ netif_rx_ni(skb);
+
+ lp->stats.rx_bytes += skb->len;
+ lp->stats.rx_packets++;
+ return pkt_len;
+ }
+ kfree_skb(skb);
+ return pkt_len;
+}
+
+static int iss_net_poll(void)
+{
+ struct list_head *ele;
+ int err, ret = 0;
+
+ spin_lock(&opened_lock);
+
+ list_for_each(ele, &opened) {
+ struct iss_net_private *lp;
+
+ lp = list_entry(ele, struct iss_net_private, opened_list);
+
+ if (!netif_running(lp->dev))
+ break;
+
+ spin_lock(&lp->lock);
+
+ while ((err = iss_net_rx(lp->dev)) > 0)
+ ret++;
+
+ spin_unlock(&lp->lock);
+
+ if (err < 0) {
+ printk(KERN_ERR "Device '%s' read returned %d, "
+ "shutting it down\n", lp->dev->name, err);
+ dev_close(lp->dev);
+ } else {
+ // FIXME reactivate_fd(lp->fd, ISS_ETH_IRQ);
+ }
+ }
+
+ spin_unlock(&opened_lock);
+ return ret;
+}
+
+
+static void iss_net_timer(unsigned long priv)
+{
+ struct iss_net_private* lp = (struct iss_net_private*) priv;
+
+ spin_lock(&lp->lock);
+
+ iss_net_poll();
+
+ mod_timer(&lp->timer, jiffies + lp->timer_val);
+
+ spin_unlock(&lp->lock);
+}
+
+
+static int iss_net_open(struct net_device *dev)
+{
+ struct iss_net_private *lp = dev->priv;
+ char addr[sizeof "255.255.255.255\0"];
+ int err;
+
+ spin_lock(&lp->lock);
+
+ if ((err = lp->tp.open(lp)) < 0)
+ goto out;
+
+ if (!lp->have_mac) {
+ dev_ip_addr(dev, addr, &lp->mac[2]);
+ set_ether_mac(dev, lp->mac);
+ }
+
+ netif_start_queue(dev);
+
+ /* clear buffer - it can happen that the host side of the interface
+ * is full when we gethere. In this case, new data is never queued,
+ * SIGIOs never arrive, and the net never works.
+ */
+ while ((err = iss_net_rx(dev)) > 0)
+ ;
+
+ spin_lock(&opened_lock);
+ list_add(&lp->opened_list, &opened);
+ spin_unlock(&opened_lock);
+
+ init_timer(&lp->timer);
+ lp->timer_val = ISS_NET_TIMER_VALUE;
+ lp->timer.data = (unsigned long) lp;
+ lp->timer.function = iss_net_timer;
+ mod_timer(&lp->timer, jiffies + lp->timer_val);
+
+out:
+ spin_unlock(&lp->lock);
+ return err;
+}
+
+static int iss_net_close(struct net_device *dev)
+{
+ struct iss_net_private *lp = dev->priv;
+printk("iss_net_close!\n");
+ netif_stop_queue(dev);
+ spin_lock(&lp->lock);
+
+ spin_lock(&opened_lock);
+ list_del(&opened);
+ spin_unlock(&opened_lock);
+
+ del_timer_sync(&lp->timer);
+
+ lp->tp.close(lp);
+
+ spin_unlock(&lp->lock);
+ return 0;
+}
+
+static int iss_net_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct iss_net_private *lp = dev->priv;
+ unsigned long flags;
+ int len;
+
+ netif_stop_queue(dev);
+ spin_lock_irqsave(&lp->lock, flags);
+
+ len = lp->tp.write(lp, &skb);
+
+ if (len == skb->len) {
+ lp->stats.tx_packets++;
+ lp->stats.tx_bytes += skb->len;
+ dev->trans_start = jiffies;
+ netif_start_queue(dev);
+
+ /* this is normally done in the interrupt when tx finishes */
+ netif_wake_queue(dev);
+
+ } else if (len == 0) {
+ netif_start_queue(dev);
+ lp->stats.tx_dropped++;
+
+ } else {
+ netif_start_queue(dev);
+ printk(KERN_ERR "iss_net_start_xmit: failed(%d)\n", len);
+ }
+
+ spin_unlock_irqrestore(&lp->lock, flags);
+
+ dev_kfree_skb(skb);
+ return 0;
+}
+
+
+static struct net_device_stats *iss_net_get_stats(struct net_device *dev)
+{
+ struct iss_net_private *lp = dev->priv;
+ return &lp->stats;
+}
+
+static void iss_net_set_multicast_list(struct net_device *dev)
+{
+#if 0
+ if (dev->flags & IFF_PROMISC)
+ return;
+ else if (dev->mc_count)
+ dev->flags |= IFF_ALLMULTI;
+ else
+ dev->flags &= ~IFF_ALLMULTI;
+#endif
+}
+
+static void iss_net_tx_timeout(struct net_device *dev)
+{
+#if 0
+ dev->trans_start = jiffies;
+ netif_wake_queue(dev);
+#endif
+}
+
+static int iss_net_set_mac(struct net_device *dev, void *addr)
+{
+#if 0
+ struct iss_net_private *lp = dev->priv;
+ struct sockaddr *hwaddr = addr;
+
+ spin_lock(&lp->lock);
+ memcpy(dev->dev_addr, hwaddr->sa_data, ETH_ALEN);
+ spin_unlock(&lp->lock);
+#endif
+
+ return 0;
+}
+
+static int iss_net_change_mtu(struct net_device *dev, int new_mtu)
+{
+#if 0
+ struct iss_net_private *lp = dev->priv;
+ int err = 0;
+
+ spin_lock(&lp->lock);
+
+ // FIXME not needed new_mtu = transport_set_mtu(new_mtu, &lp->user);
+
+ if (new_mtu < 0)
+ err = new_mtu;
+ else
+ dev->mtu = new_mtu;
+
+ spin_unlock(&lp->lock);
+ return err;
+#endif
+ return -EINVAL;
+}
+
+static int iss_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+#if 0
+ static const struct ethtool_drvinfo info = {
+ .cmd = ETHTOOL_GDRVINFO,
+ .driver = DRIVER_NAME,
+ .version = "42",
+ };
+ void *useraddr;
+ u32 ethcmd;
+
+ switch (cmd) {
+ case SIOCETHTOOL:
+ useraddr = ifr->ifr_data;
+ if (copy_from_user(&ethcmd, useraddr, sizeof(ethcmd)))
+ return -EFAULT;
+
+ switch (ethcmd) {
+ case ETHTOOL_GDRVINFO:
+ if (copy_to_user(useraddr, &info, sizeof(info)))
+ return -EFAULT;
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+ default:
+ return -EINVAL;
+ }
+#endif
+ return -EINVAL;
+}
+
+void iss_net_user_timer_expire(unsigned long _conn)
+{
+}
+
+
+static struct device_driver iss_net_driver = {
+ .name = DRIVER_NAME,
+ .bus = &platform_bus_type,
+};
+
+static int driver_registered;
+
+static int iss_net_configure(int index, char *init)
+{
+ struct net_device *dev;
+ struct iss_net_private *lp;
+ int err;
+
+ if ((dev = alloc_etherdev(sizeof *lp)) == NULL) {
+ printk(KERN_ERR "eth_configure: failed to allocate device\n");
+ return 1;
+ }
+
+ /* Initialize private element. */
+
+ lp = dev->priv;
+ *lp = ((struct iss_net_private) {
+ .device_list = LIST_HEAD_INIT(lp->device_list),
+ .opened_list = LIST_HEAD_INIT(lp->opened_list),
+ .lock = SPIN_LOCK_UNLOCKED,
+ .dev = dev,
+ .index = index,
+ //.fd = -1,
+ .mac = { 0xfe, 0xfd, 0x0, 0x0, 0x0, 0x0 },
+ .have_mac = 0,
+ });
+
+ /*
+ * Try all transport protocols.
+ * Note: more protocols can be added by adding '&& !X_init(lp, eth)'.
+ */
+
+ if (!tuntap_probe(lp, index, init)) {
+ printk("Invalid arguments. Skipping device!\n");
+ goto errout;
+ }
+
+ printk(KERN_INFO "Netdevice %d ", index);
+ if (lp->have_mac)
+ printk("(%02x:%02x:%02x:%02x:%02x:%02x) ",
+ lp->mac[0], lp->mac[1],
+ lp->mac[2], lp->mac[3],
+ lp->mac[4], lp->mac[5]);
+ printk(": ");
+
+ /* sysfs register */
+
+ if (!driver_registered) {
+ driver_register(&iss_net_driver);
+ driver_registered = 1;
+ }
+
+ spin_lock(&devices_lock);
+ list_add(&lp->device_list, &devices);
+ spin_unlock(&devices_lock);
+
+ lp->pdev.id = index;
+ lp->pdev.name = DRIVER_NAME;
+ platform_device_register(&lp->pdev);
+ SET_NETDEV_DEV(dev,&lp->pdev.dev);
+
+ /*
+ * If this name ends up conflicting with an existing registered
+ * netdevice, that is OK, register_netdev{,ice}() will notice this
+ * and fail.
+ */
+ snprintf(dev->name, sizeof dev->name, "eth%d", index);
+
+ dev->mtu = lp->mtu;
+ dev->open = iss_net_open;
+ dev->hard_start_xmit = iss_net_start_xmit;
+ dev->stop = iss_net_close;
+ dev->get_stats = iss_net_get_stats;
+ dev->set_multicast_list = iss_net_set_multicast_list;
+ dev->tx_timeout = iss_net_tx_timeout;
+ dev->set_mac_address = iss_net_set_mac;
+ dev->change_mtu = iss_net_change_mtu;
+ dev->do_ioctl = iss_net_ioctl;
+ dev->watchdog_timeo = (HZ >> 1);
+ dev->irq = -1;
+
+ rtnl_lock();
+ err = register_netdevice(dev);
+ rtnl_unlock();
+
+ if (err) {
+ printk("Error registering net device!\n");
+ /* XXX: should we call ->remove() here? */
+ free_netdev(dev);
+ return 1;
+ }
+
+ init_timer(&lp->tl);
+ lp->tl.function = iss_net_user_timer_expire;
+
+#if 0
+ if (lp->have_mac)
+ set_ether_mac(dev, lp->mac);
+#endif
+ return 0;
+
+errout:
+ // FIXME: unregister; free, etc..
+ return -EIO;
+
+}
+
+/* ------------------------------------------------------------------------- */
+
+/* Filled in during early boot */
+
+struct list_head eth_cmd_line = LIST_HEAD_INIT(eth_cmd_line);
+
+struct iss_net_init {
+ struct list_head list;
+ char *init; /* init string */
+ int index;
+};
+
+/*
+ * Parse the command line and look for 'ethX=...' fields, and register all
+ * those fields. They will be later initialized in iss_net_init.
+ */
+
+#define ERR KERN_ERR "iss_net_setup: "
+
+static int iss_net_setup(char *str)
+{
+ struct iss_net_private *device = NULL;
+ struct iss_net_init *new;
+ struct list_head *ele;
+ char *end;
+ int n;
+
+ n = simple_strtoul(str, &end, 0);
+ if (end == str) {
+ printk(ERR "Failed to parse '%s'\n", str);
+ return 1;
+ }
+ if (n < 0) {
+ printk(ERR "Device %d is negative\n", n);
+ return 1;
+ }
+ if (*(str = end) != '=') {
+ printk(ERR "Expected '=' after device number\n");
+ return 1;
+ }
+
+ spin_lock(&devices_lock);
+
+ list_for_each(ele, &devices) {
+ device = list_entry(ele, struct iss_net_private, device_list);
+ if (device->index == n)
+ break;
+ }
+
+ spin_unlock(&devices_lock);
+
+ if (device && device->index == n) {
+ printk(ERR "Device %d already configured\n", n);
+ return 1;
+ }
+
+ if ((new = alloc_bootmem(sizeof new)) == NULL) {
+ printk("Alloc_bootmem failed\n");
+ return 1;
+ }
+
+ INIT_LIST_HEAD(&new->list);
+ new->index = n;
+ new->init = str + 1;
+
+ list_add_tail(&new->list, &eth_cmd_line);
+ return 1;
+}
+
+#undef ERR
+
+__setup("eth", iss_net_setup);
+
+/*
+ * Initialize all ISS Ethernet devices previously registered in iss_net_setup.
+ */
+
+static int iss_net_init(void)
+{
+ struct list_head *ele, *next;
+
+ /* Walk through all Ethernet devices specified in the command line. */
+
+ list_for_each_safe(ele, next, &eth_cmd_line) {
+ struct iss_net_init *eth;
+ eth = list_entry(ele, struct iss_net_init, list);
+ iss_net_configure(eth->index, eth->init);
+ }
+
+ return 1;
+}
+
+module_init(iss_net_init);
+
diff --git a/arch/xtensa/platform-iss/setup.c b/arch/xtensa/platform-iss/setup.c
new file mode 100644
index 000000000000..2e6dcbf0cc04
--- /dev/null
+++ b/arch/xtensa/platform-iss/setup.c
@@ -0,0 +1,112 @@
+/*
+ *
+ * arch/xtensa/platform-iss/setup.c
+ *
+ * Platform specific initialization.
+ *
+ * Authors: Chris Zankel <chris@zankel.net>
+ * Joe Taylor <joe@tensilica.com>
+ *
+ * Copyright 2001 - 2005 Tensilica Inc.
+ *
+ * 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/stddef.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/reboot.h>
+#include <linux/pci.h>
+#include <linux/kdev_t.h>
+#include <linux/types.h>
+#include <linux/major.h>
+#include <linux/blkdev.h>
+#include <linux/console.h>
+#include <linux/delay.h>
+#include <linux/stringify.h>
+#include <linux/notifier.h>
+
+#include <asm/platform.h>
+#include <asm/bootparam.h>
+
+
+void __init platform_init(bp_tag_t* bootparam)
+{
+
+}
+
+void platform_halt(void)
+{
+ printk (" ** Called platform_halt(), looping forever! **\n");
+ while (1);
+}
+
+void platform_power_off(void)
+{
+ printk (" ** Called platform_power_off(), looping forever! **\n");
+ while (1);
+}
+void platform_restart(void)
+{
+ /* Flush and reset the mmu, simulate a processor reset, and
+ * jump to the reset vector. */
+
+ __asm__ __volatile__("movi a2, 15\n\t"
+ "wsr a2, " __stringify(ICOUNTLEVEL) "\n\t"
+ "movi a2, 0\n\t"
+ "wsr a2, " __stringify(ICOUNT) "\n\t"
+ "wsr a2, " __stringify(IBREAKENABLE) "\n\t"
+ "wsr a2, " __stringify(LCOUNT) "\n\t"
+ "movi a2, 0x1f\n\t"
+ "wsr a2, " __stringify(PS) "\n\t"
+ "isync\n\t"
+ "jx %0\n\t"
+ :
+ : "a" (XCHAL_RESET_VECTOR_VADDR)
+ : "a2");
+
+ /* control never gets here */
+}
+
+extern void iss_net_poll(void);
+
+const char twirl[]="|/-\\|/-\\";
+
+void platform_heartbeat(void)
+{
+#if 0
+ static int i = 0, j = 0;
+
+ if (--i < 0) {
+ i = 99;
+ printk("\r%c\r", twirl[j++]);
+ if (j == 8)
+ j = 0;
+ }
+#endif
+}
+
+
+
+static int
+iss_panic_event(struct notifier_block *this, unsigned long event, void *ptr)
+{
+ __asm__ __volatile__("movi a2, -1; simcall\n");
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block iss_panic_block = {
+ iss_panic_event,
+ NULL,
+ 0
+};
+
+void __init platform_setup(char **p_cmdline)
+{
+ notifier_chain_register(&panic_notifier_list, &iss_panic_block);
+}