aboutsummaryrefslogtreecommitdiff
path: root/arch
diff options
context:
space:
mode:
Diffstat (limited to 'arch')
-rw-r--r--arch/alpha/Kconfig2
-rw-r--r--arch/alpha/mm/init.c2
-rw-r--r--arch/arm/mm/ioremap.c2
-rw-r--r--arch/avr32/Kconfig196
-rw-r--r--arch/avr32/Kconfig.debug19
-rw-r--r--arch/avr32/Makefile84
-rw-r--r--arch/avr32/boards/atstk1000/Makefile2
-rw-r--r--arch/avr32/boards/atstk1000/atstk1002.c37
-rw-r--r--arch/avr32/boards/atstk1000/flash.c95
-rw-r--r--arch/avr32/boards/atstk1000/setup.c59
-rw-r--r--arch/avr32/boards/atstk1000/spi.c27
-rw-r--r--arch/avr32/boot/images/Makefile62
-rw-r--r--arch/avr32/boot/u-boot/Makefile3
-rw-r--r--arch/avr32/boot/u-boot/empty.S1
-rw-r--r--arch/avr32/boot/u-boot/head.S60
-rw-r--r--arch/avr32/configs/atstk1002_defconfig754
-rw-r--r--arch/avr32/kernel/Makefile18
-rw-r--r--arch/avr32/kernel/asm-offsets.c25
-rw-r--r--arch/avr32/kernel/avr32_ksyms.c55
-rw-r--r--arch/avr32/kernel/cpu.c327
-rw-r--r--arch/avr32/kernel/entry-avr32b.S678
-rw-r--r--arch/avr32/kernel/head.S45
-rw-r--r--arch/avr32/kernel/init_task.c38
-rw-r--r--arch/avr32/kernel/irq.c71
-rw-r--r--arch/avr32/kernel/kprobes.c270
-rw-r--r--arch/avr32/kernel/module.c324
-rw-r--r--arch/avr32/kernel/process.c276
-rw-r--r--arch/avr32/kernel/ptrace.c371
-rw-r--r--arch/avr32/kernel/semaphore.c148
-rw-r--r--arch/avr32/kernel/setup.c335
-rw-r--r--arch/avr32/kernel/signal.c328
-rw-r--r--arch/avr32/kernel/switch_to.S35
-rw-r--r--arch/avr32/kernel/sys_avr32.c51
-rw-r--r--arch/avr32/kernel/syscall-stubs.S102
-rw-r--r--arch/avr32/kernel/syscall_table.S289
-rw-r--r--arch/avr32/kernel/time.c238
-rw-r--r--arch/avr32/kernel/traps.c425
-rw-r--r--arch/avr32/kernel/vmlinux.lds.c139
-rw-r--r--arch/avr32/lib/Makefile10
-rw-r--r--arch/avr32/lib/__avr32_asr64.S31
-rw-r--r--arch/avr32/lib/__avr32_lsl64.S31
-rw-r--r--arch/avr32/lib/__avr32_lsr64.S31
-rw-r--r--arch/avr32/lib/clear_user.S76
-rw-r--r--arch/avr32/lib/copy_user.S119
-rw-r--r--arch/avr32/lib/csum_partial.S47
-rw-r--r--arch/avr32/lib/csum_partial_copy_generic.S99
-rw-r--r--arch/avr32/lib/delay.c55
-rw-r--r--arch/avr32/lib/findbit.S154
-rw-r--r--arch/avr32/lib/io-readsl.S24
-rw-r--r--arch/avr32/lib/io-readsw.S43
-rw-r--r--arch/avr32/lib/io-writesl.S20
-rw-r--r--arch/avr32/lib/io-writesw.S38
-rw-r--r--arch/avr32/lib/libgcc.h33
-rw-r--r--arch/avr32/lib/longlong.h98
-rw-r--r--arch/avr32/lib/memcpy.S62
-rw-r--r--arch/avr32/lib/memset.S72
-rw-r--r--arch/avr32/lib/strncpy_from_user.S60
-rw-r--r--arch/avr32/lib/strnlen_user.S67
-rw-r--r--arch/avr32/mach-at32ap/Makefile2
-rw-r--r--arch/avr32/mach-at32ap/at32ap.c90
-rw-r--r--arch/avr32/mach-at32ap/at32ap7000.c876
-rw-r--r--arch/avr32/mach-at32ap/clock.c148
-rw-r--r--arch/avr32/mach-at32ap/clock.h30
-rw-r--r--arch/avr32/mach-at32ap/extint.c171
-rw-r--r--arch/avr32/mach-at32ap/hsmc.c164
-rw-r--r--arch/avr32/mach-at32ap/hsmc.h127
-rw-r--r--arch/avr32/mach-at32ap/intc.c133
-rw-r--r--arch/avr32/mach-at32ap/intc.h327
-rw-r--r--arch/avr32/mach-at32ap/pio.c118
-rw-r--r--arch/avr32/mach-at32ap/pio.h178
-rw-r--r--arch/avr32/mach-at32ap/sm.c289
-rw-r--r--arch/avr32/mach-at32ap/sm.h240
-rw-r--r--arch/avr32/mm/Makefile6
-rw-r--r--arch/avr32/mm/cache.c150
-rw-r--r--arch/avr32/mm/clear_page.S25
-rw-r--r--arch/avr32/mm/copy_page.S28
-rw-r--r--arch/avr32/mm/dma-coherent.c139
-rw-r--r--arch/avr32/mm/fault.c315
-rw-r--r--arch/avr32/mm/init.c480
-rw-r--r--arch/avr32/mm/ioremap.c197
-rw-r--r--arch/avr32/mm/tlb.c378
-rw-r--r--arch/frv/Kconfig12
-rw-r--r--arch/frv/kernel/Makefile5
-rw-r--r--arch/frv/kernel/irq-mb93091.c157
-rw-r--r--arch/frv/kernel/irq-mb93093.c115
-rw-r--r--arch/frv/kernel/irq-mb93493.c160
-rw-r--r--arch/frv/kernel/irq-routing.c291
-rw-r--r--arch/frv/kernel/irq.c741
-rw-r--r--arch/frv/kernel/setup.c1
-rw-r--r--arch/frv/kernel/time.c1
-rw-r--r--arch/frv/mb93090-mb00/pci-irq.c1
-rw-r--r--arch/frv/mm/init.c2
-rw-r--r--arch/h8300/mm/init.c2
-rw-r--r--arch/i386/Kconfig4
-rw-r--r--arch/i386/kernel/apm.c26
-rw-r--r--arch/i386/kernel/cpu/mtrr/generic.c4
-rw-r--r--arch/i386/kernel/efi_stub.S1
-rw-r--r--arch/i386/kernel/reboot.c12
-rw-r--r--arch/i386/kernel/setup.c23
-rw-r--r--arch/i386/kernel/smp.c66
-rw-r--r--arch/i386/kernel/smpboot.c6
-rw-r--r--arch/i386/kernel/srat.c5
-rw-r--r--arch/i386/kernel/time.c50
-rw-r--r--arch/i386/kernel/time_hpet.c37
-rw-r--r--arch/i386/kernel/traps.c11
-rw-r--r--arch/i386/kernel/vmlinux.lds.S12
-rw-r--r--arch/i386/mach-voyager/voyager_thread.c1
-rw-r--r--arch/i386/mm/boot_ioremap.c7
-rw-r--r--arch/i386/mm/discontig.c33
-rw-r--r--arch/i386/mm/init.c44
-rw-r--r--arch/i386/mm/pgtable.c30
-rw-r--r--arch/i386/power/swsusp.S2
-rw-r--r--arch/ia64/Kconfig9
-rw-r--r--arch/ia64/kernel/acpi.c13
-rw-r--r--arch/ia64/kernel/numa.c34
-rw-r--r--arch/ia64/kernel/perfmon.c1
-rw-r--r--arch/ia64/kernel/topology.c6
-rw-r--r--arch/ia64/kernel/uncached.c2
-rw-r--r--arch/ia64/sn/kernel/sn2/sn_hwperf.c3
-rw-r--r--arch/m32r/mm/init.c2
-rw-r--r--arch/m68knommu/mm/init.c2
-rw-r--r--arch/mips/au1000/common/dbdma.c10
-rw-r--r--arch/mips/mm/init.c4
-rw-r--r--arch/mips/sgi-ip27/ip27-memory.c3
-rw-r--r--arch/parisc/mm/init.c4
-rw-r--r--arch/powerpc/kernel/swsusp_32.S4
-rw-r--r--arch/s390/appldata/appldata_mem.c3
-rw-r--r--arch/s390/mm/cmm.c205
-rw-r--r--arch/sh/mm/cache-sh7705.c2
-rw-r--r--arch/sh64/mm/init.c2
-rw-r--r--arch/sparc/mm/srmmu.c2
-rw-r--r--arch/sparc/mm/sun4c.c2
-rw-r--r--arch/sparc64/solaris/misc.c20
-rw-r--r--arch/sparc64/solaris/socksys.c6
-rw-r--r--arch/um/drivers/chan_kern.c2
-rw-r--r--arch/um/drivers/mconsole_kern.c2
-rw-r--r--arch/um/drivers/mconsole_user.c7
-rw-r--r--arch/um/drivers/net_kern.c14
-rw-r--r--arch/um/drivers/pcap_kern.c2
-rw-r--r--arch/um/include/kern_util.h1
-rw-r--r--arch/um/include/longjmp.h5
-rw-r--r--arch/um/include/net_user.h1
-rw-r--r--arch/um/include/os.h5
-rw-r--r--arch/um/include/registers.h3
-rw-r--r--arch/um/include/sysdep-i386/archsetjmp.h19
-rw-r--r--arch/um/include/sysdep-i386/signal.h27
-rw-r--r--arch/um/include/sysdep-x86_64/archsetjmp.h21
-rw-r--r--arch/um/include/sysdep-x86_64/signal.h29
-rw-r--r--arch/um/kernel/exec.c4
-rw-r--r--arch/um/kernel/irq.c34
-rw-r--r--arch/um/kernel/mem.c7
-rw-r--r--arch/um/kernel/process_kern.c3
-rw-r--r--arch/um/kernel/reboot.c13
-rw-r--r--arch/um/kernel/skas/mmu.c2
-rw-r--r--arch/um/kernel/time.c12
-rw-r--r--arch/um/kernel/tlb.c370
-rw-r--r--arch/um/kernel/trap.c11
-rw-r--r--arch/um/os-Linux/helper.c22
-rw-r--r--arch/um/os-Linux/irq.c2
-rw-r--r--arch/um/os-Linux/main.c34
-rw-r--r--arch/um/os-Linux/mem.c6
-rw-r--r--arch/um/os-Linux/process.c13
-rw-r--r--arch/um/os-Linux/sigio.c103
-rw-r--r--arch/um/os-Linux/signal.c38
-rw-r--r--arch/um/os-Linux/skas/process.c26
-rw-r--r--arch/um/os-Linux/start_up.c1
-rw-r--r--arch/um/os-Linux/sys-i386/Makefile2
-rw-r--r--arch/um/os-Linux/sys-i386/registers.c17
-rw-r--r--arch/um/os-Linux/sys-i386/signal.c15
-rw-r--r--arch/um/os-Linux/sys-x86_64/Makefile2
-rw-r--r--arch/um/os-Linux/sys-x86_64/registers.c17
-rw-r--r--arch/um/os-Linux/sys-x86_64/signal.c16
-rw-r--r--arch/um/os-Linux/time.c22
-rw-r--r--arch/um/os-Linux/trap.c1
-rw-r--r--arch/um/os-Linux/uaccess.c3
-rw-r--r--arch/um/os-Linux/util.c5
-rw-r--r--arch/um/sys-i386/Makefile2
-rw-r--r--arch/um/sys-i386/bugs.c9
-rw-r--r--arch/um/sys-i386/ldt.c3
-rw-r--r--arch/um/sys-i386/ptrace_user.c5
-rw-r--r--arch/um/sys-i386/setjmp.S58
-rw-r--r--arch/um/sys-x86_64/Makefile4
-rw-r--r--arch/um/sys-x86_64/setjmp.S54
-rw-r--r--arch/x86_64/Kconfig4
-rw-r--r--arch/x86_64/kernel/e820.c48
-rw-r--r--arch/x86_64/kernel/setup.c1
-rw-r--r--arch/x86_64/kernel/smpboot.c3
-rw-r--r--arch/x86_64/kernel/suspend_asm.S2
-rw-r--r--arch/x86_64/kernel/time.c37
-rw-r--r--arch/x86_64/mm/fault.c6
-rw-r--r--arch/x86_64/mm/init.c2
191 files changed, 13377 insertions, 1775 deletions
diff --git a/arch/alpha/Kconfig b/arch/alpha/Kconfig
index 213c7850d5f..2b36afd8e96 100644
--- a/arch/alpha/Kconfig
+++ b/arch/alpha/Kconfig
@@ -381,7 +381,7 @@ config ALPHA_EV56
config ALPHA_EV56
prompt "EV56 CPU (speed >= 333MHz)?"
- depends on ALPHA_NORITAKE && ALPHA_PRIMO
+ depends on ALPHA_NORITAKE || ALPHA_PRIMO
config ALPHA_EV56
prompt "EV56 CPU (speed >= 400MHz)?"
diff --git a/arch/alpha/mm/init.c b/arch/alpha/mm/init.c
index 917dad1b74c..550f4907d61 100644
--- a/arch/alpha/mm/init.c
+++ b/arch/alpha/mm/init.c
@@ -270,7 +270,7 @@ callback_init(void * kernel_end)
void
paging_init(void)
{
- unsigned long zones_size[MAX_NR_ZONES] = {0, 0, 0};
+ unsigned long zones_size[MAX_NR_ZONES] = {0, };
unsigned long dma_pfn, high_pfn;
dma_pfn = virt_to_phys((char *)MAX_DMA_ADDRESS) >> PAGE_SHIFT;
diff --git a/arch/arm/mm/ioremap.c b/arch/arm/mm/ioremap.c
index 88a999df0ab..591fc3187c7 100644
--- a/arch/arm/mm/ioremap.c
+++ b/arch/arm/mm/ioremap.c
@@ -177,7 +177,7 @@ static void unmap_area_sections(unsigned long virt, unsigned long size)
* Free the page table, if there was one.
*/
if ((pmd_val(pmd) & PMD_TYPE_MASK) == PMD_TYPE_TABLE)
- pte_free_kernel(pmd_page_kernel(pmd));
+ pte_free_kernel(pmd_page_vaddr(pmd));
}
addr += PGDIR_SIZE;
diff --git a/arch/avr32/Kconfig b/arch/avr32/Kconfig
new file mode 100644
index 00000000000..5f1694eea84
--- /dev/null
+++ b/arch/avr32/Kconfig
@@ -0,0 +1,196 @@
+#
+# For a description of the syntax of this configuration file,
+# see Documentation/kbuild/kconfig-language.txt.
+#
+
+mainmenu "Linux Kernel Configuration"
+
+config AVR32
+ bool
+ default y
+ # With EMBEDDED=n, we get lots of stuff automatically selected
+ # that we usually don't need on AVR32.
+ select EMBEDDED
+ help
+ AVR32 is a high-performance 32-bit RISC microprocessor core,
+ designed for cost-sensitive embedded applications, with particular
+ emphasis on low power consumption and high code density.
+
+ There is an AVR32 Linux project with a web page at
+ http://avr32linux.org/.
+
+config UID16
+ bool
+
+config GENERIC_HARDIRQS
+ bool
+ default y
+
+config HARDIRQS_SW_RESEND
+ bool
+ default y
+
+config GENERIC_IRQ_PROBE
+ bool
+ default y
+
+config RWSEM_GENERIC_SPINLOCK
+ bool
+ default y
+
+config GENERIC_TIME
+ bool
+ default y
+
+config RWSEM_XCHGADD_ALGORITHM
+ bool
+
+config GENERIC_BUST_SPINLOCK
+ bool
+
+config GENERIC_HWEIGHT
+ bool
+ default y
+
+config GENERIC_CALIBRATE_DELAY
+ bool
+ default y
+
+source "init/Kconfig"
+
+menu "System Type and features"
+
+config SUBARCH_AVR32B
+ bool
+config MMU
+ bool
+config PERFORMANCE_COUNTERS
+ bool
+
+config PLATFORM_AT32AP
+ bool
+ select SUBARCH_AVR32B
+ select MMU
+ select PERFORMANCE_COUNTERS
+
+choice
+ prompt "AVR32 CPU type"
+ default CPU_AT32AP7000
+
+config CPU_AT32AP7000
+ bool "AT32AP7000"
+ select PLATFORM_AT32AP
+endchoice
+
+#
+# CPU Daughterboards for ATSTK1000
+config BOARD_ATSTK1002
+ bool
+
+choice
+ prompt "AVR32 board type"
+ default BOARD_ATSTK1000
+
+config BOARD_ATSTK1000
+ bool "ATSTK1000 evaluation board"
+ select BOARD_ATSTK1002 if CPU_AT32AP7000
+endchoice
+
+choice
+ prompt "Boot loader type"
+ default LOADER_U_BOOT
+
+config LOADER_U_BOOT
+ bool "U-Boot (or similar) bootloader"
+endchoice
+
+config LOAD_ADDRESS
+ hex
+ default 0x10000000 if LOADER_U_BOOT=y && CPU_AT32AP7000=y
+
+config ENTRY_ADDRESS
+ hex
+ default 0x90000000 if LOADER_U_BOOT=y && CPU_AT32AP7000=y
+
+config PHYS_OFFSET
+ hex
+ default 0x10000000 if CPU_AT32AP7000=y
+
+source "kernel/Kconfig.preempt"
+
+config HAVE_ARCH_BOOTMEM_NODE
+ bool
+ default n
+
+config ARCH_HAVE_MEMORY_PRESENT
+ bool
+ default n
+
+config NEED_NODE_MEMMAP_SIZE
+ bool
+ default n
+
+config ARCH_FLATMEM_ENABLE
+ bool
+ default y
+
+config ARCH_DISCONTIGMEM_ENABLE
+ bool
+ default n
+
+config ARCH_SPARSEMEM_ENABLE
+ bool
+ default n
+
+source "mm/Kconfig"
+
+config OWNERSHIP_TRACE
+ bool "Ownership trace support"
+ default y
+ help
+ Say Y to generate an Ownership Trace message on every context switch,
+ enabling Nexus-compliant debuggers to keep track of the PID of the
+ currently executing task.
+
+# FPU emulation goes here
+
+source "kernel/Kconfig.hz"
+
+config CMDLINE
+ string "Default kernel command line"
+ default ""
+ help
+ If you don't have a boot loader capable of passing a command line string
+ to the kernel, you may specify one here. As a minimum, you should specify
+ the memory size and the root device (e.g., mem=8M, root=/dev/nfs).
+
+endmenu
+
+menu "Bus options"
+
+config PCI
+ bool
+
+source "drivers/pci/Kconfig"
+
+source "drivers/pcmcia/Kconfig"
+
+endmenu
+
+menu "Executable file formats"
+source "fs/Kconfig.binfmt"
+endmenu
+
+source "net/Kconfig"
+
+source "drivers/Kconfig"
+
+source "fs/Kconfig"
+
+source "arch/avr32/Kconfig.debug"
+
+source "security/Kconfig"
+
+source "crypto/Kconfig"
+
+source "lib/Kconfig"
diff --git a/arch/avr32/Kconfig.debug b/arch/avr32/Kconfig.debug
new file mode 100644
index 00000000000..64ace00fe6c
--- /dev/null
+++ b/arch/avr32/Kconfig.debug
@@ -0,0 +1,19 @@
+menu "Kernel hacking"
+
+config TRACE_IRQFLAGS_SUPPORT
+ bool
+ default y
+
+source "lib/Kconfig.debug"
+
+config KPROBES
+ bool "Kprobes"
+ depends on DEBUG_KERNEL
+ help
+ Kprobes allows you to trap at almost any kernel address and
+ execute a callback function. register_kprobe() establishes
+ a probepoint and specifies the callback. Kprobes is useful
+ for kernel debugging, non-intrusive instrumentation and testing.
+ If in doubt, say "N".
+
+endmenu
diff --git a/arch/avr32/Makefile b/arch/avr32/Makefile
new file mode 100644
index 00000000000..cefc95a7398
--- /dev/null
+++ b/arch/avr32/Makefile
@@ -0,0 +1,84 @@
+#
+# 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-2006 Atmel Corporation.
+
+# Default target when executing plain make
+.PHONY: all
+all: uImage vmlinux.elf linux.lst
+
+KBUILD_DEFCONFIG := atstk1002_defconfig
+
+CFLAGS += -pipe -fno-builtin -mno-pic
+AFLAGS += -mrelax -mno-pic
+CFLAGS_MODULE += -mno-relax
+LDFLAGS_vmlinux += --relax
+
+cpuflags-$(CONFIG_CPU_AP7000) += -mcpu=ap7000
+
+CFLAGS += $(cpuflags-y)
+AFLAGS += $(cpuflags-y)
+
+CHECKFLAGS += -D__avr32__
+
+LIBGCC := $(shell $(CC) $(CFLAGS) -print-libgcc-file-name)
+
+head-$(CONFIG_LOADER_U_BOOT) += arch/avr32/boot/u-boot/head.o
+head-y += arch/avr32/kernel/head.o
+core-$(CONFIG_PLATFORM_AT32AP) += arch/avr32/mach-at32ap/
+core-$(CONFIG_BOARD_ATSTK1000) += arch/avr32/boards/atstk1000/
+core-$(CONFIG_LOADER_U_BOOT) += arch/avr32/boot/u-boot/
+core-y += arch/avr32/kernel/
+core-y += arch/avr32/mm/
+libs-y += arch/avr32/lib/ #$(LIBGCC)
+
+archincdir-$(CONFIG_PLATFORM_AT32AP) := arch-at32ap
+
+include/asm-avr32/.arch: $(wildcard include/config/platform/*.h) include/config/auto.conf
+ @echo ' SYMLINK include/asm-avr32/arch -> include/asm-avr32/$(archincdir-y)'
+ifneq ($(KBUILD_SRC),)
+ $(Q)mkdir -p include/asm-avr32
+ $(Q)ln -fsn $(srctree)/include/asm-avr32/$(archincdir-y) include/asm-avr32/arch
+else
+ $(Q)ln -fsn $(archincdir-y) include/asm-avr32/arch
+endif
+ @touch $@
+
+archprepare: include/asm-avr32/.arch
+
+BOOT_TARGETS := vmlinux.elf vmlinux.bin uImage uImage.srec
+
+.PHONY: $(BOOT_TARGETS) install
+
+boot := arch/$(ARCH)/boot/images
+
+ KBUILD_IMAGE := $(boot)/uImage
+vmlinux.elf: KBUILD_IMAGE := $(boot)/vmlinux.elf
+vmlinux.cso: KBUILD_IMAGE := $(boot)/vmlinux.cso
+uImage.srec: KBUILD_IMAGE := $(boot)/uImage.srec
+uImage: KBUILD_IMAGE := $(boot)/uImage
+
+quiet_cmd_listing = LST $@
+ cmd_listing = avr32-linux-objdump $(OBJDUMPFLAGS) -lS $< > $@
+quiet_cmd_disasm = DIS $@
+ cmd_disasm = avr32-linux-objdump $(OBJDUMPFLAGS) -d $< > $@
+
+vmlinux.elf vmlinux.bin uImage.srec uImage vmlinux.cso: vmlinux
+ $(Q)$(MAKE) $(build)=$(boot) $(boot)/$@
+
+install: vmlinux
+ $(Q)$(MAKE) $(build)=$(boot) BOOTIMAGE=$(KBUILD_IMAGE) $@
+
+linux.s: vmlinux
+ $(call if_changed,disasm)
+
+linux.lst: vmlinux
+ $(call if_changed,listing)
+
+define archhelp
+ @echo '* vmlinux.elf - ELF image with load address 0'
+ @echo ' vmlinux.cso - PathFinder CSO image'
+ @echo ' uImage - Create a bootable image for U-Boot'
+endef
diff --git a/arch/avr32/boards/atstk1000/Makefile b/arch/avr32/boards/atstk1000/Makefile
new file mode 100644
index 00000000000..df949948053
--- /dev/null
+++ b/arch/avr32/boards/atstk1000/Makefile
@@ -0,0 +1,2 @@
+obj-y += setup.o spi.o flash.o
+obj-$(CONFIG_BOARD_ATSTK1002) += atstk1002.o
diff --git a/arch/avr32/boards/atstk1000/atstk1002.c b/arch/avr32/boards/atstk1000/atstk1002.c
new file mode 100644
index 00000000000..49164e9aadd
--- /dev/null
+++ b/arch/avr32/boards/atstk1000/atstk1002.c
@@ -0,0 +1,37 @@
+/*
+ * ATSTK1002 daughterboard-specific init code
+ *
+ * Copyright (C) 2005-2006 Atmel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/init.h>
+
+#include <asm/arch/board.h>
+
+struct eth_platform_data __initdata eth0_data = {
+ .valid = 1,
+ .mii_phy_addr = 0x10,
+ .is_rmii = 0,
+ .hw_addr = { 0x6a, 0x87, 0x71, 0x14, 0xcd, 0xcb },
+};
+
+extern struct lcdc_platform_data atstk1000_fb0_data;
+
+static int __init atstk1002_init(void)
+{
+ at32_add_system_devices();
+
+ at32_add_device_usart(1); /* /dev/ttyS0 */
+ at32_add_device_usart(2); /* /dev/ttyS1 */
+ at32_add_device_usart(3); /* /dev/ttyS2 */
+
+ at32_add_device_eth(0, &eth0_data);
+ at32_add_device_spi(0);
+ at32_add_device_lcdc(0, &atstk1000_fb0_data);
+
+ return 0;
+}
+postcore_initcall(atstk1002_init);
diff --git a/arch/avr32/boards/atstk1000/flash.c b/arch/avr32/boards/atstk1000/flash.c
new file mode 100644
index 00000000000..aac4300cca1
--- /dev/null
+++ b/arch/avr32/boards/atstk1000/flash.c
@@ -0,0 +1,95 @@
+/*
+ * ATSTK1000 board-specific flash initialization
+ *
+ * Copyright (C) 2005-2006 Atmel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/mtd/physmap.h>
+
+#include <asm/arch/smc.h>
+
+static struct smc_config flash_config __initdata = {
+ .ncs_read_setup = 0,
+ .nrd_setup = 40,
+ .ncs_write_setup = 0,
+ .nwe_setup = 10,
+
+ .ncs_read_pulse = 80,
+ .nrd_pulse = 40,
+ .ncs_write_pulse = 65,
+ .nwe_pulse = 55,
+
+ .read_cycle = 120,
+ .write_cycle = 120,
+
+ .bus_width = 2,
+ .nrd_controlled = 1,
+ .nwe_controlled = 1,
+ .byte_write = 1,
+};
+
+static struct mtd_partition flash_parts[] = {
+ {
+ .name = "u-boot",
+ .offset = 0x00000000,
+ .size = 0x00020000, /* 128 KiB */
+ .mask_flags = MTD_WRITEABLE,
+ },
+ {
+ .name = "root",
+ .offset = 0x00020000,
+ .size = 0x007d0000,
+ },
+ {
+ .name = "env",
+ .offset = 0x007f0000,
+ .size = 0x00010000,
+ .mask_flags = MTD_WRITEABLE,
+ },
+};
+
+static struct physmap_flash_data flash_data = {
+ .width = 2,
+ .nr_parts = ARRAY_SIZE(flash_parts),
+ .parts = flash_parts,
+};
+
+static struct resource flash_resource = {
+ .start = 0x00000000,
+ .end = 0x007fffff,
+ .flags = IORESOURCE_MEM,
+};
+
+static struct platform_device flash_device = {
+ .name = "physmap-flash",
+ .id = 0,
+ .resource = &flash_resource,
+ .num_resources = 1,
+ .dev = {
+ .platform_data = &flash_data,
+ },
+};
+
+/* This needs to be called after the SMC has been initialized */
+static int __init atstk1000_flash_init(void)
+{
+ int ret;
+
+ ret = smc_set_configuration(0, &flash_config);
+ if (ret < 0) {
+ printk(KERN_ERR "atstk1000: failed to set NOR flash timing\n");
+ return ret;
+ }
+
+ platform_device_register(&flash_device);
+
+ return 0;
+}
+device_initcall(atstk1000_flash_init);
diff --git a/arch/avr32/boards/atstk1000/setup.c b/arch/avr32/boards/atstk1000/setup.c
new file mode 100644
index 00000000000..191ab85de9a
--- /dev/null
+++ b/arch/avr32/boards/atstk1000/setup.c
@@ -0,0 +1,59 @@
+/*
+ * ATSTK1000 board-specific setup code.
+ *
+ * Copyright (C) 2005-2006 Atmel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/bootmem.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/linkage.h>
+
+#include <asm/setup.h>
+
+#include <asm/arch/board.h>
+
+/* Initialized by bootloader-specific startup code. */
+struct tag *bootloader_tags __initdata;
+
+struct lcdc_platform_data __initdata atstk1000_fb0_data;
+
+asmlinkage void __init board_early_init(void)
+{
+ extern void sdram_init(void);
+
+#ifdef CONFIG_LOADER_STANDALONE
+ sdram_init();
+#endif
+}
+
+void __init board_setup_fbmem(unsigned long fbmem_start,
+ unsigned long fbmem_size)
+{
+ if (!fbmem_size)
+ return;
+
+ if (!fbmem_start) {
+ void *fbmem;
+
+ fbmem = alloc_bootmem_low_pages(fbmem_size);
+ fbmem_start = __pa(fbmem);
+ } else {
+ pg_data_t *pgdat;
+
+ for_each_online_pgdat(pgdat) {
+ if (fbmem_start >= pgdat->bdata->node_boot_start
+ && fbmem_start <= pgdat->bdata->node_low_pfn)
+ reserve_bootmem_node(pgdat, fbmem_start,
+ fbmem_size);
+ }
+ }
+
+ printk("%luKiB framebuffer memory at address 0x%08lx\n",
+ fbmem_size >> 10, fbmem_start);
+ atstk1000_fb0_data.fbmem_start = fbmem_start;
+ atstk1000_fb0_data.fbmem_size = fbmem_size;
+}
diff --git a/arch/avr32/boards/atstk1000/spi.c b/arch/avr32/boards/atstk1000/spi.c
new file mode 100644
index 00000000000..567726c82c6
--- /dev/null
+++ b/arch/avr32/boards/atstk1000/spi.c
@@ -0,0 +1,27 @@
+/*
+ * ATSTK1000 SPI devices
+ *
+ * Copyright (C) 2005 Atmel Norway
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/device.h>
+#include <linux/spi/spi.h>
+
+static struct spi_board_info spi_board_info[] __initdata = {
+ {
+ .modalias = "ltv350qv",
+ .max_speed_hz = 16000000,
+ .bus_num = 0,
+ .chip_select = 1,
+ },
+};
+
+static int board_init_spi(void)
+{
+ spi_register_board_info(spi_board_info, ARRAY_SIZE(spi_board_info));
+ return 0;
+}
+arch_initcall(board_init_spi);
diff --git a/arch/avr32/boot/images/Makefile b/arch/avr32/boot/images/Makefile
new file mode 100644
index 00000000000..ccd74eeecec
--- /dev/null
+++ b/arch/avr32/boot/images/Makefile
@@ -0,0 +1,62 @@
+#
+# Copyright (C) 2004-2006 Atmel Corporation
+#
+# 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.
+#
+
+MKIMAGE := $(srctree)/scripts/mkuboot.sh
+
+extra-y := vmlinux.bin vmlinux.gz
+
+OBJCOPYFLAGS_vmlinux.bin := -O binary
+$(obj)/vmlinux.bin: vmlinux FORCE
+ $(call if_changed,objcopy)
+
+$(obj)/vmlinux.gz: $(obj)/vmlinux.bin FORCE
+ $(call if_changed,gzip)
+
+quiet_cmd_uimage = UIMAGE $@
+ cmd_uimage = $(CONFIG_SHELL) $(MKIMAGE) -A avr32 -O linux -T kernel \
+ -C gzip -a $(CONFIG_LOAD_ADDRESS) -e $(CONFIG_ENTRY_ADDRESS) \
+ -n 'Linux-$(KERNELRELEASE)' -d $< $@
+
+targets += uImage uImage.srec
+$(obj)/uImage: $(obj)/vmlinux.gz
+ $(call if_changed,uimage)
+ @echo ' Image $@ is ready'
+
+OBJCOPYFLAGS_uImage.srec := -I binary -O srec
+$(obj)/uImage.srec: $(obj)/uImage
+ $(call if_changed,objcopy)
+
+OBJCOPYFLAGS_vmlinux.elf := --change-section-lma .text-0x80000000 \
+ --change-section-lma __ex_table-0x80000000 \
+ --change-section-lma .rodata-0x80000000 \
+ --change-section-lma .data-0x80000000 \
+ --change-section-lma .init-0x80000000 \
+ --change-section-lma .bss-0x80000000 \
+ --change-section-lma .initrd-0x80000000 \
+ --change-section-lma __param-0x80000000 \
+ --change-section-lma __ksymtab-0x80000000 \
+ --change-section-lma __ksymtab_gpl-0x80000000 \
+ --change-section-lma __kcrctab-0x80000000 \
+ --change-section-lma __kcrctab_gpl-0x80000000 \
+ --change-section-lma __ksymtab_strings-0x80000000 \
+ --change-section-lma .got-0x80000000 \
+ --set-start 0xa0000000
+$(obj)/vmlinux.elf: vmlinux FORCE
+ $(call if_changed,objcopy)
+
+quiet_cmd_sfdwarf = SFDWARF $@
+ cmd_sfdwarf = sfdwarf $< TO $@ GNUAVR IW $(SFDWARF_FLAGS) > $(obj)/sfdwarf.log
+
+$(obj)/vmlinux.cso: $(obj)/vmlinux.elf FORCE
+ $(call if_changed,sfdwarf)
+
+install: $(BOOTIMAGE)
+ sh $(srctree)/install-kernel.sh $<
+
+# Generated files to be removed upon make clean
+clean-files := vmlinux* uImage uImage.srec
diff --git a/arch/avr32/boot/u-boot/Makefile b/arch/avr32/boot/u-boot/Makefile
new file mode 100644
index 00000000000..125ddc96c27
--- /dev/null
+++ b/arch/avr32/boot/u-boot/Makefile
@@ -0,0 +1,3 @@
+extra-y := head.o
+
+obj-y := empty.o
diff --git a/arch/avr32/boot/u-boot/empty.S b/arch/avr32/boot/u-boot/empty.S
new file mode 100644
index 00000000000..8ac91a5f12f
--- /dev/null
+++ b/arch/avr32/boot/u-boot/empty.S
@@ -0,0 +1 @@
+/* Empty file */
diff --git a/arch/avr32/boot/u-boot/head.S b/arch/avr32/boot/u-boot/head.S
new file mode 100644
index 00000000000..4488fa27fe9
--- /dev/null
+++ b/arch/avr32/boot/u-boot/head.S
@@ -0,0 +1,60 @@
+/*
+ * Startup code for use with the u-boot bootloader.
+ *
+ * Copyright (C) 2004-2006 Atmel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <asm/setup.h>
+
+ /*
+ * The kernel is loaded where we want it to be and all caches
+ * have just been flushed. We get two parameters from u-boot:
+ *
+ * r12 contains a magic number (ATAG_MAGIC)
+ * r11 points to a tag table providing information about
+ * the system.
+ */
+ .section .init.text,"ax"
+ .global _start
+_start:
+ /* Check if the boot loader actually provided a tag table */
+ lddpc r0, magic_number
+ cp.w r12, r0
+ brne no_tag_table
+
+ /* Initialize .bss */
+ lddpc r2, bss_start_addr
+ lddpc r3, end_addr
+ mov r0, 0
+ mov r1, 0
+1: st.d r2++, r0
+ cp r2, r3
+ brlo 1b
+
+ /*
+ * Save the tag table address for later use. This must be done
+ * _after_ .bss has been initialized...
+ */
+ lddpc r0, tag_table_addr
+ st.w r0[0], r11
+
+ /* Jump to loader-independent setup code */
+ rjmp kernel_entry
+
+ .align 2
+magic_number:
+ .long ATAG_MAGIC
+tag_table_addr:
+ .long bootloader_tags
+bss_start_addr:
+ .long __bss_start
+end_addr:
+ .long _end
+
+no_tag_table:
+ sub r12, pc, (. - 2f)
+ bral panic
+2: .asciz "Boot loader didn't provide correct magic number\n"
diff --git a/arch/avr32/configs/atstk1002_defconfig b/arch/avr32/configs/atstk1002_defconfig
new file mode 100644
index 00000000000..1d22255009f
--- /dev/null
+++ b/arch/avr32/configs/atstk1002_defconfig
@@ -0,0 +1,754 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.18-rc1
+# Tue Jul 11 12:41:36 2006
+#
+CONFIG_AVR32=y
+CONFIG_GENERIC_HARDIRQS=y
+CONFIG_HARDIRQS_SW_RESEND=y
+CONFIG_GENERIC_IRQ_PROBE=y
+CONFIG_RWSEM_GENERIC_SPINLOCK=y
+CONFIG_GENERIC_HWEIGHT=y
+CONFIG_GENERIC_CALIBRATE_DELAY=y
+CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
+
+#
+# Code maturity level options
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_BROKEN_ON_SMP=y
+CONFIG_INIT_ENV_ARG_LIMIT=32
+
+#
+# General setup
+#
+CONFIG_LOCALVERSION=""
+# CONFIG_LOCALVERSION_AUTO is not set
+CONFIG_SWAP=y
+# CONFIG_SYSVIPC is not set
+# CONFIG_POSIX_MQUEUE is not set
+# CONFIG_BSD_PROCESS_ACCT is not set
+CONFIG_SYSCTL=y
+# CONFIG_AUDIT is not set
+# CONFIG_IKCONFIG is not set
+# CONFIG_RELAY is not set
+CONFIG_INITRAMFS_SOURCE=""
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+CONFIG_EMBEDDED=y
+CONFIG_KALLSYMS=y
+# CONFIG_KALLSYMS_ALL is not set
+# CONFIG_KALLSYMS_EXTRA_PASS is not set
+CONFIG_HOTPLUG=y
+CONFIG_PRINTK=y
+CONFIG_BUG=y
+CONFIG_ELF_CORE=y
+# CONFIG_BASE_FULL is not set
+# CONFIG_FUTEX is not set
+# CONFIG_EPOLL is not set
+CONFIG_SHMEM=y
+# CONFIG_SLAB is not set
+# CONFIG_VM_EVENT_COUNTERS is not set
+# CONFIG_TINY_SHMEM is not set
+CONFIG_BASE_SMALL=1
+CONFIG_SLOB=y
+
+#
+# Loadable module support
+#
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+# CONFIG_MODULE_FORCE_UNLOAD is not set
+# CONFIG_MODVERSIONS is not set
+# CONFIG_MODULE_SRCVERSION_ALL is not set
+# CONFIG_KMOD is not set
+
+#
+# Block layer
+#
+# CONFIG_BLK_DEV_IO_TRACE 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_DEFAULT_AS is not set
+# CONFIG_DEFAULT_DEADLINE is not set
+# CONFIG_DEFAULT_CFQ is not set
+CONFIG_DEFAULT_NOOP=y
+CONFIG_DEFAULT_IOSCHED="noop"
+
+#
+# System Type and features
+#
+CONFIG_SUBARCH_AVR32B=y
+CONFIG_MMU=y
+CONFIG_PERFORMANCE_COUNTERS=y
+CONFIG_PLATFORM_AT32AP=y
+CONFIG_CPU_AT32AP7000=y
+CONFIG_BOARD_ATSTK1002=y
+CONFIG_BOARD_ATSTK1000=y
+CONFIG_LOADER_U_BOOT=y
+CONFIG_LOAD_ADDRESS=0x10000000
+CONFIG_ENTRY_ADDRESS=0x90000000
+CONFIG_PHYS_OFFSET=0x10000000
+CONFIG_PREEMPT_NONE=y
+# CONFIG_PREEMPT_VOLUNTARY is not set
+# CONFIG_PREEMPT is not set
+# CONFIG_HAVE_ARCH_BOOTMEM_NODE is not set
+# CONFIG_ARCH_HAVE_MEMORY_PRESENT is not set
+# CONFIG_NEED_NODE_MEMMAP_SIZE is not set
+CONFIG_ARCH_FLATMEM_ENABLE=y
+# CONFIG_ARCH_DISCONTIGMEM_ENABLE is not set
+# CONFIG_ARCH_SPARSEMEM_ENABLE is not set
+CONFIG_SELECT_MEMORY_MODEL=y
+CONFIG_FLATMEM_MANUAL=y
+# CONFIG_DISCONTIGMEM_MANUAL is not set
+# CONFIG_SPARSEMEM_MANUAL is not set
+CONFIG_FLATMEM=y
+CONFIG_FLAT_NODE_MEM_MAP=y
+# CONFIG_SPARSEMEM_STATIC is not set
+CONFIG_SPLIT_PTLOCK_CPUS=4
+# CONFIG_RESOURCES_64BIT is not set
+# CONFIG_OWNERSHIP_TRACE is not set
+# CONFIG_HZ_100 is not set
+CONFIG_HZ_250=y
+# CONFIG_HZ_1000 is not set
+CONFIG_HZ=250
+CONFIG_CMDLINE=""
+
+#
+# Bus options
+#
+
+#
+# PCCARD (PCMCIA/CardBus) support
+#
+# CONFIG_PCCARD is not set
+
+#
+# Executable file formats
+#
+CONFIG_BINFMT_ELF=y
+# CONFIG_BINFMT_MISC is not set
+
+#
+# Networking
+#
+CONFIG_NET=y
+
+#
+# Networking options
+#
+# CONFIG_NETDEBUG is not set
+CONFIG_PACKET=y
+CONFIG_PACKET_MMAP=y
+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_FIB_HASH=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+# CONFIG_IP_PNP_BOOTP is not set
+# CONFIG_IP_PNP_RARP is not set
+# 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_XFRM_TUNNEL is not set
+# CONFIG_INET_TUNNEL is not set
+# CONFIG_INET_XFRM_MODE_TRANSPORT is not set
+# CONFIG_INET_XFRM_MODE_TUNNEL is not set
+CONFIG_INET_DIAG=y
+CONFIG_INET_TCP_DIAG=y
+# CONFIG_TCP_CONG_ADVANCED is not set
+CONFIG_TCP_CONG_BIC=y
+# CONFIG_IPV6 is not set
+# CONFIG_INET6_XFRM_TUNNEL is not set
+# CONFIG_INET6_TUNNEL is not set
+# CONFIG_NETWORK_SECMARK is not set
+# CONFIG_NETFILTER is not set
+
+#
+# DCCP Configuration (EXPERIMENTAL)
+#
+# CONFIG_IP_DCCP is not set
+
+#
+# SCTP Configuration (EXPERIMENTAL)
+#
+# CONFIG_IP_SCTP is not set
+
+#
+# TIPC Configuration (EXPERIMENTAL)
+#
+# CONFIG_TIPC 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
+
+#
+# Network testing
+#
+# CONFIG_NET_PKTGEN is not set
+# CONFIG_NET_TCPPROBE is not set
+# CONFIG_HAMRADIO is not set
+# CONFIG_IRDA is not set
+# CONFIG_BT is not set
+# CONFIG_IEEE80211 is not set
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+CONFIG_STANDALONE=y
+# CONFIG_PREVENT_FIRMWARE_BUILD is not set
+# CONFIG_FW_LOADER is not set
+# CONFIG_DEBUG_DRIVER is not set
+# CONFIG_SYS_HYPERVISOR is not set
+
+#
+# Connector - unified userspace <-> kernelspace linker
+#
+# CONFIG_CONNECTOR 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_COW_COMMON is not set
+CONFIG_BLK_DEV_LOOP=m
+# CONFIG_BLK_DEV_CRYPTOLOOP is not set
+CONFIG_BLK_DEV_NBD=m
+CONFIG_BLK_DEV_RAM=m
+CONFIG_BLK_DEV_RAM_COUNT=16
+CONFIG_BLK_DEV_RAM_SIZE=4096
+CONFIG_BLK_DEV_INITRD=y
+# CONFIG_CDROM_PKTCDVD is not set
+# CONFIG_ATA_OVER_ETH is not set
+
+#
+# ATA/ATAPI/MFM/RLL support
+#
+# CONFIG_IDE is not set
+
+#
+# SCSI device support
+#
+# CONFIG_RAID_ATTRS is not set
+# CONFIG_SCSI is not set
+
+#
+# Multi-device support (RAID and LVM)
+#
+# CONFIG_MD is not set
+
+#
+# Fusion MPT device support
+#
+# CONFIG_FUSION is not set
+
+#
+# IEEE 1394 (FireWire) support
+#
+
+#
+# I2O device support
+#
+
+#
+# Network device support
+#
+CONFIG_NETDEVICES=y
+CONFIG_DUMMY=y
+# CONFIG_BONDING is not set
+# CONFIG_EQUALIZER is not set
+CONFIG_TUN=m
+
+#
+# PHY device support
+#
+# CONFIG_PHYLIB is not set
+
+#
+# Ethernet (10 or 100Mbit)
+#
+CONFIG_NET_ETHERNET=y
+CONFIG_MII=y
+CONFIG_MACB=y
+
+#
+# Ethernet (1000 Mbit)
+#
+
+#
+# Ethernet (10000 Mbit)
+#
+
+#
+# Token Ring devices
+#
+
+#
+# Wireless LAN (non-hamradio)
+#
+# CONFIG_NET_RADIO is not set
+
+#
+# Wan interfaces
+#
+# CONFIG_WAN is not set
+CONFIG_PPP=m
+# CONFIG_PPP_MULTILINK is not set
+# CONFIG_PPP_FILTER is not set
+CONFIG_PPP_ASYNC=m
+# CONFIG_PPP_SYNC_TTY is not set
+CONFIG_PPP_DEFLATE=m
+# CONFIG_PPP_BSDCOMP is not set
+# CONFIG_PPP_MPPE is not set
+# CONFIG_PPPOE is not set
+# CONFIG_SLIP is not set
+# CONFIG_SHAPER is not set
+# CONFIG_NETCONSOLE is not set
+# CONFIG_NETPOLL is not set
+# CONFIG_NET_POLL_CONTROLLER is not set
+
+#
+# ISDN subsystem
+#
+# CONFIG_ISDN is not set
+
+#
+# Telephony Support
+#
+# CONFIG_PHONE is not set
+
+#
+# Input device support
+#
+# CONFIG_INPUT is not set
+
+#
+# Hardware I/O ports
+#
+# CONFIG_SERIO is not set
+# CONFIG_GAMEPORT is not set
+
+#
+# Character devices
+#
+# CONFIG_VT is not set
+# CONFIG_SERIAL_NONSTANDARD is not set
+
+#
+# Serial drivers
+#
+# CONFIG_SERIAL_8250 is not set
+
+#
+# Non-8250 serial port support
+#
+CONFIG_SERIAL_AT91=y
+CONFIG_SERIAL_AT91_CONSOLE=y
+# CONFIG_SERIAL_AT91_TTYAT is not set
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+CONFIG_UNIX98_PTYS=y
+# CONFIG_LEGACY_PTYS is not set
+
+#
+# IPMI
+#
+# CONFIG_IPMI_HANDLER is not set
+
+#
+# Watchdog Cards
+#
+# CONFIG_WATCHDOG is not set
+# CONFIG_HW_RANDOM is not set
+# 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_RAW_DRIVER is not set
+
+#
+# TPM devices
+#
+# CONFIG_TCG_TPM is not set
+# CONFIG_TELCLOCK is not set
+
+#
+# I2C support
+#
+# CONFIG_I2C is not set
+
+#
+# SPI support
+#
+CONFIG_SPI=y
+# CONFIG_SPI_DEBUG is not set
+CONFIG_SPI_MASTER=y
+
+#
+# SPI Master Controller Drivers
+#
+CONFIG_SPI_ATMEL=m
+# CONFIG_SPI_BITBANG is not set
+
+#
+# SPI Protocol Masters
+#
+
+#
+# Dallas's 1-wire bus
+#
+
+#
+# Hardware Monitoring support
+#
+# CONFIG_HWMON is not set
+# CONFIG_HWMON_VID is not set
+
+#
+# Misc devices
+#
+
+#
+# Multimedia devices
+#
+# CONFIG_VIDEO_DEV is not set
+CONFIG_VIDEO_V4L2=y
+
+#
+# Digital Video Broadcasting Devices
+#
+# CONFIG_DVB is not set
+
+#
+# Graphics support
+#
+# CONFIG_FIRMWARE_EDID is not set
+CONFIG_FB=m
+CONFIG_FB_CFB_FILLRECT=m
+CONFIG_FB_CFB_COPYAREA=m
+CONFIG_FB_CFB_IMAGEBLIT=m
+# CONFIG_FB_MACMODES is not set
+# CONFIG_FB_BACKLIGHT is not set
+# CONFIG_FB_MODE_HELPERS is not set
+# CONFIG_FB_TILEBLITTING is not set
+CONFIG_FB_SIDSA=m
+CONFIG_FB_SIDSA_DEFAULT_BPP=24
+# CONFIG_FB_S1D13XXX is not set
+# CONFIG_FB_VIRTUAL is not set
+
+#
+# Logo configuration
+#
+# CONFIG_LOGO is not set
+CONFIG_BACKLIGHT_LCD_SUPPORT=y
+# CONFIG_BACKLIGHT_CLASS_DEVICE is not set
+CONFIG_LCD_CLASS_DEVICE=m
+CONFIG_LCD_DEVICE=y
+CONFIG_LCD_LTV350QV=m
+
+#
+# Sound
+#
+# CONFIG_SOUND is not set
+
+#
+# USB support
+#
+# CONFIG_USB_ARCH_HAS_HCD is not set
+# CONFIG_USB_ARCH_HAS_OHCI is not set
+# CONFIG_USB_ARCH_HAS_EHCI is not set
+
+#
+# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support'
+#
+
+#
+# USB Gadget Support
+#
+# CONFIG_USB_GADGET is not set
+
+#
+# MMC/SD Card support
+#
+# CONFIG_MMC is not set
+
+#
+# LED devices
+#
+# CONFIG_NEW_LEDS is not set
+
+#
+# LED drivers
+#
+
+#
+# LED Triggers
+#
+
+#
+# InfiniBand support
+#
+
+#
+# EDAC - error detection and reporting (RAS) (EXPERIMENTAL)
+#
+
+#
+# Real Time Clock
+#
+# CONFIG_RTC_CLASS is not set
+
+#
+# DMA Engine support
+#
+# CONFIG_DMA_ENGINE is not set
+
+#
+# DMA Clients
+#
+
+#
+# DMA Devices
+#
+
+#
+# File systems
+#
+CONFIG_EXT2_FS=y
+# CONFIG_EXT2_FS_XATTR is not set
+# CONFIG_EXT2_FS_XIP is not set
+# CONFIG_EXT3_FS is not set
+# CONFIG_REISERFS_FS is not set
+# CONFIG_JFS_FS is not set
+# CONFIG_FS_POSIX_ACL is not set
+# CONFIG_XFS_FS is not set
+# CONFIG_OCFS2_FS is not set
+CONFIG_MINIX_FS=m
+CONFIG_ROMFS_FS=m
+# CONFIG_INOTIFY 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
+# CONFIG_FUSE_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_FAT_FS=m
+CONFIG_MSDOS_FS=m
+CONFIG_VFAT_FS=m
+CONFIG_FAT_DEFAULT_CODEPAGE=437
+CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
+# CONFIG_NTFS_FS is not set
+
+#
+# Pseudo filesystems
+#
+CONFIG_PROC_FS=y
+CONFIG_PROC_KCORE=y
+CONFIG_SYSFS=y
+CONFIG_TMPFS=y
+# CONFIG_HUGETLB_PAGE is not set
+CONFIG_RAMFS=y
+CONFIG_CONFIGFS_FS=m
+
+#
+# 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_V3_ACL is not set
+# 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_NFS_COMMON=y
+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=m
+# CONFIG_CIFS_STATS is not set
+# CONFIG_CIFS_WEAK_PW_HASH is not set
+# CONFIG_CIFS_XATTR is not set
+# CONFIG_CIFS_DEBUG2 is not set
+# CONFIG_CIFS_EXPERIMENTAL is not set
+# CONFIG_NCP_FS is not set
+# CONFIG_CODA_FS is not set
+# CONFIG_AFS_FS is not set
+# CONFIG_9P_FS is not set
+
+#
+# Partition Types
+#
+# CONFIG_PARTITION_ADVANCED is not set
+CONFIG_MSDOS_PARTITION=y
+
+#
+# Native Language Support
+#
+CONFIG_NLS=m
+CONFIG_NLS_DEFAULT="iso8859-1"
+CONFIG_NLS_CODEPAGE_437=m
+# CONFIG_NLS_CODEPAGE_737 is not set
+# CONFIG_NLS_CODEPAGE_775 is not set
+CONFIG_NLS_CODEPAGE_850=m
+# CONFIG_NLS_CODEPAGE_852 is not set
+# CONFIG_NLS_CODEPAGE_855 is not set
+# CONFIG_NLS_CODEPAGE_857 is not set
+# CONFIG_NLS_CODEPAGE_860 is not set
+# CONFIG_NLS_CODEPAGE_861 is not set
+# CONFIG_NLS_CODEPAGE_862 is not set
+# CONFIG_NLS_CODEPAGE_863 is not set
+# CONFIG_NLS_CODEPAGE_864 is not set
+# CONFIG_NLS_CODEPAGE_865 is not set
+# CONFIG_NLS_CODEPAGE_866 is not set
+# CONFIG_NLS_CODEPAGE_869 is not set
+# CONFIG_NLS_CODEPAGE_936 is not set
+# CONFIG_NLS_CODEPAGE_950 is not set
+# CONFIG_NLS_CODEPAGE_932 is not set
+# CONFIG_NLS_CODEPAGE_949 is not set
+# CONFIG_NLS_CODEPAGE_874 is not set
+# CONFIG_NLS_ISO8859_8 is not set
+# CONFIG_NLS_CODEPAGE_1250 is not set
+# CONFIG_NLS_CODEPAGE_1251 is not set
+# CONFIG_NLS_ASCII is not set
+CONFIG_NLS_ISO8859_1=m
+# CONFIG_NLS_ISO8859_2 is not set
+# CONFIG_NLS_ISO8859_3 is not set
+# CONFIG_NLS_ISO8859_4 is not set
+# CONFIG_NLS_ISO8859_5 is not set
+# CONFIG_NLS_ISO8859_6 is not set
+# CONFIG_NLS_ISO8859_7 is not set
+# CONFIG_NLS_ISO8859_9 is not set
+# CONFIG_NLS_ISO8859_13 is not set
+# CONFIG_NLS_ISO8859_14 is not set
+# CONFIG_NLS_ISO8859_15 is not set
+# CONFIG_NLS_KOI8_R is not set
+# CONFIG_NLS_KOI8_U is not set
+CONFIG_NLS_UTF8=m
+
+#
+# Kernel hacking
+#
+CONFIG_TRACE_IRQFLAGS_SUPPORT=y
+CONFIG_PRINTK_TIME=y
+CONFIG_MAGIC_SYSRQ=y
+# CONFIG_UNUSED_SYMBOLS is not set
+CONFIG_DEBUG_KERNEL=y
+CONFIG_LOG_BUF_SHIFT=14
+CONFIG_DETECT_SOFTLOCKUP=y
+# CONFIG_SCHEDSTATS is not set
+# CONFIG_DEBUG_SPINLOCK is not set
+# CONFIG_DEBUG_MUTEXES is not set
+# CONFIG_DEBUG_RWSEMS is not set
+# CONFIG_DEBUG_SPINLOCK_SLEEP is not set
+# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set
+# CONFIG_DEBUG_KOBJECT is not set
+CONFIG_DEBUG_BUGVERBOSE=y
+# CONFIG_DEBUG_INFO is not set
+CONFIG_DEBUG_FS=y
+# CONFIG_DEBUG_VM is not set
+CONFIG_FRAME_POINTER=y
+# CONFIG_UNWIND_INFO is not set
+CONFIG_FORCED_INLINING=y
+# CONFIG_RCU_TORTURE_TEST is not set
+CONFIG_KPROBES=y
+
+#
+# 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=m
+# CONFIG_CRC16 is not set
+CONFIG_CRC32=m
+# CONFIG_LIBCRC32C is not set
+CONFIG_ZLIB_INFLATE=m
+CONFIG_ZLIB_DEFLATE=m
diff --git a/arch/avr32/kernel/Makefile b/arch/avr32/kernel/Makefile
new file mode 100644
index 00000000000..90e5afff54a
--- /dev/null
+++ b/arch/avr32/kernel/Makefile
@@ -0,0 +1,18 @@
+#
+# Makefile for the Linux/AVR32 kernel.
+#
+
+extra-y := head.o vmlinux.lds
+
+obj-$(CONFIG_SUBARCH_AVR32B) += entry-avr32b.o
+obj-y += syscall_table.o syscall-stubs.o irq.o
+obj-y += setup.o traps.o semaphore.o ptrace.o
+obj-y += signal.o sys_avr32.o process.o time.o
+obj-y += init_task.o switch_to.o cpu.o
+obj-$(CONFIG_MODULES) += module.o avr32_ksyms.o
+obj-$(CONFIG_KPROBES) += kprobes.o
+
+USE_STANDARD_AS_RULE := true
+
+%.lds: %.lds.c FORCE
+ $(call if_changed_dep,cpp_lds_S)
diff --git a/arch/avr32/kernel/asm-offsets.c b/arch/avr32/kernel/asm-offsets.c
new file mode 100644
index 00000000000..97d86586566
--- /dev/null
+++ b/arch/avr32/kernel/asm-offsets.c
@@ -0,0 +1,25 @@
+/*
+ * Generate definitions needed by assembly language modules.
+ * This code generates raw asm output which is post-processed
+ * to extract and format the required data.
+ */
+
+#include <linux/thread_info.h>
+
+#define DEFINE(sym, val) \
+ asm volatile("\n->" #sym " %0 " #val : : "i" (val))
+
+#define BLANK() asm volatile("\n->" : : )
+
+#define OFFSET(sym, str, mem) \
+ DEFINE(sym, offsetof(struct str, mem));
+
+void foo(void)
+{
+ OFFSET(TI_task, thread_info, task);
+ OFFSET(TI_exec_domain, thread_info, exec_domain);
+ OFFSET(TI_flags, thread_info, flags);
+ OFFSET(TI_cpu, thread_info, cpu);
+ OFFSET(TI_preempt_count, thread_info, preempt_count);
+ OFFSET(TI_restart_block, thread_info, restart_block);
+}
diff --git a/arch/avr32/kernel/avr32_ksyms.c b/arch/avr32/kernel/avr32_ksyms.c
new file mode 100644
index 00000000000..04f767a272b
--- /dev/null
+++ b/arch/avr32/kernel/avr32_ksyms.c
@@ -0,0 +1,55 @@
+/*
+ * Export AVR32-specific functions for loadable modules.
+ *
+ * Copyright (C) 2004-2006 Atmel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/module.h>
+
+#include <asm/checksum.h>
+#include <asm/uaccess.h>
+#include <asm/delay.h>
+
+/*
+ * GCC functions
+ */
+extern unsigned long long __avr32_lsl64(unsigned long long u, unsigned long b);
+extern unsigned long long __avr32_lsr64(unsigned long long u, unsigned long b);
+extern unsigned long long __avr32_asr64(unsigned long long u, unsigned long b);
+EXPORT_SYMBOL(__avr32_lsl64);
+EXPORT_SYMBOL(__avr32_lsr64);
+EXPORT_SYMBOL(__avr32_asr64);
+
+/*
+ * String functions
+ */
+EXPORT_SYMBOL(memset);
+EXPORT_SYMBOL(memcpy);
+
+/*
+ * Userspace access stuff.
+ */
+EXPORT_SYMBOL(copy_from_user);
+EXPORT_SYMBOL(copy_to_user);
+EXPORT_SYMBOL(__copy_user);
+EXPORT_SYMBOL(strncpy_from_user);
+EXPORT_SYMBOL(__strncpy_from_user);
+EXPORT_SYMBOL(clear_user);
+EXPORT_SYMBOL(__clear_user);
+EXPORT_SYMBOL(csum_partial);
+EXPORT_SYMBOL(csum_partial_copy_generic);
+
+/* Delay loops (lib/delay.S) */
+EXPORT_SYMBOL(__ndelay);
+EXPORT_SYMBOL(__udelay);
+EXPORT_SYMBOL(__const_udelay);
+
+/* Bit operations (lib/findbit.S) */
+EXPORT_SYMBOL(find_first_zero_bit);
+EXPORT_SYMBOL(find_next_zero_bit);
+EXPORT_SYMBOL(find_first_bit);
+EXPORT_SYMBOL(find_next_bit);
+EXPORT_SYMBOL(generic_find_next_zero_le_bit);
diff --git a/arch/avr32/kernel/cpu.c b/arch/avr32/kernel/cpu.c
new file mode 100644
index 00000000000..342452ba204
--- /dev/null
+++ b/arch/avr32/kernel/cpu.c
@@ -0,0 +1,327 @@
+/*
+ * Copyright (C) 2005-2006 Atmel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/init.h>
+#include <linux/sysdev.h>
+#include <linux/seq_file.h>
+#include <linux/cpu.h>
+#include <linux/percpu.h>
+#include <linux/param.h>
+#include <linux/errno.h>
+
+#include <asm/setup.h>
+#include <asm/sysreg.h>
+
+static DEFINE_PER_CPU(struct cpu, cpu_devices);
+
+#ifdef CONFIG_PERFORMANCE_COUNTERS
+
+/*
+ * XXX: If/when a SMP-capable implementation of AVR32 will ever be
+ * made, we must make sure that the code executes on the correct CPU.
+ */
+static ssize_t show_pc0event(struct sys_device *dev, char *buf)
+{
+ unsigned long pccr;
+
+ pccr = sysreg_read(PCCR);
+ return sprintf(buf, "0x%lx\n", (pccr >> 12) & 0x3f);
+}
+static ssize_t store_pc0event(struct sys_device *dev, const char *buf,
+ size_t count)
+{
+ unsigned long val;
+ char *endp;
+
+ val = simple_strtoul(buf, &endp, 0);
+ if (endp == buf || val > 0x3f)
+ return -EINVAL;
+ val = (val << 12) | (sysreg_read(PCCR) & 0xfffc0fff);
+ sysreg_write(PCCR, val);
+ return count;
+}
+static ssize_t show_pc0count(struct sys_device *dev, char *buf)
+{
+ unsigned long pcnt0;
+
+ pcnt0 = sysreg_read(PCNT0);
+ return sprintf(buf, "%lu\n", pcnt0);
+}
+static ssize_t store_pc0count(struct sys_device *dev, const char *buf,
+ size_t count)
+{
+ unsigned long val;
+ char *endp;
+
+ val = simple_strtoul(buf, &endp, 0);
+ if (endp == buf)
+ return -EINVAL;
+ sysreg_write(PCNT0, val);
+
+ return count;
+}
+
+static ssize_t show_pc1event(struct sys_device *dev, char *buf)
+{
+ unsigned long pccr;
+
+ pccr = sysreg_read(PCCR);
+ return sprintf(buf, "0x%lx\n", (pccr >> 18) & 0x3f);
+}
+static ssize_t store_pc1event(struct sys_device *dev, const char *buf,
+ size_t count)
+{
+ unsigned long val;
+ char *endp;
+
+ val = simple_strtoul(buf, &endp, 0);
+ if (endp == buf || val > 0x3f)
+ return -EINVAL;
+ val = (val << 18) | (sysreg_read(PCCR) & 0xff03ffff);
+ sysreg_write(PCCR, val);
+ return count;
+}
+static ssize_t show_pc1count(struct sys_device *dev, char *buf)
+{
+ unsigned long pcnt1;
+
+ pcnt1 = sysreg_read(PCNT1);
+ return sprintf(buf, "%lu\n", pcnt1);
+}
+static ssize_t store_pc1count(struct sys_device *dev, const char *buf,
+ size_t count)
+{
+ unsigned long val;
+ char *endp;
+
+ val = simple_strtoul(buf, &endp, 0);
+ if (endp == buf)
+ return -EINVAL;
+ sysreg_write(PCNT1, val);
+
+ return count;
+}
+
+static ssize_t show_pccycles(struct sys_device *dev, char *buf)
+{
+ unsigned long pccnt;
+
+ pccnt = sysreg_read(PCCNT);
+ return sprintf(buf, "%lu\n", pccnt);
+}
+static ssize_t store_pccycles(struct sys_device *dev, const char *buf,
+ size_t count)
+{
+ unsigned long val;
+ char *endp;
+
+ val = simple_strtoul(buf, &endp, 0);
+ if (endp == buf)
+ return -EINVAL;
+ sysreg_write(PCCNT, val);
+
+ return count;
+}
+
+static ssize_t show_pcenable(struct sys_device *dev, char *buf)
+{
+ unsigned long pccr;
+
+ pccr = sysreg_read(PCCR);
+ return sprintf(buf, "%c\n", (pccr & 1)?'1':'0');
+}
+static ssize_t store_pcenable(struct sys_device *dev, const char *buf,
+ size_t count)
+{
+ unsigned long pccr, val;
+ char *endp;
+
+ val = simple_strtoul(buf, &endp, 0);
+ if (endp == buf)
+ return -EINVAL;
+ if (val)
+ val = 1;
+
+ pccr = sysreg_read(PCCR);
+ pccr = (pccr & ~1UL) | val;
+ sysreg_write(PCCR, pccr);
+
+ return count;
+}
+
+static SYSDEV_ATTR(pc0event, 0600, show_pc0event, store_pc0event);
+static SYSDEV_ATTR(pc0count, 0600, show_pc0count, store_pc0count);
+static SYSDEV_ATTR(pc1event, 0600, show_pc1event, store_pc1event);
+static SYSDEV_ATTR(pc1count, 0600, show_pc1count, store_pc1count);
+static SYSDEV_ATTR(pccycles, 0600, show_pccycles, store_pccycles);
+static SYSDEV_ATTR(pcenable, 0600, show_pcenable, store_pcenable);
+
+#endif /* CONFIG_PERFORMANCE_COUNTERS */
+
+static int __init topology_init(void)
+{
+ int cpu;
+
+ for_each_possible_cpu(cpu) {
+ struct cpu *c = &per_cpu(cpu_devices, cpu);
+
+ register_cpu(c, cpu);
+
+#ifdef CONFIG_PERFORMANCE_COUNTERS
+ sysdev_create_file(&c->sysdev, &attr_pc0event);
+ sysdev_create_file(&c->sysdev, &attr_pc0count);
+ sysdev_create_file(&c->sysdev, &attr_pc1event);
+ sysdev_create_file(&c->sysdev, &attr_pc1count);
+ sysdev_create_file(&c->sysdev, &attr_pccycles);
+ sysdev_create_file(&c->sysdev, &attr_pcenable);
+#endif
+ }
+
+ return 0;
+}
+
+subsys_initcall(topology_init);
+
+static const char *cpu_names[] = {
+ "Morgan",
+ "AP7000",
+};
+#define NR_CPU_NAMES ARRAY_SIZE(cpu_names)
+
+static const char *arch_names[] = {
+ "AVR32A",
+ "AVR32B",
+};
+#define NR_ARCH_NAMES ARRAY_SIZE(arch_names)
+
+static const char *mmu_types[] = {
+ "No MMU",
+ "ITLB and DTLB",
+ "Shared TLB",
+ "MPU"
+};
+
+void __init setup_processor(void)
+{
+ unsigned long config0, config1;
+ unsigned cpu_id, cpu_rev, arch_id, arch_rev, mmu_type;
+ unsigned tmp;
+
+ config0 = sysreg_read(CONFIG0); /* 0x0000013e; */
+ config1 = sysreg_read(CONFIG1); /* 0x01f689a2; */
+ cpu_id = config0 >> 24;
+ cpu_rev = (config0 >> 16) & 0xff;
+ arch_id = (config0 >> 13) & 0x07;
+ arch_rev = (config0 >> 10) & 0x07;
+ mmu_type = (config0 >> 7) & 0x03;
+
+ boot_cpu_data.arch_type = arch_id;
+ boot_cpu_data.cpu_type = cpu_id;
+ boot_cpu_data.arch_revision = arch_rev;
+ boot_cpu_data.cpu_revision = cpu_rev;
+ boot_cpu_data.tlb_config = mmu_type;
+
+ tmp = (config1 >> 13) & 0x07;
+ if (tmp) {
+ boot_cpu_data.icache.ways = 1 << ((config1 >> 10) & 0x07);
+ boot_cpu_data.icache.sets = 1 << ((config1 >> 16) & 0x0f);
+ boot_cpu_data.icache.linesz = 1 << (tmp + 1);
+ }
+ tmp = (config1 >> 3) & 0x07;
+ if (tmp) {
+ boot_cpu_data.dcache.ways = 1 << (config1 & 0x07);
+ boot_cpu_data.dcache.sets = 1 << ((config1 >> 6) & 0x0f);
+ boot_cpu_data.dcache.linesz = 1 << (tmp + 1);
+ }
+
+ if ((cpu_id >= NR_CPU_NAMES) || (arch_id >= NR_ARCH_NAMES)) {
+ printk ("Unknown CPU configuration (ID %02x, arch %02x), "
+ "continuing anyway...\n",
+ cpu_id, arch_id);
+ return;
+ }
+
+ printk ("CPU: %s [%02x] revision %d (%s revision %d)\n",
+ cpu_names[cpu_id], cpu_id, cpu_rev,
+ arch_names[arch_id], arch_rev);
+ printk ("CPU: MMU configuration: %s\n", mmu_types[mmu_type]);
+ printk ("CPU: features:");
+ if (config0 & (1 << 6))
+ printk(" fpu");
+ if (config0 & (1 << 5))
+ printk(" java");
+ if (config0 & (1 << 4))
+ printk(" perfctr");
+ if (config0 & (1 << 3))
+ printk(" ocd");
+ printk("\n");
+}
+
+#ifdef CONFIG_PROC_FS
+static int c_show(struct seq_file *m, void *v)
+{
+ unsigned int icache_size, dcache_size;
+ unsigned int cpu = smp_processor_id();
+
+ icache_size = boot_cpu_data.icache.ways *
+ boot_cpu_data.icache.sets *
+ boot_cpu_data.icache.linesz;
+ dcache_size = boot_cpu_data.dcache.ways *
+ boot_cpu_data.dcache.sets *
+ boot_cpu_data.dcache.linesz;
+
+ seq_printf(m, "processor\t: %d\n", cpu);
+
+ if (boot_cpu_data.arch_type < NR_ARCH_NAMES)
+ seq_printf(m, "cpu family\t: %s revision %d\n",
+ arch_names[boot_cpu_data.arch_type],
+ boot_cpu_data.arch_revision);
+ if (boot_cpu_data.cpu_type < NR_CPU_NAMES)
+ seq_printf(m, "cpu type\t: %s revision %d\n",
+ cpu_names[boot_cpu_data.cpu_type],
+ boot_cpu_data.cpu_revision);
+
+ seq_printf(m, "i-cache\t\t: %dK (%u ways x %u sets x %u)\n",
+ icache_size >> 10,
+ boot_cpu_data.icache.ways,
+ boot_cpu_data.icache.sets,
+ boot_cpu_data.icache.linesz);
+ seq_printf(m, "d-cache\t\t: %dK (%u ways x %u sets x %u)\n",
+ dcache_size >> 10,
+ boot_cpu_data.dcache.ways,
+ boot_cpu_data.dcache.sets,
+ boot_cpu_data.dcache.linesz);
+ seq_printf(m, "bogomips\t: %lu.%02lu\n",
+ boot_cpu_data.loops_per_jiffy / (500000/HZ),
+ (boot_cpu_data.loops_per_jiffy / (5000/HZ)) % 100);
+
+ return 0;
+}
+
+static void *c_start(struct seq_file *m, loff_t *pos)
+{
+ return *pos < 1 ? (void *)1 : NULL;
+}
+
+static void *c_next(struct seq_file *m, void *v, loff_t *pos)
+{
+ ++*pos;
+ return NULL;
+}
+
+static void c_stop(struct seq_file *m, void *v)
+{
+
+}
+
+struct seq_operations cpuinfo_op = {
+ .start = c_start,
+ .next = c_next,
+ .stop = c_stop,
+ .show = c_show
+};
+#endif /* CONFIG_PROC_FS */
diff --git a/arch/avr32/kernel/entry-avr32b.S b/arch/avr32/kernel/entry-avr32b.S
new file mode 100644
index 00000000000..eeb66792bc3
--- /dev/null
+++ b/arch/avr32/kernel/entry-avr32b.S
@@ -0,0 +1,678 @@
+/*
+ * Copyright (C) 2004-2006 Atmel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+/*
+ * This file contains the low-level entry-points into the kernel, that is,
+ * exception handlers, debug trap handlers, interrupt handlers and the
+ * system call handler.
+ */
+#include <linux/errno.h>
+
+#include <asm/asm.h>
+#include <asm/hardirq.h>
+#include <asm/irq.h>
+#include <asm/ocd.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/ptrace.h>
+#include <asm/sysreg.h>
+#include <asm/thread_info.h>
+#include <asm/unistd.h>
+
+#ifdef CONFIG_PREEMPT
+# define preempt_stop mask_interrupts
+#else
+# define preempt_stop
+# define fault_resume_kernel fault_restore_all
+#endif
+
+#define __MASK(x) ((1 << (x)) - 1)
+#define IRQ_MASK ((__MASK(SOFTIRQ_BITS) << SOFTIRQ_SHIFT) | \
+ (__MASK(HARDIRQ_BITS) << HARDIRQ_SHIFT))
+
+ .section .ex.text,"ax",@progbits
+ .align 2
+exception_vectors:
+ bral handle_critical
+ .align 2
+ bral handle_critical
+ .align 2
+ bral do_bus_error_write
+ .align 2
+ bral do_bus_error_read
+ .align 2
+ bral do_nmi_ll
+ .align 2
+ bral handle_address_fault
+ .align 2
+ bral handle_protection_fault
+ .align 2
+ bral handle_debug
+ .align 2
+ bral do_illegal_opcode_ll
+ .align 2
+ bral do_illegal_opcode_ll
+ .align 2
+ bral do_illegal_opcode_ll
+ .align 2
+ bral do_fpe_ll
+ .align 2
+ bral do_illegal_opcode_ll
+ .align 2
+ bral handle_address_fault
+ .align 2
+ bral handle_address_fault
+ .align 2
+ bral handle_protection_fault
+ .align 2
+ bral handle_protection_fault
+ .align 2
+ bral do_dtlb_modified
+
+ /*
+ * r0 : PGD/PT/PTE
+ * r1 : Offending address
+ * r2 : Scratch register
+ * r3 : Cause (5, 12 or 13)
+ */
+#define tlbmiss_save pushm r0-r3
+#define tlbmiss_restore popm r0-r3
+
+ .section .tlbx.ex.text,"ax",@progbits
+ .global itlb_miss
+itlb_miss:
+ tlbmiss_save
+ rjmp tlb_miss_common
+
+ .section .tlbr.ex.text,"ax",@progbits
+dtlb_miss_read:
+ tlbmiss_save
+ rjmp tlb_miss_common
+
+ .section .tlbw.ex.text,"ax",@progbits
+dtlb_miss_write:
+ tlbmiss_save
+
+ .global tlb_miss_common
+tlb_miss_common:
+ mfsr r0, SYSREG_PTBR
+ mfsr r1, SYSREG_TLBEAR
+
+ /* Is it the vmalloc space? */
+ bld r1, 31
+ brcs handle_vmalloc_miss
+
+ /* First level lookup */
+pgtbl_lookup:
+ lsr r2, r1, PGDIR_SHIFT
+ ld.w r0, r0[r2 << 2]
+ bld r0, _PAGE_BIT_PRESENT
+ brcc page_table_not_present
+
+ /* TODO: Check access rights on page table if necessary */
+
+ /* Translate to virtual address in P1. */
+ andl r0, 0xf000
+ sbr r0, 31
+
+ /* Second level lookup */
+ lsl r1, (32 - PGDIR_SHIFT)
+ lsr r1, (32 - PGDIR_SHIFT) + PAGE_SHIFT
+ add r2, r0, r1 << 2
+ ld.w r1, r2[0]
+ bld r1, _PAGE_BIT_PRESENT
+ brcc page_not_present
+
+ /* Mark the page as accessed */
+ sbr r1, _PAGE_BIT_ACCESSED
+ st.w r2[0], r1
+
+ /* Drop software flags */
+ andl r1, _PAGE_FLAGS_HARDWARE_MASK & 0xffff
+ mtsr SYSREG_TLBELO, r1
+
+ /* Figure out which entry we want to replace */
+ mfsr r0, SYSREG_TLBARLO
+ clz r2, r0
+ brcc 1f
+ mov r1, -1 /* All entries have been accessed, */
+ mtsr SYSREG_TLBARLO, r1 /* so reset TLBAR */
+ mov r2, 0 /* and start at 0 */
+1: mfsr r1, SYSREG_MMUCR
+ lsl r2, 14
+ andl r1, 0x3fff, COH
+ or r1, r2
+ mtsr SYSREG_MMUCR, r1
+
+ tlbw
+
+ tlbmiss_restore
+ rete
+
+handle_vmalloc_miss:
+ /* Simply do the lookup in init's page table */
+ mov r0, lo(swapper_pg_dir)
+ orh r0, hi(swapper_pg_dir)
+ rjmp pgtbl_lookup
+
+
+ /* --- System Call --- */
+
+ .section .scall.text,"ax",@progbits
+system_call:
+ pushm r12 /* r12_orig */
+ stmts --sp, r0-lr
+ zero_fp
+ mfsr r0, SYSREG_RAR_SUP
+ mfsr r1, SYSREG_RSR_SUP
+ stm --sp, r0-r1
+
+ /* check for syscall tracing */
+ get_thread_info r0
+ ld.w r1, r0[TI_flags]
+ bld r1, TIF_SYSCALL_TRACE
+ brcs syscall_trace_enter
+
+syscall_trace_cont:
+ cp.w r8, NR_syscalls
+ brhs syscall_badsys
+
+ lddpc lr, syscall_table_addr
+ ld.w lr, lr[r8 << 2]
+ mov r8, r5 /* 5th argument (6th is pushed by stub) */
+ icall lr
+
+ .global syscall_return
+syscall_return:
+ get_thread_info r0
+ mask_interrupts /* make sure we don't miss an interrupt
+ setting need_resched or sigpending
+ between sampling and the rets */
+
+ /* Store the return value so that the correct value is loaded below */
+ stdsp sp[REG_R12], r12
+
+ ld.w r1, r0[TI_flags]
+ andl r1, _TIF_ALLWORK_MASK, COH
+ brne syscall_exit_work
+
+syscall_exit_cont:
+ popm r8-r9
+ mtsr SYSREG_RAR_SUP, r8
+ mtsr SYSREG_RSR_SUP, r9
+ ldmts sp++, r0-lr
+ sub sp, -4 /* r12_orig */
+ rets
+
+ .align 2
+syscall_table_addr:
+ .long sys_call_table
+
+syscall_badsys:
+ mov r12, -ENOSYS
+ rjmp syscall_return
+
+ .global ret_from_fork
+ret_from_fork:
+ rcall schedule_tail
+
+ /* check for syscall tracing */
+ get_thread_info r0
+ ld.w r1, r0[TI_flags]
+ andl r1, _TIF_ALLWORK_MASK, COH
+ brne syscall_exit_work
+ rjmp syscall_exit_cont
+
+syscall_trace_enter:
+ pushm r8-r12
+ rcall syscall_trace
+ popm r8-r12
+ rjmp syscall_trace_cont
+
+syscall_exit_work:
+ bld r1, TIF_SYSCALL_TRACE
+ brcc 1f
+ unmask_interrupts
+ rcall syscall_trace
+ mask_interrupts
+ ld.w r1, r0[TI_flags]
+
+1: bld r1, TIF_NEED_RESCHED
+ brcc 2f
+ unmask_interrupts
+ rcall schedule
+ mask_interrupts
+ ld.w r1, r0[TI_flags]
+ rjmp 1b
+
+2: mov r2, _TIF_SIGPENDING | _TIF_RESTORE_SIGMASK
+ tst r1, r2
+ breq 3f
+ unmask_interrupts
+ mov r12, sp
+ mov r11, r0
+ rcall do_notify_resume
+ mask_interrupts
+ ld.w r1, r0[TI_flags]
+ rjmp 1b
+
+3: bld r1, TIF_BREAKPOINT
+ brcc syscall_exit_cont
+ mfsr r3, SYSREG_TLBEHI
+ lddsp r2, sp[REG_PC]
+ andl r3, 0xff, COH
+ lsl r3, 1
+ sbr r3, 30
+ sbr r3, 0
+ mtdr DBGREG_BWA2A, r2
+ mtdr DBGREG_BWC2A, r3
+ rjmp syscall_exit_cont
+
+
+ /* The slow path of the TLB miss handler */
+page_table_not_present:
+page_not_present:
+ tlbmiss_restore
+ sub sp, 4
+ stmts --sp, r0-lr
+ rcall save_full_context_ex
+ mfsr r12, SYSREG_ECR
+ mov r11, sp
+ rcall do_page_fault
+ rjmp ret_from_exception
+
+ /* This function expects to find offending PC in SYSREG_RAR_EX */
+save_full_context_ex:
+ mfsr r8, SYSREG_RSR_EX
+ mov r12, r8
+ andh r8, (MODE_MASK >> 16), COH
+ mfsr r11, SYSREG_RAR_EX
+ brne 2f
+
+1: pushm r11, r12 /* PC and SR */
+ unmask_exceptions
+ ret r12
+
+2: sub r10, sp, -(FRAME_SIZE_FULL - REG_LR)
+ stdsp sp[4], r10 /* replace saved SP */
+ rjmp 1b
+
+ /* Low-level exception handlers */
+handle_critical:
+ pushm r12
+ pushm r0-r12
+ rcall save_full_context_ex
+ mfsr r12, SYSREG_ECR
+ mov r11, sp
+ rcall do_critical_exception
+
+ /* We should never get here... */
+bad_return:
+ sub r12, pc, (. - 1f)
+ bral panic
+ .align 2
+1: .asciz "Return from critical exception!"
+
+ .align 1
+do_bus_error_write:
+ sub sp, 4
+ stmts --sp, r0-lr
+ rcall save_full_context_ex
+ mov r11, 1
+ rjmp 1f
+
+do_bus_error_read:
+ sub sp, 4
+ stmts --sp, r0-lr
+ rcall save_full_context_ex
+ mov r11, 0
+1: mfsr r12, SYSREG_BEAR
+ mov r10, sp
+ rcall do_bus_error
+ rjmp ret_from_exception
+
+ .align 1
+do_nmi_ll:
+ sub sp, 4
+ stmts --sp, r0-lr
+ /* FIXME: Make sure RAR_NMI and RSR_NMI are pushed instead of *_EX */
+ rcall save_full_context_ex
+ mfsr r12, SYSREG_ECR
+ mov r11, sp
+ rcall do_nmi
+ rjmp bad_return
+
+handle_address_fault:
+ sub sp, 4
+ stmts --sp, r0-lr
+ rcall save_full_context_ex
+ mfsr r12, SYSREG_ECR
+ mov r11, sp
+ rcall do_address_exception
+ rjmp ret_from_exception
+
+handle_protection_fault:
+ sub sp, 4
+ stmts --sp, r0-lr
+ rcall save_full_context_ex
+ mfsr r12, SYSREG_ECR
+ mov r11, sp
+ rcall do_page_fault
+ rjmp ret_from_exception
+
+ .align 1
+do_illegal_opcode_ll:
+ sub sp, 4
+ stmts --sp, r0-lr
+ rcall save_full_context_ex
+ mfsr r12, SYSREG_ECR
+ mov r11, sp
+ rcall do_illegal_opcode
+ rjmp ret_from_exception
+
+do_dtlb_modified:
+ pushm r0-r3
+ mfsr r1, SYSREG_TLBEAR
+ mfsr r0, SYSREG_PTBR
+ lsr r2, r1, PGDIR_SHIFT
+ ld.w r0, r0[r2 << 2]
+ lsl r1, (32 - PGDIR_SHIFT)
+ lsr r1, (32 - PGDIR_SHIFT) + PAGE_SHIFT
+
+ /* Translate to virtual address in P1 */
+ andl r0, 0xf000
+ sbr r0, 31
+ add r2, r0, r1 << 2
+ ld.w r3, r2[0]
+ sbr r3, _PAGE_BIT_DIRTY
+ mov r0, r3
+ st.w r2[0], r3
+
+ /* The page table is up-to-date. Update the TLB entry as well */
+ andl r0, lo(_PAGE_FLAGS_HARDWARE_MASK)
+ mtsr SYSREG_TLBELO, r0
+
+ /* MMUCR[DRP] is updated automatically, so let's go... */
+ tlbw
+
+ popm r0-r3
+ rete
+
+do_fpe_ll:
+ sub sp, 4
+ stmts --sp, r0-lr
+ rcall save_full_context_ex
+ unmask_interrupts
+ mov r12, 26
+ mov r11, sp
+ rcall do_fpe
+ rjmp ret_from_exception
+
+ret_from_exception:
+ mask_interrupts
+ lddsp r4, sp[REG_SR]
+ andh r4, (MODE_MASK >> 16), COH
+ brne fault_resume_kernel
+
+ get_thread_info r0
+ ld.w r1, r0[TI_flags]
+ andl r1, _TIF_WORK_MASK, COH
+ brne fault_exit_work
+
+fault_resume_user:
+ popm r8-r9
+ mask_exceptions
+ mtsr SYSREG_RAR_EX, r8
+ mtsr SYSREG_RSR_EX, r9
+ ldmts sp++, r0-lr
+ sub sp, -4
+ rete
+
+fault_resume_kernel:
+#ifdef CONFIG_PREEMPT
+ get_thread_info r0
+ ld.w r2, r0[TI_preempt_count]
+ cp.w r2, 0
+ brne 1f
+ ld.w r1, r0[TI_flags]
+ bld r1, TIF_NEED_RESCHED
+ brcc 1f
+ lddsp r4, sp[REG_SR]
+ bld r4, SYSREG_GM_OFFSET
+ brcs 1f
+ rcall preempt_schedule_irq
+1:
+#endif
+
+ popm r8-r9
+ mask_exceptions
+ mfsr r1, SYSREG_SR
+ mtsr SYSREG_RAR_EX, r8
+ mtsr SYSREG_RSR_EX, r9
+ popm lr
+ sub sp, -4 /* ignore SP */
+ popm r0-r12
+ sub sp, -4 /* ignore r12_orig */
+ rete
+
+irq_exit_work:
+ /* Switch to exception mode so that we can share the same code. */
+ mfsr r8, SYSREG_SR
+ cbr r8, SYSREG_M0_OFFSET
+ orh r8, hi(SYSREG_BIT(M1) | SYSREG_BIT(M2))
+ mtsr SYSREG_SR, r8
+ sub pc, -2
+ get_thread_info r0
+ ld.w r1, r0[TI_flags]
+
+fault_exit_work:
+ bld r1, TIF_NEED_RESCHED
+ brcc 1f
+ unmask_interrupts
+ rcall schedule
+ mask_interrupts
+ ld.w r1, r0[TI_flags]
+ rjmp fault_exit_work
+
+1: mov r2, _TIF_SIGPENDING | _TIF_RESTORE_SIGMASK
+ tst r1, r2
+ breq 2f
+ unmask_interrupts
+ mov r12, sp
+ mov r11, r0
+ rcall do_notify_resume
+ mask_interrupts
+ ld.w r1, r0[TI_flags]
+ rjmp fault_exit_work
+
+2: bld r1, TIF_BREAKPOINT
+ brcc fault_resume_user
+ mfsr r3, SYSREG_TLBEHI
+ lddsp r2, sp[REG_PC]
+ andl r3, 0xff, COH
+ lsl r3, 1
+ sbr r3, 30
+ sbr r3, 0
+ mtdr DBGREG_BWA2A, r2
+ mtdr DBGREG_BWC2A, r3
+ rjmp fault_resume_user
+
+ /* If we get a debug trap from privileged context we end up here */
+handle_debug_priv:
+ /* Fix up LR and SP in regs. r11 contains the mode we came from */
+ mfsr r8, SYSREG_SR
+ mov r9, r8
+ andh r8, hi(~MODE_MASK)
+ or r8, r11
+ mtsr SYSREG_SR, r8
+ sub pc, -2
+ stdsp sp[REG_LR], lr
+ mtsr SYSREG_SR, r9
+ sub pc, -2
+ sub r10, sp, -FRAME_SIZE_FULL
+ stdsp sp[REG_SP], r10
+ mov r12, sp
+ rcall do_debug_priv
+
+ /* Now, put everything back */
+ ssrf SR_EM_BIT
+ popm r10, r11
+ mtsr SYSREG_RAR_DBG, r10
+ mtsr SYSREG_RSR_DBG, r11
+ mfsr r8, SYSREG_SR
+ mov r9, r8
+ andh r8, hi(~MODE_MASK)
+ andh r11, hi(MODE_MASK)
+ or r8, r11
+ mtsr SYSREG_SR, r8
+ sub pc, -2
+ popm lr
+ mtsr SYSREG_SR, r9
+ sub pc, -2
+ sub sp, -4 /* skip SP */
+ popm r0-r12
+ sub sp, -4
+ retd
+
+ /*
+ * At this point, everything is masked, that is, interrupts,
+ * exceptions and debugging traps. We might get called from
+ * interrupt or exception context in some rare cases, but this
+ * will be taken care of by do_debug(), so we're not going to
+ * do a 100% correct context save here.
+ */
+handle_debug:
+ sub sp, 4 /* r12_orig */
+ stmts --sp, r0-lr
+ mfsr r10, SYSREG_RAR_DBG
+ mfsr r11, SYSREG_RSR_DBG
+ unmask_exceptions
+ pushm r10,r11
+ andh r11, (MODE_MASK >> 16), COH
+ brne handle_debug_priv
+
+ mov r12, sp
+ rcall do_debug
+
+ lddsp r10, sp[REG_SR]
+ andh r10, (MODE_MASK >> 16), COH
+ breq debug_resume_user
+
+debug_restore_all:
+ popm r10,r11
+ mask_exceptions
+ mtsr SYSREG_RSR_DBG, r11
+ mtsr SYSREG_RAR_DBG, r10
+ ldmts sp++, r0-lr
+ sub sp, -4
+ retd
+
+debug_resume_user:
+ get_thread_info r0
+ mask_interrupts
+
+ ld.w r1, r0[TI_flags]
+ andl r1, _TIF_DBGWORK_MASK, COH
+ breq debug_restore_all
+
+1: bld r1, TIF_NEED_RESCHED
+ brcc 2f
+ unmask_interrupts
+ rcall schedule
+ mask_interrupts
+ ld.w r1, r0[TI_flags]
+ rjmp 1b
+
+2: mov r2, _TIF_SIGPENDING | _TIF_RESTORE_SIGMASK
+ tst r1, r2
+ breq 3f
+ unmask_interrupts
+ mov r12, sp
+ mov r11, r0
+ rcall do_notify_resume
+ mask_interrupts
+ ld.w r1, r0[TI_flags]
+ rjmp 1b
+
+3: bld r1, TIF_SINGLE_STEP
+ brcc debug_restore_all
+ mfdr r2, DBGREG_DC
+ sbr r2, DC_SS_BIT
+ mtdr DBGREG_DC, r2
+ rjmp debug_restore_all
+
+ .set rsr_int0, SYSREG_RSR_INT0
+ .set rsr_int1, SYSREG_RSR_INT1
+ .set rsr_int2, SYSREG_RSR_INT2
+ .set rsr_int3, SYSREG_RSR_INT3
+ .set rar_int0, SYSREG_RAR_INT0
+ .set rar_int1, SYSREG_RAR_INT1
+ .set rar_int2, SYSREG_RAR_INT2
+ .set rar_int3, SYSREG_RAR_INT3
+
+ .macro IRQ_LEVEL level
+ .type irq_level\level, @function
+irq_level\level:
+ sub sp, 4 /* r12_orig */
+ stmts --sp,r0-lr
+ mfsr r8, rar_int\level
+ mfsr r9, rsr_int\level
+ pushm r8-r9
+
+ mov r11, sp
+ mov r12, \level
+
+ rcall do_IRQ
+
+ lddsp r4, sp[REG_SR]
+ andh r4, (MODE_MASK >> 16), COH
+#ifdef CONFIG_PREEMPT
+ brne 2f
+#else
+ brne 1f
+#endif
+
+ get_thread_info r0
+ ld.w r1, r0[TI_flags]
+ andl r1, _TIF_WORK_MASK, COH
+ brne irq_exit_work
+
+1: popm r8-r9
+ mtsr rar_int\level, r8
+ mtsr rsr_int\level, r9
+ ldmts sp++,r0-lr
+ sub sp, -4 /* ignore r12_orig */
+ rete
+
+#ifdef CONFIG_PREEMPT
+2:
+ get_thread_info r0
+ ld.w r2, r0[TI_preempt_count]
+ cp.w r2, 0
+ brne 1b
+ ld.w r1, r0[TI_flags]
+ bld r1, TIF_NEED_RESCHED
+ brcc 1b
+ lddsp r4, sp[REG_SR]
+ bld r4, SYSREG_GM_OFFSET
+ brcs 1b
+ rcall preempt_schedule_irq
+ rjmp 1b
+#endif
+ .endm
+
+ .section .irq.text,"ax",@progbits
+
+ .global irq_level0
+ .global irq_level1
+ .global irq_level2
+ .global irq_level3
+ IRQ_LEVEL 0
+ IRQ_LEVEL 1
+ IRQ_LEVEL 2
+ IRQ_LEVEL 3
diff --git a/arch/avr32/kernel/head.S b/arch/avr32/kernel/head.S
new file mode 100644
index 00000000000..773b7ad87be
--- /dev/null
+++ b/arch/avr32/kernel/head.S
@@ -0,0 +1,45 @@
+/*
+ * Non-board-specific low-level startup code
+ *
+ * Copyright (C) 2004-2006 Atmel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/linkage.h>
+
+#include <asm/page.h>
+#include <asm/thread_info.h>
+#include <asm/sysreg.h>
+
+ .section .init.text,"ax"
+ .global kernel_entry
+kernel_entry:
+ /* Initialize status register */
+ lddpc r0, init_sr
+ mtsr SYSREG_SR, r0
+
+ /* Set initial stack pointer */
+ lddpc sp, stack_addr
+ sub sp, -THREAD_SIZE
+
+#ifdef CONFIG_FRAME_POINTER
+ /* Mark last stack frame */
+ mov lr, 0
+ mov r7, 0
+#endif
+
+ /* Set up the PIO, SDRAM controller, early printk, etc. */
+ rcall board_early_init
+
+ /* Start the show */
+ lddpc pc, kernel_start_addr
+
+ .align 2
+init_sr:
+ .long 0x007f0000 /* Supervisor mode, everything masked */
+stack_addr:
+ .long init_thread_union
+kernel_start_addr:
+ .long start_kernel
diff --git a/arch/avr32/kernel/init_task.c b/arch/avr32/kernel/init_task.c
new file mode 100644
index 00000000000..effcacf9d1a
--- /dev/null
+++ b/arch/avr32/kernel/init_task.c
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2004-2006 Atmel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <linux/init_task.h>
+#include <linux/mqueue.h>
+
+#include <asm/pgtable.h>
+
+static struct fs_struct init_fs = INIT_FS;
+static struct files_struct init_files = INIT_FILES;
+static struct signal_struct init_signals = INIT_SIGNALS(init_signals);
+static struct sighand_struct init_sighand = INIT_SIGHAND(init_sighand);
+struct mm_struct init_mm = INIT_MM(init_mm);
+
+EXPORT_SYMBOL(init_mm);
+
+/*
+ * Initial thread structure. Must be aligned on an 8192-byte boundary.
+ */
+union thread_union init_thread_union
+ __attribute__((__section__(".data.init_task"))) =
+ { INIT_THREAD_INFO(init_task) };
+
+/*
+ * Initial task structure.
+ *
+ * All other task structs will be allocated on slabs in fork.c
+ */
+struct task_struct init_task = INIT_TASK(init_task);
+
+EXPORT_SYMBOL(init_task);
diff --git a/arch/avr32/kernel/irq.c b/arch/avr32/kernel/irq.c
new file mode 100644
index 00000000000..856f3548e66
--- /dev/null
+++ b/arch/avr32/kernel/irq.c
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2004-2006 Atmel Corporation
+ *
+ * Based on arch/i386/kernel/irq.c
+ * Copyright (C) 1992, 1998 Linus Torvalds, Ingo Molnar
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This file contains the code used by various IRQ handling routines:
+ * asking for different IRQ's should be done through these routines
+ * instead of just grabbing them. Thus setups with different IRQ numbers
+ * shouldn't result in any weird surprises, and installing new handlers
+ * should be easier.
+ *
+ * IRQ's are in fact implemented a bit like signal handlers for the kernel.
+ * Naturally it's not a 1:1 relation, but there are similarities.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/kernel_stat.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/sysdev.h>
+
+/*
+ * '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 %u\n", irq);
+}
+
+#ifdef CONFIG_PROC_FS
+int show_interrupts(struct seq_file *p, void *v)
+{
+ int i = *(loff_t *)v, cpu;
+ struct irqaction *action;
+ unsigned long flags;
+
+ if (i == 0) {
+ seq_puts(p, " ");
+ for_each_online_cpu(cpu)
+ seq_printf(p, "CPU%d ", cpu);
+ seq_putc(p, '\n');
+ }
+
+ if (i < NR_IRQS) {
+ spin_lock_irqsave(&irq_desc[i].lock, flags);
+ action = irq_desc[i].action;
+ if (!action)
+ goto unlock;
+
+ seq_printf(p, "%3d: ", i);
+ for_each_online_cpu(cpu)
+ seq_printf(p, "%10u ", kstat_cpu(cpu).irqs[i]);
+ seq_printf(p, " %s", action->name);
+ for (action = action->next; action; action = action->next)
+ seq_printf(p, ", %s", action->name);
+
+ seq_putc(p, '\n');
+ unlock:
+ spin_unlock_irqrestore(&irq_desc[i].lock, flags);
+ }
+
+ return 0;
+}
+#endif
diff --git a/arch/avr32/kernel/kprobes.c b/arch/avr32/kernel/kprobes.c
new file mode 100644
index 00000000000..6caf9e8d808
--- /dev/null
+++ b/arch/avr32/kernel/kprobes.c
@@ -0,0 +1,270 @@
+/*
+ * Kernel Probes (KProbes)
+ *
+ * Copyright (C) 2005-2006 Atmel Corporation
+ *
+ * Based on arch/ppc64/kernel/kprobes.c
+ * Copyright (C) IBM Corporation, 2002, 2004
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kprobes.h>
+#include <linux/ptrace.h>
+
+#include <asm/cacheflush.h>
+#include <asm/kdebug.h>
+#include <asm/ocd.h>
+
+DEFINE_PER_CPU(struct kprobe *, current_kprobe);
+static unsigned long kprobe_status;
+static struct pt_regs jprobe_saved_regs;
+
+int __kprobes arch_prepare_kprobe(struct kprobe *p)
+{
+ int ret = 0;
+
+ if ((unsigned long)p->addr & 0x01) {
+ printk("Attempt to register kprobe at an unaligned address\n");
+ ret = -EINVAL;
+ }
+
+ /* XXX: Might be a good idea to check if p->addr is a valid
+ * kernel address as well... */
+
+ if (!ret) {
+ pr_debug("copy kprobe at %p\n", p->addr);
+ memcpy(p->ainsn.insn, p->addr, MAX_INSN_SIZE * sizeof(kprobe_opcode_t));
+ p->opcode = *p->addr;
+ }
+
+ return ret;
+}
+
+void __kprobes arch_arm_kprobe(struct kprobe *p)
+{
+ pr_debug("arming kprobe at %p\n", p->addr);
+ *p->addr = BREAKPOINT_INSTRUCTION;
+ flush_icache_range((unsigned long)p->addr,
+ (unsigned long)p->addr + sizeof(kprobe_opcode_t));
+}
+
+void __kprobes arch_disarm_kprobe(struct kprobe *p)
+{
+ pr_debug("disarming kprobe at %p\n", p->addr);
+ *p->addr = p->opcode;
+ flush_icache_range((unsigned long)p->addr,
+ (unsigned long)p->addr + sizeof(kprobe_opcode_t));
+}
+
+static void __kprobes prepare_singlestep(struct kprobe *p, struct pt_regs *regs)
+{
+ unsigned long dc;
+
+ pr_debug("preparing to singlestep over %p (PC=%08lx)\n",
+ p->addr, regs->pc);
+
+ BUG_ON(!(sysreg_read(SR) & SYSREG_BIT(SR_D)));
+
+ dc = __mfdr(DBGREG_DC);
+ dc |= DC_SS;
+ __mtdr(DBGREG_DC, dc);
+
+ /*
+ * We must run the instruction from its original location
+ * since it may actually reference PC.
+ *
+ * TODO: Do the instruction replacement directly in icache.
+ */
+ *p->addr = p->opcode;
+ flush_icache_range((unsigned long)p->addr,
+ (unsigned long)p->addr + sizeof(kprobe_opcode_t));
+}
+
+static void __kprobes resume_execution(struct kprobe *p, struct pt_regs *regs)
+{
+ unsigned long dc;
+
+ pr_debug("resuming execution at PC=%08lx\n", regs->pc);
+
+ dc = __mfdr(DBGREG_DC);
+ dc &= ~DC_SS;
+ __mtdr(DBGREG_DC, dc);
+
+ *p->addr = BREAKPOINT_INSTRUCTION;
+ flush_icache_range((unsigned long)p->addr,
+ (unsigned long)p->addr + sizeof(kprobe_opcode_t));
+}
+
+static void __kprobes set_current_kprobe(struct kprobe *p)
+{
+ __get_cpu_var(current_kprobe) = p;
+}
+
+static int __kprobes kprobe_handler(struct pt_regs *regs)
+{
+ struct kprobe *p;
+ void *addr = (void *)regs->pc;
+ int ret = 0;
+
+ pr_debug("kprobe_handler: kprobe_running=%d\n",
+ kprobe_running());
+
+ /*
+ * We don't want to be preempted for the entire
+ * duration of kprobe processing
+ */
+ preempt_disable();
+
+ /* Check that we're not recursing */
+ if (kprobe_running()) {
+ p = get_kprobe(addr);
+ if (p) {
+ if (kprobe_status == KPROBE_HIT_SS) {
+ printk("FIXME: kprobe hit while single-stepping!\n");
+ goto no_kprobe;
+ }
+
+ printk("FIXME: kprobe hit while handling another kprobe\n");
+ goto no_kprobe;
+ } else {
+ p = kprobe_running();
+ if (p->break_handler && p->break_handler(p, regs))
+ goto ss_probe;
+ }
+ /* If it's not ours, can't be delete race, (we hold lock). */
+ goto no_kprobe;
+ }
+
+ p = get_kprobe(addr);
+ if (!p)
+ goto no_kprobe;
+
+ kprobe_status = KPROBE_HIT_ACTIVE;
+ set_current_kprobe(p);
+ if (p->pre_handler && p->pre_handler(p, regs))
+ /* handler has already set things up, so skip ss setup */
+ return 1;
+
+ss_probe:
+ prepare_singlestep(p, regs);
+ kprobe_status = KPROBE_HIT_SS;
+ return 1;
+
+no_kprobe:
+ return ret;
+}
+
+static int __kprobes post_kprobe_handler(struct pt_regs *regs)
+{
+ struct kprobe *cur = kprobe_running();
+
+ pr_debug("post_kprobe_handler, cur=%p\n", cur);
+
+ if (!cur)
+ return 0;
+
+ if (cur->post_handler) {
+ kprobe_status = KPROBE_HIT_SSDONE;
+ cur->post_handler(cur, regs, 0);
+ }
+
+ resume_execution(cur, regs);
+ reset_current_kprobe();
+ preempt_enable_no_resched();
+
+ return 1;
+}
+
+static int __kprobes kprobe_fault_handler(struct pt_regs *regs, int trapnr)
+{
+ struct kprobe *cur = kprobe_running();
+
+ pr_debug("kprobe_fault_handler: trapnr=%d\n", trapnr);
+
+ if (cur->fault_handler && cur->fault_handler(cur, regs, trapnr))
+ return 1;
+
+ if (kprobe_status & KPROBE_HIT_SS) {
+ resume_execution(cur, regs);
+ preempt_enable_no_resched();
+ }
+ return 0;
+}
+
+/*
+ * Wrapper routine to for handling exceptions.
+ */
+int __kprobes kprobe_exceptions_notify(struct notifier_block *self,
+ unsigned long val, void *data)
+{
+ struct die_args *args = (struct die_args *)data;
+ int ret = NOTIFY_DONE;
+
+ pr_debug("kprobe_exceptions_notify: val=%lu, data=%p\n",
+ val, data);
+
+ switch (val) {
+ case DIE_BREAKPOINT:
+ if (kprobe_handler(args->regs))
+ ret = NOTIFY_STOP;
+ break;
+ case DIE_SSTEP:
+ if (post_kprobe_handler(args->regs))
+ ret = NOTIFY_STOP;
+ break;
+ case DIE_FAULT:
+ if (kprobe_running()
+ && kprobe_fault_handler(args->regs, args->trapnr))
+ ret = NOTIFY_STOP;
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+int __kprobes setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs)
+{
+ struct jprobe *jp = container_of(p, struct jprobe, kp);
+
+ memcpy(&jprobe_saved_regs, regs, sizeof(struct pt_regs));
+
+ /*
+ * TODO: We should probably save some of the stack here as
+ * well, since gcc may pass arguments on the stack for certain
+ * functions (lots of arguments, large aggregates, varargs)
+ */
+
+ /* setup return addr to the jprobe handler routine */
+ regs->pc = (unsigned long)jp->entry;
+ return 1;
+}
+
+void __kprobes jprobe_return(void)
+{
+ asm volatile("breakpoint" ::: "memory");
+}
+
+int __kprobes longjmp_break_handler(struct kprobe *p, struct pt_regs *regs)
+{
+ /*
+ * FIXME - we should ideally be validating that we got here 'cos
+ * of the "trap" in jprobe_return() above, before restoring the
+ * saved regs...
+ */
+ memcpy(regs, &jprobe_saved_regs, sizeof(struct pt_regs));
+ return 1;
+}
+
+int __init arch_init_kprobes(void)
+{
+ printk("KPROBES: Enabling monitor mode (MM|DBE)...\n");
+ __mtdr(DBGREG_DC, DC_MM | DC_DBE);
+
+ /* TODO: Register kretprobe trampoline */
+ return 0;
+}
diff --git a/arch/avr32/kernel/module.c b/arch/avr32/kernel/module.c
new file mode 100644
index 00000000000..dfc32f2817b
--- /dev/null
+++ b/arch/avr32/kernel/module.c
@@ -0,0 +1,324 @@
+/*
+ * AVR32-specific kernel module loader
+ *
+ * Copyright (C) 2005-2006 Atmel Corporation
+ *
+ * GOT initialization parts are based on the s390 version
+ * Copyright (C) 2002, 2003 IBM Deutschland Entwicklung GmbH,
+ * IBM Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/moduleloader.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/elf.h>
+#include <linux/vmalloc.h>
+
+void *module_alloc(unsigned long size)
+{
+ if (size == 0)
+ return NULL;
+ return vmalloc(size);
+}
+
+void module_free(struct module *mod, void *module_region)
+{
+ vfree(mod->arch.syminfo);
+ mod->arch.syminfo = NULL;
+
+ vfree(module_region);
+ /* FIXME: if module_region == mod->init_region, trim exception
+ * table entries. */
+}
+
+static inline int check_rela(Elf32_Rela *rela, struct module *module,
+ char *strings, Elf32_Sym *symbols)
+{
+ struct mod_arch_syminfo *info;
+
+ info = module->arch.syminfo + ELF32_R_SYM(rela->r_info);
+ switch (ELF32_R_TYPE(rela->r_info)) {
+ case R_AVR32_GOT32:
+ case R_AVR32_GOT16:
+ case R_AVR32_GOT8:
+ case R_AVR32_GOT21S:
+ case R_AVR32_GOT18SW: /* mcall */
+ case R_AVR32_GOT16S: /* ld.w */
+ if (rela->r_addend != 0) {
+ printk(KERN_ERR
+ "GOT relocation against %s at offset %u with addend\n",
+ strings + symbols[ELF32_R_SYM(rela->r_info)].st_name,
+ rela->r_offset);
+ return -ENOEXEC;
+ }
+ if (info->got_offset == -1UL) {
+ info->got_offset = module->arch.got_size;
+ module->arch.got_size += sizeof(void *);
+ }
+ pr_debug("GOT[%3lu] %s\n", info->got_offset,
+ strings + symbols[ELF32_R_SYM(rela->r_info)].st_name);
+ break;
+ }
+
+ return 0;
+}
+
+int module_frob_arch_sections(Elf_Ehdr *hdr, Elf_Shdr *sechdrs,
+ char *secstrings, struct module *module)
+{
+ Elf32_Shdr *symtab;
+ Elf32_Sym *symbols;
+ Elf32_Rela *rela;
+ char *strings;
+ int nrela, i, j;
+ int ret;
+
+ /* Find the symbol table */
+ symtab = NULL;
+ for (i = 0; i < hdr->e_shnum; i++)
+ switch (sechdrs[i].sh_type) {
+ case SHT_SYMTAB:
+ symtab = &sechdrs[i];
+ break;
+ }
+ if (!symtab) {
+ printk(KERN_ERR "module %s: no symbol table\n", module->name);
+ return -ENOEXEC;
+ }
+
+ /* Allocate room for one syminfo structure per symbol. */
+ module->arch.nsyms = symtab->sh_size / sizeof(Elf_Sym);
+ module->arch.syminfo = vmalloc(module->arch.nsyms
+ * sizeof(struct mod_arch_syminfo));
+ if (!module->arch.syminfo)
+ return -ENOMEM;
+
+ symbols = (void *)hdr + symtab->sh_offset;
+ strings = (void *)hdr + sechdrs[symtab->sh_link].sh_offset;
+ for (i = 0; i < module->arch.nsyms; i++) {
+ if (symbols[i].st_shndx == SHN_UNDEF &&
+ strcmp(strings + symbols[i].st_name,
+ "_GLOBAL_OFFSET_TABLE_") == 0)
+ /* "Define" it as absolute. */
+ symbols[i].st_shndx = SHN_ABS;
+ module->arch.syminfo[i].got_offset = -1UL;
+ module->arch.syminfo[i].got_initialized = 0;
+ }
+
+ /* Allocate GOT entries for symbols that need it. */
+ module->arch.got_size = 0;
+ for (i = 0; i < hdr->e_shnum; i++) {
+ if (sechdrs[i].sh_type != SHT_RELA)
+ continue;
+ nrela = sechdrs[i].sh_size / sizeof(Elf32_Rela);
+ rela = (void *)hdr + sechdrs[i].sh_offset;
+ for (j = 0; j < nrela; j++) {
+ ret = check_rela(rela + j, module,
+ strings, symbols);
+ if (ret)
+ goto out_free_syminfo;
+ }
+ }
+
+ /*
+ * Increase core size to make room for GOT and set start
+ * offset for GOT.
+ */
+ module->core_size = ALIGN(module->core_size, 4);
+ module->arch.got_offset = module->core_size;
+ module->core_size += module->arch.got_size;
+
+ return 0;
+
+out_free_syminfo:
+ vfree(module->arch.syminfo);
+ module->arch.syminfo = NULL;
+
+ return ret;
+}
+
+static inline int reloc_overflow(struct module *module, const char *reloc_name,
+ Elf32_Addr relocation)
+{
+ printk(KERN_ERR "module %s: Value %lx does not fit relocation %s\n",
+ module->name, (unsigned long)relocation, reloc_name);
+ return -ENOEXEC;
+}
+
+#define get_u16(loc) (*((uint16_t *)loc))
+#define put_u16(loc, val) (*((uint16_t *)loc) = (val))
+
+int apply_relocate_add(Elf32_Shdr *sechdrs, const char *strtab,
+ unsigned int symindex, unsigned int relindex,
+ struct module *module)
+{
+ Elf32_Shdr *symsec = sechdrs + symindex;
+ Elf32_Shdr *relsec = sechdrs + relindex;
+ Elf32_Shdr *dstsec = sechdrs + relsec->sh_info;
+ Elf32_Rela *rel = (void *)relsec->sh_addr;
+ unsigned int i;
+ int ret = 0;
+
+ for (i = 0; i < relsec->sh_size / sizeof(Elf32_Rela); i++, rel++) {
+ struct mod_arch_syminfo *info;
+ Elf32_Sym *sym;
+ Elf32_Addr relocation;
+ uint32_t *location;
+ uint32_t value;
+
+ location = (void *)dstsec->sh_addr + rel->r_offset;
+ sym = (Elf32_Sym *)symsec->sh_addr + ELF32_R_SYM(rel->r_info);
+ relocation = sym->st_value + rel->r_addend;
+
+ info = module->arch.syminfo + ELF32_R_SYM(rel->r_info);
+
+ /* Initialize GOT entry if necessary */
+ switch (ELF32_R_TYPE(rel->r_info)) {
+ case R_AVR32_GOT32:
+ case R_AVR32_GOT16:
+ case R_AVR32_GOT8:
+ case R_AVR32_GOT21S:
+ case R_AVR32_GOT18SW:
+ case R_AVR32_GOT16S:
+ if (!info->got_initialized) {
+ Elf32_Addr *gotent;
+
+ gotent = (module->module_core
+ + module->arch.got_offset
+ + info->got_offset);
+ *gotent = relocation;
+ info->got_initialized = 1;
+ }
+
+ relocation = info->got_offset;
+ break;
+ }
+
+ switch (ELF32_R_TYPE(rel->r_info)) {
+ case R_AVR32_32:
+ case R_AVR32_32_CPENT:
+ *location = relocation;
+ break;
+ case R_AVR32_22H_PCREL:
+ relocation -= (Elf32_Addr)location;
+ if ((relocation & 0xffe00001) != 0
+ && (relocation & 0xffc00001) != 0xffc00000)
+ return reloc_overflow(module,
+ "R_AVR32_22H_PCREL",
+ relocation);
+ relocation >>= 1;
+
+ value = *location;
+ value = ((value & 0xe1ef0000)
+ | (relocation & 0xffff)
+ | ((relocation & 0x10000) << 4)
+ | ((relocation & 0x1e0000) << 8));
+ *location = value;
+ break;
+ case R_AVR32_11H_PCREL:
+ relocation -= (Elf32_Addr)location;
+ if ((relocation & 0xfffffc01) != 0
+ && (relocation & 0xfffff801) != 0xfffff800)
+ return reloc_overflow(module,
+ "R_AVR32_11H_PCREL",
+ relocation);
+ value = get_u16(location);
+ value = ((value & 0xf00c)
+ | ((relocation & 0x1fe) << 3)
+ | ((relocation & 0x600) >> 9));
+ put_u16(location, value);
+ break;
+ case R_AVR32_9H_PCREL:
+ relocation -= (Elf32_Addr)location;
+ if ((relocation & 0xffffff01) != 0
+ && (relocation & 0xfffffe01) != 0xfffffe00)
+ return reloc_overflow(module,
+ "R_AVR32_9H_PCREL",
+ relocation);
+ value = get_u16(location);
+ value = ((value & 0xf00f)
+ | ((relocation & 0x1fe) << 3));
+ put_u16(location, value);
+ break;
+ case R_AVR32_9UW_PCREL:
+ relocation -= ((Elf32_Addr)location) & 0xfffffffc;
+ if ((relocation & 0xfffffc03) != 0)
+ return reloc_overflow(module,
+ "R_AVR32_9UW_PCREL",
+ relocation);
+ value = get_u16(location);
+ value = ((value & 0xf80f)
+ | ((relocation & 0x1fc) << 2));
+ put_u16(location, value);
+ break;
+ case R_AVR32_GOTPC:
+ /*
+ * R6 = PC - (PC - GOT)
+ *
+ * At this point, relocation contains the
+ * value of PC. Just subtract the value of
+ * GOT, and we're done.
+ */
+ pr_debug("GOTPC: PC=0x%lx, got_offset=0x%lx, core=0x%p\n",
+ relocation, module->arch.got_offset,
+ module->module_core);
+ relocation -= ((unsigned long)module->module_core
+ + module->arch.got_offset);
+ *location = relocation;
+ break;
+ case R_AVR32_GOT18SW:
+ if ((relocation & 0xfffe0003) != 0
+ && (relocation & 0xfffc0003) != 0xffff0000)
+ return reloc_overflow(module, "R_AVR32_GOT18SW",
+ relocation);
+ relocation >>= 2;
+ /* fall through */
+ case R_AVR32_GOT16S:
+ if ((relocation & 0xffff8000) != 0
+ && (relocation & 0xffff0000) != 0xffff0000)
+ return reloc_overflow(module, "R_AVR32_GOT16S",
+ relocation);
+ pr_debug("GOT reloc @ 0x%lx -> %lu\n",
+ rel->r_offset, relocation);
+ value = *location;
+ value = ((value & 0xffff0000)
+ | (relocation & 0xffff));
+ *location = value;
+ break;
+
+ default:
+ printk(KERN_ERR "module %s: Unknown relocation: %u\n",
+ module->name, ELF32_R_TYPE(rel->r_info));
+ return -ENOEXEC;
+ }
+ }
+
+ return ret;
+}
+
+int apply_relocate(Elf32_Shdr *sechdrs, const char *strtab,
+ unsigned int symindex, unsigned int relindex,
+ struct module *module)
+{
+ printk(KERN_ERR "module %s: REL relocations are not supported\n",
+ module->name);
+ return -ENOEXEC;
+}
+
+int module_finalize(const Elf_Ehdr *hdr, const Elf_Shdr *sechdrs,
+ struct module *module)
+{
+ vfree(module->arch.syminfo);
+ module->arch.syminfo = NULL;
+
+ return 0;
+}
+
+void module_arch_cleanup(struct module *module)
+{
+
+}
diff --git a/arch/avr32/kernel/process.c b/arch/avr32/kernel/process.c
new file mode 100644
index 00000000000..317dc50945f
--- /dev/null
+++ b/arch/avr32/kernel/process.c
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2004-2006 Atmel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/sched.h>
+#include <linux/module.h>
+#include <linux/kallsyms.h>
+#include <linux/fs.h>
+#include <linux/ptrace.h>
+#include <linux/reboot.h>
+#include <linux/unistd.h>
+
+#include <asm/sysreg.h>
+#include <asm/ocd.h>
+
+void (*pm_power_off)(void) = NULL;
+EXPORT_SYMBOL(pm_power_off);
+
+/*
+ * This file handles the architecture-dependent parts of process handling..
+ */
+
+void cpu_idle(void)
+{
+ /* endless idle loop with no priority at all */
+ while (1) {
+ /* TODO: Enter sleep mode */
+ while (!need_resched())
+ cpu_relax();
+ preempt_enable_no_resched();
+ schedule();
+ preempt_disable();
+ }
+}
+
+void machine_halt(void)
+{
+}
+
+void machine_power_off(void)
+{
+}
+
+void machine_restart(char *cmd)
+{
+ __mtdr(DBGREG_DC, DC_DBE);
+ __mtdr(DBGREG_DC, DC_RES);
+ while (1) ;
+}
+
+/*
+ * PC is actually discarded when returning from a system call -- the
+ * return address must be stored in LR. This function will make sure
+ * LR points to do_exit before starting the thread.
+ *
+ * Also, when returning from fork(), r12 is 0, so we must copy the
+ * argument as well.
+ *
+ * r0 : The argument to the main thread function
+ * r1 : The address of do_exit
+ * r2 : The address of the main thread function
+ */
+asmlinkage extern void kernel_thread_helper(void);
+__asm__(" .type kernel_thread_helper, @function\n"
+ "kernel_thread_helper:\n"
+ " mov r12, r0\n"
+ " mov lr, r2\n"
+ " mov pc, r1\n"
+ " .size kernel_thread_helper, . - kernel_thread_helper");
+
+int kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)
+{
+ struct pt_regs regs;
+
+ memset(&regs, 0, sizeof(regs));
+
+ regs.r0 = (unsigned long)arg;
+ regs.r1 = (unsigned long)fn;
+ regs.r2 = (unsigned long)do_exit;
+ regs.lr = (unsigned long)kernel_thread_helper;
+ regs.pc = (unsigned long)kernel_thread_helper;
+ regs.sr = MODE_SUPERVISOR;
+
+ return do_fork(flags | CLONE_VM | CLONE_UNTRACED,
+ 0, &regs, 0, NULL, NULL);
+}
+EXPORT_SYMBOL(kernel_thread);
+
+/*
+ * Free current thread data structures etc
+ */
+void exit_thread(void)
+{
+ /* nothing to do */
+}
+
+void flush_thread(void)
+{
+ /* nothing to do */
+}
+
+void release_thread(struct task_struct *dead_task)
+{
+ /* do nothing */
+}
+
+static const char *cpu_modes[] = {
+ "Application", "Supervisor", "Interrupt level 0", "Interrupt level 1",
+ "Interrupt level 2", "Interrupt level 3", "Exception", "NMI"
+};
+
+void show_regs(struct pt_regs *regs)
+{
+ unsigned long sp = regs->sp;
+ unsigned long lr = regs->lr;
+ unsigned long mode = (regs->sr & MODE_MASK) >> MODE_SHIFT;
+
+ if (!user_mode(regs))
+ sp = (unsigned long)regs + FRAME_SIZE_FULL;
+
+ print_symbol("PC is at %s\n", instruction_pointer(regs));
+ print_symbol("LR is at %s\n", lr);
+ printk("pc : [<%08lx>] lr : [<%08lx>] %s\n"
+ "sp : %08lx r12: %08lx r11: %08lx\n",
+ instruction_pointer(regs),
+ lr, print_tainted(), sp, regs->r12, regs->r11);
+ printk("r10: %08lx r9 : %08lx r8 : %08lx\n",
+ regs->r10, regs->r9, regs->r8);
+ printk("r7 : %08lx r6 : %08lx r5 : %08lx r4 : %08lx\n",
+ regs->r7, regs->r6, regs->r5, regs->r4);
+ printk("r3 : %08lx r2 : %08lx r1 : %08lx r0 : %08lx\n",
+ regs->r3, regs->r2, regs->r1, regs->r0);
+ printk("Flags: %c%c%c%c%c\n",
+ regs->sr & SR_Q ? 'Q' : 'q',
+ regs->sr & SR_V ? 'V' : 'v',
+ regs->sr & SR_N ? 'N' : 'n',
+ regs->sr & SR_Z ? 'Z' : 'z',
+ regs->sr & SR_C ? 'C' : 'c');
+ printk("Mode bits: %c%c%c%c%c%c%c%c%c\n",
+ regs->sr & SR_H ? 'H' : 'h',
+ regs->sr & SR_R ? 'R' : 'r',
+ regs->sr & SR_J ? 'J' : 'j',
+ regs->sr & SR_EM ? 'E' : 'e',
+ regs->sr & SR_I3M ? '3' : '.',
+ regs->sr & SR_I2M ? '2' : '.',
+ regs->sr & SR_I1M ? '1' : '.',
+ regs->sr & SR_I0M ? '0' : '.',
+ regs->sr & SR_GM ? 'G' : 'g');
+ printk("CPU Mode: %s\n", cpu_modes[mode]);
+
+ show_trace(NULL, (unsigned long *)sp, regs);
+}
+EXPORT_SYMBOL(show_regs);
+
+/* Fill in the fpu structure for a core dump. This is easy -- we don't have any */
+int dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpu)
+{
+ /* Not valid */
+ return 0;
+}
+
+asmlinkage void ret_from_fork(void);
+
+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;
+
+ childregs = ((struct pt_regs *)(THREAD_SIZE + (unsigned long)p->thread_info)) - 1;
+ *childregs = *regs;
+
+ if (user_mode(regs))
+ childregs->sp = usp;
+ else
+ childregs->sp = (unsigned long)p->thread_info + THREAD_SIZE;
+
+ childregs->r12 = 0; /* Set return value for child */
+
+ p->thread.cpu_context.sr = MODE_SUPERVISOR | SR_GM;
+ p->thread.cpu_context.ksp = (unsigned long)childregs;
+ p->thread.cpu_context.pc = (unsigned long)ret_from_fork;
+
+ return 0;
+}
+
+/* r12-r8 are dummy parameters to force the compiler to use the stack */
+asmlinkage int sys_fork(struct pt_regs *regs)
+{
+ return do_fork(SIGCHLD, regs->sp, regs, 0, NULL, NULL);
+}
+
+asmlinkage int sys_clone(unsigned long clone_flags, unsigned long newsp,
+ unsigned long parent_tidptr,
+ unsigned long child_tidptr, struct pt_regs *regs)
+{
+ if (!newsp)
+ newsp = regs->sp;
+ return do_fork(clone_flags, newsp, regs, 0,
+ (int __user *)parent_tidptr,
+ (int __user *)child_tidptr);
+}
+
+asmlinkage int sys_vfork(struct pt_regs *regs)
+{
+ return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs->sp, regs,
+ 0, NULL, NULL);
+}
+
+asmlinkage int sys_execve(char __user *ufilename, char __user *__user *uargv,
+ char __user *__user *uenvp, struct pt_regs *regs)
+{
+ int error;
+ char *filename;
+
+ filename = getname(ufilename);
+ error = PTR_ERR(filename);
+ if (IS_ERR(filename))
+ goto out;
+
+ error = do_execve(filename, uargv, uenvp, regs);
+ if (error == 0)
+ current->ptrace &= ~PT_DTRACE;
+ putname(filename);
+
+out:
+ return error;
+}
+
+
+/*
+ * This function is supposed to answer the question "who called
+ * schedule()?"
+ */
+unsigned long get_wchan(struct task_struct *p)
+{
+ unsigned long pc;
+ unsigned long stack_page;
+
+ if (!p || p == current || p->state == TASK_RUNNING)
+ return 0;
+
+ stack_page = (unsigned long)p->thread_info;
+ BUG_ON(!stack_page);
+
+ /*
+ * The stored value of PC is either the address right after
+ * the call to __switch_to() or ret_from_fork.
+ */
+ pc = thread_saved_pc(p);
+ if (in_sched_functions(pc)) {
+#ifdef CONFIG_FRAME_POINTER
+ unsigned long fp = p->thread.cpu_context.r7;
+ BUG_ON(fp < stack_page || fp > (THREAD_SIZE + stack_page));
+ pc = *(unsigned long *)fp;
+#else
+ /*
+ * We depend on the frame size of schedule here, which
+ * is actually quite ugly. It might be possible to
+ * determine the frame size automatically at build
+ * time by doing this:
+ * - compile sched.c
+ * - disassemble the resulting sched.o
+ * - look for 'sub sp,??' shortly after '<schedule>:'
+ */
+ unsigned long sp = p->thread.cpu_context.ksp + 16;
+ BUG_ON(sp < stack_page || sp > (THREAD_SIZE + stack_page));
+ pc = *(unsigned long *)sp;
+#endif
+ }
+
+ return pc;
+}
diff --git a/arch/avr32/kernel/ptrace.c b/arch/avr32/kernel/ptrace.c
new file mode 100644
index 00000000000..3c89e59029a
--- /dev/null
+++ b/arch/avr32/kernel/ptrace.c
@@ -0,0 +1,371 @@
+/*
+ * Copyright (C) 2004-2006 Atmel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#undef DEBUG
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/smp_lock.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/user.h>
+#include <linux/security.h>
+#include <linux/unistd.h>
+#include <linux/notifier.h>
+
+#include <asm/traps.h>
+#include <asm/uaccess.h>
+#include <asm/ocd.h>
+#include <asm/mmu_context.h>
+#include <asm/kdebug.h>
+
+static struct pt_regs *get_user_regs(struct task_struct *tsk)
+{
+ return (struct pt_regs *)((unsigned long) tsk->thread_info +
+ THREAD_SIZE - sizeof(struct pt_regs));
+}
+
+static void ptrace_single_step(struct task_struct *tsk)
+{
+ pr_debug("ptrace_single_step: pid=%u, SR=0x%08lx\n",
+ tsk->pid, tsk->thread.cpu_context.sr);
+ if (!(tsk->thread.cpu_context.sr & SR_D)) {
+ /*
+ * Set a breakpoint at the current pc to force the
+ * process into debug mode. The syscall/exception
+ * exit code will set a breakpoint at the return
+ * address when this flag is set.
+ */
+ pr_debug("ptrace_single_step: Setting TIF_BREAKPOINT\n");
+ set_tsk_thread_flag(tsk, TIF_BREAKPOINT);
+ }
+
+ /* The monitor code will do the actual step for us */
+ set_tsk_thread_flag(tsk, TIF_SINGLE_STEP);
+}
+
+/*
+ * Called by kernel/ptrace.c when detaching
+ *
+ * Make sure any single step bits, etc. are not set
+ */
+void ptrace_disable(struct task_struct *child)
+{
+ clear_tsk_thread_flag(child, TIF_SINGLE_STEP);
+}
+
+/*
+ * Handle hitting a breakpoint
+ */
+static void ptrace_break(struct task_struct *tsk, struct pt_regs *regs)
+{
+ siginfo_t info;
+
+ info.si_signo = SIGTRAP;
+ info.si_errno = 0;
+ info.si_code = TRAP_BRKPT;
+ info.si_addr = (void __user *)instruction_pointer(regs);
+
+ pr_debug("ptrace_break: Sending SIGTRAP to PID %u (pc = 0x%p)\n",
+ tsk->pid, info.si_addr);
+ force_sig_info(SIGTRAP, &info, tsk);
+}
+
+/*
+ * Read the word at offset "offset" into the task's "struct user". We
+ * actually access the pt_regs struct stored on the kernel stack.
+ */
+static int ptrace_read_user(struct task_struct *tsk, unsigned long offset,
+ unsigned long __user *data)
+{
+ unsigned long *regs;
+ unsigned long value;
+
+ pr_debug("ptrace_read_user(%p, %#lx, %p)\n",
+ tsk, offset, data);
+
+ if (offset & 3 || offset >= sizeof(struct user)) {
+ printk("ptrace_read_user: invalid offset 0x%08lx\n", offset);
+ return -EIO;
+ }
+
+ regs = (unsigned long *)get_user_regs(tsk);
+
+ value = 0;
+ if (offset < sizeof(struct pt_regs))
+ value = regs[offset / sizeof(regs[0])];
+
+ return put_user(value, data);
+}
+
+/*
+ * Write the word "value" to offset "offset" into the task's "struct
+ * user". We actually access the pt_regs struct stored on the kernel
+ * stack.
+ */
+static int ptrace_write_user(struct task_struct *tsk, unsigned long offset,
+ unsigned long value)
+{
+ unsigned long *regs;
+
+ if (offset & 3 || offset >= sizeof(struct user)) {
+ printk("ptrace_write_user: invalid offset 0x%08lx\n", offset);
+ return -EIO;
+ }
+
+ if (offset >= sizeof(struct pt_regs))
+ return 0;
+
+ regs = (unsigned long *)get_user_regs(tsk);
+ regs[offset / sizeof(regs[0])] = value;
+
+ return 0;
+}
+
+static int ptrace_getregs(struct task_struct *tsk, void __user *uregs)
+{
+ struct pt_regs *regs = get_user_regs(tsk);
+
+ return copy_to_user(uregs, regs, sizeof(*regs)) ? -EFAULT : 0;
+}
+
+static int ptrace_setregs(struct task_struct *tsk, const void __user *uregs)
+{
+ struct pt_regs newregs;
+ int ret;
+
+ ret = -EFAULT;
+ if (copy_from_user(&newregs, uregs, sizeof(newregs)) == 0) {
+ struct pt_regs *regs = get_user_regs(tsk);
+
+ ret = -EINVAL;
+ if (valid_user_regs(&newregs)) {
+ *regs = newregs;
+ ret = 0;
+ }
+ }
+
+ return ret;
+}
+
+long arch_ptrace(struct task_struct *child, long request, long addr, long data)
+{
+ unsigned long tmp;
+ int ret;
+
+ pr_debug("arch_ptrace(%ld, %ld, %#lx, %#lx)\n",
+ request, child->pid, addr, data);
+
+ pr_debug("ptrace: Enabling monitor mode...\n");
+ __mtdr(DBGREG_DC, __mfdr(DBGREG_DC) | DC_MM | DC_DBE);
+
+ switch (request) {
+ /* Read the word at location addr in the child process */
+ case PTRACE_PEEKTEXT:
+ case PTRACE_PEEKDATA:
+ ret = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
+ if (ret == sizeof(tmp))
+ ret = put_user(tmp, (unsigned long __user *)data);
+ else
+ ret = -EIO;
+ break;
+
+ case PTRACE_PEEKUSR:
+ ret = ptrace_read_user(child, addr,
+ (unsigned long __user *)data);
+ break;
+
+ /* Write the word in data at location addr */
+ case PTRACE_POKETEXT:
+ case PTRACE_POKEDATA:
+ ret = access_process_vm(child, addr, &data, sizeof(data), 1);
+ if (ret == sizeof(data))
+ ret = 0;
+ else
+ ret = -EIO;
+ break;
+
+ case PTRACE_POKEUSR:
+ ret = ptrace_write_user(child, addr, data);
+ break;
+
+ /* continue and stop at next (return from) syscall */
+ case PTRACE_SYSCALL:
+ /* restart after signal */
+ case PTRACE_CONT:
+ 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;
+ /* XXX: Are we sure no breakpoints are active here? */
+ wake_up_process(child);
+ ret = 0;
+ break;
+
+ /*
+ * Make the child exit. Best I can do is send it a
+ * SIGKILL. Perhaps it should be put in the status that it
+ * wants to exit.
+ */
+ case PTRACE_KILL:
+ ret = 0;
+ if (child->exit_state == EXIT_ZOMBIE)
+ break;
+ child->exit_code = SIGKILL;
+ wake_up_process(child);
+ break;
+
+ /*
+ * execute single instruction.
+ */
+ case PTRACE_SINGLESTEP:
+ ret = -EIO;
+ if (!valid_signal(data))
+ break;
+ clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
+ ptrace_single_step(child);
+ child->exit_code = data;
+ wake_up_process(child);
+ ret = 0;
+ break;
+
+ /* Detach a process that was attached */
+ case PTRACE_DETACH:
+ ret = ptrace_detach(child, data);
+ break;
+
+ case PTRACE_GETREGS:
+ ret = ptrace_getregs(child, (void __user *)data);
+ break;
+
+ case PTRACE_SETREGS:
+ ret = ptrace_setregs(child, (const void __user *)data);
+ break;
+
+ default:
+ ret = ptrace_request(child, request, addr, data);
+ break;
+ }
+
+ pr_debug("sys_ptrace returning %d (DC = 0x%08lx)\n", ret, __mfdr(DBGREG_DC));
+ return ret;
+}
+
+asmlinkage void syscall_trace(void)
+{
+ pr_debug("syscall_trace called\n");
+ if (!test_thread_flag(TIF_SYSCALL_TRACE))
+ return;
+ if (!(current->ptrace & PT_PTRACED))
+ return;
+
+ pr_debug("syscall_trace: notifying parent\n");
+ /* 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) {
+ pr_debug("syscall_trace: sending signal %d to PID %u\n",
+ current->exit_code, current->pid);
+ send_sig(current->exit_code, current, 1);
+ current->exit_code = 0;
+ }
+}
+
+asmlinkage void do_debug_priv(struct pt_regs *regs)
+{
+ unsigned long dc, ds;
+ unsigned long die_val;
+
+ ds = __mfdr(DBGREG_DS);
+
+ pr_debug("do_debug_priv: pc = %08lx, ds = %08lx\n", regs->pc, ds);
+
+ if (ds & DS_SSS)
+ die_val = DIE_SSTEP;
+ else
+ die_val = DIE_BREAKPOINT;
+
+ if (notify_die(die_val, regs, 0, SIGTRAP) == NOTIFY_STOP)
+ return;
+
+ if (likely(ds & DS_SSS)) {
+ extern void itlb_miss(void);
+ extern void tlb_miss_common(void);
+ struct thread_info *ti;
+
+ dc = __mfdr(DBGREG_DC);
+ dc &= ~DC_SS;
+ __mtdr(DBGREG_DC, dc);
+
+ ti = current_thread_info();
+ ti->flags |= _TIF_BREAKPOINT;
+
+ /* The TLB miss handlers don't check thread flags */
+ if ((regs->pc >= (unsigned long)&itlb_miss)
+ && (regs->pc <= (unsigned long)&tlb_miss_common)) {
+ __mtdr(DBGREG_BWA2A, sysreg_read(RAR_EX));
+ __mtdr(DBGREG_BWC2A, 0x40000001 | (get_asid() << 1));
+ }
+
+ /*
+ * If we're running in supervisor mode, the breakpoint
+ * will take us where we want directly, no need to
+ * single step.
+ */
+ if ((regs->sr & MODE_MASK) != MODE_SUPERVISOR)
+ ti->flags |= TIF_SINGLE_STEP;
+ } else {
+ panic("Unable to handle debug trap at pc = %08lx\n",
+ regs->pc);
+ }
+}
+
+/*
+ * Handle breakpoints, single steps and other debuggy things. To keep
+ * things simple initially, we run with interrupts and exceptions
+ * disabled all the time.
+ */
+asmlinkage void do_debug(struct pt_regs *regs)
+{
+ unsigned long dc, ds;
+
+ ds = __mfdr(DBGREG_DS);
+ pr_debug("do_debug: pc = %08lx, ds = %08lx\n", regs->pc, ds);
+
+ if (test_thread_flag(TIF_BREAKPOINT)) {
+ pr_debug("TIF_BREAKPOINT set\n");
+ /* We're taking care of it */
+ clear_thread_flag(TIF_BREAKPOINT);
+ __mtdr(DBGREG_BWC2A, 0);
+ }
+
+ if (test_thread_flag(TIF_SINGLE_STEP)) {
+ pr_debug("TIF_SINGLE_STEP set, ds = 0x%08lx\n", ds);
+ if (ds & DS_SSS) {
+ dc = __mfdr(DBGREG_DC);
+ dc &= ~DC_SS;
+ __mtdr(DBGREG_DC, dc);
+
+ clear_thread_flag(TIF_SINGLE_STEP);
+ ptrace_break(current, regs);
+ }
+ } else {
+ /* regular breakpoint */
+ ptrace_break(current, regs);
+ }
+}
diff --git a/arch/avr32/kernel/semaphore.c b/arch/avr32/kernel/semaphore.c
new file mode 100644
index 00000000000..1e2705a0501
--- /dev/null
+++ b/arch/avr32/kernel/semaphore.c
@@ -0,0 +1,148 @@
+/*
+ * AVR32 sempahore implementation.
+ *
+ * Copyright (C) 2004-2006 Atmel Corporation
+ *
+ * Based on linux/arch/i386/kernel/semaphore.c
+ * Copyright (C) 1999 Linus Torvalds
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+
+#include <asm/semaphore.h>
+#include <asm/atomic.h>
+
+/*
+ * Semaphores are implemented using a two-way counter:
+ * The "count" variable is decremented for each process
+ * that tries to acquire the semaphore, while the "sleeping"
+ * variable is a count of such acquires.
+ *
+ * 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.
+ *
+ * "sleeping" and the contention routine ordering is protected
+ * by the spinlock in the semaphore's waitqueue head.
+ *
+ * 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.
+ */
+
+/*
+ * Logic:
+ * - only on a boundary condition do we need to care. When we go
+ * from a negative count to a non-negative, we wake people up.
+ * - when we go from a non-negative count to a negative do we
+ * (a) synchronize with the "sleeper" count and (b) make sure
+ * that we're on the wakeup list before we synchronize so that
+ * we cannot lose wakeup events.
+ */
+
+void __up(struct semaphore *sem)
+{
+ wake_up(&sem->wait);
+}
+EXPORT_SYMBOL(__up);
+
+void __sched __down(struct semaphore *sem)
+{
+ struct task_struct *tsk = current;
+ DECLARE_WAITQUEUE(wait, tsk);
+ unsigned long flags;
+
+ tsk->state = TASK_UNINTERRUPTIBLE;
+ spin_lock_irqsave(&sem->wait.lock, flags);
+ add_wait_queue_exclusive_locked(&sem->wait, &wait);
+
+ sem->sleepers++;
+ for (;;) {
+ int sleepers = sem->sleepers;
+
+ /*
+ * Add "everybody else" into it. They aren't
+ * playing, because we own the spinlock in
+ * the wait_queue_head.
+ */
+ if (atomic_add_return(sleepers - 1, &sem->count) >= 0) {
+ sem->sleepers = 0;
+ break;
+ }
+ sem->sleepers = 1; /* us - see -1 above */
+ spin_unlock_irqrestore(&sem->wait.lock, flags);
+
+ schedule();
+
+ spin_lock_irqsave(&sem->wait.lock, flags);
+ tsk->state = TASK_UNINTERRUPTIBLE;
+ }
+ remove_wait_queue_locked(&sem->wait, &wait);
+ wake_up_locked(&sem->wait);
+ spin_unlock_irqrestore(&sem->wait.lock, flags);
+ tsk->state = TASK_RUNNING;
+}
+EXPORT_SYMBOL(__down);
+
+int __sched __down_interruptible(struct semaphore *sem)
+{
+ int retval = 0;
+ struct task_struct *tsk = current;
+ DECLARE_WAITQUEUE(wait, tsk);
+ unsigned long flags;
+
+ tsk->state = TASK_INTERRUPTIBLE;
+ spin_lock_irqsave(&sem->wait.lock, flags);
+ add_wait_queue_exclusive_locked(&sem->wait, &wait);
+
+ sem->sleepers++;
+ for (;;) {
+ int sleepers = sem->sleepers;
+
+ /*
+ * With signals pending, this turns into the trylock
+ * failure case - we won't be sleeping, and we can't
+ * get the lock as it has contention. Just correct the
+ * count and exit.
+ */
+ if (signal_pending(current)) {
+ retval = -EINTR;
+ sem->sleepers = 0;
+ atomic_add(sleepers, &sem->count);
+ break;
+ }
+
+ /*
+ * Add "everybody else" into it. They aren't
+ * playing, because we own the spinlock in
+ * the wait_queue_head.
+ */
+ if (atomic_add_return(sleepers - 1, &sem->count) >= 0) {
+ sem->sleepers = 0;
+ break;
+ }
+ sem->sleepers = 1; /* us - see -1 above */
+ spin_unlock_irqrestore(&sem->wait.lock, flags);
+
+ schedule();
+
+ spin_lock_irqsave(&sem->wait.lock, flags);
+ tsk->state = TASK_INTERRUPTIBLE;
+ }
+ remove_wait_queue_locked(&sem->wait, &wait);
+ wake_up_locked(&sem->wait);
+ spin_unlock_irqrestore(&sem->wait.lock, flags);
+
+ tsk->state = TASK_RUNNING;
+ return retval;
+}
+EXPORT_SYMBOL(__down_interruptible);
diff --git a/arch/avr32/kernel/setup.c b/arch/avr32/kernel/setup.c
new file mode 100644
index 00000000000..5d68f3c6990
--- /dev/null
+++ b/arch/avr32/kernel/setup.c
@@ -0,0 +1,335 @@
+/*
+ * Copyright (C) 2004-2006 Atmel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/clk.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/console.h>
+#include <linux/ioport.h>
+#include <linux/bootmem.h>
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/root_dev.h>
+#include <linux/cpu.h>
+
+#include <asm/sections.h>
+#include <asm/processor.h>
+#include <asm/pgtable.h>
+#include <asm/setup.h>
+#include <asm/sysreg.h>
+
+#include <asm/arch/board.h>
+#include <asm/arch/init.h>
+
+extern int root_mountflags;
+
+/*
+ * Bootloader-provided information about physical memory
+ */
+struct tag_mem_range *mem_phys;
+struct tag_mem_range *mem_reserved;
+struct tag_mem_range *mem_ramdisk;
+
+/*
+ * Initialize loops_per_jiffy as 5000000 (500MIPS).
+ * Better make it too large than too small...
+ */
+struct avr32_cpuinfo boot_cpu_data = {
+ .loops_per_jiffy = 5000000
+};
+EXPORT_SYMBOL(boot_cpu_data);
+
+static char command_line[COMMAND_LINE_SIZE];
+
+/*
+ * Should be more than enough, but if you have a _really_ complex
+ * setup, you might need to increase the size of this...
+ */
+static struct tag_mem_range __initdata mem_range_cache[32];
+static unsigned mem_range_next_free;
+
+/*
+ * Standard memory resources
+ */
+static struct resource mem_res[] = {
+ {
+ .name = "Kernel code",
+ .start = 0,
+ .end = 0,
+ .flags = IORESOURCE_MEM
+ },
+ {
+ .name = "Kernel data",
+ .start = 0,
+ .end = 0,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+#define kernel_code mem_res[0]
+#define kernel_data mem_res[1]
+
+/*
+ * Early framebuffer allocation. Works as follows:
+ * - If fbmem_size is zero, nothing will be allocated or reserved.
+ * - If fbmem_start is zero when setup_bootmem() is called,
+ * fbmem_size bytes will be allocated from the bootmem allocator.
+ * - If fbmem_start is nonzero, an area of size fbmem_size will be
+ * reserved at the physical address fbmem_start if necessary. If
+ * the area isn't in a memory region known to the kernel, it will
+ * be left alone.
+ *
+ * Board-specific code may use these variables to set up platform data
+ * for the framebuffer driver if fbmem_size is nonzero.
+ */
+static unsigned long __initdata fbmem_start;
+static unsigned long __initdata fbmem_size;
+
+/*
+ * "fbmem=xxx[kKmM]" allocates the specified amount of boot memory for
+ * use as framebuffer.
+ *
+ * "fbmem=xxx[kKmM]@yyy[kKmM]" defines a memory region of size xxx and
+ * starting at yyy to be reserved for use as framebuffer.
+ *
+ * The kernel won't verify that the memory region starting at yyy
+ * actually contains usable RAM.
+ */
+static int __init early_parse_fbmem(char *p)
+{
+ fbmem_size = memparse(p, &p);
+ if (*p == '@')
+ fbmem_start = memparse(p, &p);
+ return 0;
+}
+early_param("fbmem", early_parse_fbmem);
+
+static inline void __init resource_init(void)
+{
+ struct tag_mem_range *region;
+
+ kernel_code.start = __pa(init_mm.start_code);
+ kernel_code.end = __pa(init_mm.end_code - 1);
+ kernel_data.start = __pa(init_mm.end_code);
+ kernel_data.end = __pa(init_mm.brk - 1);
+
+ for (region = mem_phys; region; region = region->next) {
+ struct resource *res;
+ unsigned long phys_start, phys_end;
+
+ if (region->size == 0)
+ continue;
+
+ phys_start = region->addr;
+ phys_end = phys_start + region->size - 1;
+
+ res = alloc_bootmem_low(sizeof(*res));
+ res->name = "System RAM";
+ res->start = phys_start;
+ res->end = phys_end;
+ res->flags = IORESOURCE_MEM | IORESOURCE_BUSY;
+
+ request_resource (&iomem_resource, res);
+
+ if (kernel_code.start >= res->start &&
+ kernel_code.end <= res->end)
+ request_resource (res, &kernel_code);
+ if (kernel_data.start >= res->start &&
+ kernel_data.end <= res->end)
+ request_resource (res, &kernel_data);
+ }
+}
+
+static int __init parse_tag_core(struct tag *tag)
+{
+ if (tag->hdr.size > 2) {
+ if ((tag->u.core.flags & 1) == 0)
+ root_mountflags &= ~MS_RDONLY;
+ ROOT_DEV = new_decode_dev(tag->u.core.rootdev);
+ }
+ return 0;
+}
+__tagtable(ATAG_CORE, parse_tag_core);
+
+static int __init parse_tag_mem_range(struct tag *tag,
+ struct tag_mem_range **root)
+{
+ struct tag_mem_range *cur, **pprev;
+ struct tag_mem_range *new;
+
+ /*
+ * Ignore zero-sized entries. If we're running standalone, the
+ * SDRAM code may emit such entries if something goes
+ * wrong...
+ */
+ if (tag->u.mem_range.size == 0)
+ return 0;
+
+ /*
+ * Copy the data so the bootmem init code doesn't need to care
+ * about it.
+ */
+ if (mem_range_next_free >=
+ (sizeof(mem_range_cache) / sizeof(mem_range_cache[0])))
+ panic("Physical memory map too complex!\n");
+
+ new = &mem_range_cache[mem_range_next_free++];
+ *new = tag->u.mem_range;
+
+ pprev = root;
+ cur = *root;
+ while (cur) {
+ pprev = &cur->next;
+ cur = cur->next;
+ }
+
+ *pprev = new;
+ new->next = NULL;
+
+ return 0;
+}
+
+static int __init parse_tag_mem(struct tag *tag)
+{
+ return parse_tag_mem_range(tag, &mem_phys);
+}
+__tagtable(ATAG_MEM, parse_tag_mem);
+
+static int __init parse_tag_cmdline(struct tag *tag)
+{
+ strlcpy(saved_command_line, tag->u.cmdline.cmdline, COMMAND_LINE_SIZE);
+ return 0;
+}
+__tagtable(ATAG_CMDLINE, parse_tag_cmdline);
+
+static int __init parse_tag_rdimg(struct tag *tag)
+{
+ return parse_tag_mem_range(tag, &mem_ramdisk);
+}
+__tagtable(ATAG_RDIMG, parse_tag_rdimg);
+
+static int __init parse_tag_clock(struct tag *tag)
+{
+ /*
+ * We'll figure out the clocks by peeking at the system
+ * manager regs directly.
+ */
+ return 0;
+}
+__tagtable(ATAG_CLOCK, parse_tag_clock);
+
+static int __init parse_tag_rsvd_mem(struct tag *tag)
+{
+ return parse_tag_mem_range(tag, &mem_reserved);
+}
+__tagtable(ATAG_RSVD_MEM, parse_tag_rsvd_mem);
+
+static int __init parse_tag_ethernet(struct tag *tag)
+{
+#if 0
+ const struct platform_device *pdev;
+
+ /*
+ * We really need a bus type that supports "classes"...this
+ * will do for now (until we must handle other kinds of
+ * ethernet controllers)
+ */
+ pdev = platform_get_device("macb", tag->u.ethernet.mac_index);
+ if (pdev && pdev->dev.platform_data) {
+ struct eth_platform_data *data = pdev->dev.platform_data;
+
+ data->valid = 1;
+ data->mii_phy_addr = tag->u.ethernet.mii_phy_addr;
+ memcpy(data->hw_addr, tag->u.ethernet.hw_address,
+ sizeof(data->hw_addr));
+ }
+#endif
+ return 0;
+}
+__tagtable(ATAG_ETHERNET, parse_tag_ethernet);
+
+/*
+ * Scan the tag table for this tag, and call its parse function. The
+ * tag table is built by the linker from all the __tagtable
+ * declarations.
+ */
+static int __init parse_tag(struct tag *tag)
+{
+ extern struct tagtable __tagtable_begin, __tagtable_end;
+ struct tagtable *t;
+
+ for (t = &__tagtable_begin; t < &__tagtable_end; t++)
+ if (tag->hdr.tag == t->tag) {
+ t->parse(tag);
+ break;
+ }
+
+ return t < &__tagtable_end;
+}
+
+/*
+ * Parse all tags in the list we got from the boot loader
+ */
+static void __init parse_tags(struct tag *t)
+{
+ for (; t->hdr.tag != ATAG_NONE; t = tag_next(t))
+ if (!parse_tag(t))
+ printk(KERN_WARNING
+ "Ignoring unrecognised tag 0x%08x\n",
+ t->hdr.tag);
+}
+
+void __init setup_arch (char **cmdline_p)
+{
+ struct clk *cpu_clk;
+
+ parse_tags(bootloader_tags);
+
+ setup_processor();
+ setup_platform();
+
+ cpu_clk = clk_get(NULL, "cpu");
+ if (IS_ERR(cpu_clk)) {
+ printk(KERN_WARNING "Warning: Unable to get CPU clock\n");
+ } else {
+ unsigned long cpu_hz = clk_get_rate(cpu_clk);
+
+ /*
+ * Well, duh, but it's probably a good idea to
+ * increment the use count.
+ */
+ clk_enable(cpu_clk);
+
+ boot_cpu_data.clk = cpu_clk;
+ boot_cpu_data.loops_per_jiffy = cpu_hz * 4;
+ printk("CPU: Running at %lu.%03lu MHz\n",
+ ((cpu_hz + 500) / 1000) / 1000,
+ ((cpu_hz + 500) / 1000) % 1000);
+ }
+
+ init_mm.start_code = (unsigned long) &_text;
+ init_mm.end_code = (unsigned long) &_etext;
+ init_mm.end_data = (unsigned long) &_edata;
+ init_mm.brk = (unsigned long) &_end;
+
+ strlcpy(command_line, saved_command_line, COMMAND_LINE_SIZE);
+ *cmdline_p = command_line;
+ parse_early_param();
+
+ setup_bootmem();
+
+ board_setup_fbmem(fbmem_start, fbmem_size);
+
+#ifdef CONFIG_VT
+ conswitchp = &dummy_con;
+#endif
+
+ paging_init();
+
+ resource_init();
+}
diff --git a/arch/avr32/kernel/signal.c b/arch/avr32/kernel/signal.c
new file mode 100644
index 00000000000..33096651c24
--- /dev/null
+++ b/arch/avr32/kernel/signal.c
@@ -0,0 +1,328 @@
+/*
+ * Copyright (C) 2004-2006 Atmel Corporation
+ *
+ * Based on linux/arch/sh/kernel/signal.c
+ * Copyright (C) 1999, 2000 Niibe Yutaka & Kaz Kojima
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/errno.h>
+#include <linux/ptrace.h>
+#include <linux/unistd.h>
+#include <linux/suspend.h>
+
+#include <asm/uaccess.h>
+#include <asm/ucontext.h>
+
+#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
+
+asmlinkage int sys_sigaltstack(const stack_t __user *uss, stack_t __user *uoss,
+ struct pt_regs *regs)
+{
+ return do_sigaltstack(uss, uoss, regs->sp);
+}
+
+struct rt_sigframe
+{
+ struct siginfo info;
+ struct ucontext uc;
+ unsigned long retcode;
+};
+
+static int
+restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc)
+{
+ int err = 0;
+
+#define COPY(x) err |= __get_user(regs->x, &sc->x)
+ COPY(sr);
+ COPY(pc);
+ COPY(lr);
+ COPY(sp);
+ COPY(r12);
+ COPY(r11);
+ COPY(r10);
+ COPY(r9);
+ COPY(r8);
+ COPY(r7);
+ COPY(r6);
+ COPY(r5);
+ COPY(r4);
+ COPY(r3);
+ COPY(r2);
+ COPY(r1);
+ COPY(r0);
+#undef COPY
+
+ /*
+ * Don't allow anyone to pretend they're running in supervisor
+ * mode or something...
+ */
+ err |= !valid_user_regs(regs);
+
+ return err;
+}
+
+
+asmlinkage int sys_rt_sigreturn(struct pt_regs *regs)
+{
+ struct rt_sigframe __user *frame;
+ sigset_t set;
+
+ frame = (struct rt_sigframe __user *)regs->sp;
+ pr_debug("SIG return: frame = %p\n", frame);
+
+ if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
+ goto badframe;
+
+ if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set)))
+ goto badframe;
+
+ sigdelsetmask(&set, ~_BLOCKABLE);
+ spin_lock_irq(&current->sighand->siglock);
+ current->blocked = set;
+ recalc_sigpending();
+ spin_unlock_irq(&current->sighand->siglock);
+
+ if (restore_sigcontext(regs, &frame->uc.uc_mcontext))
+ goto badframe;
+
+ pr_debug("Context restored: pc = %08lx, lr = %08lx, sp = %08lx\n",
+ regs->pc, regs->lr, regs->sp);
+
+ return regs->r12;
+
+badframe:
+ force_sig(SIGSEGV, current);
+ return 0;
+}
+
+static int
+setup_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs)
+{
+ int err = 0;
+
+#define COPY(x) err |= __put_user(regs->x, &sc->x)
+ COPY(sr);
+ COPY(pc);
+ COPY(lr);
+ COPY(sp);
+ COPY(r12);
+ COPY(r11);
+ COPY(r10);
+ COPY(r9);
+ COPY(r8);
+ COPY(r7);
+ COPY(r6);
+ COPY(r5);
+ COPY(r4);
+ COPY(r3);
+ COPY(r2);
+ COPY(r1);
+ COPY(r0);
+#undef COPY
+
+ return err;
+}
+
+static inline void __user *
+get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, int framesize)
+{
+ unsigned long sp = regs->sp;
+
+ if ((ka->sa.sa_flags & SA_ONSTACK) && !sas_ss_flags(sp))
+ sp = current->sas_ss_sp + current->sas_ss_size;
+
+ return (void __user *)((sp - framesize) & ~3);
+}
+
+static int
+setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
+ sigset_t *set, struct pt_regs *regs)
+{
+ struct rt_sigframe __user *frame;
+ int err = 0;
+
+ frame = get_sigframe(ka, regs, sizeof(*frame));
+ err = -EFAULT;
+ if (!access_ok(VERIFY_WRITE, frame, sizeof (*frame)))
+ goto out;
+
+ /*
+ * Set up the return code:
+ *
+ * mov r8, __NR_rt_sigreturn
+ * scall
+ *
+ * Note: This will blow up since we're using a non-executable
+ * stack. Better use SA_RESTORER.
+ */
+#if __NR_rt_sigreturn > 127
+# error __NR_rt_sigreturn must be < 127 to fit in a short mov
+#endif
+ err = __put_user(0x3008d733 | (__NR_rt_sigreturn << 20),
+ &frame->retcode);
+
+ err |= copy_siginfo_to_user(&frame->info, info);
+
+ /* Set up the ucontext */
+ err |= __put_user(0, &frame->uc.uc_flags);
+ err |= __put_user(NULL, &frame->uc.uc_link);
+ err |= __put_user((void __user *)current->sas_ss_sp,
+ &frame->uc.uc_stack.ss_sp);
+ err |= __put_user(sas_ss_flags(regs->sp),
+ &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, regs);
+ err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set));
+
+ if (err)
+ goto out;
+
+ regs->r12 = sig;
+ regs->r11 = (unsigned long) &frame->info;
+ regs->r10 = (unsigned long) &frame->uc;
+ regs->sp = (unsigned long) frame;
+ if (ka->sa.sa_flags & SA_RESTORER)
+ regs->lr = (unsigned long)ka->sa.sa_restorer;
+ else {
+ printk(KERN_NOTICE "[%s:%d] did not set SA_RESTORER\n",
+ current->comm, current->pid);
+ regs->lr = (unsigned long) &frame->retcode;
+ }
+
+ pr_debug("SIG deliver [%s:%d]: sig=%d sp=0x%lx pc=0x%lx->0x%p lr=0x%lx\n",
+ current->comm, current->pid, sig, regs->sp,
+ regs->pc, ka->sa.sa_handler, regs->lr);
+
+ regs->pc = (unsigned long) ka->sa.sa_handler;
+
+out:
+ return err;
+}
+
+static inline void restart_syscall(struct pt_regs *regs)
+{
+ if (regs->r12 == -ERESTART_RESTARTBLOCK)
+ regs->r8 = __NR_restart_syscall;
+ else
+ regs->r12 = regs->r12_orig;
+ regs->pc -= 2;
+}
+
+static inline void
+handle_signal(unsigned long sig, struct k_sigaction *ka, siginfo_t *info,
+ sigset_t *oldset, struct pt_regs *regs, int syscall)
+{
+ int ret;
+
+ /*
+ * Set up the stack frame
+ */
+ ret = setup_rt_frame(sig, ka, info, oldset, regs);
+
+ /*
+ * Check that the resulting registers are sane
+ */
+ ret |= !valid_user_regs(regs);
+
+ /*
+ * Block the signal if we were unsuccessful.
+ */
+ if (ret != 0 || !(ka->sa.sa_flags & SA_NODEFER)) {
+ spin_lock_irq(&current->sighand->siglock);
+ sigorsets(&current->blocked, &current->blocked,
+ &ka->sa.sa_mask);
+ sigaddset(&current->blocked, sig);
+ recalc_sigpending();
+ spin_unlock_irq(&current->sighand->siglock);
+ }
+
+ if (ret == 0)
+ return;
+
+ force_sigsegv(sig, 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.
+ */
+int do_signal(struct pt_regs *regs, sigset_t *oldset, int syscall)
+{
+ siginfo_t info;
+ int signr;
+ struct k_sigaction ka;
+
+ /*
+ * We want the common case to go fast, which is why we may in
+ * certain cases get here from kernel mode. Just return
+ * without doing anything if so.
+ */
+ if (!user_mode(regs))
+ return 0;
+
+ if (try_to_freeze()) {
+ signr = 0;
+ if (!signal_pending(current))
+ goto no_signal;
+ }
+
+ if (test_thread_flag(TIF_RESTORE_SIGMASK))
+ oldset = &current->saved_sigmask;
+ else if (!oldset)
+ oldset = &current->blocked;
+
+ signr = get_signal_to_deliver(&info, &ka, regs, NULL);
+no_signal:
+ if (syscall) {
+ switch (regs->r12) {
+ case -ERESTART_RESTARTBLOCK:
+ case -ERESTARTNOHAND:
+ if (signr > 0) {
+ regs->r12 = -EINTR;
+ break;
+ }
+ /* fall through */
+ case -ERESTARTSYS:
+ if (signr > 0 && !(ka.sa.sa_flags & SA_RESTART)) {
+ regs->r12 = -EINTR;
+ break;
+ }
+ /* fall through */
+ case -ERESTARTNOINTR:
+ restart_syscall(regs);
+ }
+ }
+
+ if (signr == 0) {
+ /* No signal to deliver -- put the saved sigmask back */
+ if (test_thread_flag(TIF_RESTORE_SIGMASK)) {
+ clear_thread_flag(TIF_RESTORE_SIGMASK);
+ sigprocmask(SIG_SETMASK, &current->saved_sigmask, NULL);
+ }
+ return 0;
+ }
+
+ handle_signal(signr, &ka, &info, oldset, regs, syscall);
+ return 1;
+}
+
+asmlinkage void do_notify_resume(struct pt_regs *regs, struct thread_info *ti)
+{
+ int syscall = 0;
+
+ if ((sysreg_read(SR) & MODE_MASK) == MODE_SUPERVISOR)
+ syscall = 1;
+
+ if (ti->flags & (_TIF_SIGPENDING | _TIF_RESTORE_SIGMASK))
+ do_signal(regs, &current->blocked, syscall);
+}
diff --git a/arch/avr32/kernel/switch_to.S b/arch/avr32/kernel/switch_to.S
new file mode 100644
index 00000000000..a48d046723c
--- /dev/null
+++ b/arch/avr32/kernel/switch_to.S
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2004-2006 Atmel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <asm/sysreg.h>
+
+ .text
+ .global __switch_to
+ .type __switch_to, @function
+
+ /* Switch thread context from "prev" to "next", returning "last"
+ * r12 : prev
+ * r11 : &prev->thread + 1
+ * r10 : &next->thread
+ */
+__switch_to:
+ stm --r11, r0,r1,r2,r3,r4,r5,r6,r7,sp,lr
+ mfsr r9, SYSREG_SR
+ st.w --r11, r9
+ ld.w r8, r10++
+ /*
+ * schedule() may have been called from a mode with a different
+ * set of registers. Make sure we don't lose anything here.
+ */
+ pushm r10,r12
+ mtsr SYSREG_SR, r8
+ frs /* flush the return stack */
+ sub pc, -2 /* flush the pipeline */
+ popm r10,r12
+ ldm r10++, r0,r1,r2,r3,r4,r5,r6,r7,sp,pc
+ .size __switch_to, . - __switch_to
diff --git a/arch/avr32/kernel/sys_avr32.c b/arch/avr32/kernel/sys_avr32.c
new file mode 100644
index 00000000000..6ec5693da44
--- /dev/null
+++ b/arch/avr32/kernel/sys_avr32.c
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2004-2006 Atmel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/file.h>
+#include <linux/mm.h>
+#include <linux/unistd.h>
+
+#include <asm/mman.h>
+#include <asm/uaccess.h>
+
+asmlinkage int sys_pipe(unsigned long __user *filedes)
+{
+ int fd[2];
+ int error;
+
+ error = do_pipe(fd);
+ if (!error) {
+ if (copy_to_user(filedes, fd, sizeof(fd)))
+ error = -EFAULT;
+ }
+ return error;
+}
+
+asmlinkage long sys_mmap2(unsigned long addr, unsigned long len,
+ unsigned long prot, unsigned long flags,
+ unsigned long fd, off_t offset)
+{
+ int error = -EBADF;
+ struct file *file = NULL;
+
+ flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
+ if (!(flags & MAP_ANONYMOUS)) {
+ file = fget(fd);
+ if (!file)
+ return error;
+ }
+
+ down_write(&current->mm->mmap_sem);
+ error = do_mmap_pgoff(file, addr, len, prot, flags, offset);
+ up_write(&current->mm->mmap_sem);
+
+ if (file)
+ fput(file);
+ return error;
+}
diff --git a/arch/avr32/kernel/syscall-stubs.S b/arch/avr32/kernel/syscall-stubs.S
new file mode 100644
index 00000000000..7589a9b426c
--- /dev/null
+++ b/arch/avr32/kernel/syscall-stubs.S
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2005-2006 Atmel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+/*
+ * Stubs for syscalls that require access to pt_regs or that take more
+ * than five parameters.
+ */
+
+#define ARG6 r3
+
+ .text
+ .global __sys_rt_sigsuspend
+ .type __sys_rt_sigsuspend,@function
+__sys_rt_sigsuspend:
+ mov r10, sp
+ rjmp sys_rt_sigsuspend
+
+ .global __sys_sigaltstack
+ .type __sys_sigaltstack,@function
+__sys_sigaltstack:
+ mov r10, sp
+ rjmp sys_sigaltstack
+
+ .global __sys_rt_sigreturn
+ .type __sys_rt_sigreturn,@function
+__sys_rt_sigreturn:
+ mov r12, sp
+ rjmp sys_rt_sigreturn
+
+ .global __sys_fork
+ .type __sys_fork,@function
+__sys_fork:
+ mov r12, sp
+ rjmp sys_fork
+
+ .global __sys_clone
+ .type __sys_clone,@function
+__sys_clone:
+ mov r8, sp
+ rjmp sys_clone
+
+ .global __sys_vfork
+ .type __sys_vfork,@function
+__sys_vfork:
+ mov r12, sp
+ rjmp sys_vfork
+
+ .global __sys_execve
+ .type __sys_execve,@function
+__sys_execve:
+ mov r9, sp
+ rjmp sys_execve
+
+ .global __sys_mmap2
+ .type __sys_mmap2,@function
+__sys_mmap2:
+ pushm lr
+ st.w --sp, ARG6
+ rcall sys_mmap2
+ sub sp, -4
+ popm pc
+
+ .global __sys_sendto
+ .type __sys_sendto,@function
+__sys_sendto:
+ pushm lr
+ st.w --sp, ARG6
+ rcall sys_sendto
+ sub sp, -4
+ popm pc
+
+ .global __sys_recvfrom
+ .type __sys_recvfrom,@function
+__sys_recvfrom:
+ pushm lr
+ st.w --sp, ARG6
+ rcall sys_recvfrom
+ sub sp, -4
+ popm pc
+
+ .global __sys_pselect6
+ .type __sys_pselect6,@function
+__sys_pselect6:
+ pushm lr
+ st.w --sp, ARG6
+ rcall sys_pselect6
+ sub sp, -4
+ popm pc
+
+ .global __sys_splice
+ .type __sys_splice,@function
+__sys_splice:
+ pushm lr
+ st.w --sp, ARG6
+ rcall sys_splice
+ sub sp, -4
+ popm pc
diff --git a/arch/avr32/kernel/syscall_table.S b/arch/avr32/kernel/syscall_table.S
new file mode 100644
index 00000000000..63b206965d0
--- /dev/null
+++ b/arch/avr32/kernel/syscall_table.S
@@ -0,0 +1,289 @@
+/*
+ * AVR32 system call table
+ *
+ * Copyright (C) 2004-2006 Atmel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#if !defined(CONFIG_NFSD) && !defined(CONFIG_NFSD_MODULE)
+#define sys_nfsservctl sys_ni_syscall
+#endif
+
+#if !defined(CONFIG_SYSV_IPC)
+# define sys_ipc sys_ni_syscall
+#endif
+
+ .section .rodata,"a",@progbits
+ .type sys_call_table,@object
+ .global sys_call_table
+ .align 2
+sys_call_table:
+ .long sys_restart_syscall
+ .long sys_exit
+ .long __sys_fork
+ .long sys_read
+ .long sys_write
+ .long sys_open /* 5 */
+ .long sys_close
+ .long sys_umask
+ .long sys_creat
+ .long sys_link
+ .long sys_unlink /* 10 */
+ .long __sys_execve
+ .long sys_chdir
+ .long sys_time
+ .long sys_mknod
+ .long sys_chmod /* 15 */
+ .long sys_chown
+ .long sys_lchown
+ .long sys_lseek
+ .long sys_llseek
+ .long sys_getpid /* 20 */
+ .long sys_mount
+ .long sys_umount
+ .long sys_setuid
+ .long sys_getuid
+ .long sys_stime /* 25 */
+ .long sys_ptrace
+ .long sys_alarm
+ .long sys_pause
+ .long sys_utime
+ .long sys_newstat /* 30 */
+ .long sys_newfstat
+ .long sys_newlstat
+ .long sys_access
+ .long sys_chroot
+ .long sys_sync /* 35 */
+ .long sys_fsync
+ .long sys_kill
+ .long sys_rename
+ .long sys_mkdir
+ .long sys_rmdir /* 40 */
+ .long sys_dup
+ .long sys_pipe
+ .long sys_times
+ .long __sys_clone
+ .long sys_brk /* 45 */
+ .long sys_setgid
+ .long sys_getgid
+ .long sys_getcwd
+ .long sys_geteuid
+ .long sys_getegid /* 50 */
+ .long sys_acct
+ .long sys_setfsuid
+ .long sys_setfsgid
+ .long sys_ioctl
+ .long sys_fcntl /* 55 */
+ .long sys_setpgid
+ .long sys_mremap
+ .long sys_setresuid
+ .long sys_getresuid
+ .long sys_setreuid /* 60 */
+ .long sys_setregid
+ .long sys_ustat
+ .long sys_dup2
+ .long sys_getppid
+ .long sys_getpgrp /* 65 */
+ .long sys_setsid
+ .long sys_rt_sigaction
+ .long __sys_rt_sigreturn
+ .long sys_rt_sigprocmask
+ .long sys_rt_sigpending /* 70 */
+ .long sys_rt_sigtimedwait
+ .long sys_rt_sigqueueinfo
+ .long __sys_rt_sigsuspend
+ .long sys_sethostname
+ .long sys_setrlimit /* 75 */
+ .long sys_getrlimit
+ .long sys_getrusage
+ .long sys_gettimeofday
+ .long sys_settimeofday
+ .long sys_getgroups /* 80 */
+ .long sys_setgroups
+ .long sys_select
+ .long sys_symlink
+ .long sys_fchdir
+ .long sys_readlink /* 85 */
+ .long sys_pread64
+ .long sys_pwrite64
+ .long sys_swapon
+ .long sys_reboot
+ .long __sys_mmap2 /* 90 */
+ .long sys_munmap
+ .long sys_truncate
+ .long sys_ftruncate
+ .long sys_fchmod
+ .long sys_fchown /* 95 */
+ .long sys_getpriority
+ .long sys_setpriority
+ .long sys_wait4
+ .long sys_statfs
+ .long sys_fstatfs /* 100 */
+ .long sys_vhangup
+ .long __sys_sigaltstack
+ .long sys_syslog
+ .long sys_setitimer
+ .long sys_getitimer /* 105 */
+ .long sys_swapoff
+ .long sys_sysinfo
+ .long sys_ipc
+ .long sys_sendfile
+ .long sys_setdomainname /* 110 */
+ .long sys_newuname
+ .long sys_adjtimex
+ .long sys_mprotect
+ .long __sys_vfork
+ .long sys_init_module /* 115 */
+ .long sys_delete_module
+ .long sys_quotactl
+ .long sys_getpgid
+ .long sys_bdflush
+ .long sys_sysfs /* 120 */
+ .long sys_personality
+ .long sys_ni_syscall /* reserved for afs_syscall */
+ .long sys_getdents
+ .long sys_flock
+ .long sys_msync /* 125 */
+ .long sys_readv
+ .long sys_writev
+ .long sys_getsid
+ .long sys_fdatasync
+ .long sys_sysctl /* 130 */
+ .long sys_mlock
+ .long sys_munlock
+ .long sys_mlockall
+ .long sys_munlockall
+ .long sys_sched_setparam /* 135 */
+ .long sys_sched_getparam
+ .long sys_sched_setscheduler
+ .long sys_sched_getscheduler
+ .long sys_sched_yield
+ .long sys_sched_get_priority_max /* 140 */
+ .long sys_sched_get_priority_min
+ .long sys_sched_rr_get_interval
+ .long sys_nanosleep
+ .long sys_poll
+ .long sys_nfsservctl /* 145 */
+ .long sys_setresgid
+ .long sys_getresgid
+ .long sys_prctl
+ .long sys_socket
+ .long sys_bind /* 150 */
+ .long sys_connect
+ .long sys_listen
+ .long sys_accept
+ .long sys_getsockname
+ .long sys_getpeername /* 155 */
+ .long sys_socketpair
+ .long sys_send
+ .long sys_recv
+ .long __sys_sendto
+ .long __sys_recvfrom /* 160 */
+ .long sys_shutdown
+ .long sys_setsockopt
+ .long sys_getsockopt
+ .long sys_sendmsg
+ .long sys_recvmsg /* 165 */
+ .long sys_truncate64
+ .long sys_ftruncate64
+ .long sys_stat64
+ .long sys_lstat64
+ .long sys_fstat64 /* 170 */
+ .long sys_pivot_root
+ .long sys_mincore
+ .long sys_madvise
+ .long sys_getdents64
+ .long sys_fcntl64 /* 175 */
+ .long sys_gettid
+ .long sys_readahead
+ .long sys_setxattr
+ .long sys_lsetxattr
+ .long sys_fsetxattr /* 180 */
+ .long sys_getxattr
+ .long sys_lgetxattr
+ .long sys_fgetxattr
+ .long sys_listxattr
+ .long sys_llistxattr /* 185 */
+ .long sys_flistxattr
+ .long sys_removexattr
+ .long sys_lremovexattr
+ .long sys_fremovexattr
+ .long sys_tkill /* 190 */
+ .long sys_sendfile64
+ .long sys_futex
+ .long sys_sched_setaffinity
+ .long sys_sched_getaffinity
+ .long sys_capget /* 195 */
+ .long sys_capset
+ .long sys_io_setup
+ .long sys_io_destroy
+ .long sys_io_getevents
+ .long sys_io_submit /* 200 */
+ .long sys_io_cancel
+ .long sys_fadvise64
+ .long sys_exit_group
+ .long sys_lookup_dcookie
+ .long sys_epoll_create /* 205 */
+ .long sys_epoll_ctl
+ .long sys_epoll_wait
+ .long sys_remap_file_pages
+ .long sys_set_tid_address
+ .long sys_timer_create /* 210 */
+ .long sys_timer_settime
+ .long sys_timer_gettime
+ .long sys_timer_getoverrun
+ .long sys_timer_delete
+ .long sys_clock_settime /* 215 */
+ .long sys_clock_gettime
+ .long sys_clock_getres
+ .long sys_clock_nanosleep
+ .long sys_statfs64
+ .long sys_fstatfs64 /* 220 */
+ .long sys_tgkill
+ .long sys_ni_syscall /* reserved for TUX */
+ .long sys_utimes
+ .long sys_fadvise64_64
+ .long sys_cacheflush /* 225 */
+ .long sys_ni_syscall /* sys_vserver */
+ .long sys_mq_open
+ .long sys_mq_unlink
+ .long sys_mq_timedsend
+ .long sys_mq_timedreceive /* 230 */
+ .long sys_mq_notify
+ .long sys_mq_getsetattr
+ .long sys_kexec_load
+ .long sys_waitid
+ .long sys_add_key /* 235 */
+ .long sys_request_key
+ .long sys_keyctl
+ .long sys_ioprio_set
+ .long sys_ioprio_get
+ .long sys_inotify_init /* 240 */
+ .long sys_inotify_add_watch
+ .long sys_inotify_rm_watch
+ .long sys_openat
+ .long sys_mkdirat
+ .long sys_mknodat /* 245 */
+ .long sys_fchownat
+ .long sys_futimesat
+ .long sys_fstatat64
+ .long sys_unlinkat
+ .long sys_renameat /* 250 */
+ .long sys_linkat
+ .long sys_symlinkat
+ .long sys_readlinkat
+ .long sys_fchmodat
+ .long sys_faccessat /* 255 */
+ .long __sys_pselect6
+ .long sys_ppoll
+ .long sys_unshare
+ .long sys_set_robust_list
+ .long sys_get_robust_list /* 260 */
+ .long __sys_splice
+ .long sys_sync_file_range
+ .long sys_tee
+ .long sys_vmsplice
+ .long sys_ni_syscall /* r8 is saturated at nr_syscalls */
diff --git a/arch/avr32/kernel/time.c b/arch/avr32/kernel/time.c
new file mode 100644
index 00000000000..b0e6b5855a3
--- /dev/null
+++ b/arch/avr32/kernel/time.c
@@ -0,0 +1,238 @@
+/*
+ * Copyright (C) 2004-2006 Atmel Corporation
+ *
+ * Based on MIPS implementation arch/mips/kernel/time.c
+ * Copyright 2001 MontaVista Software Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/clk.h>
+#include <linux/clocksource.h>
+#include <linux/time.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/kernel_stat.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/profile.h>
+#include <linux/sysdev.h>
+
+#include <asm/div64.h>
+#include <asm/sysreg.h>
+#include <asm/io.h>
+#include <asm/sections.h>
+
+static cycle_t read_cycle_count(void)
+{
+ return (cycle_t)sysreg_read(COUNT);
+}
+
+static struct clocksource clocksource_avr32 = {
+ .name = "avr32",
+ .rating = 350,
+ .read = read_cycle_count,
+ .mask = CLOCKSOURCE_MASK(32),
+ .shift = 16,
+ .is_continuous = 1,
+};
+
+/*
+ * By default we provide the null RTC ops
+ */
+static unsigned long null_rtc_get_time(void)
+{
+ return mktime(2004, 1, 1, 0, 0, 0);
+}
+
+static int null_rtc_set_time(unsigned long sec)
+{
+ return 0;
+}
+
+static unsigned long (*rtc_get_time)(void) = null_rtc_get_time;
+static int (*rtc_set_time)(unsigned long) = null_rtc_set_time;
+
+/* how many counter cycles in a jiffy? */
+static unsigned long cycles_per_jiffy;
+
+/* cycle counter value at the previous timer interrupt */
+static unsigned int timerhi, timerlo;
+
+/* the count value for the next timer interrupt */
+static unsigned int expirelo;
+
+static void avr32_timer_ack(void)
+{
+ unsigned int count;
+
+ /* Ack this timer interrupt and set the next one */
+ expirelo += cycles_per_jiffy;
+ if (expirelo == 0) {
+ printk(KERN_DEBUG "expirelo == 0\n");
+ sysreg_write(COMPARE, expirelo + 1);
+ } else {
+ sysreg_write(COMPARE, expirelo);
+ }
+
+ /* Check to see if we have missed any timer interrupts */
+ count = sysreg_read(COUNT);
+ if ((count - expirelo) < 0x7fffffff) {
+ expirelo = count + cycles_per_jiffy;
+ sysreg_write(COMPARE, expirelo);
+ }
+}
+
+static unsigned int avr32_hpt_read(void)
+{
+ return sysreg_read(COUNT);
+}
+
+/*
+ * Taken from MIPS c0_hpt_timer_init().
+ *
+ * Why is it so complicated, and what is "count"? My assumption is
+ * that `count' specifies the "reference cycle", i.e. the cycle since
+ * reset that should mean "zero". The reason COUNT is written twice is
+ * probably to make sure we don't get any timer interrupts while we
+ * are messing with the counter.
+ */
+static void avr32_hpt_init(unsigned int count)
+{
+ count = sysreg_read(COUNT) - count;
+ expirelo = (count / cycles_per_jiffy + 1) * cycles_per_jiffy;
+ sysreg_write(COUNT, expirelo - cycles_per_jiffy);
+ sysreg_write(COMPARE, expirelo);
+ sysreg_write(COUNT, count);
+}
+
+/*
+ * Scheduler clock - returns current time in nanosec units.
+ */
+unsigned long long sched_clock(void)
+{
+ /* There must be better ways...? */
+ return (unsigned long long)jiffies * (1000000000 / HZ);
+}
+
+/*
+ * local_timer_interrupt() does profiling and process accounting on a
+ * per-CPU basis.
+ *
+ * In UP mode, it is invoked from the (global) timer_interrupt.
+ */
+static void local_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ if (current->pid)
+ profile_tick(CPU_PROFILING, regs);
+ update_process_times(user_mode(regs));
+}
+
+static irqreturn_t
+timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ unsigned int count;
+
+ /* ack timer interrupt and try to set next interrupt */
+ count = avr32_hpt_read();
+ avr32_timer_ack();
+
+ /* Update timerhi/timerlo for intra-jiffy calibration */
+ timerhi += count < timerlo; /* Wrap around */
+ timerlo = count;
+
+ /*
+ * Call the generic timer interrupt handler
+ */
+ write_seqlock(&xtime_lock);
+ do_timer(regs);
+ write_sequnlock(&xtime_lock);
+
+ /*
+ * In UP mode, we call local_timer_interrupt() to do profiling
+ * and process accounting.
+ *
+ * SMP is not supported yet.
+ */
+ local_timer_interrupt(irq, dev_id, regs);
+
+ return IRQ_HANDLED;
+}
+
+static struct irqaction timer_irqaction = {
+ .handler = timer_interrupt,
+ .flags = IRQF_DISABLED,
+ .name = "timer",
+};
+
+void __init time_init(void)
+{
+ unsigned long mult, shift, count_hz;
+ int ret;
+
+ xtime.tv_sec = rtc_get_time();
+ xtime.tv_nsec = 0;
+
+ set_normalized_timespec(&wall_to_monotonic,
+ -xtime.tv_sec, -xtime.tv_nsec);
+
+ printk("Before time_init: count=%08lx, compare=%08lx\n",
+ (unsigned long)sysreg_read(COUNT),
+ (unsigned long)sysreg_read(COMPARE));
+
+ count_hz = clk_get_rate(boot_cpu_data.clk);
+ shift = clocksource_avr32.shift;
+ mult = clocksource_hz2mult(count_hz, shift);
+ clocksource_avr32.mult = mult;
+
+ printk("Cycle counter: mult=%lu, shift=%lu\n", mult, shift);
+
+ {
+ u64 tmp;
+
+ tmp = TICK_NSEC;
+ tmp <<= shift;
+ tmp += mult / 2;
+ do_div(tmp, mult);
+
+ cycles_per_jiffy = tmp;
+ }
+
+ /* This sets up the high precision timer for the first interrupt. */
+ avr32_hpt_init(avr32_hpt_read());
+
+ printk("After time_init: count=%08lx, compare=%08lx\n",
+ (unsigned long)sysreg_read(COUNT),
+ (unsigned long)sysreg_read(COMPARE));
+
+ ret = clocksource_register(&clocksource_avr32);
+ if (ret)
+ printk(KERN_ERR
+ "timer: could not register clocksource: %d\n", ret);
+
+ ret = setup_irq(0, &timer_irqaction);
+ if (ret)
+ printk("timer: could not request IRQ 0: %d\n", ret);
+}
+
+static struct sysdev_class timer_class = {
+ set_kset_name("timer"),
+};
+
+static struct sys_device timer_device = {
+ .id = 0,
+ .cls = &timer_class,
+};
+
+static int __init init_timer_sysfs(void)
+{
+ int err = sysdev_class_register(&timer_class);
+ if (!err)
+ err = sysdev_register(&timer_device);
+ return err;
+}
+
+device_initcall(init_timer_sysfs);
diff --git a/arch/avr32/kernel/traps.c b/arch/avr32/kernel/traps.c
new file mode 100644
index 00000000000..7e803f4d7a1
--- /dev/null
+++ b/arch/avr32/kernel/traps.c
@@ -0,0 +1,425 @@
+/*
+ * Copyright (C) 2004-2006 Atmel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#undef DEBUG
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kallsyms.h>
+#include <linux/notifier.h>
+
+#include <asm/traps.h>
+#include <asm/sysreg.h>
+#include <asm/addrspace.h>
+#include <asm/ocd.h>
+#include <asm/mmu_context.h>
+#include <asm/uaccess.h>
+
+static void dump_mem(const char *str, unsigned long bottom, unsigned long top)
+{
+ unsigned long p;
+ int i;
+
+ printk("%s(0x%08lx to 0x%08lx)\n", str, bottom, top);
+
+ for (p = bottom & ~31; p < top; ) {
+ printk("%04lx: ", p & 0xffff);
+
+ for (i = 0; i < 8; i++, p += 4) {
+ unsigned int val;
+
+ if (p < bottom || p >= top)
+ printk(" ");
+ else {
+ if (__get_user(val, (unsigned int __user *)p)) {
+ printk("\n");
+ goto out;
+ }
+ printk("%08x ", val);
+ }
+ }
+ printk("\n");
+ }
+
+out:
+ return;
+}
+
+#ifdef CONFIG_FRAME_POINTER
+static inline void __show_trace(struct task_struct *tsk, unsigned long *sp,
+ struct pt_regs *regs)
+{
+ unsigned long __user *fp;
+ unsigned long __user *last_fp = NULL;
+
+ if (regs) {
+ fp = (unsigned long __user *)regs->r7;
+ } else if (tsk == current) {
+ register unsigned long __user *real_fp __asm__("r7");
+ fp = real_fp;
+ } else {
+ fp = (unsigned long __user *)tsk->thread.cpu_context.r7;
+ }
+
+ /*
+ * Walk the stack until (a) we get an exception, (b) the frame
+ * pointer becomes zero, or (c) the frame pointer gets stuck
+ * at the same value.
+ */
+ while (fp && fp != last_fp) {
+ unsigned long lr, new_fp = 0;
+
+ last_fp = fp;
+ if (__get_user(lr, fp))
+ break;
+ if (fp && __get_user(new_fp, fp + 1))
+ break;
+ fp = (unsigned long __user *)new_fp;
+
+ printk(" [<%08lx>] ", lr);
+ print_symbol("%s\n", lr);
+ }
+ printk("\n");
+}
+#else
+static inline void __show_trace(struct task_struct *tsk, unsigned long *sp,
+ struct pt_regs *regs)
+{
+ unsigned long addr;
+
+ while (!kstack_end(sp)) {
+ addr = *sp++;
+ if (kernel_text_address(addr)) {
+ printk(" [<%08lx>] ", addr);
+ print_symbol("%s\n", addr);
+ }
+ }
+}
+#endif
+
+void show_trace(struct task_struct *tsk, unsigned long *sp,
+ struct pt_regs *regs)
+{
+ if (regs &&
+ (((regs->sr & MODE_MASK) == MODE_EXCEPTION) ||
+ ((regs->sr & MODE_MASK) == MODE_USER)))
+ return;
+
+ printk ("Call trace:");
+#ifdef CONFIG_KALLSYMS
+ printk("\n");
+#endif
+
+ __show_trace(tsk, sp, regs);
+ printk("\n");
+}
+
+void show_stack(struct task_struct *tsk, unsigned long *sp)
+{
+ unsigned long stack;
+
+ if (!tsk)
+ tsk = current;
+ if (sp == 0) {
+ if (tsk == current) {
+ register unsigned long *real_sp __asm__("sp");
+ sp = real_sp;
+ } else {
+ sp = (unsigned long *)tsk->thread.cpu_context.ksp;
+ }
+ }
+
+ stack = (unsigned long)sp;
+ dump_mem("Stack: ", stack,
+ THREAD_SIZE + (unsigned long)tsk->thread_info);
+ show_trace(tsk, sp, NULL);
+}
+
+void dump_stack(void)
+{
+ show_stack(NULL, NULL);
+}
+EXPORT_SYMBOL(dump_stack);
+
+ATOMIC_NOTIFIER_HEAD(avr32_die_chain);
+
+int register_die_notifier(struct notifier_block *nb)
+{
+ pr_debug("register_die_notifier: %p\n", nb);
+
+ return atomic_notifier_chain_register(&avr32_die_chain, nb);
+}
+EXPORT_SYMBOL(register_die_notifier);
+
+int unregister_die_notifier(struct notifier_block *nb)
+{
+ return atomic_notifier_chain_unregister(&avr32_die_chain, nb);
+}
+EXPORT_SYMBOL(unregister_die_notifier);
+
+static DEFINE_SPINLOCK(die_lock);
+
+void __die(const char *str, struct pt_regs *regs, unsigned long err,
+ const char *file, const char *func, unsigned long line)
+{
+ struct task_struct *tsk = current;
+ static int die_counter;
+
+ console_verbose();
+ spin_lock_irq(&die_lock);
+ bust_spinlocks(1);
+
+ printk(KERN_ALERT "%s", str);
+ if (file && func)
+ printk(" in %s:%s, line %ld", file, func, line);
+ printk("[#%d]:\n", ++die_counter);
+ print_modules();
+ show_regs(regs);
+ printk("Process %s (pid: %d, stack limit = 0x%p)\n",
+ tsk->comm, tsk->pid, tsk->thread_info + 1);
+
+ if (!user_mode(regs) || in_interrupt()) {
+ dump_mem("Stack: ", regs->sp,
+ THREAD_SIZE + (unsigned long)tsk->thread_info);
+ }
+
+ bust_spinlocks(0);
+ spin_unlock_irq(&die_lock);
+ do_exit(SIGSEGV);
+}
+
+void __die_if_kernel(const char *str, struct pt_regs *regs, unsigned long err,
+ const char *file, const char *func, unsigned long line)
+{
+ if (!user_mode(regs))
+ __die(str, regs, err, file, func, line);
+}
+
+asmlinkage void do_nmi(unsigned long ecr, struct pt_regs *regs)
+{
+#ifdef CONFIG_SUBARCH_AVR32B
+ /*
+ * The exception entry always saves RSR_EX. For NMI, this is
+ * wrong; it should be RSR_NMI
+ */
+ regs->sr = sysreg_read(RSR_NMI);
+#endif
+
+ printk("NMI taken!!!!\n");
+ die("NMI", regs, ecr);
+ BUG();
+}
+
+asmlinkage void do_critical_exception(unsigned long ecr, struct pt_regs *regs)
+{
+ printk("Unable to handle critical exception %lu at pc = %08lx!\n",
+ ecr, regs->pc);
+ die("Oops", regs, ecr);
+ BUG();
+}
+
+asmlinkage void do_address_exception(unsigned long ecr, struct pt_regs *regs)
+{
+ siginfo_t info;
+
+ die_if_kernel("Oops: Address exception in kernel mode", regs, ecr);
+
+#ifdef DEBUG
+ if (ecr == ECR_ADDR_ALIGN_X)
+ pr_debug("Instruction Address Exception at pc = %08lx\n",
+ regs->pc);
+ else if (ecr == ECR_ADDR_ALIGN_R)
+ pr_debug("Data Address Exception (Read) at pc = %08lx\n",
+ regs->pc);
+ else if (ecr == ECR_ADDR_ALIGN_W)
+ pr_debug("Data Address Exception (Write) at pc = %08lx\n",
+ regs->pc);
+ else
+ BUG();
+
+ show_regs(regs);
+#endif
+
+ info.si_signo = SIGBUS;
+ info.si_errno = 0;
+ info.si_code = BUS_ADRALN;
+ info.si_addr = (void __user *)regs->pc;
+
+ force_sig_info(SIGBUS, &info, current);
+}
+
+/* This way of handling undefined instructions is stolen from ARM */
+static LIST_HEAD(undef_hook);
+static spinlock_t undef_lock = SPIN_LOCK_UNLOCKED;
+
+void register_undef_hook(struct undef_hook *hook)
+{
+ spin_lock_irq(&undef_lock);
+ list_add(&hook->node, &undef_hook);
+ spin_unlock_irq(&undef_lock);
+}
+
+void unregister_undef_hook(struct undef_hook *hook)
+{
+ spin_lock_irq(&undef_lock);
+ list_del(&hook->node);
+ spin_unlock_irq(&undef_lock);
+}
+
+static int do_cop_absent(u32 insn)
+{
+ int cop_nr;
+ u32 cpucr;
+ if ( (insn & 0xfdf00000) == 0xf1900000 )
+ /* LDC0 */
+ cop_nr = 0;
+ else
+ cop_nr = (insn >> 13) & 0x7;
+
+ /* Try enabling the coprocessor */
+ cpucr = sysreg_read(CPUCR);
+ cpucr |= (1 << (24 + cop_nr));
+ sysreg_write(CPUCR, cpucr);
+
+ cpucr = sysreg_read(CPUCR);
+ if ( !(cpucr & (1 << (24 + cop_nr))) ){
+ printk("Coprocessor #%i not found!\n", cop_nr);
+ return -1;
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_BUG
+#ifdef CONFIG_DEBUG_BUGVERBOSE
+static inline void do_bug_verbose(struct pt_regs *regs, u32 insn)
+{
+ char *file;
+ u16 line;
+ char c;
+
+ if (__get_user(line, (u16 __user *)(regs->pc + 2)))
+ return;
+ if (__get_user(file, (char * __user *)(regs->pc + 4))
+ || (unsigned long)file < PAGE_OFFSET
+ || __get_user(c, file))
+ file = "<bad filename>";
+
+ printk(KERN_ALERT "kernel BUG at %s:%d!\n", file, line);
+}
+#else
+static inline void do_bug_verbose(struct pt_regs *regs, u32 insn)
+{
+
+}
+#endif
+#endif
+
+asmlinkage void do_illegal_opcode(unsigned long ecr, struct pt_regs *regs)
+{
+ u32 insn;
+ struct undef_hook *hook;
+ siginfo_t info;
+ void __user *pc;
+
+ if (!user_mode(regs))
+ goto kernel_trap;
+
+ local_irq_enable();
+
+ pc = (void __user *)instruction_pointer(regs);
+ if (__get_user(insn, (u32 __user *)pc))
+ goto invalid_area;
+
+ if (ecr == ECR_COPROC_ABSENT) {
+ if (do_cop_absent(insn) == 0)
+ return;
+ }
+
+ spin_lock_irq(&undef_lock);
+ list_for_each_entry(hook, &undef_hook, node) {
+ if ((insn & hook->insn_mask) == hook->insn_val) {
+ if (hook->fn(regs, insn) == 0) {
+ spin_unlock_irq(&undef_lock);
+ return;
+ }
+ }
+ }
+ spin_unlock_irq(&undef_lock);
+
+invalid_area:
+
+#ifdef DEBUG
+ printk("Illegal instruction at pc = %08lx\n", regs->pc);
+ if (regs->pc < TASK_SIZE) {
+ unsigned long ptbr, pgd, pte, *p;
+
+ ptbr = sysreg_read(PTBR);
+ p = (unsigned long *)ptbr;
+ pgd = p[regs->pc >> 22];
+ p = (unsigned long *)((pgd & 0x1ffff000) | 0x80000000);
+ pte = p[(regs->pc >> 12) & 0x3ff];
+ printk("page table: 0x%08lx -> 0x%08lx -> 0x%08lx\n", ptbr, pgd, pte);
+ }
+#endif
+
+ info.si_signo = SIGILL;
+ info.si_errno = 0;
+ info.si_addr = (void __user *)regs->pc;
+ switch (ecr) {
+ case ECR_ILLEGAL_OPCODE:
+ case ECR_UNIMPL_INSTRUCTION:
+ info.si_code = ILL_ILLOPC;
+ break;
+ case ECR_PRIVILEGE_VIOLATION:
+ info.si_code = ILL_PRVOPC;
+ break;
+ case ECR_COPROC_ABSENT:
+ info.si_code = ILL_COPROC;
+ break;
+ default:
+ BUG();
+ }
+
+ force_sig_info(SIGILL, &info, current);
+ return;
+
+kernel_trap:
+#ifdef CONFIG_BUG
+ if (__kernel_text_address(instruction_pointer(regs))) {
+ insn = *(u16 *)instruction_pointer(regs);
+ if (insn == AVR32_BUG_OPCODE) {
+ do_bug_verbose(regs, insn);
+ die("Kernel BUG", regs, 0);
+ return;
+ }
+ }
+#endif
+
+ die("Oops: Illegal instruction in kernel code", regs, ecr);
+}
+
+asmlinkage void do_fpe(unsigned long ecr, struct pt_regs *regs)
+{
+ siginfo_t info;
+
+ printk("Floating-point exception at pc = %08lx\n", regs->pc);
+
+ /* We have no FPU... */
+ info.si_signo = SIGILL;
+ info.si_errno = 0;
+ info.si_addr = (void __user *)regs->pc;
+ info.si_code = ILL_COPROC;
+
+ force_sig_info(SIGILL, &info, current);
+}
+
+
+void __init trap_init(void)
+{
+
+}
diff --git a/arch/avr32/kernel/vmlinux.lds.c b/arch/avr32/kernel/vmlinux.lds.c
new file mode 100644
index 00000000000..cdd627c6b7d
--- /dev/null
+++ b/arch/avr32/kernel/vmlinux.lds.c
@@ -0,0 +1,139 @@
+/*
+ * AVR32 linker script for the Linux kernel
+ *
+ * Copyright (C) 2004-2006 Atmel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#define LOAD_OFFSET 0x00000000
+#include <asm-generic/vmlinux.lds.h>
+
+OUTPUT_FORMAT("elf32-avr32", "elf32-avr32", "elf32-avr32")
+OUTPUT_ARCH(avr32)
+ENTRY(_start)
+
+/* Big endian */
+jiffies = jiffies_64 + 4;
+
+SECTIONS
+{
+ . = CONFIG_ENTRY_ADDRESS;
+ .init : AT(ADDR(.init) - LOAD_OFFSET) {
+ _stext = .;
+ __init_begin = .;
+ _sinittext = .;
+ *(.text.reset)
+ *(.init.text)
+ _einittext = .;
+ . = ALIGN(4);
+ __tagtable_begin = .;
+ *(.taglist)
+ __tagtable_end = .;
+ *(.init.data)
+ . = ALIGN(16);
+ __setup_start = .;
+ *(.init.setup)
+ __setup_end = .;
+ . = ALIGN(4);
+ __initcall_start = .;
+ *(.initcall1.init)
+ *(.initcall2.init)
+ *(.initcall3.init)
+ *(.initcall4.init)
+ *(.initcall5.init)
+ *(.initcall6.init)
+ *(.initcall7.init)
+ __initcall_end = .;
+ __con_initcall_start = .;
+ *(.con_initcall.init)
+ __con_initcall_end = .;
+ __security_initcall_start = .;
+ *(.security_initcall.init)
+ __security_initcall_end = .;
+ . = ALIGN(32);
+ __initramfs_start = .;
+ *(.init.ramfs)
+ __initramfs_end = .;
+ . = ALIGN(4096);
+ __init_end = .;
+ }
+
+ . = ALIGN(8192);
+ .text : AT(ADDR(.text) - LOAD_OFFSET) {
+ _evba = .;
+ _text = .;
+ *(.ex.text)
+ . = 0x50;
+ *(.tlbx.ex.text)
+ . = 0x60;
+ *(.tlbr.ex.text)
+ . = 0x70;
+ *(.tlbw.ex.text)
+ . = 0x100;
+ *(.scall.text)
+ *(.irq.text)
+ *(.text)
+ SCHED_TEXT
+ LOCK_TEXT
+ KPROBES_TEXT
+ *(.fixup)
+ *(.gnu.warning)
+ _etext = .;
+ } = 0xd703d703
+
+ . = ALIGN(4);
+ __ex_table : AT(ADDR(__ex_table) - LOAD_OFFSET) {
+ __start___ex_table = .;
+ *(__ex_table)
+ __stop___ex_table = .;
+ }
+
+ RODATA
+
+ . = ALIGN(8192);
+
+ .data : AT(ADDR(.data) - LOAD_OFFSET) {
+ _data = .;
+ _sdata = .;
+ /*
+ * First, the init task union, aligned to an 8K boundary.
+ */
+ *(.data.init_task)
+
+ /* Then, the cacheline aligned data */
+ . = ALIGN(32);
+ *(.data.cacheline_aligned)
+
+ /* And the rest... */
+ *(.data.rel*)
+ *(.data)
+ CONSTRUCTORS
+
+ _edata = .;
+ }
+
+
+ . = ALIGN(8);
+ .bss : AT(ADDR(.bss) - LOAD_OFFSET) {
+ __bss_start = .;
+ *(.bss)
+ *(COMMON)
+ . = ALIGN(8);
+ __bss_stop = .;
+ _end = .;
+ }
+
+ /* When something in the kernel is NOT compiled as a module, the module
+ * cleanup code and data are put into these segments. Both can then be
+ * thrown away, as cleanup code is never called unless it's a module.
+ */
+ /DISCARD/ : {
+ *(.exit.text)
+ *(.exit.data)
+ *(.exitcall.exit)
+ }
+
+ DWARF_DEBUG
+}
diff --git a/arch/avr32/lib/Makefile b/arch/avr32/lib/Makefile
new file mode 100644
index 00000000000..09ac43e4052
--- /dev/null
+++ b/arch/avr32/lib/Makefile
@@ -0,0 +1,10 @@
+#
+# Makefile for AVR32-specific library files
+#
+
+lib-y := copy_user.o clear_user.o
+lib-y += strncpy_from_user.o strnlen_user.o
+lib-y += delay.o memset.o memcpy.o findbit.o
+lib-y += csum_partial.o csum_partial_copy_generic.o
+lib-y += io-readsw.o io-readsl.o io-writesw.o io-writesl.o
+lib-y += __avr32_lsl64.o __avr32_lsr64.o __avr32_asr64.o
diff --git a/arch/avr32/lib/__avr32_asr64.S b/arch/avr32/lib/__avr32_asr64.S
new file mode 100644
index 00000000000..368b6bca4c7
--- /dev/null
+++ b/arch/avr32/lib/__avr32_asr64.S
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2005-2006 Atmel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+ /*
+ * DWtype __avr32_asr64(DWtype u, word_type b)
+ */
+ .text
+ .global __avr32_asr64
+ .type __avr32_asr64,@function
+__avr32_asr64:
+ cp.w r12, 0
+ reteq r12
+
+ rsub r9, r12, 32
+ brle 1f
+
+ lsl r8, r11, r9
+ lsr r10, r10, r12
+ asr r11, r11, r12
+ or r10, r8
+ retal r12
+
+1: neg r9
+ asr r10, r11, r9
+ asr r11, 31
+ retal r12
diff --git a/arch/avr32/lib/__avr32_lsl64.S b/arch/avr32/lib/__avr32_lsl64.S
new file mode 100644
index 00000000000..f1dbc2b3625
--- /dev/null
+++ b/arch/avr32/lib/__avr32_lsl64.S
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2005-2006 Atmel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+ /*
+ * DWtype __avr32_lsl64(DWtype u, word_type b)
+ */
+ .text
+ .global __avr32_lsl64
+ .type __avr32_lsl64,@function
+__avr32_lsl64:
+ cp.w r12, 0
+ reteq r12
+
+ rsub r9, r12, 32
+ brle 1f
+
+ lsr r8, r10, r9
+ lsl r10, r10, r12
+ lsl r11, r11, r12
+ or r11, r8
+ retal r12
+
+1: neg r9
+ lsl r11, r10, r9
+ mov r10, 0
+ retal r12
diff --git a/arch/avr32/lib/__avr32_lsr64.S b/arch/avr32/lib/__avr32_lsr64.S
new file mode 100644
index 00000000000..e65bb7f0d24
--- /dev/null
+++ b/arch/avr32/lib/__avr32_lsr64.S
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2005-2006 Atmel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+ /*
+ * DWtype __avr32_lsr64(DWtype u, word_type b)
+ */
+ .text
+ .global __avr32_lsr64
+ .type __avr32_lsr64,@function
+__avr32_lsr64:
+ cp.w r12, 0
+ reteq r12
+
+ rsub r9, r12, 32
+ brle 1f
+
+ lsl r8, r11, r9
+ lsr r11, r11, r12
+ lsr r10, r10, r12
+ or r10, r8
+ retal r12
+
+1: neg r9
+ lsr r10, r11, r9
+ mov r11, 0
+ retal r12
diff --git a/arch/avr32/lib/clear_user.S b/arch/avr32/lib/clear_user.S
new file mode 100644
index 00000000000..d8991b6f8eb
--- /dev/null
+++ b/arch/avr32/lib/clear_user.S
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2004-2006 Atmel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <asm/page.h>
+#include <asm/thread_info.h>
+#include <asm/asm.h>
+
+ .text
+ .align 1
+ .global clear_user
+ .type clear_user, "function"
+clear_user:
+ branch_if_kernel r8, __clear_user
+ ret_if_privileged r8, r12, r11, r11
+
+ .global __clear_user
+ .type __clear_user, "function"
+__clear_user:
+ mov r9, r12
+ mov r8, 0
+ andl r9, 3, COH
+ brne 5f
+
+1: sub r11, 4
+ brlt 2f
+
+10: st.w r12++, r8
+ sub r11, 4
+ brge 10b
+
+2: sub r11, -4
+ reteq 0
+
+ /* Unaligned count or address */
+ bld r11, 1
+ brcc 12f
+11: st.h r12++, r8
+ sub r11, 2
+ reteq 0
+12: st.b r12++, r8
+ retal 0
+
+ /* Unaligned address */
+5: cp.w r11, 4
+ brlt 2b
+
+ lsl r9, 2
+ add pc, pc, r9
+13: st.b r12++, r8
+ sub r11, 1
+14: st.b r12++, r8
+ sub r11, 1
+15: st.b r12++, r8
+ sub r11, 1
+ rjmp 1b
+
+ .size clear_user, . - clear_user
+ .size __clear_user, . - __clear_user
+
+ .section .fixup, "ax"
+ .align 1
+18: sub r11, -4
+19: retal r11
+
+ .section __ex_table, "a"
+ .align 2
+ .long 10b, 18b
+ .long 11b, 19b
+ .long 12b, 19b
+ .long 13b, 19b
+ .long 14b, 19b
+ .long 15b, 19b
diff --git a/arch/avr32/lib/copy_user.S b/arch/avr32/lib/copy_user.S
new file mode 100644
index 00000000000..ea59c04b07d
--- /dev/null
+++ b/arch/avr32/lib/copy_user.S
@@ -0,0 +1,119 @@
+/*
+ * Copy to/from userspace with optional address space checking.
+ *
+ * Copyright 2004-2006 Atmel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <asm/page.h>
+#include <asm/thread_info.h>
+#include <asm/asm.h>
+
+ /*
+ * __kernel_size_t
+ * __copy_user(void *to, const void *from, __kernel_size_t n)
+ *
+ * Returns the number of bytes not copied. Might be off by
+ * max 3 bytes if we get a fault in the main loop.
+ *
+ * The address-space checking functions simply fall through to
+ * the non-checking version.
+ */
+ .text
+ .align 1
+ .global copy_from_user
+ .type copy_from_user, @function
+copy_from_user:
+ branch_if_kernel r8, __copy_user
+ ret_if_privileged r8, r11, r10, r10
+ rjmp __copy_user
+ .size copy_from_user, . - copy_from_user
+
+ .global copy_to_user
+ .type copy_to_user, @function
+copy_to_user:
+ branch_if_kernel r8, __copy_user
+ ret_if_privileged r8, r12, r10, r10
+ .size copy_to_user, . - copy_to_user
+
+ .global __copy_user
+ .type __copy_user, @function
+__copy_user:
+ mov r9, r11
+ andl r9, 3, COH
+ brne 6f
+
+ /* At this point, from is word-aligned */
+1: sub r10, 4
+ brlt 3f
+
+2:
+10: ld.w r8, r11++
+11: st.w r12++, r8
+ sub r10, 4
+ brge 2b
+
+3: sub r10, -4
+ reteq 0
+
+ /*
+ * Handle unaligned count. Need to be careful with r10 here so
+ * that we return the correct value even if we get a fault
+ */
+4:
+20: ld.ub r8, r11++
+21: st.b r12++, r8
+ sub r10, 1
+ reteq 0
+22: ld.ub r8, r11++
+23: st.b r12++, r8
+ sub r10, 1
+ reteq 0
+24: ld.ub r8, r11++
+25: st.b r12++, r8
+ retal 0
+
+ /* Handle unaligned from-pointer */
+6: cp.w r10, 4
+ brlt 4b
+ rsub r9, r9, 4
+
+30: ld.ub r8, r11++
+31: st.b r12++, r8
+ sub r10, 1
+ sub r9, 1
+ breq 1b
+32: ld.ub r8, r11++
+33: st.b r12++, r8
+ sub r10, 1
+ sub r9, 1
+ breq 1b
+34: ld.ub r8, r11++
+35: st.b r12++, r8
+ sub r10, 1
+ rjmp 1b
+ .size __copy_user, . - __copy_user
+
+ .section .fixup,"ax"
+ .align 1
+19: sub r10, -4
+29: retal r10
+
+ .section __ex_table,"a"
+ .align 2
+ .long 10b, 19b
+ .long 11b, 19b
+ .long 20b, 29b
+ .long 21b, 29b
+ .long 22b, 29b
+ .long 23b, 29b
+ .long 24b, 29b
+ .long 25b, 29b
+ .long 30b, 29b
+ .long 31b, 29b
+ .long 32b, 29b
+ .long 33b, 29b
+ .long 34b, 29b
+ .long 35b, 29b
diff --git a/arch/avr32/lib/csum_partial.S b/arch/avr32/lib/csum_partial.S
new file mode 100644
index 00000000000..6a262b528eb
--- /dev/null
+++ b/arch/avr32/lib/csum_partial.S
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2004-2006 Atmel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+ /*
+ * unsigned int csum_partial(const unsigned char *buff,
+ * int len, unsigned int sum)
+ */
+ .text
+ .global csum_partial
+ .type csum_partial,"function"
+ .align 1
+csum_partial:
+ /* checksum complete words, aligned or not */
+3: sub r11, 4
+ brlt 5f
+4: ld.w r9, r12++
+ add r10, r9
+ acr r10
+ sub r11, 4
+ brge 4b
+
+ /* return if we had a whole number of words */
+5: sub r11, -4
+ reteq r10
+
+ /* checksum any remaining bytes at the end */
+ mov r9, 0
+ mov r8, 0
+ cp r11, 2
+ brlt 6f
+ ld.uh r9, r12++
+ sub r11, 2
+ breq 7f
+ lsl r9, 16
+6: ld.ub r8, r12++
+ lsl r8, 8
+7: or r9, r8
+ add r10, r9
+ acr r10
+
+ retal r10
+ .size csum_partial, . - csum_partial
diff --git a/arch/avr32/lib/csum_partial_copy_generic.S b/arch/avr32/lib/csum_partial_copy_generic.S
new file mode 100644
index 00000000000..a3a0f9b8929
--- /dev/null
+++ b/arch/avr32/lib/csum_partial_copy_generic.S
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2004-2006 Atmel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <asm/errno.h>
+#include <asm/asm.h>
+
+ /*
+ * unsigned int csum_partial_copy_generic(const char *src, char *dst, int len
+ * int sum, int *src_err_ptr,
+ * int *dst_err_ptr)
+ *
+ * Copy src to dst while checksumming, otherwise like csum_partial.
+ */
+
+ .macro ld_src size, reg, ptr
+9999: ld.\size \reg, \ptr
+ .section __ex_table, "a"
+ .long 9999b, fixup_ld_src
+ .previous
+ .endm
+
+ .macro st_dst size, ptr, reg
+9999: st.\size \ptr, \reg
+ .section __ex_table, "a"
+ .long 9999b, fixup_st_dst
+ .previous
+ .endm
+
+ .text
+ .global csum_partial_copy_generic
+ .type csum_partial_copy_generic,"function"
+ .align 1
+csum_partial_copy_generic:
+ pushm r4-r7,lr
+
+ /* The inner loop */
+1: sub r10, 4
+ brlt 5f
+2: ld_src w, r5, r12++
+ st_dst w, r11++, r5
+ add r9, r5
+ acr r9
+ sub r10, 4
+ brge 2b
+
+ /* return if we had a whole number of words */
+5: sub r10, -4
+ brne 7f
+
+6: mov r12, r9
+ popm r4-r7,pc
+
+ /* handle additional bytes at the tail */
+7: mov r5, 0
+ mov r4, 32
+8: ld_src ub, r6, r12++
+ st_dst b, r11++, r6
+ lsl r5, 8
+ sub r4, 8
+ bfins r5, r6, 0, 8
+ sub r10, 1
+ brne 8b
+
+ lsl r5, r5, r4
+ add r9, r5
+ acr r9
+ rjmp 6b
+
+ /* Exception handler */
+ .section .fixup,"ax"
+ .align 1
+fixup_ld_src:
+ mov r9, -EFAULT
+ cp.w r8, 0
+ breq 1f
+ st.w r8[0], r9
+
+1: /*
+ * TODO: zero the complete destination - computing the rest
+ * is too much work
+ */
+
+ mov r9, 0
+ rjmp 6b
+
+fixup_st_dst:
+ mov r9, -EFAULT
+ lddsp r8, sp[20]
+ cp.w r8, 0
+ breq 1f
+ st.w r8[0], r9
+1: mov r9, 0
+ rjmp 6b
+
+ .previous
diff --git a/arch/avr32/lib/delay.c b/arch/avr32/lib/delay.c
new file mode 100644
index 00000000000..462c8307b68
--- /dev/null
+++ b/arch/avr32/lib/delay.c
@@ -0,0 +1,55 @@
+/*
+ * Precise Delay Loops for avr32
+ *
+ * Copyright (C) 1993 Linus Torvalds
+ * Copyright (C) 1997 Martin Mares <mj@atrey.karlin.mff.cuni.cz>
+ * Copyright (C) 2005-2006 Atmel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/types.h>
+
+#include <asm/delay.h>
+#include <asm/processor.h>
+#include <asm/sysreg.h>
+
+int read_current_timer(unsigned long *timer_value)
+{
+ *timer_value = sysreg_read(COUNT);
+ return 0;
+}
+
+void __delay(unsigned long loops)
+{
+ unsigned bclock, now;
+
+ bclock = sysreg_read(COUNT);
+ do {
+ now = sysreg_read(COUNT);
+ } while ((now - bclock) < loops);
+}
+
+inline void __const_udelay(unsigned long xloops)
+{
+ unsigned long long loops;
+
+ asm("mulu.d %0, %1, %2"
+ : "=r"(loops)
+ : "r"(current_cpu_data.loops_per_jiffy * HZ), "r"(xloops));
+ __delay(loops >> 32);
+}
+
+void __udelay(unsigned long usecs)
+{
+ __const_udelay(usecs * 0x000010c7); /* 2**32 / 1000000 (rounded up) */
+}
+
+void __ndelay(unsigned long nsecs)
+{
+ __const_udelay(nsecs * 0x00005); /* 2**32 / 1000000000 (rounded up) */
+}
diff --git a/arch/avr32/lib/findbit.S b/arch/avr32/lib/findbit.S
new file mode 100644
index 00000000000..2b4856f4bf7
--- /dev/null
+++ b/arch/avr32/lib/findbit.S
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2006 Atmel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/linkage.h>
+
+ .text
+ /*
+ * unsigned long find_first_zero_bit(const unsigned long *addr,
+ * unsigned long size)
+ */
+ENTRY(find_first_zero_bit)
+ cp.w r11, 0
+ reteq r11
+ mov r9, r11
+1: ld.w r8, r12[0]
+ com r8
+ brne .L_found
+ sub r12, -4
+ sub r9, 32
+ brgt 1b
+ retal r11
+
+ /*
+ * unsigned long find_next_zero_bit(const unsigned long *addr,
+ * unsigned long size,
+ * unsigned long offset)
+ */
+ENTRY(find_next_zero_bit)
+ lsr r8, r10, 5
+ sub r9, r11, r10
+ retle r11
+
+ lsl r8, 2
+ add r12, r8
+ andl r10, 31, COH
+ breq 1f
+
+ /* offset is not word-aligned. Handle the first (32 - r10) bits */
+ ld.w r8, r12[0]
+ com r8
+ sub r12, -4
+ lsr r8, r8, r10
+ brne .L_found
+
+ /* r9 = r9 - (32 - r10) = r9 + r10 - 32 */
+ add r9, r10
+ sub r9, 32
+ retle r11
+
+ /* Main loop. offset must be word-aligned */
+1: ld.w r8, r12[0]
+ com r8
+ brne .L_found
+ sub r12, -4
+ sub r9, 32
+ brgt 1b
+ retal r11
+
+ /* Common return path for when a bit is actually found. */
+.L_found:
+ brev r8
+ clz r10, r8
+ rsub r9, r11
+ add r10, r9
+
+ /* XXX: If we don't have to return exactly "size" when the bit
+ is not found, we may drop this "min" thing */
+ min r12, r11, r10
+ retal r12
+
+ /*
+ * unsigned long find_first_bit(const unsigned long *addr,
+ * unsigned long size)
+ */
+ENTRY(find_first_bit)
+ cp.w r11, 0
+ reteq r11
+ mov r9, r11
+1: ld.w r8, r12[0]
+ cp.w r8, 0
+ brne .L_found
+ sub r12, -4
+ sub r9, 32
+ brgt 1b
+ retal r11
+
+ /*
+ * unsigned long find_next_bit(const unsigned long *addr,
+ * unsigned long size,
+ * unsigned long offset)
+ */
+ENTRY(find_next_bit)
+ lsr r8, r10, 5
+ sub r9, r11, r10
+ retle r11
+
+ lsl r8, 2
+ add r12, r8
+ andl r10, 31, COH
+ breq 1f
+
+ /* offset is not word-aligned. Handle the first (32 - r10) bits */
+ ld.w r8, r12[0]
+ sub r12, -4
+ lsr r8, r8, r10
+ brne .L_found
+
+ /* r9 = r9 - (32 - r10) = r9 + r10 - 32 */
+ add r9, r10
+ sub r9, 32
+ retle r11
+
+ /* Main loop. offset must be word-aligned */
+1: ld.w r8, r12[0]
+ cp.w r8, 0
+ brne .L_found
+ sub r12, -4
+ sub r9, 32
+ brgt 1b
+ retal r11
+
+ENTRY(generic_find_next_zero_le_bit)
+ lsr r8, r10, 5
+ sub r9, r11, r10
+ retle r11
+
+ lsl r8, 2
+ add r12, r8
+ andl r10, 31, COH
+ breq 1f
+
+ /* offset is not word-aligned. Handle the first (32 - r10) bits */
+ ldswp.w r8, r12[0]
+ sub r12, -4
+ lsr r8, r8, r10
+ brne .L_found
+
+ /* r9 = r9 - (32 - r10) = r9 + r10 - 32 */
+ add r9, r10
+ sub r9, 32
+ retle r11
+
+ /* Main loop. offset must be word-aligned */
+1: ldswp.w r8, r12[0]
+ cp.w r8, 0
+ brne .L_found
+ sub r12, -4
+ sub r9, 32
+ brgt 1b
+ retal r11
diff --git a/arch/avr32/lib/io-readsl.S b/arch/avr32/lib/io-readsl.S
new file mode 100644
index 00000000000..b103511ed6c
--- /dev/null
+++ b/arch/avr32/lib/io-readsl.S
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2004-2006 Atmel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+ .global __raw_readsl
+ .type __raw_readsl,@function
+__raw_readsl:
+ cp.w r10, 0
+ reteq r12
+
+ /*
+ * If r11 isn't properly aligned, we might get an exception on
+ * some implementations. But there's not much we can do about it.
+ */
+1: ld.w r8, r12[0]
+ sub r10, 1
+ st.w r11++, r8
+ brne 1b
+
+ retal r12
diff --git a/arch/avr32/lib/io-readsw.S b/arch/avr32/lib/io-readsw.S
new file mode 100644
index 00000000000..456be990902
--- /dev/null
+++ b/arch/avr32/lib/io-readsw.S
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2004-2006 Atmel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+.Lnot_word_aligned:
+ /*
+ * Bad alignment will cause a hardware exception, which is as
+ * good as anything. No need for us to check for proper alignment.
+ */
+ ld.uh r8, r12[0]
+ sub r10, 1
+ st.h r11++, r8
+
+ /* fall through */
+
+ .global __raw_readsw
+ .type __raw_readsw,@function
+__raw_readsw:
+ cp.w r10, 0
+ reteq r12
+ mov r9, 3
+ tst r11, r9
+ brne .Lnot_word_aligned
+
+ sub r10, 2
+ brlt 2f
+
+1: ldins.h r8:t, r12[0]
+ ldins.h r8:b, r12[0]
+ st.w r11++, r8
+ sub r10, 2
+ brge 1b
+
+2: sub r10, -2
+ reteq r12
+
+ ld.uh r8, r12[0]
+ st.h r11++, r8
+ retal r12
diff --git a/arch/avr32/lib/io-writesl.S b/arch/avr32/lib/io-writesl.S
new file mode 100644
index 00000000000..22138b3a16e
--- /dev/null
+++ b/arch/avr32/lib/io-writesl.S
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2004-2006 Atmel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+ .global __raw_writesl
+ .type __raw_writesl,@function
+__raw_writesl:
+ cp.w r10, 0
+ reteq r12
+
+1: ld.w r8, r11++
+ sub r10, 1
+ st.w r12[0], r8
+ brne 1b
+
+ retal r12
diff --git a/arch/avr32/lib/io-writesw.S b/arch/avr32/lib/io-writesw.S
new file mode 100644
index 00000000000..8c4a53f1c52
--- /dev/null
+++ b/arch/avr32/lib/io-writesw.S
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2004-2006 Atmel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+.Lnot_word_aligned:
+ ld.uh r8, r11++
+ sub r10, 1
+ st.h r12[0], r8
+
+ .global __raw_writesw
+ .type __raw_writesw,@function
+__raw_writesw:
+ cp.w r10, 0
+ mov r9, 3
+ reteq r12
+ tst r11, r9
+ brne .Lnot_word_aligned
+
+ sub r10, 2
+ brlt 2f
+
+1: ld.w r8, r11++
+ bfextu r9, r8, 16, 16
+ st.h r12[0], r9
+ st.h r12[0], r8
+ sub r10, 2
+ brge 1b
+
+2: sub r10, -2
+ reteq r12
+
+ ld.uh r8, r11++
+ st.h r12[0], r8
+ retal r12
diff --git a/arch/avr32/lib/libgcc.h b/arch/avr32/lib/libgcc.h
new file mode 100644
index 00000000000..5a091b5e361
--- /dev/null
+++ b/arch/avr32/lib/libgcc.h
@@ -0,0 +1,33 @@
+/* Definitions for various functions 'borrowed' from gcc-3.4.3 */
+
+#define BITS_PER_UNIT 8
+
+typedef int QItype __attribute__ ((mode (QI)));
+typedef unsigned int UQItype __attribute__ ((mode (QI)));
+typedef int HItype __attribute__ ((mode (HI)));
+typedef unsigned int UHItype __attribute__ ((mode (HI)));
+typedef int SItype __attribute__ ((mode (SI)));
+typedef unsigned int USItype __attribute__ ((mode (SI)));
+typedef int DItype __attribute__ ((mode (DI)));
+typedef unsigned int UDItype __attribute__ ((mode (DI)));
+typedef float SFtype __attribute__ ((mode (SF)));
+typedef float DFtype __attribute__ ((mode (DF)));
+typedef int word_type __attribute__ ((mode (__word__)));
+
+#define W_TYPE_SIZE (4 * BITS_PER_UNIT)
+#define Wtype SItype
+#define UWtype USItype
+#define HWtype SItype
+#define UHWtype USItype
+#define DWtype DItype
+#define UDWtype UDItype
+#define __NW(a,b) __ ## a ## si ## b
+#define __NDW(a,b) __ ## a ## di ## b
+
+struct DWstruct {Wtype high, low;};
+
+typedef union
+{
+ struct DWstruct s;
+ DWtype ll;
+} DWunion;
diff --git a/arch/avr32/lib/longlong.h b/arch/avr32/lib/longlong.h
new file mode 100644
index 00000000000..cd5e369ac43
--- /dev/null
+++ b/arch/avr32/lib/longlong.h
@@ -0,0 +1,98 @@
+/* longlong.h -- definitions for mixed size 32/64 bit arithmetic.
+ Copyright (C) 1991, 1992, 1994, 1995, 1996, 1997, 1998, 1999, 2000
+ Free Software Foundation, Inc.
+
+ This definition file 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, or (at your option) any later version.
+
+ This definition file is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even the implied
+ warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+/* Borrowed from gcc-3.4.3 */
+
+#define __BITS4 (W_TYPE_SIZE / 4)
+#define __ll_B ((UWtype) 1 << (W_TYPE_SIZE / 2))
+#define __ll_lowpart(t) ((UWtype) (t) & (__ll_B - 1))
+#define __ll_highpart(t) ((UWtype) (t) >> (W_TYPE_SIZE / 2))
+
+#define count_leading_zeros(count, x) ((count) = __builtin_clz(x))
+
+#define __udiv_qrnnd_c(q, r, n1, n0, d) \
+ do { \
+ UWtype __d1, __d0, __q1, __q0; \
+ UWtype __r1, __r0, __m; \
+ __d1 = __ll_highpart (d); \
+ __d0 = __ll_lowpart (d); \
+ \
+ __r1 = (n1) % __d1; \
+ __q1 = (n1) / __d1; \
+ __m = (UWtype) __q1 * __d0; \
+ __r1 = __r1 * __ll_B | __ll_highpart (n0); \
+ if (__r1 < __m) \
+ { \
+ __q1--, __r1 += (d); \
+ if (__r1 >= (d)) /* i.e. we didn't get carry when adding to __r1 */\
+ if (__r1 < __m) \
+ __q1--, __r1 += (d); \
+ } \
+ __r1 -= __m; \
+ \
+ __r0 = __r1 % __d1; \
+ __q0 = __r1 / __d1; \
+ __m = (UWtype) __q0 * __d0; \
+ __r0 = __r0 * __ll_B | __ll_lowpart (n0); \
+ if (__r0 < __m) \
+ { \
+ __q0--, __r0 += (d); \
+ if (__r0 >= (d)) \
+ if (__r0 < __m) \
+ __q0--, __r0 += (d); \
+ } \
+ __r0 -= __m; \
+ \
+ (q) = (UWtype) __q1 * __ll_B | __q0; \
+ (r) = __r0; \
+ } while (0)
+
+#define udiv_qrnnd __udiv_qrnnd_c
+
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+ do { \
+ UWtype __x; \
+ __x = (al) - (bl); \
+ (sh) = (ah) - (bh) - (__x > (al)); \
+ (sl) = __x; \
+ } while (0)
+
+#define umul_ppmm(w1, w0, u, v) \
+ do { \
+ UWtype __x0, __x1, __x2, __x3; \
+ UHWtype __ul, __vl, __uh, __vh; \
+ \
+ __ul = __ll_lowpart (u); \
+ __uh = __ll_highpart (u); \
+ __vl = __ll_lowpart (v); \
+ __vh = __ll_highpart (v); \
+ \
+ __x0 = (UWtype) __ul * __vl; \
+ __x1 = (UWtype) __ul * __vh; \
+ __x2 = (UWtype) __uh * __vl; \
+ __x3 = (UWtype) __uh * __vh; \
+ \
+ __x1 += __ll_highpart (__x0);/* this can't give carry */ \
+ __x1 += __x2; /* but this indeed can */ \
+ if (__x1 < __x2) /* did we get it? */ \
+ __x3 += __ll_B; /* yes, add it in the proper pos. */ \
+ \
+ (w1) = __x3 + __ll_highpart (__x1); \
+ (w0) = __ll_lowpart (__x1) * __ll_B + __ll_lowpart (__x0); \
+ } while (0)
diff --git a/arch/avr32/lib/memcpy.S b/arch/avr32/lib/memcpy.S
new file mode 100644
index 00000000000..0abb26142b6
--- /dev/null
+++ b/arch/avr32/lib/memcpy.S
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2004-2006 Atmel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+ /*
+ * void *memcpy(void *to, const void *from, unsigned long n)
+ *
+ * This implementation does word-aligned loads in the main loop,
+ * possibly sacrificing alignment of stores.
+ *
+ * Hopefully, in most cases, both "to" and "from" will be
+ * word-aligned to begin with.
+ */
+ .text
+ .global memcpy
+ .type memcpy, @function
+memcpy:
+ mov r9, r11
+ andl r9, 3, COH
+ brne 1f
+
+ /* At this point, "from" is word-aligned */
+2: sub r10, 4
+ mov r9, r12
+ brlt 4f
+
+3: ld.w r8, r11++
+ sub r10, 4
+ st.w r12++, r8
+ brge 3b
+
+4: neg r10
+ reteq r9
+
+ /* Handle unaligned count */
+ lsl r10, 2
+ add pc, pc, r10
+ ld.ub r8, r11++
+ st.b r12++, r8
+ ld.ub r8, r11++
+ st.b r12++, r8
+ ld.ub r8, r11++
+ st.b r12++, r8
+ retal r9
+
+ /* Handle unaligned "from" pointer */
+1: sub r10, 4
+ brlt 4b
+ add r10, r9
+ lsl r9, 2
+ add pc, pc, r9
+ ld.ub r8, r11++
+ st.b r12++, r8
+ ld.ub r8, r11++
+ st.b r12++, r8
+ ld.ub r8, r11++
+ st.b r12++, r8
+ rjmp 2b
diff --git a/arch/avr32/lib/memset.S b/arch/avr32/lib/memset.S
new file mode 100644
index 00000000000..40da32c0480
--- /dev/null
+++ b/arch/avr32/lib/memset.S
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2004-2006 Atmel Corporation
+ *
+ * Based on linux/arch/arm/lib/memset.S
+ * Copyright (C) 1995-2000 Russell King
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * ASM optimised string functions
+ */
+#include <asm/asm.h>
+
+ /*
+ * r12: void *b
+ * r11: int c
+ * r10: size_t len
+ *
+ * Returns b in r12
+ */
+ .text
+ .global memset
+ .type memset, @function
+ .align 5
+memset:
+ mov r9, r12
+ mov r8, r12
+ or r11, r11, r11 << 8
+ andl r9, 3, COH
+ brne 1f
+
+2: or r11, r11, r11 << 16
+ sub r10, 4
+ brlt 5f
+
+ /* Let's do some real work */
+4: st.w r8++, r11
+ sub r10, 4
+ brge 4b
+
+ /*
+ * When we get here, we've got less than 4 bytes to set. r10
+ * might be negative.
+ */
+5: sub r10, -4
+ reteq r12
+
+ /* Fastpath ends here, exactly 32 bytes from memset */
+
+ /* Handle unaligned count or pointer */
+ bld r10, 1
+ brcc 6f
+ st.b r8++, r11
+ st.b r8++, r11
+ bld r10, 0
+ retcc r12
+6: st.b r8++, r11
+ retal r12
+
+ /* Handle unaligned pointer */
+1: sub r10, 4
+ brlt 5b
+ add r10, r9
+ lsl r9, 1
+ add pc, r9
+ st.b r8++, r11
+ st.b r8++, r11
+ st.b r8++, r11
+ rjmp 2b
+
+ .size memset, . - memset
diff --git a/arch/avr32/lib/strncpy_from_user.S b/arch/avr32/lib/strncpy_from_user.S
new file mode 100644
index 00000000000..72bd50599ec
--- /dev/null
+++ b/arch/avr32/lib/strncpy_from_user.S
@@ -0,0 +1,60 @@
+/*
+ * Copy to/from userspace with optional address space checking.
+ *
+ * Copyright 2004-2006 Atmel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/errno.h>
+
+#include <asm/page.h>
+#include <asm/thread_info.h>
+#include <asm/asm.h>
+
+ /*
+ * long strncpy_from_user(char *dst, const char *src, long count)
+ *
+ * On success, returns the length of the string, not including
+ * the terminating NUL.
+ *
+ * If the string is longer than count, returns count
+ *
+ * If userspace access fails, returns -EFAULT
+ */
+ .text
+ .align 1
+ .global strncpy_from_user
+ .type strncpy_from_user, "function"
+strncpy_from_user:
+ mov r9, -EFAULT
+ branch_if_kernel r8, __strncpy_from_user
+ ret_if_privileged r8, r11, r10, r9
+
+ .global __strncpy_from_user
+ .type __strncpy_from_user, "function"
+__strncpy_from_user:
+ cp.w r10, 0
+ reteq 0
+
+ mov r9, r10
+
+1: ld.ub r8, r11++
+ st.b r12++, r8
+ cp.w r8, 0
+ breq 2f
+ sub r9, 1
+ brne 1b
+
+2: sub r10, r9
+ retal r10
+
+ .section .fixup, "ax"
+ .align 1
+3: mov r12, -EFAULT
+ retal r12
+
+ .section __ex_table, "a"
+ .align 2
+ .long 1b, 3b
diff --git a/arch/avr32/lib/strnlen_user.S b/arch/avr32/lib/strnlen_user.S
new file mode 100644
index 00000000000..65ce11afa66
--- /dev/null
+++ b/arch/avr32/lib/strnlen_user.S
@@ -0,0 +1,67 @@
+/*
+ * Copy to/from userspace with optional address space checking.
+ *
+ * Copyright 2004-2006 Atmel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <asm/page.h>
+#include <asm/thread_info.h>
+#include <asm/processor.h>
+#include <asm/asm.h>
+
+ .text
+ .align 1
+ .global strnlen_user
+ .type strnlen_user, "function"
+strnlen_user:
+ branch_if_kernel r8, __strnlen_user
+ sub r8, r11, 1
+ add r8, r12
+ retcs 0
+ brmi adjust_length /* do a closer inspection */
+
+ .global __strnlen_user
+ .type __strnlen_user, "function"
+__strnlen_user:
+ mov r10, r12
+
+10: ld.ub r8, r12++
+ cp.w r8, 0
+ breq 2f
+ sub r11, 1
+ brne 10b
+
+ sub r12, -1
+2: sub r12, r10
+ retal r12
+
+
+ .type adjust_length, "function"
+adjust_length:
+ cp.w r12, 0 /* addr must always be < TASK_SIZE */
+ retmi 0
+
+ pushm lr
+ lddpc lr, _task_size
+ sub r11, lr, r12
+ mov r9, r11
+ rcall __strnlen_user
+ cp.w r12, r9
+ brgt 1f
+ popm pc
+1: popm pc, r12=0
+
+ .align 2
+_task_size:
+ .long TASK_SIZE
+
+ .section .fixup, "ax"
+ .align 1
+19: retal 0
+
+ .section __ex_table, "a"
+ .align 2
+ .long 10b, 19b
diff --git a/arch/avr32/mach-at32ap/Makefile b/arch/avr32/mach-at32ap/Makefile
new file mode 100644
index 00000000000..f62eb691551
--- /dev/null
+++ b/arch/avr32/mach-at32ap/Makefile
@@ -0,0 +1,2 @@
+obj-y += at32ap.o clock.o pio.o intc.o extint.o hsmc.o
+obj-$(CONFIG_CPU_AT32AP7000) += at32ap7000.o
diff --git a/arch/avr32/mach-at32ap/at32ap.c b/arch/avr32/mach-at32ap/at32ap.c
new file mode 100644
index 00000000000..f7cedf5aabe
--- /dev/null
+++ b/arch/avr32/mach-at32ap/at32ap.c
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2006 Atmel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+
+#include <asm/io.h>
+
+#include <asm/arch/init.h>
+#include <asm/arch/sm.h>
+
+struct at32_sm system_manager;
+
+static int __init at32_sm_init(void)
+{
+ struct resource *regs;
+ struct at32_sm *sm = &system_manager;
+ int ret = -ENXIO;
+
+ regs = platform_get_resource(&at32_sm_device, IORESOURCE_MEM, 0);
+ if (!regs)
+ goto fail;
+
+ spin_lock_init(&sm->lock);
+ sm->pdev = &at32_sm_device;
+
+ ret = -ENOMEM;
+ sm->regs = ioremap(regs->start, regs->end - regs->start + 1);
+ if (!sm->regs)
+ goto fail;
+
+ return 0;
+
+fail:
+ printk(KERN_ERR "Failed to initialize System Manager: %d\n", ret);
+ return ret;
+}
+
+void __init setup_platform(void)
+{
+ at32_sm_init();
+ at32_clock_init();
+ at32_portmux_init();
+
+ /* FIXME: This doesn't belong here */
+ at32_setup_serial_console(1);
+}
+
+static int __init pdc_probe(struct platform_device *pdev)
+{
+ struct clk *pclk, *hclk;
+
+ pclk = clk_get(&pdev->dev, "pclk");
+ if (IS_ERR(pclk)) {
+ dev_err(&pdev->dev, "no pclk defined\n");
+ return PTR_ERR(pclk);
+ }
+ hclk = clk_get(&pdev->dev, "hclk");
+ if (IS_ERR(hclk)) {
+ dev_err(&pdev->dev, "no hclk defined\n");
+ clk_put(pclk);
+ return PTR_ERR(hclk);
+ }
+
+ clk_enable(pclk);
+ clk_enable(hclk);
+
+ dev_info(&pdev->dev, "Atmel Peripheral DMA Controller enabled\n");
+ return 0;
+}
+
+static struct platform_driver pdc_driver = {
+ .probe = pdc_probe,
+ .driver = {
+ .name = "pdc",
+ },
+};
+
+static int __init pdc_init(void)
+{
+ return platform_driver_register(&pdc_driver);
+}
+arch_initcall(pdc_init);
diff --git a/arch/avr32/mach-at32ap/at32ap7000.c b/arch/avr32/mach-at32ap/at32ap7000.c
new file mode 100644
index 00000000000..37982b60398
--- /dev/null
+++ b/arch/avr32/mach-at32ap/at32ap7000.c
@@ -0,0 +1,876 @@
+/*
+ * Copyright (C) 2005-2006 Atmel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/clk.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+
+#include <asm/io.h>
+
+#include <asm/arch/board.h>
+#include <asm/arch/portmux.h>
+#include <asm/arch/sm.h>
+
+#include "clock.h"
+#include "pio.h"
+#include "sm.h"
+
+#define PBMEM(base) \
+ { \
+ .start = base, \
+ .end = base + 0x3ff, \
+ .flags = IORESOURCE_MEM, \
+ }
+#define IRQ(num) \
+ { \
+ .start = num, \
+ .end = num, \
+ .flags = IORESOURCE_IRQ, \
+ }
+#define NAMED_IRQ(num, _name) \
+ { \
+ .start = num, \
+ .end = num, \
+ .name = _name, \
+ .flags = IORESOURCE_IRQ, \
+ }
+
+#define DEFINE_DEV(_name, _id) \
+static struct platform_device _name##_id##_device = { \
+ .name = #_name, \
+ .id = _id, \
+ .resource = _name##_id##_resource, \
+ .num_resources = ARRAY_SIZE(_name##_id##_resource), \
+}
+#define DEFINE_DEV_DATA(_name, _id) \
+static struct platform_device _name##_id##_device = { \
+ .name = #_name, \
+ .id = _id, \
+ .dev = { \
+ .platform_data = &_name##_id##_data, \
+ }, \
+ .resource = _name##_id##_resource, \
+ .num_resources = ARRAY_SIZE(_name##_id##_resource), \
+}
+
+#define DEV_CLK(_name, devname, bus, _index) \
+static struct clk devname##_##_name = { \
+ .name = #_name, \
+ .dev = &devname##_device.dev, \
+ .parent = &bus##_clk, \
+ .mode = bus##_clk_mode, \
+ .get_rate = bus##_clk_get_rate, \
+ .index = _index, \
+}
+
+enum {
+ PIOA,
+ PIOB,
+ PIOC,
+ PIOD,
+};
+
+enum {
+ FUNC_A,
+ FUNC_B,
+};
+
+unsigned long at32ap7000_osc_rates[3] = {
+ [0] = 32768,
+ /* FIXME: these are ATSTK1002-specific */
+ [1] = 20000000,
+ [2] = 12000000,
+};
+
+static unsigned long osc_get_rate(struct clk *clk)
+{
+ return at32ap7000_osc_rates[clk->index];
+}
+
+static unsigned long pll_get_rate(struct clk *clk, unsigned long control)
+{
+ unsigned long div, mul, rate;
+
+ if (!(control & SM_BIT(PLLEN)))
+ return 0;
+
+ div = SM_BFEXT(PLLDIV, control) + 1;
+ mul = SM_BFEXT(PLLMUL, control) + 1;
+
+ rate = clk->parent->get_rate(clk->parent);
+ rate = (rate + div / 2) / div;
+ rate *= mul;
+
+ return rate;
+}
+
+static unsigned long pll0_get_rate(struct clk *clk)
+{
+ u32 control;
+
+ control = sm_readl(&system_manager, PM_PLL0);
+
+ return pll_get_rate(clk, control);
+}
+
+static unsigned long pll1_get_rate(struct clk *clk)
+{
+ u32 control;
+
+ control = sm_readl(&system_manager, PM_PLL1);
+
+ return pll_get_rate(clk, control);
+}
+
+/*
+ * The AT32AP7000 has five primary clock sources: One 32kHz
+ * oscillator, two crystal oscillators and two PLLs.
+ */
+static struct clk osc32k = {
+ .name = "osc32k",
+ .get_rate = osc_get_rate,
+ .users = 1,
+ .index = 0,
+};
+static struct clk osc0 = {
+ .name = "osc0",
+ .get_rate = osc_get_rate,
+ .users = 1,
+ .index = 1,
+};
+static struct clk osc1 = {
+ .name = "osc1",
+ .get_rate = osc_get_rate,
+ .index = 2,
+};
+static struct clk pll0 = {
+ .name = "pll0",
+ .get_rate = pll0_get_rate,
+ .parent = &osc0,
+};
+static struct clk pll1 = {
+ .name = "pll1",
+ .get_rate = pll1_get_rate,
+ .parent = &osc0,
+};
+
+/*
+ * The main clock can be either osc0 or pll0. The boot loader may
+ * have chosen one for us, so we don't really know which one until we
+ * have a look at the SM.
+ */
+static struct clk *main_clock;
+
+/*
+ * Synchronous clocks are generated from the main clock. The clocks
+ * must satisfy the constraint
+ * fCPU >= fHSB >= fPB
+ * i.e. each clock must not be faster than its parent.
+ */
+static unsigned long bus_clk_get_rate(struct clk *clk, unsigned int shift)
+{
+ return main_clock->get_rate(main_clock) >> shift;
+};
+
+static void cpu_clk_mode(struct clk *clk, int enabled)
+{
+ struct at32_sm *sm = &system_manager;
+ unsigned long flags;
+ u32 mask;
+
+ spin_lock_irqsave(&sm->lock, flags);
+ mask = sm_readl(sm, PM_CPU_MASK);
+ if (enabled)
+ mask |= 1 << clk->index;
+ else
+ mask &= ~(1 << clk->index);
+ sm_writel(sm, PM_CPU_MASK, mask);
+ spin_unlock_irqrestore(&sm->lock, flags);
+}
+
+static unsigned long cpu_clk_get_rate(struct clk *clk)
+{
+ unsigned long cksel, shift = 0;
+
+ cksel = sm_readl(&system_manager, PM_CKSEL);
+ if (cksel & SM_BIT(CPUDIV))
+ shift = SM_BFEXT(CPUSEL, cksel) + 1;
+
+ return bus_clk_get_rate(clk, shift);
+}
+
+static void hsb_clk_mode(struct clk *clk, int enabled)
+{
+ struct at32_sm *sm = &system_manager;
+ unsigned long flags;
+ u32 mask;
+
+ spin_lock_irqsave(&sm->lock, flags);
+ mask = sm_readl(sm, PM_HSB_MASK);
+ if (enabled)
+ mask |= 1 << clk->index;
+ else
+ mask &= ~(1 << clk->index);
+ sm_writel(sm, PM_HSB_MASK, mask);
+ spin_unlock_irqrestore(&sm->lock, flags);
+}
+
+static unsigned long hsb_clk_get_rate(struct clk *clk)
+{
+ unsigned long cksel, shift = 0;
+
+ cksel = sm_readl(&system_manager, PM_CKSEL);
+ if (cksel & SM_BIT(HSBDIV))
+ shift = SM_BFEXT(HSBSEL, cksel) + 1;
+
+ return bus_clk_get_rate(clk, shift);
+}
+
+static void pba_clk_mode(struct clk *clk, int enabled)
+{
+ struct at32_sm *sm = &system_manager;
+ unsigned long flags;
+ u32 mask;
+
+ spin_lock_irqsave(&sm->lock, flags);
+ mask = sm_readl(sm, PM_PBA_MASK);
+ if (enabled)
+ mask |= 1 << clk->index;
+ else
+ mask &= ~(1 << clk->index);
+ sm_writel(sm, PM_PBA_MASK, mask);
+ spin_unlock_irqrestore(&sm->lock, flags);
+}
+
+static unsigned long pba_clk_get_rate(struct clk *clk)
+{
+ unsigned long cksel, shift = 0;
+
+ cksel = sm_readl(&system_manager, PM_CKSEL);
+ if (cksel & SM_BIT(PBADIV))
+ shift = SM_BFEXT(PBASEL, cksel) + 1;
+
+ return bus_clk_get_rate(clk, shift);
+}
+
+static void pbb_clk_mode(struct clk *clk, int enabled)
+{
+ struct at32_sm *sm = &system_manager;
+ unsigned long flags;
+ u32 mask;
+
+ spin_lock_irqsave(&sm->lock, flags);
+ mask = sm_readl(sm, PM_PBB_MASK);
+ if (enabled)
+ mask |= 1 << clk->index;
+ else
+ mask &= ~(1 << clk->index);
+ sm_writel(sm, PM_PBB_MASK, mask);
+ spin_unlock_irqrestore(&sm->lock, flags);
+}
+
+static unsigned long pbb_clk_get_rate(struct clk *clk)
+{
+ unsigned long cksel, shift = 0;
+
+ cksel = sm_readl(&system_manager, PM_CKSEL);
+ if (cksel & SM_BIT(PBBDIV))
+ shift = SM_BFEXT(PBBSEL, cksel) + 1;
+
+ return bus_clk_get_rate(clk, shift);
+}
+
+static struct clk cpu_clk = {
+ .name = "cpu",
+ .get_rate = cpu_clk_get_rate,
+ .users = 1,
+};
+static struct clk hsb_clk = {
+ .name = "hsb",
+ .parent = &cpu_clk,
+ .get_rate = hsb_clk_get_rate,
+};
+static struct clk pba_clk = {
+ .name = "pba",
+ .parent = &hsb_clk,
+ .mode = hsb_clk_mode,
+ .get_rate = pba_clk_get_rate,
+ .index = 1,
+};
+static struct clk pbb_clk = {
+ .name = "pbb",
+ .parent = &hsb_clk,
+ .mode = hsb_clk_mode,
+ .get_rate = pbb_clk_get_rate,
+ .users = 1,
+ .index = 2,
+};
+
+/* --------------------------------------------------------------------
+ * Generic Clock operations
+ * -------------------------------------------------------------------- */
+
+static void genclk_mode(struct clk *clk, int enabled)
+{
+ u32 control;
+
+ BUG_ON(clk->index > 7);
+
+ control = sm_readl(&system_manager, PM_GCCTRL + 4 * clk->index);
+ if (enabled)
+ control |= SM_BIT(CEN);
+ else
+ control &= ~SM_BIT(CEN);
+ sm_writel(&system_manager, PM_GCCTRL + 4 * clk->index, control);
+}
+
+static unsigned long genclk_get_rate(struct clk *clk)
+{
+ u32 control;
+ unsigned long div = 1;
+
+ BUG_ON(clk->index > 7);
+
+ if (!clk->parent)
+ return 0;
+
+ control = sm_readl(&system_manager, PM_GCCTRL + 4 * clk->index);
+ if (control & SM_BIT(DIVEN))
+ div = 2 * (SM_BFEXT(DIV, control) + 1);
+
+ return clk->parent->get_rate(clk->parent) / div;
+}
+
+static long genclk_set_rate(struct clk *clk, unsigned long rate, int apply)
+{
+ u32 control;
+ unsigned long parent_rate, actual_rate, div;
+
+ BUG_ON(clk->index > 7);
+
+ if (!clk->parent)
+ return 0;
+
+ parent_rate = clk->parent->get_rate(clk->parent);
+ control = sm_readl(&system_manager, PM_GCCTRL + 4 * clk->index);
+
+ if (rate > 3 * parent_rate / 4) {
+ actual_rate = parent_rate;
+ control &= ~SM_BIT(DIVEN);
+ } else {
+ div = (parent_rate + rate) / (2 * rate) - 1;
+ control = SM_BFINS(DIV, div, control) | SM_BIT(DIVEN);
+ actual_rate = parent_rate / (2 * (div + 1));
+ }
+
+ printk("clk %s: new rate %lu (actual rate %lu)\n",
+ clk->name, rate, actual_rate);
+
+ if (apply)
+ sm_writel(&system_manager, PM_GCCTRL + 4 * clk->index,
+ control);
+
+ return actual_rate;
+}
+
+int genclk_set_parent(struct clk *clk, struct clk *parent)
+{
+ u32 control;
+
+ BUG_ON(clk->index > 7);
+
+ printk("clk %s: new parent %s (was %s)\n",
+ clk->name, parent->name,
+ clk->parent ? clk->parent->name : "(null)");
+
+ control = sm_readl(&system_manager, PM_GCCTRL + 4 * clk->index);
+
+ if (parent == &osc1 || parent == &pll1)
+ control |= SM_BIT(OSCSEL);
+ else if (parent == &osc0 || parent == &pll0)
+ control &= ~SM_BIT(OSCSEL);
+ else
+ return -EINVAL;
+
+ if (parent == &pll0 || parent == &pll1)
+ control |= SM_BIT(PLLSEL);
+ else
+ control &= ~SM_BIT(PLLSEL);
+
+ sm_writel(&system_manager, PM_GCCTRL + 4 * clk->index, control);
+ clk->parent = parent;
+
+ return 0;
+}
+
+/* --------------------------------------------------------------------
+ * System peripherals
+ * -------------------------------------------------------------------- */
+static struct resource sm_resource[] = {
+ PBMEM(0xfff00000),
+ NAMED_IRQ(19, "eim"),
+ NAMED_IRQ(20, "pm"),
+ NAMED_IRQ(21, "rtc"),
+};
+struct platform_device at32_sm_device = {
+ .name = "sm",
+ .id = 0,
+ .resource = sm_resource,
+ .num_resources = ARRAY_SIZE(sm_resource),
+};
+DEV_CLK(pclk, at32_sm, pbb, 0);
+
+static struct resource intc0_resource[] = {
+ PBMEM(0xfff00400),
+};
+struct platform_device at32_intc0_device = {
+ .name = "intc",
+ .id = 0,
+ .resource = intc0_resource,
+ .num_resources = ARRAY_SIZE(intc0_resource),
+};
+DEV_CLK(pclk, at32_intc0, pbb, 1);
+
+static struct clk ebi_clk = {
+ .name = "ebi",
+ .parent = &hsb_clk,
+ .mode = hsb_clk_mode,
+ .get_rate = hsb_clk_get_rate,
+ .users = 1,
+};
+static struct clk hramc_clk = {
+ .name = "hramc",
+ .parent = &hsb_clk,
+ .mode = hsb_clk_mode,
+ .get_rate = hsb_clk_get_rate,
+ .users = 1,
+};
+
+static struct resource smc0_resource[] = {
+ PBMEM(0xfff03400),
+};
+DEFINE_DEV(smc, 0);
+DEV_CLK(pclk, smc0, pbb, 13);
+DEV_CLK(mck, smc0, hsb, 0);
+
+static struct platform_device pdc_device = {
+ .name = "pdc",
+ .id = 0,
+};
+DEV_CLK(hclk, pdc, hsb, 4);
+DEV_CLK(pclk, pdc, pba, 16);
+
+static struct clk pico_clk = {
+ .name = "pico",
+ .parent = &cpu_clk,
+ .mode = cpu_clk_mode,
+ .get_rate = cpu_clk_get_rate,
+ .users = 1,
+};
+
+/* --------------------------------------------------------------------
+ * PIO
+ * -------------------------------------------------------------------- */
+
+static struct resource pio0_resource[] = {
+ PBMEM(0xffe02800),
+ IRQ(13),
+};
+DEFINE_DEV(pio, 0);
+DEV_CLK(mck, pio0, pba, 10);
+
+static struct resource pio1_resource[] = {
+ PBMEM(0xffe02c00),
+ IRQ(14),
+};
+DEFINE_DEV(pio, 1);
+DEV_CLK(mck, pio1, pba, 11);
+
+static struct resource pio2_resource[] = {
+ PBMEM(0xffe03000),
+ IRQ(15),
+};
+DEFINE_DEV(pio, 2);
+DEV_CLK(mck, pio2, pba, 12);
+
+static struct resource pio3_resource[] = {
+ PBMEM(0xffe03400),
+ IRQ(16),
+};
+DEFINE_DEV(pio, 3);
+DEV_CLK(mck, pio3, pba, 13);
+
+void __init at32_add_system_devices(void)
+{
+ system_manager.eim_first_irq = NR_INTERNAL_IRQS;
+
+ platform_device_register(&at32_sm_device);
+ platform_device_register(&at32_intc0_device);
+ platform_device_register(&smc0_device);
+ platform_device_register(&pdc_device);
+
+ platform_device_register(&pio0_device);
+ platform_device_register(&pio1_device);
+ platform_device_register(&pio2_device);
+ platform_device_register(&pio3_device);
+}
+
+/* --------------------------------------------------------------------
+ * USART
+ * -------------------------------------------------------------------- */
+
+static struct resource usart0_resource[] = {
+ PBMEM(0xffe00c00),
+ IRQ(7),
+};
+DEFINE_DEV(usart, 0);
+DEV_CLK(usart, usart0, pba, 4);
+
+static struct resource usart1_resource[] = {
+ PBMEM(0xffe01000),
+ IRQ(7),
+};
+DEFINE_DEV(usart, 1);
+DEV_CLK(usart, usart1, pba, 4);
+
+static struct resource usart2_resource[] = {
+ PBMEM(0xffe01400),
+ IRQ(8),
+};
+DEFINE_DEV(usart, 2);
+DEV_CLK(usart, usart2, pba, 5);
+
+static struct resource usart3_resource[] = {
+ PBMEM(0xffe01800),
+ IRQ(9),
+};
+DEFINE_DEV(usart, 3);
+DEV_CLK(usart, usart3, pba, 6);
+
+static inline void configure_usart0_pins(void)
+{
+ portmux_set_func(PIOA, 8, FUNC_B); /* RXD */
+ portmux_set_func(PIOA, 9, FUNC_B); /* TXD */
+}
+
+static inline void configure_usart1_pins(void)
+{
+ portmux_set_func(PIOA, 17, FUNC_A); /* RXD */
+ portmux_set_func(PIOA, 18, FUNC_A); /* TXD */
+}
+
+static inline void configure_usart2_pins(void)
+{
+ portmux_set_func(PIOB, 26, FUNC_B); /* RXD */
+ portmux_set_func(PIOB, 27, FUNC_B); /* TXD */
+}
+
+static inline void configure_usart3_pins(void)
+{
+ portmux_set_func(PIOB, 18, FUNC_B); /* RXD */
+ portmux_set_func(PIOB, 17, FUNC_B); /* TXD */
+}
+
+static struct platform_device *setup_usart(unsigned int id)
+{
+ struct platform_device *pdev;
+
+ switch (id) {
+ case 0:
+ pdev = &usart0_device;
+ configure_usart0_pins();
+ break;
+ case 1:
+ pdev = &usart1_device;
+ configure_usart1_pins();
+ break;
+ case 2:
+ pdev = &usart2_device;
+ configure_usart2_pins();
+ break;
+ case 3:
+ pdev = &usart3_device;
+ configure_usart3_pins();
+ break;
+ default:
+ pdev = NULL;
+ break;
+ }
+
+ return pdev;
+}
+
+struct platform_device *__init at32_add_device_usart(unsigned int id)
+{
+ struct platform_device *pdev;
+
+ pdev = setup_usart(id);
+ if (pdev)
+ platform_device_register(pdev);
+
+ return pdev;
+}
+
+struct platform_device *at91_default_console_device;
+
+void __init at32_setup_serial_console(unsigned int usart_id)
+{
+ at91_default_console_device = setup_usart(usart_id);
+}
+
+/* --------------------------------------------------------------------
+ * Ethernet
+ * -------------------------------------------------------------------- */
+
+static struct eth_platform_data macb0_data;
+static struct resource macb0_resource[] = {
+ PBMEM(0xfff01800),
+ IRQ(25),
+};
+DEFINE_DEV_DATA(macb, 0);
+DEV_CLK(hclk, macb0, hsb, 8);
+DEV_CLK(pclk, macb0, pbb, 6);
+
+struct platform_device *__init
+at32_add_device_eth(unsigned int id, struct eth_platform_data *data)
+{
+ struct platform_device *pdev;
+
+ switch (id) {
+ case 0:
+ pdev = &macb0_device;
+
+ portmux_set_func(PIOC, 3, FUNC_A); /* TXD0 */
+ portmux_set_func(PIOC, 4, FUNC_A); /* TXD1 */
+ portmux_set_func(PIOC, 7, FUNC_A); /* TXEN */
+ portmux_set_func(PIOC, 8, FUNC_A); /* TXCK */
+ portmux_set_func(PIOC, 9, FUNC_A); /* RXD0 */
+ portmux_set_func(PIOC, 10, FUNC_A); /* RXD1 */
+ portmux_set_func(PIOC, 13, FUNC_A); /* RXER */
+ portmux_set_func(PIOC, 15, FUNC_A); /* RXDV */
+ portmux_set_func(PIOC, 16, FUNC_A); /* MDC */
+ portmux_set_func(PIOC, 17, FUNC_A); /* MDIO */
+
+ if (!data->is_rmii) {
+ portmux_set_func(PIOC, 0, FUNC_A); /* COL */
+ portmux_set_func(PIOC, 1, FUNC_A); /* CRS */
+ portmux_set_func(PIOC, 2, FUNC_A); /* TXER */
+ portmux_set_func(PIOC, 5, FUNC_A); /* TXD2 */
+ portmux_set_func(PIOC, 6, FUNC_A); /* TXD3 */
+ portmux_set_func(PIOC, 11, FUNC_A); /* RXD2 */
+ portmux_set_func(PIOC, 12, FUNC_A); /* RXD3 */
+ portmux_set_func(PIOC, 14, FUNC_A); /* RXCK */
+ portmux_set_func(PIOC, 18, FUNC_A); /* SPD */
+ }
+ break;
+
+ default:
+ return NULL;
+ }
+
+ memcpy(pdev->dev.platform_data, data, sizeof(struct eth_platform_data));
+ platform_device_register(pdev);
+
+ return pdev;
+}
+
+/* --------------------------------------------------------------------
+ * SPI
+ * -------------------------------------------------------------------- */
+static struct resource spi0_resource[] = {
+ PBMEM(0xffe00000),
+ IRQ(3),
+};
+DEFINE_DEV(spi, 0);
+DEV_CLK(mck, spi0, pba, 0);
+
+struct platform_device *__init at32_add_device_spi(unsigned int id)
+{
+ struct platform_device *pdev;
+
+ switch (id) {
+ case 0:
+ pdev = &spi0_device;
+ portmux_set_func(PIOA, 0, FUNC_A); /* MISO */
+ portmux_set_func(PIOA, 1, FUNC_A); /* MOSI */
+ portmux_set_func(PIOA, 2, FUNC_A); /* SCK */
+ portmux_set_func(PIOA, 3, FUNC_A); /* NPCS0 */
+ portmux_set_func(PIOA, 4, FUNC_A); /* NPCS1 */
+ portmux_set_func(PIOA, 5, FUNC_A); /* NPCS2 */
+ break;
+
+ default:
+ return NULL;
+ }
+
+ platform_device_register(pdev);
+ return pdev;
+}
+
+/* --------------------------------------------------------------------
+ * LCDC
+ * -------------------------------------------------------------------- */
+static struct lcdc_platform_data lcdc0_data;
+static struct resource lcdc0_resource[] = {
+ {
+ .start = 0xff000000,
+ .end = 0xff000fff,
+ .flags = IORESOURCE_MEM,
+ },
+ IRQ(1),
+};
+DEFINE_DEV_DATA(lcdc, 0);
+DEV_CLK(hclk, lcdc0, hsb, 7);
+static struct clk lcdc0_pixclk = {
+ .name = "pixclk",
+ .dev = &lcdc0_device.dev,
+ .mode = genclk_mode,
+ .get_rate = genclk_get_rate,
+ .set_rate = genclk_set_rate,
+ .set_parent = genclk_set_parent,
+ .index = 7,
+};
+
+struct platform_device *__init
+at32_add_device_lcdc(unsigned int id, struct lcdc_platform_data *data)
+{
+ struct platform_device *pdev;
+
+ switch (id) {
+ case 0:
+ pdev = &lcdc0_device;
+ portmux_set_func(PIOC, 19, FUNC_A); /* CC */
+ portmux_set_func(PIOC, 20, FUNC_A); /* HSYNC */
+ portmux_set_func(PIOC, 21, FUNC_A); /* PCLK */
+ portmux_set_func(PIOC, 22, FUNC_A); /* VSYNC */
+ portmux_set_func(PIOC, 23, FUNC_A); /* DVAL */
+ portmux_set_func(PIOC, 24, FUNC_A); /* MODE */
+ portmux_set_func(PIOC, 25, FUNC_A); /* PWR */
+ portmux_set_func(PIOC, 26, FUNC_A); /* DATA0 */
+ portmux_set_func(PIOC, 27, FUNC_A); /* DATA1 */
+ portmux_set_func(PIOC, 28, FUNC_A); /* DATA2 */
+ portmux_set_func(PIOC, 29, FUNC_A); /* DATA3 */
+ portmux_set_func(PIOC, 30, FUNC_A); /* DATA4 */
+ portmux_set_func(PIOC, 31, FUNC_A); /* DATA5 */
+ portmux_set_func(PIOD, 0, FUNC_A); /* DATA6 */
+ portmux_set_func(PIOD, 1, FUNC_A); /* DATA7 */
+ portmux_set_func(PIOD, 2, FUNC_A); /* DATA8 */
+ portmux_set_func(PIOD, 3, FUNC_A); /* DATA9 */
+ portmux_set_func(PIOD, 4, FUNC_A); /* DATA10 */
+ portmux_set_func(PIOD, 5, FUNC_A); /* DATA11 */
+ portmux_set_func(PIOD, 6, FUNC_A); /* DATA12 */
+ portmux_set_func(PIOD, 7, FUNC_A); /* DATA13 */
+ portmux_set_func(PIOD, 8, FUNC_A); /* DATA14 */
+ portmux_set_func(PIOD, 9, FUNC_A); /* DATA15 */
+ portmux_set_func(PIOD, 10, FUNC_A); /* DATA16 */
+ portmux_set_func(PIOD, 11, FUNC_A); /* DATA17 */
+ portmux_set_func(PIOD, 12, FUNC_A); /* DATA18 */
+ portmux_set_func(PIOD, 13, FUNC_A); /* DATA19 */
+ portmux_set_func(PIOD, 14, FUNC_A); /* DATA20 */
+ portmux_set_func(PIOD, 15, FUNC_A); /* DATA21 */
+ portmux_set_func(PIOD, 16, FUNC_A); /* DATA22 */
+ portmux_set_func(PIOD, 17, FUNC_A); /* DATA23 */
+
+ clk_set_parent(&lcdc0_pixclk, &pll0);
+ clk_set_rate(&lcdc0_pixclk, clk_get_rate(&pll0));
+ break;
+
+ default:
+ return NULL;
+ }
+
+ memcpy(pdev->dev.platform_data, data,
+ sizeof(struct lcdc_platform_data));
+
+ platform_device_register(pdev);
+ return pdev;
+}
+
+struct clk *at32_clock_list[] = {
+ &osc32k,
+ &osc0,
+ &osc1,
+ &pll0,
+ &pll1,
+ &cpu_clk,
+ &hsb_clk,
+ &pba_clk,
+ &pbb_clk,
+ &at32_sm_pclk,
+ &at32_intc0_pclk,
+ &ebi_clk,
+ &hramc_clk,
+ &smc0_pclk,
+ &smc0_mck,
+ &pdc_hclk,
+ &pdc_pclk,
+ &pico_clk,
+ &pio0_mck,
+ &pio1_mck,
+ &pio2_mck,
+ &pio3_mck,
+ &usart0_usart,
+ &usart1_usart,
+ &usart2_usart,
+ &usart3_usart,
+ &macb0_hclk,
+ &macb0_pclk,
+ &spi0_mck,
+ &lcdc0_hclk,
+ &lcdc0_pixclk,
+};
+unsigned int at32_nr_clocks = ARRAY_SIZE(at32_clock_list);
+
+void __init at32_portmux_init(void)
+{
+ at32_init_pio(&pio0_device);
+ at32_init_pio(&pio1_device);
+ at32_init_pio(&pio2_device);
+ at32_init_pio(&pio3_device);
+}
+
+void __init at32_clock_init(void)
+{
+ struct at32_sm *sm = &system_manager;
+ u32 cpu_mask = 0, hsb_mask = 0, pba_mask = 0, pbb_mask = 0;
+ int i;
+
+ if (sm_readl(sm, PM_MCCTRL) & SM_BIT(PLLSEL))
+ main_clock = &pll0;
+ else
+ main_clock = &osc0;
+
+ if (sm_readl(sm, PM_PLL0) & SM_BIT(PLLOSC))
+ pll0.parent = &osc1;
+ if (sm_readl(sm, PM_PLL1) & SM_BIT(PLLOSC))
+ pll1.parent = &osc1;
+
+ /*
+ * Turn on all clocks that have at least one user already, and
+ * turn off everything else. We only do this for module
+ * clocks, and even though it isn't particularly pretty to
+ * check the address of the mode function, it should do the
+ * trick...
+ */
+ for (i = 0; i < ARRAY_SIZE(at32_clock_list); i++) {
+ struct clk *clk = at32_clock_list[i];
+
+ if (clk->mode == &cpu_clk_mode)
+ cpu_mask |= 1 << clk->index;
+ else if (clk->mode == &hsb_clk_mode)
+ hsb_mask |= 1 << clk->index;
+ else if (clk->mode == &pba_clk_mode)
+ pba_mask |= 1 << clk->index;
+ else if (clk->mode == &pbb_clk_mode)
+ pbb_mask |= 1 << clk->index;
+ }
+
+ sm_writel(sm, PM_CPU_MASK, cpu_mask);
+ sm_writel(sm, PM_HSB_MASK, hsb_mask);
+ sm_writel(sm, PM_PBA_MASK, pba_mask);
+ sm_writel(sm, PM_PBB_MASK, pbb_mask);
+}
diff --git a/arch/avr32/mach-at32ap/clock.c b/arch/avr32/mach-at32ap/clock.c
new file mode 100644
index 00000000000..3d0d1097389
--- /dev/null
+++ b/arch/avr32/mach-at32ap/clock.c
@@ -0,0 +1,148 @@
+/*
+ * Clock management for AT32AP CPUs
+ *
+ * Copyright (C) 2006 Atmel Corporation
+ *
+ * Based on arch/arm/mach-at91rm9200/clock.c
+ * Copyright (C) 2005 David Brownell
+ * Copyright (C) 2005 Ivan Kokshaysky
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/device.h>
+#include <linux/string.h>
+
+#include "clock.h"
+
+static spinlock_t clk_lock = SPIN_LOCK_UNLOCKED;
+
+struct clk *clk_get(struct device *dev, const char *id)
+{
+ int i;
+
+ for (i = 0; i < at32_nr_clocks; i++) {
+ struct clk *clk = at32_clock_list[i];
+
+ if (clk->dev == dev && strcmp(id, clk->name) == 0)
+ return clk;
+ }
+
+ return ERR_PTR(-ENOENT);
+}
+EXPORT_SYMBOL(clk_get);
+
+void clk_put(struct clk *clk)
+{
+ /* clocks are static for now, we can't free them */
+}
+EXPORT_SYMBOL(clk_put);
+
+static void __clk_enable(struct clk *clk)
+{
+ if (clk->parent)
+ __clk_enable(clk->parent);
+ if (clk->users++ == 0 && clk->mode)
+ clk->mode(clk, 1);
+}
+
+int clk_enable(struct clk *clk)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&clk_lock, flags);
+ __clk_enable(clk);
+ spin_unlock_irqrestore(&clk_lock, flags);
+
+ return 0;
+}
+EXPORT_SYMBOL(clk_enable);
+
+static void __clk_disable(struct clk *clk)
+{
+ BUG_ON(clk->users == 0);
+
+ if (--clk->users == 0 && clk->mode)
+ clk->mode(clk, 0);
+ if (clk->parent)
+ __clk_disable(clk->parent);
+}
+
+void clk_disable(struct clk *clk)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&clk_lock, flags);
+ __clk_disable(clk);
+ spin_unlock_irqrestore(&clk_lock, flags);
+}
+EXPORT_SYMBOL(clk_disable);
+
+unsigned long clk_get_rate(struct clk *clk)
+{
+ unsigned long flags;
+ unsigned long rate;
+
+ spin_lock_irqsave(&clk_lock, flags);
+ rate = clk->get_rate(clk);
+ spin_unlock_irqrestore(&clk_lock, flags);
+
+ return rate;
+}
+EXPORT_SYMBOL(clk_get_rate);
+
+long clk_round_rate(struct clk *clk, unsigned long rate)
+{
+ unsigned long flags, actual_rate;
+
+ if (!clk->set_rate)
+ return -ENOSYS;
+
+ spin_lock_irqsave(&clk_lock, flags);
+ actual_rate = clk->set_rate(clk, rate, 0);
+ spin_unlock_irqrestore(&clk_lock, flags);
+
+ return actual_rate;
+}
+EXPORT_SYMBOL(clk_round_rate);
+
+int clk_set_rate(struct clk *clk, unsigned long rate)
+{
+ unsigned long flags;
+ long ret;
+
+ if (!clk->set_rate)
+ return -ENOSYS;
+
+ spin_lock_irqsave(&clk_lock, flags);
+ ret = clk->set_rate(clk, rate, 1);
+ spin_unlock_irqrestore(&clk_lock, flags);
+
+ return (ret < 0) ? ret : 0;
+}
+EXPORT_SYMBOL(clk_set_rate);
+
+int clk_set_parent(struct clk *clk, struct clk *parent)
+{
+ unsigned long flags;
+ int ret;
+
+ if (!clk->set_parent)
+ return -ENOSYS;
+
+ spin_lock_irqsave(&clk_lock, flags);
+ ret = clk->set_parent(clk, parent);
+ spin_unlock_irqrestore(&clk_lock, flags);
+
+ return ret;
+}
+EXPORT_SYMBOL(clk_set_parent);
+
+struct clk *clk_get_parent(struct clk *clk)
+{
+ return clk->parent;
+}
+EXPORT_SYMBOL(clk_get_parent);
diff --git a/arch/avr32/mach-at32ap/clock.h b/arch/avr32/mach-at32ap/clock.h
new file mode 100644
index 00000000000..f953f044ba4
--- /dev/null
+++ b/arch/avr32/mach-at32ap/clock.h
@@ -0,0 +1,30 @@
+/*
+ * Clock management for AT32AP CPUs
+ *
+ * Copyright (C) 2006 Atmel Corporation
+ *
+ * Based on arch/arm/mach-at91rm9200/clock.c
+ * Copyright (C) 2005 David Brownell
+ * Copyright (C) 2005 Ivan Kokshaysky
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/clk.h>
+
+struct clk {
+ const char *name; /* Clock name/function */
+ struct device *dev; /* Device the clock is used by */
+ struct clk *parent; /* Parent clock, if any */
+ void (*mode)(struct clk *clk, int enabled);
+ unsigned long (*get_rate)(struct clk *clk);
+ long (*set_rate)(struct clk *clk, unsigned long rate,
+ int apply);
+ int (*set_parent)(struct clk *clk, struct clk *parent);
+ u16 users; /* Enabled if non-zero */
+ u16 index; /* Sibling index */
+};
+
+extern struct clk *at32_clock_list[];
+extern unsigned int at32_nr_clocks;
diff --git a/arch/avr32/mach-at32ap/extint.c b/arch/avr32/mach-at32ap/extint.c
new file mode 100644
index 00000000000..7da9c5f7a0e
--- /dev/null
+++ b/arch/avr32/mach-at32ap/extint.c
@@ -0,0 +1,171 @@
+/*
+ * External interrupt handling for AT32AP CPUs
+ *
+ * Copyright (C) 2006 Atmel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/platform_device.h>
+#include <linux/random.h>
+
+#include <asm/io.h>
+
+#include <asm/arch/sm.h>
+
+#include "sm.h"
+
+static void eim_ack_irq(unsigned int irq)
+{
+ struct at32_sm *sm = get_irq_chip_data(irq);
+ sm_writel(sm, EIM_ICR, 1 << (irq - sm->eim_first_irq));
+}
+
+static void eim_mask_irq(unsigned int irq)
+{
+ struct at32_sm *sm = get_irq_chip_data(irq);
+ sm_writel(sm, EIM_IDR, 1 << (irq - sm->eim_first_irq));
+}
+
+static void eim_mask_ack_irq(unsigned int irq)
+{
+ struct at32_sm *sm = get_irq_chip_data(irq);
+ sm_writel(sm, EIM_ICR, 1 << (irq - sm->eim_first_irq));
+ sm_writel(sm, EIM_IDR, 1 << (irq - sm->eim_first_irq));
+}
+
+static void eim_unmask_irq(unsigned int irq)
+{
+ struct at32_sm *sm = get_irq_chip_data(irq);
+ sm_writel(sm, EIM_IER, 1 << (irq - sm->eim_first_irq));
+}
+
+static int eim_set_irq_type(unsigned int irq, unsigned int flow_type)
+{
+ struct at32_sm *sm = get_irq_chip_data(irq);
+ unsigned int i = irq - sm->eim_first_irq;
+ u32 mode, edge, level;
+ unsigned long flags;
+ int ret = 0;
+
+ flow_type &= IRQ_TYPE_SENSE_MASK;
+
+ spin_lock_irqsave(&sm->lock, flags);
+
+ mode = sm_readl(sm, EIM_MODE);
+ edge = sm_readl(sm, EIM_EDGE);
+ level = sm_readl(sm, EIM_LEVEL);
+
+ switch (flow_type) {
+ case IRQ_TYPE_LEVEL_LOW:
+ mode |= 1 << i;
+ level &= ~(1 << i);
+ break;
+ case IRQ_TYPE_LEVEL_HIGH:
+ mode |= 1 << i;
+ level |= 1 << i;
+ break;
+ case IRQ_TYPE_EDGE_RISING:
+ mode &= ~(1 << i);
+ edge |= 1 << i;
+ break;
+ case IRQ_TYPE_EDGE_FALLING:
+ mode &= ~(1 << i);
+ edge &= ~(1 << i);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ sm_writel(sm, EIM_MODE, mode);
+ sm_writel(sm, EIM_EDGE, edge);
+ sm_writel(sm, EIM_LEVEL, level);
+
+ spin_unlock_irqrestore(&sm->lock, flags);
+
+ return ret;
+}
+
+struct irq_chip eim_chip = {
+ .name = "eim",
+ .ack = eim_ack_irq,
+ .mask = eim_mask_irq,
+ .mask_ack = eim_mask_ack_irq,
+ .unmask = eim_unmask_irq,
+ .set_type = eim_set_irq_type,
+};
+
+static void demux_eim_irq(unsigned int irq, struct irq_desc *desc,
+ struct pt_regs *regs)
+{
+ struct at32_sm *sm = desc->handler_data;
+ struct irq_desc *ext_desc;
+ unsigned long status, pending;
+ unsigned int i, ext_irq;
+
+ spin_lock(&sm->lock);
+
+ status = sm_readl(sm, EIM_ISR);
+ pending = status & sm_readl(sm, EIM_IMR);
+
+ while (pending) {
+ i = fls(pending) - 1;
+ pending &= ~(1 << i);
+
+ ext_irq = i + sm->eim_first_irq;
+ ext_desc = irq_desc + ext_irq;
+ ext_desc->handle_irq(ext_irq, ext_desc, regs);
+ }
+
+ spin_unlock(&sm->lock);
+}
+
+static int __init eim_init(void)
+{
+ struct at32_sm *sm = &system_manager;
+ unsigned int i;
+ unsigned int nr_irqs;
+ unsigned int int_irq;
+ u32 pattern;
+
+ /*
+ * The EIM is really the same module as SM, so register
+ * mapping, etc. has been taken care of already.
+ */
+
+ /*
+ * Find out how many interrupt lines that are actually
+ * implemented in hardware.
+ */
+ sm_writel(sm, EIM_IDR, ~0UL);
+ sm_writel(sm, EIM_MODE, ~0UL);
+ pattern = sm_readl(sm, EIM_MODE);
+ nr_irqs = fls(pattern);
+
+ sm->eim_chip = &eim_chip;
+
+ for (i = 0; i < nr_irqs; i++) {
+ set_irq_chip(sm->eim_first_irq + i, &eim_chip);
+ set_irq_chip_data(sm->eim_first_irq + i, sm);
+ }
+
+ int_irq = platform_get_irq_byname(sm->pdev, "eim");
+
+ set_irq_chained_handler(int_irq, demux_eim_irq);
+ set_irq_data(int_irq, sm);
+
+ printk("EIM: External Interrupt Module at 0x%p, IRQ %u\n",
+ sm->regs, int_irq);
+ printk("EIM: Handling %u external IRQs, starting with IRQ %u\n",
+ nr_irqs, sm->eim_first_irq);
+
+ return 0;
+}
+arch_initcall(eim_init);
diff --git a/arch/avr32/mach-at32ap/hsmc.c b/arch/avr32/mach-at32ap/hsmc.c
new file mode 100644
index 00000000000..7691721928a
--- /dev/null
+++ b/arch/avr32/mach-at32ap/hsmc.c
@@ -0,0 +1,164 @@
+/*
+ * Static Memory Controller for AT32 chips
+ *
+ * Copyright (C) 2006 Atmel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#define DEBUG
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include <asm/io.h>
+#include <asm/arch/smc.h>
+
+#include "hsmc.h"
+
+#define NR_CHIP_SELECTS 6
+
+struct hsmc {
+ void __iomem *regs;
+ struct clk *pclk;
+ struct clk *mck;
+};
+
+static struct hsmc *hsmc;
+
+int smc_set_configuration(int cs, const struct smc_config *config)
+{
+ unsigned long mul;
+ unsigned long offset;
+ u32 setup, pulse, cycle, mode;
+
+ if (!hsmc)
+ return -ENODEV;
+ if (cs >= NR_CHIP_SELECTS)
+ return -EINVAL;
+
+ /*
+ * cycles = x / T = x * f
+ * = ((x * 1000000000) * ((f * 65536) / 1000000000)) / 65536
+ * = ((x * 1000000000) * (((f / 10000) * 65536) / 100000)) / 65536
+ */
+ mul = (clk_get_rate(hsmc->mck) / 10000) << 16;
+ mul /= 100000;
+
+#define ns2cyc(x) ((((x) * mul) + 65535) >> 16)
+
+ setup = (HSMC_BF(NWE_SETUP, ns2cyc(config->nwe_setup))
+ | HSMC_BF(NCS_WR_SETUP, ns2cyc(config->ncs_write_setup))
+ | HSMC_BF(NRD_SETUP, ns2cyc(config->nrd_setup))
+ | HSMC_BF(NCS_RD_SETUP, ns2cyc(config->ncs_read_setup)));
+ pulse = (HSMC_BF(NWE_PULSE, ns2cyc(config->nwe_pulse))
+ | HSMC_BF(NCS_WR_PULSE, ns2cyc(config->ncs_write_pulse))
+ | HSMC_BF(NRD_PULSE, ns2cyc(config->nrd_pulse))
+ | HSMC_BF(NCS_RD_PULSE, ns2cyc(config->ncs_read_pulse)));
+ cycle = (HSMC_BF(NWE_CYCLE, ns2cyc(config->write_cycle))
+ | HSMC_BF(NRD_CYCLE, ns2cyc(config->read_cycle)));
+
+ switch (config->bus_width) {
+ case 1:
+ mode = HSMC_BF(DBW, HSMC_DBW_8_BITS);
+ break;
+ case 2:
+ mode = HSMC_BF(DBW, HSMC_DBW_16_BITS);
+ break;
+ case 4:
+ mode = HSMC_BF(DBW, HSMC_DBW_32_BITS);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (config->nrd_controlled)
+ mode |= HSMC_BIT(READ_MODE);
+ if (config->nwe_controlled)
+ mode |= HSMC_BIT(WRITE_MODE);
+ if (config->byte_write)
+ mode |= HSMC_BIT(BAT);
+
+ pr_debug("smc cs%d: setup/%08x pulse/%08x cycle/%08x mode/%08x\n",
+ cs, setup, pulse, cycle, mode);
+
+ offset = cs * 0x10;
+ hsmc_writel(hsmc, SETUP0 + offset, setup);
+ hsmc_writel(hsmc, PULSE0 + offset, pulse);
+ hsmc_writel(hsmc, CYCLE0 + offset, cycle);
+ hsmc_writel(hsmc, MODE0 + offset, mode);
+ hsmc_readl(hsmc, MODE0); /* I/O barrier */
+
+ return 0;
+}
+EXPORT_SYMBOL(smc_set_configuration);
+
+static int hsmc_probe(struct platform_device *pdev)
+{
+ struct resource *regs;
+ struct clk *pclk, *mck;
+ int ret;
+
+ if (hsmc)
+ return -EBUSY;
+
+ regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!regs)
+ return -ENXIO;
+ pclk = clk_get(&pdev->dev, "pclk");
+ if (IS_ERR(pclk))
+ return PTR_ERR(pclk);
+ mck = clk_get(&pdev->dev, "mck");
+ if (IS_ERR(mck)) {
+ ret = PTR_ERR(mck);
+ goto out_put_pclk;
+ }
+
+ ret = -ENOMEM;
+ hsmc = kzalloc(sizeof(struct hsmc), GFP_KERNEL);
+ if (!hsmc)
+ goto out_put_clocks;
+
+ clk_enable(pclk);
+ clk_enable(mck);
+
+ hsmc->pclk = pclk;
+ hsmc->mck = mck;
+ hsmc->regs = ioremap(regs->start, regs->end - regs->start + 1);
+ if (!hsmc->regs)
+ goto out_disable_clocks;
+
+ dev_info(&pdev->dev, "Atmel Static Memory Controller at 0x%08lx\n",
+ (unsigned long)regs->start);
+
+ platform_set_drvdata(pdev, hsmc);
+
+ return 0;
+
+out_disable_clocks:
+ clk_disable(mck);
+ clk_disable(pclk);
+ kfree(hsmc);
+out_put_clocks:
+ clk_put(mck);
+out_put_pclk:
+ clk_put(pclk);
+ hsmc = NULL;
+ return ret;
+}
+
+static struct platform_driver hsmc_driver = {
+ .probe = hsmc_probe,
+ .driver = {
+ .name = "smc",
+ },
+};
+
+static int __init hsmc_init(void)
+{
+ return platform_driver_register(&hsmc_driver);
+}
+arch_initcall(hsmc_init);
diff --git a/arch/avr32/mach-at32ap/hsmc.h b/arch/avr32/mach-at32ap/hsmc.h
new file mode 100644
index 00000000000..5681276fafd
--- /dev/null
+++ b/arch/avr32/mach-at32ap/hsmc.h
@@ -0,0 +1,127 @@
+/*
+ * Register definitions for Atmel Static Memory Controller (SMC)
+ *
+ * Copyright (C) 2006 Atmel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef __ASM_AVR32_HSMC_H__
+#define __ASM_AVR32_HSMC_H__
+
+/* HSMC register offsets */
+#define HSMC_SETUP0 0x0000
+#define HSMC_PULSE0 0x0004
+#define HSMC_CYCLE0 0x0008
+#define HSMC_MODE0 0x000c
+#define HSMC_SETUP1 0x0010
+#define HSMC_PULSE1 0x0014
+#define HSMC_CYCLE1 0x0018
+#define HSMC_MODE1 0x001c
+#define HSMC_SETUP2 0x0020
+#define HSMC_PULSE2 0x0024
+#define HSMC_CYCLE2 0x0028
+#define HSMC_MODE2 0x002c
+#define HSMC_SETUP3 0x0030
+#define HSMC_PULSE3 0x0034
+#define HSMC_CYCLE3 0x0038
+#define HSMC_MODE3 0x003c
+#define HSMC_SETUP4 0x0040
+#define HSMC_PULSE4 0x0044
+#define HSMC_CYCLE4 0x0048
+#define HSMC_MODE4 0x004c
+#define HSMC_SETUP5 0x0050
+#define HSMC_PULSE5 0x0054
+#define HSMC_CYCLE5 0x0058
+#define HSMC_MODE5 0x005c
+
+/* Bitfields in SETUP0 */
+#define HSMC_NWE_SETUP_OFFSET 0
+#define HSMC_NWE_SETUP_SIZE 6
+#define HSMC_NCS_WR_SETUP_OFFSET 8
+#define HSMC_NCS_WR_SETUP_SIZE 6
+#define HSMC_NRD_SETUP_OFFSET 16
+#define HSMC_NRD_SETUP_SIZE 6
+#define HSMC_NCS_RD_SETUP_OFFSET 24
+#define HSMC_NCS_RD_SETUP_SIZE 6
+
+/* Bitfields in PULSE0 */
+#define HSMC_NWE_PULSE_OFFSET 0
+#define HSMC_NWE_PULSE_SIZE 7
+#define HSMC_NCS_WR_PULSE_OFFSET 8
+#define HSMC_NCS_WR_PULSE_SIZE 7
+#define HSMC_NRD_PULSE_OFFSET 16
+#define HSMC_NRD_PULSE_SIZE 7
+#define HSMC_NCS_RD_PULSE_OFFSET 24
+#define HSMC_NCS_RD_PULSE_SIZE 7
+
+/* Bitfields in CYCLE0 */
+#define HSMC_NWE_CYCLE_OFFSET 0
+#define HSMC_NWE_CYCLE_SIZE 9
+#define HSMC_NRD_CYCLE_OFFSET 16
+#define HSMC_NRD_CYCLE_SIZE 9
+
+/* Bitfields in MODE0 */
+#define HSMC_READ_MODE_OFFSET 0
+#define HSMC_READ_MODE_SIZE 1
+#define HSMC_WRITE_MODE_OFFSET 1
+#define HSMC_WRITE_MODE_SIZE 1
+#define HSMC_EXNW_MODE_OFFSET 4
+#define HSMC_EXNW_MODE_SIZE 2
+#define HSMC_BAT_OFFSET 8
+#define HSMC_BAT_SIZE 1
+#define HSMC_DBW_OFFSET 12
+#define HSMC_DBW_SIZE 2
+#define HSMC_TDF_CYCLES_OFFSET 16
+#define HSMC_TDF_CYCLES_SIZE 4
+#define HSMC_TDF_MODE_OFFSET 20
+#define HSMC_TDF_MODE_SIZE 1
+#define HSMC_PMEN_OFFSET 24
+#define HSMC_PMEN_SIZE 1
+#define HSMC_PS_OFFSET 28
+#define HSMC_PS_SIZE 2
+
+/* Constants for READ_MODE */
+#define HSMC_READ_MODE_NCS_CONTROLLED 0
+#define HSMC_READ_MODE_NRD_CONTROLLED 1
+
+/* Constants for WRITE_MODE */
+#define HSMC_WRITE_MODE_NCS_CONTROLLED 0
+#define HSMC_WRITE_MODE_NWE_CONTROLLED 1
+
+/* Constants for EXNW_MODE */
+#define HSMC_EXNW_MODE_DISABLED 0
+#define HSMC_EXNW_MODE_RESERVED 1
+#define HSMC_EXNW_MODE_FROZEN 2
+#define HSMC_EXNW_MODE_READY 3
+
+/* Constants for BAT */
+#define HSMC_BAT_BYTE_SELECT 0
+#define HSMC_BAT_BYTE_WRITE 1
+
+/* Constants for DBW */
+#define HSMC_DBW_8_BITS 0
+#define HSMC_DBW_16_BITS 1
+#define HSMC_DBW_32_BITS 2
+
+/* Bit manipulation macros */
+#define HSMC_BIT(name) \
+ (1 << HSMC_##name##_OFFSET)
+#define HSMC_BF(name,value) \
+ (((value) & ((1 << HSMC_##name##_SIZE) - 1)) \
+ << HSMC_##name##_OFFSET)
+#define HSMC_BFEXT(name,value) \
+ (((value) >> HSMC_##name##_OFFSET) \
+ & ((1 << HSMC_##name##_SIZE) - 1))
+#define HSMC_BFINS(name,value,old) \
+ (((old) & ~(((1 << HSMC_##name##_SIZE) - 1) \
+ << HSMC_##name##_OFFSET)) | HSMC_BF(name,value))
+
+/* Register access macros */
+#define hsmc_readl(port,reg) \
+ readl((port)->regs + HSMC_##reg)
+#define hsmc_writel(port,reg,value) \
+ writel((value), (port)->regs + HSMC_##reg)
+
+#endif /* __ASM_AVR32_HSMC_H__ */
diff --git a/arch/avr32/mach-at32ap/intc.c b/arch/avr32/mach-at32ap/intc.c
new file mode 100644
index 00000000000..74f8c9f2f03
--- /dev/null
+++ b/arch/avr32/mach-at32ap/intc.c
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2006 Atmel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/platform_device.h>
+
+#include <asm/io.h>
+
+#include "intc.h"
+
+struct intc {
+ void __iomem *regs;
+ struct irq_chip chip;
+};
+
+extern struct platform_device at32_intc0_device;
+
+/*
+ * TODO: We may be able to implement mask/unmask by setting IxM flags
+ * in the status register.
+ */
+static void intc_mask_irq(unsigned int irq)
+{
+
+}
+
+static void intc_unmask_irq(unsigned int irq)
+{
+
+}
+
+static struct intc intc0 = {
+ .chip = {
+ .name = "intc",
+ .mask = intc_mask_irq,
+ .unmask = intc_unmask_irq,
+ },
+};
+
+/*
+ * All interrupts go via intc at some point.
+ */
+asmlinkage void do_IRQ(int level, struct pt_regs *regs)
+{
+ struct irq_desc *desc;
+ unsigned int irq;
+ unsigned long status_reg;
+
+ local_irq_disable();
+
+ irq_enter();
+
+ irq = intc_readl(&intc0, INTCAUSE0 - 4 * level);
+ desc = irq_desc + irq;
+ desc->handle_irq(irq, desc, regs);
+
+ /*
+ * Clear all interrupt level masks so that we may handle
+ * interrupts during softirq processing. If this is a nested
+ * interrupt, interrupts must stay globally disabled until we
+ * return.
+ */
+ status_reg = sysreg_read(SR);
+ status_reg &= ~(SYSREG_BIT(I0M) | SYSREG_BIT(I1M)
+ | SYSREG_BIT(I2M) | SYSREG_BIT(I3M));
+ sysreg_write(SR, status_reg);
+
+ irq_exit();
+}
+
+void __init init_IRQ(void)
+{
+ extern void _evba(void);
+ extern void irq_level0(void);
+ struct resource *regs;
+ struct clk *pclk;
+ unsigned int i;
+ u32 offset, readback;
+
+ regs = platform_get_resource(&at32_intc0_device, IORESOURCE_MEM, 0);
+ if (!regs) {
+ printk(KERN_EMERG "intc: no mmio resource defined\n");
+ goto fail;
+ }
+ pclk = clk_get(&at32_intc0_device.dev, "pclk");
+ if (IS_ERR(pclk)) {
+ printk(KERN_EMERG "intc: no clock defined\n");
+ goto fail;
+ }
+
+ clk_enable(pclk);
+
+ intc0.regs = ioremap(regs->start, regs->end - regs->start + 1);
+ if (!intc0.regs) {
+ printk(KERN_EMERG "intc: failed to map registers (0x%08lx)\n",
+ (unsigned long)regs->start);
+ goto fail;
+ }
+
+ /*
+ * Initialize all interrupts to level 0 (lowest priority). The
+ * priority level may be changed by calling
+ * irq_set_priority().
+ *
+ */
+ offset = (unsigned long)&irq_level0 - (unsigned long)&_evba;
+ for (i = 0; i < NR_INTERNAL_IRQS; i++) {
+ intc_writel(&intc0, INTPR0 + 4 * i, offset);
+ readback = intc_readl(&intc0, INTPR0 + 4 * i);
+ if (readback == offset)
+ set_irq_chip_and_handler(i, &intc0.chip,
+ handle_simple_irq);
+ }
+
+ /* Unmask all interrupt levels */
+ sysreg_write(SR, (sysreg_read(SR)
+ & ~(SR_I3M | SR_I2M | SR_I1M | SR_I0M)));
+
+ return;
+
+fail:
+ panic("Interrupt controller initialization failed!\n");
+}
+
diff --git a/arch/avr32/mach-at32ap/intc.h b/arch/avr32/mach-at32ap/intc.h
new file mode 100644
index 00000000000..d289ca2fff1
--- /dev/null
+++ b/arch/avr32/mach-at32ap/intc.h
@@ -0,0 +1,327 @@
+/*
+ * Automatically generated by gen-header.xsl
+ */
+#ifndef __ASM_AVR32_PERIHP_INTC_H__
+#define __ASM_AVR32_PERIHP_INTC_H__
+
+#define INTC_NUM_INT_GRPS 33
+
+#define INTC_INTPR0 0x0
+# define INTC_INTPR0_INTLEV_OFFSET 30
+# define INTC_INTPR0_INTLEV_SIZE 2
+# define INTC_INTPR0_OFFSET_OFFSET 0
+# define INTC_INTPR0_OFFSET_SIZE 24
+#define INTC_INTREQ0 0x100
+# define INTC_INTREQ0_IREQUEST0_OFFSET 0
+# define INTC_INTREQ0_IREQUEST0_SIZE 1
+# define INTC_INTREQ0_IREQUEST1_OFFSET 1
+# define INTC_INTREQ0_IREQUEST1_SIZE 1
+#define INTC_INTPR1 0x4
+# define INTC_INTPR1_INTLEV_OFFSET 30
+# define INTC_INTPR1_INTLEV_SIZE 2
+# define INTC_INTPR1_OFFSET_OFFSET 0
+# define INTC_INTPR1_OFFSET_SIZE 24
+#define INTC_INTREQ1 0x104
+# define INTC_INTREQ1_IREQUEST32_OFFSET 0
+# define INTC_INTREQ1_IREQUEST32_SIZE 1
+# define INTC_INTREQ1_IREQUEST33_OFFSET 1
+# define INTC_INTREQ1_IREQUEST33_SIZE 1
+# define INTC_INTREQ1_IREQUEST34_OFFSET 2
+# define INTC_INTREQ1_IREQUEST34_SIZE 1
+# define INTC_INTREQ1_IREQUEST35_OFFSET 3
+# define INTC_INTREQ1_IREQUEST35_SIZE 1
+# define INTC_INTREQ1_IREQUEST36_OFFSET 4
+# define INTC_INTREQ1_IREQUEST36_SIZE 1
+# define INTC_INTREQ1_IREQUEST37_OFFSET 5
+# define INTC_INTREQ1_IREQUEST37_SIZE 1
+#define INTC_INTPR2 0x8
+# define INTC_INTPR2_INTLEV_OFFSET 30
+# define INTC_INTPR2_INTLEV_SIZE 2
+# define INTC_INTPR2_OFFSET_OFFSET 0
+# define INTC_INTPR2_OFFSET_SIZE 24
+#define INTC_INTREQ2 0x108
+# define INTC_INTREQ2_IREQUEST64_OFFSET 0
+# define INTC_INTREQ2_IREQUEST64_SIZE 1
+# define INTC_INTREQ2_IREQUEST65_OFFSET 1
+# define INTC_INTREQ2_IREQUEST65_SIZE 1
+# define INTC_INTREQ2_IREQUEST66_OFFSET 2
+# define INTC_INTREQ2_IREQUEST66_SIZE 1
+# define INTC_INTREQ2_IREQUEST67_OFFSET 3
+# define INTC_INTREQ2_IREQUEST67_SIZE 1
+# define INTC_INTREQ2_IREQUEST68_OFFSET 4
+# define INTC_INTREQ2_IREQUEST68_SIZE 1
+#define INTC_INTPR3 0xc
+# define INTC_INTPR3_INTLEV_OFFSET 30
+# define INTC_INTPR3_INTLEV_SIZE 2
+# define INTC_INTPR3_OFFSET_OFFSET 0
+# define INTC_INTPR3_OFFSET_SIZE 24
+#define INTC_INTREQ3 0x10c
+# define INTC_INTREQ3_IREQUEST96_OFFSET 0
+# define INTC_INTREQ3_IREQUEST96_SIZE 1
+#define INTC_INTPR4 0x10
+# define INTC_INTPR4_INTLEV_OFFSET 30
+# define INTC_INTPR4_INTLEV_SIZE 2
+# define INTC_INTPR4_OFFSET_OFFSET 0
+# define INTC_INTPR4_OFFSET_SIZE 24
+#define INTC_INTREQ4 0x110
+# define INTC_INTREQ4_IREQUEST128_OFFSET 0
+# define INTC_INTREQ4_IREQUEST128_SIZE 1
+#define INTC_INTPR5 0x14
+# define INTC_INTPR5_INTLEV_OFFSET 30
+# define INTC_INTPR5_INTLEV_SIZE 2
+# define INTC_INTPR5_OFFSET_OFFSET 0
+# define INTC_INTPR5_OFFSET_SIZE 24
+#define INTC_INTREQ5 0x114
+# define INTC_INTREQ5_IREQUEST160_OFFSET 0
+# define INTC_INTREQ5_IREQUEST160_SIZE 1
+#define INTC_INTPR6 0x18
+# define INTC_INTPR6_INTLEV_OFFSET 30
+# define INTC_INTPR6_INTLEV_SIZE 2
+# define INTC_INTPR6_OFFSET_OFFSET 0
+# define INTC_INTPR6_OFFSET_SIZE 24
+#define INTC_INTREQ6 0x118
+# define INTC_INTREQ6_IREQUEST192_OFFSET 0
+# define INTC_INTREQ6_IREQUEST192_SIZE 1
+#define INTC_INTPR7 0x1c
+# define INTC_INTPR7_INTLEV_OFFSET 30
+# define INTC_INTPR7_INTLEV_SIZE 2
+# define INTC_INTPR7_OFFSET_OFFSET 0
+# define INTC_INTPR7_OFFSET_SIZE 24
+#define INTC_INTREQ7 0x11c
+# define INTC_INTREQ7_IREQUEST224_OFFSET 0
+# define INTC_INTREQ7_IREQUEST224_SIZE 1
+#define INTC_INTPR8 0x20
+# define INTC_INTPR8_INTLEV_OFFSET 30
+# define INTC_INTPR8_INTLEV_SIZE 2
+# define INTC_INTPR8_OFFSET_OFFSET 0
+# define INTC_INTPR8_OFFSET_SIZE 24
+#define INTC_INTREQ8 0x120
+# define INTC_INTREQ8_IREQUEST256_OFFSET 0
+# define INTC_INTREQ8_IREQUEST256_SIZE 1
+#define INTC_INTPR9 0x24
+# define INTC_INTPR9_INTLEV_OFFSET 30
+# define INTC_INTPR9_INTLEV_SIZE 2
+# define INTC_INTPR9_OFFSET_OFFSET 0
+# define INTC_INTPR9_OFFSET_SIZE 24
+#define INTC_INTREQ9 0x124
+# define INTC_INTREQ9_IREQUEST288_OFFSET 0
+# define INTC_INTREQ9_IREQUEST288_SIZE 1
+#define INTC_INTPR10 0x28
+# define INTC_INTPR10_INTLEV_OFFSET 30
+# define INTC_INTPR10_INTLEV_SIZE 2
+# define INTC_INTPR10_OFFSET_OFFSET 0
+# define INTC_INTPR10_OFFSET_SIZE 24
+#define INTC_INTREQ10 0x128
+# define INTC_INTREQ10_IREQUEST320_OFFSET 0
+# define INTC_INTREQ10_IREQUEST320_SIZE 1
+#define INTC_INTPR11 0x2c
+# define INTC_INTPR11_INTLEV_OFFSET 30
+# define INTC_INTPR11_INTLEV_SIZE 2
+# define INTC_INTPR11_OFFSET_OFFSET 0
+# define INTC_INTPR11_OFFSET_SIZE 24
+#define INTC_INTREQ11 0x12c
+# define INTC_INTREQ11_IREQUEST352_OFFSET 0
+# define INTC_INTREQ11_IREQUEST352_SIZE 1
+#define INTC_INTPR12 0x30
+# define INTC_INTPR12_INTLEV_OFFSET 30
+# define INTC_INTPR12_INTLEV_SIZE 2
+# define INTC_INTPR12_OFFSET_OFFSET 0
+# define INTC_INTPR12_OFFSET_SIZE 24
+#define INTC_INTREQ12 0x130
+# define INTC_INTREQ12_IREQUEST384_OFFSET 0
+# define INTC_INTREQ12_IREQUEST384_SIZE 1
+#define INTC_INTPR13 0x34
+# define INTC_INTPR13_INTLEV_OFFSET 30
+# define INTC_INTPR13_INTLEV_SIZE 2
+# define INTC_INTPR13_OFFSET_OFFSET 0
+# define INTC_INTPR13_OFFSET_SIZE 24
+#define INTC_INTREQ13 0x134
+# define INTC_INTREQ13_IREQUEST416_OFFSET 0
+# define INTC_INTREQ13_IREQUEST416_SIZE 1
+#define INTC_INTPR14 0x38
+# define INTC_INTPR14_INTLEV_OFFSET 30
+# define INTC_INTPR14_INTLEV_SIZE 2
+# define INTC_INTPR14_OFFSET_OFFSET 0
+# define INTC_INTPR14_OFFSET_SIZE 24
+#define INTC_INTREQ14 0x138
+# define INTC_INTREQ14_IREQUEST448_OFFSET 0
+# define INTC_INTREQ14_IREQUEST448_SIZE 1
+#define INTC_INTPR15 0x3c
+# define INTC_INTPR15_INTLEV_OFFSET 30
+# define INTC_INTPR15_INTLEV_SIZE 2
+# define INTC_INTPR15_OFFSET_OFFSET 0
+# define INTC_INTPR15_OFFSET_SIZE 24
+#define INTC_INTREQ15 0x13c
+# define INTC_INTREQ15_IREQUEST480_OFFSET 0
+# define INTC_INTREQ15_IREQUEST480_SIZE 1
+#define INTC_INTPR16 0x40
+# define INTC_INTPR16_INTLEV_OFFSET 30
+# define INTC_INTPR16_INTLEV_SIZE 2
+# define INTC_INTPR16_OFFSET_OFFSET 0
+# define INTC_INTPR16_OFFSET_SIZE 24
+#define INTC_INTREQ16 0x140
+# define INTC_INTREQ16_IREQUEST512_OFFSET 0
+# define INTC_INTREQ16_IREQUEST512_SIZE 1
+#define INTC_INTPR17 0x44
+# define INTC_INTPR17_INTLEV_OFFSET 30
+# define INTC_INTPR17_INTLEV_SIZE 2
+# define INTC_INTPR17_OFFSET_OFFSET 0
+# define INTC_INTPR17_OFFSET_SIZE 24
+#define INTC_INTREQ17 0x144
+# define INTC_INTREQ17_IREQUEST544_OFFSET 0
+# define INTC_INTREQ17_IREQUEST544_SIZE 1
+#define INTC_INTPR18 0x48
+# define INTC_INTPR18_INTLEV_OFFSET 30
+# define INTC_INTPR18_INTLEV_SIZE 2
+# define INTC_INTPR18_OFFSET_OFFSET 0
+# define INTC_INTPR18_OFFSET_SIZE 24
+#define INTC_INTREQ18 0x148
+# define INTC_INTREQ18_IREQUEST576_OFFSET 0
+# define INTC_INTREQ18_IREQUEST576_SIZE 1
+#define INTC_INTPR19 0x4c
+# define INTC_INTPR19_INTLEV_OFFSET 30
+# define INTC_INTPR19_INTLEV_SIZE 2
+# define INTC_INTPR19_OFFSET_OFFSET 0
+# define INTC_INTPR19_OFFSET_SIZE 24
+#define INTC_INTREQ19 0x14c
+# define INTC_INTREQ19_IREQUEST608_OFFSET 0
+# define INTC_INTREQ19_IREQUEST608_SIZE 1
+# define INTC_INTREQ19_IREQUEST609_OFFSET 1
+# define INTC_INTREQ19_IREQUEST609_SIZE 1
+# define INTC_INTREQ19_IREQUEST610_OFFSET 2
+# define INTC_INTREQ19_IREQUEST610_SIZE 1
+# define INTC_INTREQ19_IREQUEST611_OFFSET 3
+# define INTC_INTREQ19_IREQUEST611_SIZE 1
+#define INTC_INTPR20 0x50
+# define INTC_INTPR20_INTLEV_OFFSET 30
+# define INTC_INTPR20_INTLEV_SIZE 2
+# define INTC_INTPR20_OFFSET_OFFSET 0
+# define INTC_INTPR20_OFFSET_SIZE 24
+#define INTC_INTREQ20 0x150
+# define INTC_INTREQ20_IREQUEST640_OFFSET 0
+# define INTC_INTREQ20_IREQUEST640_SIZE 1
+#define INTC_INTPR21 0x54
+# define INTC_INTPR21_INTLEV_OFFSET 30
+# define INTC_INTPR21_INTLEV_SIZE 2
+# define INTC_INTPR21_OFFSET_OFFSET 0
+# define INTC_INTPR21_OFFSET_SIZE 24
+#define INTC_INTREQ21 0x154
+# define INTC_INTREQ21_IREQUEST672_OFFSET 0
+# define INTC_INTREQ21_IREQUEST672_SIZE 1
+#define INTC_INTPR22 0x58
+# define INTC_INTPR22_INTLEV_OFFSET 30
+# define INTC_INTPR22_INTLEV_SIZE 2
+# define INTC_INTPR22_OFFSET_OFFSET 0
+# define INTC_INTPR22_OFFSET_SIZE 24
+#define INTC_INTREQ22 0x158
+# define INTC_INTREQ22_IREQUEST704_OFFSET 0
+# define INTC_INTREQ22_IREQUEST704_SIZE 1
+# define INTC_INTREQ22_IREQUEST705_OFFSET 1
+# define INTC_INTREQ22_IREQUEST705_SIZE 1
+# define INTC_INTREQ22_IREQUEST706_OFFSET 2
+# define INTC_INTREQ22_IREQUEST706_SIZE 1
+#define INTC_INTPR23 0x5c
+# define INTC_INTPR23_INTLEV_OFFSET 30
+# define INTC_INTPR23_INTLEV_SIZE 2
+# define INTC_INTPR23_OFFSET_OFFSET 0
+# define INTC_INTPR23_OFFSET_SIZE 24
+#define INTC_INTREQ23 0x15c
+# define INTC_INTREQ23_IREQUEST736_OFFSET 0
+# define INTC_INTREQ23_IREQUEST736_SIZE 1
+# define INTC_INTREQ23_IREQUEST737_OFFSET 1
+# define INTC_INTREQ23_IREQUEST737_SIZE 1
+# define INTC_INTREQ23_IREQUEST738_OFFSET 2
+# define INTC_INTREQ23_IREQUEST738_SIZE 1
+#define INTC_INTPR24 0x60
+# define INTC_INTPR24_INTLEV_OFFSET 30
+# define INTC_INTPR24_INTLEV_SIZE 2
+# define INTC_INTPR24_OFFSET_OFFSET 0
+# define INTC_INTPR24_OFFSET_SIZE 24
+#define INTC_INTREQ24 0x160
+# define INTC_INTREQ24_IREQUEST768_OFFSET 0
+# define INTC_INTREQ24_IREQUEST768_SIZE 1
+#define INTC_INTPR25 0x64
+# define INTC_INTPR25_INTLEV_OFFSET 30
+# define INTC_INTPR25_INTLEV_SIZE 2
+# define INTC_INTPR25_OFFSET_OFFSET 0
+# define INTC_INTPR25_OFFSET_SIZE 24
+#define INTC_INTREQ25 0x164
+# define INTC_INTREQ25_IREQUEST800_OFFSET 0
+# define INTC_INTREQ25_IREQUEST800_SIZE 1
+#define INTC_INTPR26 0x68
+# define INTC_INTPR26_INTLEV_OFFSET 30
+# define INTC_INTPR26_INTLEV_SIZE 2
+# define INTC_INTPR26_OFFSET_OFFSET 0
+# define INTC_INTPR26_OFFSET_SIZE 24
+#define INTC_INTREQ26 0x168
+# define INTC_INTREQ26_IREQUEST832_OFFSET 0
+# define INTC_INTREQ26_IREQUEST832_SIZE 1
+#define INTC_INTPR27 0x6c
+# define INTC_INTPR27_INTLEV_OFFSET 30
+# define INTC_INTPR27_INTLEV_SIZE 2
+# define INTC_INTPR27_OFFSET_OFFSET 0
+# define INTC_INTPR27_OFFSET_SIZE 24
+#define INTC_INTREQ27 0x16c
+# define INTC_INTREQ27_IREQUEST864_OFFSET 0
+# define INTC_INTREQ27_IREQUEST864_SIZE 1
+#define INTC_INTPR28 0x70
+# define INTC_INTPR28_INTLEV_OFFSET 30
+# define INTC_INTPR28_INTLEV_SIZE 2
+# define INTC_INTPR28_OFFSET_OFFSET 0
+# define INTC_INTPR28_OFFSET_SIZE 24
+#define INTC_INTREQ28 0x170
+# define INTC_INTREQ28_IREQUEST896_OFFSET 0
+# define INTC_INTREQ28_IREQUEST896_SIZE 1
+#define INTC_INTPR29 0x74
+# define INTC_INTPR29_INTLEV_OFFSET 30
+# define INTC_INTPR29_INTLEV_SIZE 2
+# define INTC_INTPR29_OFFSET_OFFSET 0
+# define INTC_INTPR29_OFFSET_SIZE 24
+#define INTC_INTREQ29 0x174
+# define INTC_INTREQ29_IREQUEST928_OFFSET 0
+# define INTC_INTREQ29_IREQUEST928_SIZE 1
+#define INTC_INTPR30 0x78
+# define INTC_INTPR30_INTLEV_OFFSET 30
+# define INTC_INTPR30_INTLEV_SIZE 2
+# define INTC_INTPR30_OFFSET_OFFSET 0
+# define INTC_INTPR30_OFFSET_SIZE 24
+#define INTC_INTREQ30 0x178
+# define INTC_INTREQ30_IREQUEST960_OFFSET 0
+# define INTC_INTREQ30_IREQUEST960_SIZE 1
+#define INTC_INTPR31 0x7c
+# define INTC_INTPR31_INTLEV_OFFSET 30
+# define INTC_INTPR31_INTLEV_SIZE 2
+# define INTC_INTPR31_OFFSET_OFFSET 0
+# define INTC_INTPR31_OFFSET_SIZE 24
+#define INTC_INTREQ31 0x17c
+# define INTC_INTREQ31_IREQUEST992_OFFSET 0
+# define INTC_INTREQ31_IREQUEST992_SIZE 1
+#define INTC_INTPR32 0x80
+# define INTC_INTPR32_INTLEV_OFFSET 30
+# define INTC_INTPR32_INTLEV_SIZE 2
+# define INTC_INTPR32_OFFSET_OFFSET 0
+# define INTC_INTPR32_OFFSET_SIZE 24
+#define INTC_INTREQ32 0x180
+# define INTC_INTREQ32_IREQUEST1024_OFFSET 0
+# define INTC_INTREQ32_IREQUEST1024_SIZE 1
+#define INTC_INTCAUSE0 0x20c
+# define INTC_INTCAUSE0_CAUSEGRP_OFFSET 0
+# define INTC_INTCAUSE0_CAUSEGRP_SIZE 6
+#define INTC_INTCAUSE1 0x208
+# define INTC_INTCAUSE1_CAUSEGRP_OFFSET 0
+# define INTC_INTCAUSE1_CAUSEGRP_SIZE 6
+#define INTC_INTCAUSE2 0x204
+# define INTC_INTCAUSE2_CAUSEGRP_OFFSET 0
+# define INTC_INTCAUSE2_CAUSEGRP_SIZE 6
+#define INTC_INTCAUSE3 0x200
+# define INTC_INTCAUSE3_CAUSEGRP_OFFSET 0
+# define INTC_INTCAUSE3_CAUSEGRP_SIZE 6
+
+#define INTC_BIT(name) (1 << INTC_##name##_OFFSET)
+#define INTC_MKBF(name, value) (((value) & ((1 << INTC_##name##_SIZE) - 1)) << INTC_##name##_OFFSET)
+#define INTC_GETBF(name, value) (((value) >> INTC_##name##_OFFSET) & ((1 << INTC_##name##_SIZE) - 1))
+
+#define intc_readl(port,reg) readl((port)->regs + INTC_##reg)
+#define intc_writel(port,reg,value) writel((value), (port)->regs + INTC_##reg)
+
+#endif /* __ASM_AVR32_PERIHP_INTC_H__ */
diff --git a/arch/avr32/mach-at32ap/pio.c b/arch/avr32/mach-at32ap/pio.c
new file mode 100644
index 00000000000..d3aabfca859
--- /dev/null
+++ b/arch/avr32/mach-at32ap/pio.c
@@ -0,0 +1,118 @@
+/*
+ * Atmel PIO2 Port Multiplexer support
+ *
+ * Copyright (C) 2004-2006 Atmel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/clk.h>
+#include <linux/debugfs.h>
+#include <linux/fs.h>
+#include <linux/platform_device.h>
+
+#include <asm/io.h>
+
+#include <asm/arch/portmux.h>
+
+#include "pio.h"
+
+#define MAX_NR_PIO_DEVICES 8
+
+struct pio_device {
+ void __iomem *regs;
+ const struct platform_device *pdev;
+ struct clk *clk;
+ u32 alloc_mask;
+ char name[32];
+};
+
+static struct pio_device pio_dev[MAX_NR_PIO_DEVICES];
+
+void portmux_set_func(unsigned int portmux_id, unsigned int pin_id,
+ unsigned int function_id)
+{
+ struct pio_device *pio;
+ u32 mask = 1 << pin_id;
+
+ BUG_ON(portmux_id >= MAX_NR_PIO_DEVICES);
+
+ pio = &pio_dev[portmux_id];
+
+ if (function_id)
+ pio_writel(pio, BSR, mask);
+ else
+ pio_writel(pio, ASR, mask);
+ pio_writel(pio, PDR, mask);
+}
+
+static int __init pio_probe(struct platform_device *pdev)
+{
+ struct pio_device *pio = NULL;
+
+ BUG_ON(pdev->id >= MAX_NR_PIO_DEVICES);
+ pio = &pio_dev[pdev->id];
+ BUG_ON(!pio->regs);
+
+ /* TODO: Interrupts */
+
+ platform_set_drvdata(pdev, pio);
+
+ printk(KERN_INFO "%s: Atmel Port Multiplexer at 0x%p (irq %d)\n",
+ pio->name, pio->regs, platform_get_irq(pdev, 0));
+
+ return 0;
+}
+
+static struct platform_driver pio_driver = {
+ .probe = pio_probe,
+ .driver = {
+ .name = "pio",
+ },
+};
+
+static int __init pio_init(void)
+{
+ return platform_driver_register(&pio_driver);
+}
+subsys_initcall(pio_init);
+
+void __init at32_init_pio(struct platform_device *pdev)
+{
+ struct resource *regs;
+ struct pio_device *pio;
+
+ if (pdev->id > MAX_NR_PIO_DEVICES) {
+ dev_err(&pdev->dev, "only %d PIO devices supported\n",
+ MAX_NR_PIO_DEVICES);
+ return;
+ }
+
+ pio = &pio_dev[pdev->id];
+ snprintf(pio->name, sizeof(pio->name), "pio%d", pdev->id);
+
+ regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!regs) {
+ dev_err(&pdev->dev, "no mmio resource defined\n");
+ return;
+ }
+
+ pio->clk = clk_get(&pdev->dev, "mck");
+ if (IS_ERR(pio->clk))
+ /*
+ * This is a fatal error, but if we continue we might
+ * be so lucky that we manage to initialize the
+ * console and display this message...
+ */
+ dev_err(&pdev->dev, "no mck clock defined\n");
+ else
+ clk_enable(pio->clk);
+
+ pio->pdev = pdev;
+ pio->regs = ioremap(regs->start, regs->end - regs->start + 1);
+
+ pio_writel(pio, ODR, ~0UL);
+ pio_writel(pio, PER, ~0UL);
+}
diff --git a/arch/avr32/mach-at32ap/pio.h b/arch/avr32/mach-at32ap/pio.h
new file mode 100644
index 00000000000..cfea1235159
--- /dev/null
+++ b/arch/avr32/mach-at32ap/pio.h
@@ -0,0 +1,178 @@
+/*
+ * Atmel PIO2 Port Multiplexer support
+ *
+ * Copyright (C) 2004-2006 Atmel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef __ARCH_AVR32_AT32AP_PIO_H__
+#define __ARCH_AVR32_AT32AP_PIO_H__
+
+/* PIO register offsets */
+#define PIO_PER 0x0000
+#define PIO_PDR 0x0004
+#define PIO_PSR 0x0008
+#define PIO_OER 0x0010
+#define PIO_ODR 0x0014
+#define PIO_OSR 0x0018
+#define PIO_IFER 0x0020
+#define PIO_IFDR 0x0024
+#define PIO_ISFR 0x0028
+#define PIO_SODR 0x0030
+#define PIO_CODR 0x0034
+#define PIO_ODSR 0x0038
+#define PIO_PDSR 0x003c
+#define PIO_IER 0x0040
+#define PIO_IDR 0x0044
+#define PIO_IMR 0x0048
+#define PIO_ISR 0x004c
+#define PIO_MDER 0x0050
+#define PIO_MDDR 0x0054
+#define PIO_MDSR 0x0058
+#define PIO_PUDR 0x0060
+#define PIO_PUER 0x0064
+#define PIO_PUSR 0x0068
+#define PIO_ASR 0x0070
+#define PIO_BSR 0x0074
+#define PIO_ABSR 0x0078
+#define PIO_OWER 0x00a0
+#define PIO_OWDR 0x00a4
+#define PIO_OWSR 0x00a8
+
+/* Bitfields in PER */
+
+/* Bitfields in PDR */
+
+/* Bitfields in PSR */
+
+/* Bitfields in OER */
+
+/* Bitfields in ODR */
+
+/* Bitfields in OSR */
+
+/* Bitfields in IFER */
+
+/* Bitfields in IFDR */
+
+/* Bitfields in ISFR */
+
+/* Bitfields in SODR */
+
+/* Bitfields in CODR */
+
+/* Bitfields in ODSR */
+
+/* Bitfields in PDSR */
+
+/* Bitfields in IER */
+
+/* Bitfields in IDR */
+
+/* Bitfields in IMR */
+
+/* Bitfields in ISR */
+
+/* Bitfields in MDER */
+
+/* Bitfields in MDDR */
+
+/* Bitfields in MDSR */
+
+/* Bitfields in PUDR */
+
+/* Bitfields in PUER */
+
+/* Bitfields in PUSR */
+
+/* Bitfields in ASR */
+
+/* Bitfields in BSR */
+
+/* Bitfields in ABSR */
+#define PIO_P0_OFFSET 0
+#define PIO_P0_SIZE 1
+#define PIO_P1_OFFSET 1
+#define PIO_P1_SIZE 1
+#define PIO_P2_OFFSET 2
+#define PIO_P2_SIZE 1
+#define PIO_P3_OFFSET 3
+#define PIO_P3_SIZE 1
+#define PIO_P4_OFFSET 4
+#define PIO_P4_SIZE 1
+#define PIO_P5_OFFSET 5
+#define PIO_P5_SIZE 1
+#define PIO_P6_OFFSET 6
+#define PIO_P6_SIZE 1
+#define PIO_P7_OFFSET 7
+#define PIO_P7_SIZE 1
+#define PIO_P8_OFFSET 8
+#define PIO_P8_SIZE 1
+#define PIO_P9_OFFSET 9
+#define PIO_P9_SIZE 1
+#define PIO_P10_OFFSET 10
+#define PIO_P10_SIZE 1
+#define PIO_P11_OFFSET 11
+#define PIO_P11_SIZE 1
+#define PIO_P12_OFFSET 12
+#define PIO_P12_SIZE 1
+#define PIO_P13_OFFSET 13
+#define PIO_P13_SIZE 1
+#define PIO_P14_OFFSET 14
+#define PIO_P14_SIZE 1
+#define PIO_P15_OFFSET 15
+#define PIO_P15_SIZE 1
+#define PIO_P16_OFFSET 16
+#define PIO_P16_SIZE 1
+#define PIO_P17_OFFSET 17
+#define PIO_P17_SIZE 1
+#define PIO_P18_OFFSET 18
+#define PIO_P18_SIZE 1
+#define PIO_P19_OFFSET 19
+#define PIO_P19_SIZE 1
+#define PIO_P20_OFFSET 20
+#define PIO_P20_SIZE 1
+#define PIO_P21_OFFSET 21
+#define PIO_P21_SIZE 1
+#define PIO_P22_OFFSET 22
+#define PIO_P22_SIZE 1
+#define PIO_P23_OFFSET 23
+#define PIO_P23_SIZE 1
+#define PIO_P24_OFFSET 24
+#define PIO_P24_SIZE 1
+#define PIO_P25_OFFSET 25
+#define PIO_P25_SIZE 1
+#define PIO_P26_OFFSET 26
+#define PIO_P26_SIZE 1
+#define PIO_P27_OFFSET 27
+#define PIO_P27_SIZE 1
+#define PIO_P28_OFFSET 28
+#define PIO_P28_SIZE 1
+#define PIO_P29_OFFSET 29
+#define PIO_P29_SIZE 1
+#define PIO_P30_OFFSET 30
+#define PIO_P30_SIZE 1
+#define PIO_P31_OFFSET 31
+#define PIO_P31_SIZE 1
+
+/* Bitfields in OWER */
+
+/* Bitfields in OWDR */
+
+/* Bitfields in OWSR */
+
+/* Bit manipulation macros */
+#define PIO_BIT(name) (1 << PIO_##name##_OFFSET)
+#define PIO_BF(name,value) (((value) & ((1 << PIO_##name##_SIZE) - 1)) << PIO_##name##_OFFSET)
+#define PIO_BFEXT(name,value) (((value) >> PIO_##name##_OFFSET) & ((1 << PIO_##name##_SIZE) - 1))
+#define PIO_BFINS(name,value,old) (((old) & ~(((1 << PIO_##name##_SIZE) - 1) << PIO_##name##_OFFSET)) | PIO_BF(name,value))
+
+/* Register access macros */
+#define pio_readl(port,reg) readl((port)->regs + PIO_##reg)
+#define pio_writel(port,reg,value) writel((value), (port)->regs + PIO_##reg)
+
+void at32_init_pio(struct platform_device *pdev);
+
+#endif /* __ARCH_AVR32_AT32AP_PIO_H__ */
diff --git a/arch/avr32/mach-at32ap/sm.c b/arch/avr32/mach-at32ap/sm.c
new file mode 100644
index 00000000000..03306eb0345
--- /dev/null
+++ b/arch/avr32/mach-at32ap/sm.c
@@ -0,0 +1,289 @@
+/*
+ * System Manager driver for AT32AP CPUs
+ *
+ * Copyright (C) 2006 Atmel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/random.h>
+#include <linux/spinlock.h>
+
+#include <asm/intc.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#include <asm/arch/sm.h>
+
+#include "sm.h"
+
+#define SM_EIM_IRQ_RESOURCE 1
+#define SM_PM_IRQ_RESOURCE 2
+#define SM_RTC_IRQ_RESOURCE 3
+
+#define to_eim(irqc) container_of(irqc, struct at32_sm, irqc)
+
+struct at32_sm system_manager;
+
+int __init at32_sm_init(void)
+{
+ struct resource *regs;
+ struct at32_sm *sm = &system_manager;
+ int ret = -ENXIO;
+
+ regs = platform_get_resource(&at32_sm_device, IORESOURCE_MEM, 0);
+ if (!regs)
+ goto fail;
+
+ spin_lock_init(&sm->lock);
+ sm->pdev = &at32_sm_device;
+
+ ret = -ENOMEM;
+ sm->regs = ioremap(regs->start, regs->end - regs->start + 1);
+ if (!sm->regs)
+ goto fail;
+
+ return 0;
+
+fail:
+ printk(KERN_ERR "Failed to initialize System Manager: %d\n", ret);
+ return ret;
+}
+
+/*
+ * External Interrupt Module (EIM).
+ *
+ * EIM gets level- or edge-triggered interrupts of either polarity
+ * from the outside and converts it to active-high level-triggered
+ * interrupts that the internal interrupt controller can handle. EIM
+ * also provides masking/unmasking of interrupts, as well as
+ * acknowledging of edge-triggered interrupts.
+ */
+
+static irqreturn_t spurious_eim_interrupt(int irq, void *dev_id,
+ struct pt_regs *regs)
+{
+ printk(KERN_WARNING "Spurious EIM interrupt %d\n", irq);
+ disable_irq(irq);
+ return IRQ_NONE;
+}
+
+static struct irqaction eim_spurious_action = {
+ .handler = spurious_eim_interrupt,
+};
+
+static irqreturn_t eim_handle_irq(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct irq_controller * irqc = dev_id;
+ struct at32_sm *sm = to_eim(irqc);
+ unsigned long pending;
+
+ /*
+ * No need to disable interrupts globally. The interrupt
+ * level relevant to this group must be masked all the time,
+ * so we know that this particular EIM instance will not be
+ * re-entered.
+ */
+ spin_lock(&sm->lock);
+
+ pending = intc_get_pending(sm->irqc.irq_group);
+ if (unlikely(!pending)) {
+ printk(KERN_ERR "EIM (group %u): No interrupts pending!\n",
+ sm->irqc.irq_group);
+ goto unlock;
+ }
+
+ do {
+ struct irqaction *action;
+ unsigned int i;
+
+ i = fls(pending) - 1;
+ pending &= ~(1 << i);
+ action = sm->action[i];
+
+ /* Acknowledge the interrupt */
+ sm_writel(sm, EIM_ICR, 1 << i);
+
+ spin_unlock(&sm->lock);
+
+ if (action->flags & SA_INTERRUPT)
+ local_irq_disable();
+ action->handler(sm->irqc.first_irq + i, action->dev_id, regs);
+ local_irq_enable();
+ spin_lock(&sm->lock);
+ if (action->flags & SA_SAMPLE_RANDOM)
+ add_interrupt_randomness(sm->irqc.first_irq + i);
+ } while (pending);
+
+unlock:
+ spin_unlock(&sm->lock);
+ return IRQ_HANDLED;
+}
+
+static void eim_mask(struct irq_controller *irqc, unsigned int irq)
+{
+ struct at32_sm *sm = to_eim(irqc);
+ unsigned int i;
+
+ i = irq - sm->irqc.first_irq;
+ sm_writel(sm, EIM_IDR, 1 << i);
+}
+
+static void eim_unmask(struct irq_controller *irqc, unsigned int irq)
+{
+ struct at32_sm *sm = to_eim(irqc);
+ unsigned int i;
+
+ i = irq - sm->irqc.first_irq;
+ sm_writel(sm, EIM_IER, 1 << i);
+}
+
+static int eim_setup(struct irq_controller *irqc, unsigned int irq,
+ struct irqaction *action)
+{
+ struct at32_sm *sm = to_eim(irqc);
+ sm->action[irq - sm->irqc.first_irq] = action;
+ /* Acknowledge earlier interrupts */
+ sm_writel(sm, EIM_ICR, (1<<(irq - sm->irqc.first_irq)));
+ eim_unmask(irqc, irq);
+ return 0;
+}
+
+static void eim_free(struct irq_controller *irqc, unsigned int irq,
+ void *dev)
+{
+ struct at32_sm *sm = to_eim(irqc);
+ eim_mask(irqc, irq);
+ sm->action[irq - sm->irqc.first_irq] = &eim_spurious_action;
+}
+
+static int eim_set_type(struct irq_controller *irqc, unsigned int irq,
+ unsigned int type)
+{
+ struct at32_sm *sm = to_eim(irqc);
+ unsigned long flags;
+ u32 value, pattern;
+
+ spin_lock_irqsave(&sm->lock, flags);
+
+ pattern = 1 << (irq - sm->irqc.first_irq);
+
+ value = sm_readl(sm, EIM_MODE);
+ if (type & IRQ_TYPE_LEVEL)
+ value |= pattern;
+ else
+ value &= ~pattern;
+ sm_writel(sm, EIM_MODE, value);
+ value = sm_readl(sm, EIM_EDGE);
+ if (type & IRQ_EDGE_RISING)
+ value |= pattern;
+ else
+ value &= ~pattern;
+ sm_writel(sm, EIM_EDGE, value);
+ value = sm_readl(sm, EIM_LEVEL);
+ if (type & IRQ_LEVEL_HIGH)
+ value |= pattern;
+ else
+ value &= ~pattern;
+ sm_writel(sm, EIM_LEVEL, value);
+
+ spin_unlock_irqrestore(&sm->lock, flags);
+
+ return 0;
+}
+
+static unsigned int eim_get_type(struct irq_controller *irqc,
+ unsigned int irq)
+{
+ struct at32_sm *sm = to_eim(irqc);
+ unsigned long flags;
+ unsigned int type = 0;
+ u32 mode, edge, level, pattern;
+
+ pattern = 1 << (irq - sm->irqc.first_irq);
+
+ spin_lock_irqsave(&sm->lock, flags);
+ mode = sm_readl(sm, EIM_MODE);
+ edge = sm_readl(sm, EIM_EDGE);
+ level = sm_readl(sm, EIM_LEVEL);
+ spin_unlock_irqrestore(&sm->lock, flags);
+
+ if (mode & pattern)
+ type |= IRQ_TYPE_LEVEL;
+ if (edge & pattern)
+ type |= IRQ_EDGE_RISING;
+ if (level & pattern)
+ type |= IRQ_LEVEL_HIGH;
+
+ return type;
+}
+
+static struct irq_controller_class eim_irq_class = {
+ .typename = "EIM",
+ .handle = eim_handle_irq,
+ .setup = eim_setup,
+ .free = eim_free,
+ .mask = eim_mask,
+ .unmask = eim_unmask,
+ .set_type = eim_set_type,
+ .get_type = eim_get_type,
+};
+
+static int __init eim_init(void)
+{
+ struct at32_sm *sm = &system_manager;
+ unsigned int i;
+ u32 pattern;
+ int ret;
+
+ /*
+ * The EIM is really the same module as SM, so register
+ * mapping, etc. has been taken care of already.
+ */
+
+ /*
+ * Find out how many interrupt lines that are actually
+ * implemented in hardware.
+ */
+ sm_writel(sm, EIM_IDR, ~0UL);
+ sm_writel(sm, EIM_MODE, ~0UL);
+ pattern = sm_readl(sm, EIM_MODE);
+ sm->irqc.nr_irqs = fls(pattern);
+
+ ret = -ENOMEM;
+ sm->action = kmalloc(sizeof(*sm->action) * sm->irqc.nr_irqs,
+ GFP_KERNEL);
+ if (!sm->action)
+ goto out;
+
+ for (i = 0; i < sm->irqc.nr_irqs; i++)
+ sm->action[i] = &eim_spurious_action;
+
+ spin_lock_init(&sm->lock);
+ sm->irqc.irq_group = sm->pdev->resource[SM_EIM_IRQ_RESOURCE].start;
+ sm->irqc.class = &eim_irq_class;
+
+ ret = intc_register_controller(&sm->irqc);
+ if (ret < 0)
+ goto out_free_actions;
+
+ printk("EIM: External Interrupt Module at 0x%p, IRQ group %u\n",
+ sm->regs, sm->irqc.irq_group);
+ printk("EIM: Handling %u external IRQs, starting with IRQ%u\n",
+ sm->irqc.nr_irqs, sm->irqc.first_irq);
+
+ return 0;
+
+out_free_actions:
+ kfree(sm->action);
+out:
+ return ret;
+}
+arch_initcall(eim_init);
diff --git a/arch/avr32/mach-at32ap/sm.h b/arch/avr32/mach-at32ap/sm.h
new file mode 100644
index 00000000000..27565822ae2
--- /dev/null
+++ b/arch/avr32/mach-at32ap/sm.h
@@ -0,0 +1,240 @@
+/*
+ * Register definitions for SM
+ *
+ * System Manager
+ */
+#ifndef __ASM_AVR32_SM_H__
+#define __ASM_AVR32_SM_H__
+
+/* SM register offsets */
+#define SM_PM_MCCTRL 0x0000
+#define SM_PM_CKSEL 0x0004
+#define SM_PM_CPU_MASK 0x0008
+#define SM_PM_HSB_MASK 0x000c
+#define SM_PM_PBA_MASK 0x0010
+#define SM_PM_PBB_MASK 0x0014
+#define SM_PM_PLL0 0x0020
+#define SM_PM_PLL1 0x0024
+#define SM_PM_VCTRL 0x0030
+#define SM_PM_VMREF 0x0034
+#define SM_PM_VMV 0x0038
+#define SM_PM_IER 0x0040
+#define SM_PM_IDR 0x0044
+#define SM_PM_IMR 0x0048
+#define SM_PM_ISR 0x004c
+#define SM_PM_ICR 0x0050
+#define SM_PM_GCCTRL 0x0060
+#define SM_RTC_CTRL 0x0080
+#define SM_RTC_VAL 0x0084
+#define SM_RTC_TOP 0x0088
+#define SM_RTC_IER 0x0090
+#define SM_RTC_IDR 0x0094
+#define SM_RTC_IMR 0x0098
+#define SM_RTC_ISR 0x009c
+#define SM_RTC_ICR 0x00a0
+#define SM_WDT_CTRL 0x00b0
+#define SM_WDT_CLR 0x00b4
+#define SM_WDT_EXT 0x00b8
+#define SM_RC_RCAUSE 0x00c0
+#define SM_EIM_IER 0x0100
+#define SM_EIM_IDR 0x0104
+#define SM_EIM_IMR 0x0108
+#define SM_EIM_ISR 0x010c
+#define SM_EIM_ICR 0x0110
+#define SM_EIM_MODE 0x0114
+#define SM_EIM_EDGE 0x0118
+#define SM_EIM_LEVEL 0x011c
+#define SM_EIM_TEST 0x0120
+#define SM_EIM_NMIC 0x0124
+
+/* Bitfields in PM_MCCTRL */
+
+/* Bitfields in PM_CKSEL */
+#define SM_CPUSEL_OFFSET 0
+#define SM_CPUSEL_SIZE 3
+#define SM_CPUDIV_OFFSET 7
+#define SM_CPUDIV_SIZE 1
+#define SM_HSBSEL_OFFSET 8
+#define SM_HSBSEL_SIZE 3
+#define SM_HSBDIV_OFFSET 15
+#define SM_HSBDIV_SIZE 1
+#define SM_PBASEL_OFFSET 16
+#define SM_PBASEL_SIZE 3
+#define SM_PBADIV_OFFSET 23
+#define SM_PBADIV_SIZE 1
+#define SM_PBBSEL_OFFSET 24
+#define SM_PBBSEL_SIZE 3
+#define SM_PBBDIV_OFFSET 31
+#define SM_PBBDIV_SIZE 1
+
+/* Bitfields in PM_CPU_MASK */
+
+/* Bitfields in PM_HSB_MASK */
+
+/* Bitfields in PM_PBA_MASK */
+
+/* Bitfields in PM_PBB_MASK */
+
+/* Bitfields in PM_PLL0 */
+#define SM_PLLEN_OFFSET 0
+#define SM_PLLEN_SIZE 1
+#define SM_PLLOSC_OFFSET 1
+#define SM_PLLOSC_SIZE 1
+#define SM_PLLOPT_OFFSET 2
+#define SM_PLLOPT_SIZE 3
+#define SM_PLLDIV_OFFSET 8
+#define SM_PLLDIV_SIZE 8
+#define SM_PLLMUL_OFFSET 16
+#define SM_PLLMUL_SIZE 8
+#define SM_PLLCOUNT_OFFSET 24
+#define SM_PLLCOUNT_SIZE 6
+#define SM_PLLTEST_OFFSET 31
+#define SM_PLLTEST_SIZE 1
+
+/* Bitfields in PM_PLL1 */
+
+/* Bitfields in PM_VCTRL */
+#define SM_VAUTO_OFFSET 0
+#define SM_VAUTO_SIZE 1
+#define SM_PM_VCTRL_VAL_OFFSET 8
+#define SM_PM_VCTRL_VAL_SIZE 7
+
+/* Bitfields in PM_VMREF */
+#define SM_REFSEL_OFFSET 0
+#define SM_REFSEL_SIZE 4
+
+/* Bitfields in PM_VMV */
+#define SM_PM_VMV_VAL_OFFSET 0
+#define SM_PM_VMV_VAL_SIZE 8
+
+/* Bitfields in PM_IER */
+
+/* Bitfields in PM_IDR */
+
+/* Bitfields in PM_IMR */
+
+/* Bitfields in PM_ISR */
+
+/* Bitfields in PM_ICR */
+#define SM_LOCK0_OFFSET 0
+#define SM_LOCK0_SIZE 1
+#define SM_LOCK1_OFFSET 1
+#define SM_LOCK1_SIZE 1
+#define SM_WAKE_OFFSET 2
+#define SM_WAKE_SIZE 1
+#define SM_VOK_OFFSET 3
+#define SM_VOK_SIZE 1
+#define SM_VMRDY_OFFSET 4
+#define SM_VMRDY_SIZE 1
+#define SM_CKRDY_OFFSET 5
+#define SM_CKRDY_SIZE 1
+
+/* Bitfields in PM_GCCTRL */
+#define SM_OSCSEL_OFFSET 0
+#define SM_OSCSEL_SIZE 1
+#define SM_PLLSEL_OFFSET 1
+#define SM_PLLSEL_SIZE 1
+#define SM_CEN_OFFSET 2
+#define SM_CEN_SIZE 1
+#define SM_CPC_OFFSET 3
+#define SM_CPC_SIZE 1
+#define SM_DIVEN_OFFSET 4
+#define SM_DIVEN_SIZE 1
+#define SM_DIV_OFFSET 8
+#define SM_DIV_SIZE 8
+
+/* Bitfields in RTC_CTRL */
+#define SM_PCLR_OFFSET 1
+#define SM_PCLR_SIZE 1
+#define SM_TOPEN_OFFSET 2
+#define SM_TOPEN_SIZE 1
+#define SM_CLKEN_OFFSET 3
+#define SM_CLKEN_SIZE 1
+#define SM_PSEL_OFFSET 8
+#define SM_PSEL_SIZE 16
+
+/* Bitfields in RTC_VAL */
+#define SM_RTC_VAL_VAL_OFFSET 0
+#define SM_RTC_VAL_VAL_SIZE 31
+
+/* Bitfields in RTC_TOP */
+#define SM_RTC_TOP_VAL_OFFSET 0
+#define SM_RTC_TOP_VAL_SIZE 32
+
+/* Bitfields in RTC_IER */
+
+/* Bitfields in RTC_IDR */
+
+/* Bitfields in RTC_IMR */
+
+/* Bitfields in RTC_ISR */
+
+/* Bitfields in RTC_ICR */
+#define SM_TOPI_OFFSET 0
+#define SM_TOPI_SIZE 1
+
+/* Bitfields in WDT_CTRL */
+#define SM_KEY_OFFSET 24
+#define SM_KEY_SIZE 8
+
+/* Bitfields in WDT_CLR */
+
+/* Bitfields in WDT_EXT */
+
+/* Bitfields in RC_RCAUSE */
+#define SM_POR_OFFSET 0
+#define SM_POR_SIZE 1
+#define SM_BOD_OFFSET 1
+#define SM_BOD_SIZE 1
+#define SM_EXT_OFFSET 2
+#define SM_EXT_SIZE 1
+#define SM_WDT_OFFSET 3
+#define SM_WDT_SIZE 1
+#define SM_NTAE_OFFSET 4
+#define SM_NTAE_SIZE 1
+#define SM_SERP_OFFSET 5
+#define SM_SERP_SIZE 1
+
+/* Bitfields in EIM_IER */
+
+/* Bitfields in EIM_IDR */
+
+/* Bitfields in EIM_IMR */
+
+/* Bitfields in EIM_ISR */
+
+/* Bitfields in EIM_ICR */
+
+/* Bitfields in EIM_MODE */
+
+/* Bitfields in EIM_EDGE */
+#define SM_INT0_OFFSET 0
+#define SM_INT0_SIZE 1
+#define SM_INT1_OFFSET 1
+#define SM_INT1_SIZE 1
+#define SM_INT2_OFFSET 2
+#define SM_INT2_SIZE 1
+#define SM_INT3_OFFSET 3
+#define SM_INT3_SIZE 1
+
+/* Bitfields in EIM_LEVEL */
+
+/* Bitfields in EIM_TEST */
+#define SM_TESTEN_OFFSET 31
+#define SM_TESTEN_SIZE 1
+
+/* Bitfields in EIM_NMIC */
+#define SM_EN_OFFSET 0
+#define SM_EN_SIZE 1
+
+/* Bit manipulation macros */
+#define SM_BIT(name) (1 << SM_##name##_OFFSET)
+#define SM_BF(name,value) (((value) & ((1 << SM_##name##_SIZE) - 1)) << SM_##name##_OFFSET)
+#define SM_BFEXT(name,value) (((value) >> SM_##name##_OFFSET) & ((1 << SM_##name##_SIZE) - 1))
+#define SM_BFINS(name,value,old) (((old) & ~(((1 << SM_##name##_SIZE) - 1) << SM_##name##_OFFSET)) | SM_BF(name,value))
+
+/* Register access macros */
+#define sm_readl(port,reg) readl((port)->regs + SM_##reg)
+#define sm_writel(port,reg,value) writel((value), (port)->regs + SM_##reg)
+
+#endif /* __ASM_AVR32_SM_H__ */
diff --git a/arch/avr32/mm/Makefile b/arch/avr32/mm/Makefile
new file mode 100644
index 00000000000..0066491f90d
--- /dev/null
+++ b/arch/avr32/mm/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for the Linux/AVR32 kernel.
+#
+
+obj-y += init.o clear_page.o copy_page.o dma-coherent.o
+obj-y += ioremap.o cache.o fault.o tlb.o
diff --git a/arch/avr32/mm/cache.c b/arch/avr32/mm/cache.c
new file mode 100644
index 00000000000..450515b245a
--- /dev/null
+++ b/arch/avr32/mm/cache.c
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2004-2006 Atmel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/highmem.h>
+#include <linux/unistd.h>
+
+#include <asm/cacheflush.h>
+#include <asm/cachectl.h>
+#include <asm/processor.h>
+#include <asm/uaccess.h>
+
+/*
+ * If you attempt to flush anything more than this, you need superuser
+ * privileges. The value is completely arbitrary.
+ */
+#define CACHEFLUSH_MAX_LEN 1024
+
+void invalidate_dcache_region(void *start, size_t size)
+{
+ unsigned long v, begin, end, linesz;
+
+ linesz = boot_cpu_data.dcache.linesz;
+
+ //printk("invalidate dcache: %p + %u\n", start, size);
+
+ /* You asked for it, you got it */
+ begin = (unsigned long)start & ~(linesz - 1);
+ end = ((unsigned long)start + size + linesz - 1) & ~(linesz - 1);
+
+ for (v = begin; v < end; v += linesz)
+ invalidate_dcache_line((void *)v);
+}
+
+void clean_dcache_region(void *start, size_t size)
+{
+ unsigned long v, begin, end, linesz;
+
+ linesz = boot_cpu_data.dcache.linesz;
+ begin = (unsigned long)start & ~(linesz - 1);
+ end = ((unsigned long)start + size + linesz - 1) & ~(linesz - 1);
+
+ for (v = begin; v < end; v += linesz)
+ clean_dcache_line((void *)v);
+ flush_write_buffer();
+}
+
+void flush_dcache_region(void *start, size_t size)
+{
+ unsigned long v, begin, end, linesz;
+
+ linesz = boot_cpu_data.dcache.linesz;
+ begin = (unsigned long)start & ~(linesz - 1);
+ end = ((unsigned long)start + size + linesz - 1) & ~(linesz - 1);
+
+ for (v = begin; v < end; v += linesz)
+ flush_dcache_line((void *)v);
+ flush_write_buffer();
+}
+
+void invalidate_icache_region(void *start, size_t size)
+{
+ unsigned long v, begin, end, linesz;
+
+ linesz = boot_cpu_data.icache.linesz;
+ begin = (unsigned long)start & ~(linesz - 1);
+ end = ((unsigned long)start + size + linesz - 1) & ~(linesz - 1);
+
+ for (v = begin; v < end; v += linesz)
+ invalidate_icache_line((void *)v);
+}
+
+static inline void __flush_icache_range(unsigned long start, unsigned long end)
+{
+ unsigned long v, linesz;
+
+ linesz = boot_cpu_data.dcache.linesz;
+ for (v = start; v < end; v += linesz) {
+ clean_dcache_line((void *)v);
+ invalidate_icache_line((void *)v);
+ }
+
+ flush_write_buffer();
+}
+
+/*
+ * This one is called after a module has been loaded.
+ */
+void flush_icache_range(unsigned long start, unsigned long end)
+{
+ unsigned long linesz;
+
+ linesz = boot_cpu_data.dcache.linesz;
+ __flush_icache_range(start & ~(linesz - 1),
+ (end + linesz - 1) & ~(linesz - 1));
+}
+
+/*
+ * This one is called from do_no_page(), do_swap_page() and install_page().
+ */
+void flush_icache_page(struct vm_area_struct *vma, struct page *page)
+{
+ if (vma->vm_flags & VM_EXEC) {
+ void *v = kmap(page);
+ __flush_icache_range((unsigned long)v, (unsigned long)v + PAGE_SIZE);
+ kunmap(v);
+ }
+}
+
+/*
+ * This one is used by copy_to_user_page()
+ */
+void flush_icache_user_range(struct vm_area_struct *vma, struct page *page,
+ unsigned long addr, int len)
+{
+ if (vma->vm_flags & VM_EXEC)
+ flush_icache_range(addr, addr + len);
+}
+
+asmlinkage int sys_cacheflush(int operation, void __user *addr, size_t len)
+{
+ int ret;
+
+ if (len > CACHEFLUSH_MAX_LEN) {
+ ret = -EPERM;
+ if (!capable(CAP_SYS_ADMIN))
+ goto out;
+ }
+
+ ret = -EFAULT;
+ if (!access_ok(VERIFY_WRITE, addr, len))
+ goto out;
+
+ switch (operation) {
+ case CACHE_IFLUSH:
+ flush_icache_range((unsigned long)addr,
+ (unsigned long)addr + len);
+ ret = 0;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+out:
+ return ret;
+}
diff --git a/arch/avr32/mm/clear_page.S b/arch/avr32/mm/clear_page.S
new file mode 100644
index 00000000000..5d70dca0069
--- /dev/null
+++ b/arch/avr32/mm/clear_page.S
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2004-2006 Atmel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/linkage.h>
+#include <asm/page.h>
+
+/*
+ * clear_page
+ * r12: P1 address (to)
+ */
+ .text
+ .global clear_page
+clear_page:
+ sub r9, r12, -PAGE_SIZE
+ mov r10, 0
+ mov r11, 0
+0: st.d r12++, r10
+ cp r12, r9
+ brne 0b
+ mov pc, lr
diff --git a/arch/avr32/mm/copy_page.S b/arch/avr32/mm/copy_page.S
new file mode 100644
index 00000000000..c2b3752946b
--- /dev/null
+++ b/arch/avr32/mm/copy_page.S
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2004-2006 Atmel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/linkage.h>
+#include <asm/page.h>
+
+/*
+ * copy_page
+ *
+ * r12 to (P1 address)
+ * r11 from (P1 address)
+ * r8-r10 scratch
+ */
+ .text
+ .global copy_page
+copy_page:
+ sub r10, r11, -(1 << PAGE_SHIFT)
+ /* pref r11[0] */
+1: /* pref r11[8] */
+ ld.d r8, r11++
+ st.d r12++, r8
+ cp r11, r10
+ brlo 1b
+ mov pc, lr
diff --git a/arch/avr32/mm/dma-coherent.c b/arch/avr32/mm/dma-coherent.c
new file mode 100644
index 00000000000..44ab8a7bdae
--- /dev/null
+++ b/arch/avr32/mm/dma-coherent.c
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2004-2006 Atmel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/dma-mapping.h>
+
+#include <asm/addrspace.h>
+#include <asm/cacheflush.h>
+
+void dma_cache_sync(void *vaddr, size_t size, int direction)
+{
+ /*
+ * No need to sync an uncached area
+ */
+ if (PXSEG(vaddr) == P2SEG)
+ return;
+
+ switch (direction) {
+ case DMA_FROM_DEVICE: /* invalidate only */
+ dma_cache_inv(vaddr, size);
+ break;
+ case DMA_TO_DEVICE: /* writeback only */
+ dma_cache_wback(vaddr, size);
+ break;
+ case DMA_BIDIRECTIONAL: /* writeback and invalidate */
+ dma_cache_wback_inv(vaddr, size);
+ break;
+ default:
+ BUG();
+ }
+}
+EXPORT_SYMBOL(dma_cache_sync);
+
+static struct page *__dma_alloc(struct device *dev, size_t size,
+ dma_addr_t *handle, gfp_t gfp)
+{
+ struct page *page, *free, *end;
+ int order;
+
+ size = PAGE_ALIGN(size);
+ order = get_order(size);
+
+ page = alloc_pages(gfp, order);
+ if (!page)
+ return NULL;
+ split_page(page, order);
+
+ /*
+ * When accessing physical memory with valid cache data, we
+ * get a cache hit even if the virtual memory region is marked
+ * as uncached.
+ *
+ * Since the memory is newly allocated, there is no point in
+ * doing a writeback. If the previous owner cares, he should
+ * have flushed the cache before releasing the memory.
+ */
+ invalidate_dcache_region(phys_to_virt(page_to_phys(page)), size);
+
+ *handle = page_to_bus(page);
+ free = page + (size >> PAGE_SHIFT);
+ end = page + (1 << order);
+
+ /*
+ * Free any unused pages
+ */
+ while (free < end) {
+ __free_page(free);
+ free++;
+ }
+
+ return page;
+}
+
+static void __dma_free(struct device *dev, size_t size,
+ struct page *page, dma_addr_t handle)
+{
+ struct page *end = page + (PAGE_ALIGN(size) >> PAGE_SHIFT);
+
+ while (page < end)
+ __free_page(page++);
+}
+
+void *dma_alloc_coherent(struct device *dev, size_t size,
+ dma_addr_t *handle, gfp_t gfp)
+{
+ struct page *page;
+ void *ret = NULL;
+
+ page = __dma_alloc(dev, size, handle, gfp);
+ if (page)
+ ret = phys_to_uncached(page_to_phys(page));
+
+ return ret;
+}
+EXPORT_SYMBOL(dma_alloc_coherent);
+
+void dma_free_coherent(struct device *dev, size_t size,
+ void *cpu_addr, dma_addr_t handle)
+{
+ void *addr = phys_to_cached(uncached_to_phys(cpu_addr));
+ struct page *page;
+
+ pr_debug("dma_free_coherent addr %p (phys %08lx) size %u\n",
+ cpu_addr, (unsigned long)handle, (unsigned)size);
+ BUG_ON(!virt_addr_valid(addr));
+ page = virt_to_page(addr);
+ __dma_free(dev, size, page, handle);
+}
+EXPORT_SYMBOL(dma_free_coherent);
+
+#if 0
+void *dma_alloc_writecombine(struct device *dev, size_t size,
+ dma_addr_t *handle, gfp_t gfp)
+{
+ struct page *page;
+
+ page = __dma_alloc(dev, size, handle, gfp);
+
+ /* Now, map the page into P3 with write-combining turned on */
+ return __ioremap(page_to_phys(page), size, _PAGE_BUFFER);
+}
+EXPORT_SYMBOL(dma_alloc_writecombine);
+
+void dma_free_writecombine(struct device *dev, size_t size,
+ void *cpu_addr, dma_addr_t handle)
+{
+ struct page *page;
+
+ iounmap(cpu_addr);
+
+ page = bus_to_page(handle);
+ __dma_free(dev, size, page, handle);
+}
+EXPORT_SYMBOL(dma_free_writecombine);
+#endif
diff --git a/arch/avr32/mm/fault.c b/arch/avr32/mm/fault.c
new file mode 100644
index 00000000000..678557260a3
--- /dev/null
+++ b/arch/avr32/mm/fault.c
@@ -0,0 +1,315 @@
+/*
+ * Copyright (C) 2004-2006 Atmel Corporation
+ *
+ * Based on linux/arch/sh/mm/fault.c:
+ * Copyright (C) 1999 Niibe Yutaka
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/pagemap.h>
+
+#include <asm/kdebug.h>
+#include <asm/mmu_context.h>
+#include <asm/sysreg.h>
+#include <asm/uaccess.h>
+#include <asm/tlb.h>
+
+#ifdef DEBUG
+static void dump_code(unsigned long pc)
+{
+ char *p = (char *)pc;
+ char val;
+ int i;
+
+
+ printk(KERN_DEBUG "Code:");
+ for (i = 0; i < 16; i++) {
+ if (__get_user(val, p + i))
+ break;
+ printk(" %02x", val);
+ }
+ printk("\n");
+}
+#endif
+
+#ifdef CONFIG_KPROBES
+ATOMIC_NOTIFIER_HEAD(notify_page_fault_chain);
+
+/* Hook to register for page fault notifications */
+int register_page_fault_notifier(struct notifier_block *nb)
+{
+ return atomic_notifier_chain_register(&notify_page_fault_chain, nb);
+}
+
+int unregister_page_fault_notifier(struct notifier_block *nb)
+{
+ return atomic_notifier_chain_unregister(&notify_page_fault_chain, nb);
+}
+
+static inline int notify_page_fault(enum die_val val, struct pt_regs *regs,
+ int trap, int sig)
+{
+ struct die_args args = {
+ .regs = regs,
+ .trapnr = trap,
+ };
+ return atomic_notifier_call_chain(&notify_page_fault_chain, val, &args);
+}
+#else
+static inline int notify_page_fault(enum die_val val, struct pt_regs *regs,
+ int trap, int sig)
+{
+ return NOTIFY_DONE;
+}
+#endif
+
+/*
+ * This routine handles page faults. It determines the address and the
+ * problem, and then passes it off to one of the appropriate routines.
+ *
+ * ecr is the Exception Cause Register. Possible values are:
+ * 5: Page not found (instruction access)
+ * 6: Protection fault (instruction access)
+ * 12: Page not found (read access)
+ * 13: Page not found (write access)
+ * 14: Protection fault (read access)
+ * 15: Protection fault (write access)
+ */
+asmlinkage void do_page_fault(unsigned long ecr, struct pt_regs *regs)
+{
+ struct task_struct *tsk;
+ struct mm_struct *mm;
+ struct vm_area_struct *vma;
+ const struct exception_table_entry *fixup;
+ unsigned long address;
+ unsigned long page;
+ int writeaccess = 0;
+
+ if (notify_page_fault(DIE_PAGE_FAULT, regs,
+ ecr, SIGSEGV) == NOTIFY_STOP)
+ return;
+
+ address = sysreg_read(TLBEAR);
+
+ tsk = current;
+ mm = tsk->mm;
+
+ /*
+ * If we're in an interrupt or have no user context, we must
+ * not take the fault...
+ */
+ if (in_atomic() || !mm || regs->sr & SYSREG_BIT(GM))
+ goto no_context;
+
+ local_irq_enable();
+
+ 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:
+ //pr_debug("good area: vm_flags = 0x%lx\n", vma->vm_flags);
+ switch (ecr) {
+ case ECR_PROTECTION_X:
+ case ECR_TLB_MISS_X:
+ if (!(vma->vm_flags & VM_EXEC))
+ goto bad_area;
+ break;
+ case ECR_PROTECTION_R:
+ case ECR_TLB_MISS_R:
+ if (!(vma->vm_flags & (VM_READ | VM_WRITE | VM_EXEC)))
+ goto bad_area;
+ break;
+ case ECR_PROTECTION_W:
+ case ECR_TLB_MISS_W:
+ if (!(vma->vm_flags & VM_WRITE))
+ goto bad_area;
+ writeaccess = 1;
+ break;
+ default:
+ panic("Unhandled case %lu in do_page_fault!", ecr);
+ }
+
+ /*
+ * 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, writeaccess)) {
+ case VM_FAULT_MINOR:
+ tsk->min_flt++;
+ break;
+ case VM_FAULT_MAJOR:
+ tsk->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:
+ pr_debug("Bad area [%s:%u]: addr %08lx, ecr %lu\n",
+ tsk->comm, tsk->pid, address, ecr);
+
+ up_read(&mm->mmap_sem);
+
+ if (user_mode(regs)) {
+ /* Hmm...we have to pass address and ecr somehow... */
+ /* tsk->thread.address = address;
+ tsk->thread.error_code = ecr; */
+#ifdef DEBUG
+ show_regs(regs);
+ dump_code(regs->pc);
+
+ page = sysreg_read(PTBR);
+ printk("ptbr = %08lx", page);
+ if (page) {
+ page = ((unsigned long *)page)[address >> 22];
+ printk(" pgd = %08lx", page);
+ if (page & _PAGE_PRESENT) {
+ page &= PAGE_MASK;
+ address &= 0x003ff000;
+ page = ((unsigned long *)__va(page))[address >> PAGE_SHIFT];
+ printk(" pte = %08lx\n", page);
+ }
+ }
+#endif
+ pr_debug("Sending SIGSEGV to PID %d...\n",
+ tsk->pid);
+ force_sig(SIGSEGV, tsk);
+ return;
+ }
+
+no_context:
+ pr_debug("No context\n");
+
+ /* Are we prepared to handle this kernel fault? */
+ fixup = search_exception_tables(regs->pc);
+ if (fixup) {
+ regs->pc = fixup->fixup;
+ pr_debug("Found fixup at %08lx\n", fixup->fixup);
+ return;
+ }
+
+ /*
+ * Oops. The kernel tried to access some bad page. We'll have
+ * to terminate things with extreme prejudice.
+ */
+ if (address < PAGE_SIZE)
+ printk(KERN_ALERT
+ "Unable to handle kernel NULL pointer dereference");
+ else
+ printk(KERN_ALERT
+ "Unable to handle kernel paging request");
+ printk(" at virtual address %08lx\n", address);
+ printk(KERN_ALERT "pc = %08lx\n", regs->pc);
+
+ page = sysreg_read(PTBR);
+ printk(KERN_ALERT "ptbr = %08lx", page);
+ if (page) {
+ page = ((unsigned long *)page)[address >> 22];
+ printk(" pgd = %08lx", page);
+ if (page & _PAGE_PRESENT) {
+ page &= PAGE_MASK;
+ address &= 0x003ff000;
+ page = ((unsigned long *)__va(page))[address >> PAGE_SHIFT];
+ printk(" pte = %08lx\n", page);
+ }
+ }
+ die("\nOops", regs, ecr);
+ do_exit(SIGKILL);
+
+ /*
+ * We ran out of memory, or some other thing happened to us
+ * that made us unable to handle the page fault gracefully.
+ */
+out_of_memory:
+ printk("Out of memory\n");
+ up_read(&mm->mmap_sem);
+ if (current->pid == 1) {
+ yield();
+ down_read(&mm->mmap_sem);
+ goto survive;
+ }
+ printk("VM: Killing process %s\n", tsk->comm);
+ if (user_mode(regs))
+ do_exit(SIGKILL);
+ goto no_context;
+
+do_sigbus:
+ up_read(&mm->mmap_sem);
+
+ /*
+ * Send a sigbus, regardless of whether we were in kernel or
+ * user mode.
+ */
+ /* address, error_code, trap_no, ... */
+#ifdef DEBUG
+ show_regs(regs);
+ dump_code(regs->pc);
+#endif
+ pr_debug("Sending SIGBUS to PID %d...\n", tsk->pid);
+ force_sig(SIGBUS, tsk);
+
+ /* Kernel mode? Handle exceptions or die */
+ if (!user_mode(regs))
+ goto no_context;
+}
+
+asmlinkage void do_bus_error(unsigned long addr, int write_access,
+ struct pt_regs *regs)
+{
+ printk(KERN_ALERT
+ "Bus error at physical address 0x%08lx (%s access)\n",
+ addr, write_access ? "write" : "read");
+ printk(KERN_INFO "DTLB dump:\n");
+ dump_dtlb();
+ die("Bus Error", regs, write_access);
+ do_exit(SIGKILL);
+}
+
+/*
+ * This functionality is currently not possible to implement because
+ * we're using segmentation to ensure a fixed mapping of the kernel
+ * virtual address space.
+ *
+ * It would be possible to implement this, but it would require us to
+ * disable segmentation at startup and load the kernel mappings into
+ * the TLB like any other pages. There will be lots of trickery to
+ * avoid recursive invocation of the TLB miss handler, though...
+ */
+#ifdef CONFIG_DEBUG_PAGEALLOC
+void kernel_map_pages(struct page *page, int numpages, int enable)
+{
+
+}
+EXPORT_SYMBOL(kernel_map_pages);
+#endif
diff --git a/arch/avr32/mm/init.c b/arch/avr32/mm/init.c
new file mode 100644
index 00000000000..3e6c4103980
--- /dev/null
+++ b/arch/avr32/mm/init.c
@@ -0,0 +1,480 @@
+/*
+ * Copyright (C) 2004-2006 Atmel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/swap.h>
+#include <linux/init.h>
+#include <linux/initrd.h>
+#include <linux/mmzone.h>
+#include <linux/bootmem.h>
+#include <linux/pagemap.h>
+#include <linux/pfn.h>
+#include <linux/nodemask.h>
+
+#include <asm/page.h>
+#include <asm/mmu_context.h>
+#include <asm/tlb.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <asm/setup.h>
+#include <asm/sections.h>
+
+DEFINE_PER_CPU(struct mmu_gather, mmu_gathers);
+
+pgd_t swapper_pg_dir[PTRS_PER_PGD];
+
+struct page *empty_zero_page;
+
+/*
+ * Cache of MMU context last used.
+ */
+unsigned long mmu_context_cache = NO_CONTEXT;
+
+#define START_PFN (NODE_DATA(0)->bdata->node_boot_start >> PAGE_SHIFT)
+#define MAX_LOW_PFN (NODE_DATA(0)->bdata->node_low_pfn)
+
+void show_mem(void)
+{
+ int total = 0, reserved = 0, cached = 0;
+ int slab = 0, free = 0, shared = 0;
+ pg_data_t *pgdat;
+
+ printk("Mem-info:\n");
+ show_free_areas();
+
+ for_each_online_pgdat(pgdat) {
+ struct page *page, *end;
+
+ page = pgdat->node_mem_map;
+ end = page + pgdat->node_spanned_pages;
+
+ do {
+ total++;
+ if (PageReserved(page))
+ reserved++;
+ else if (PageSwapCache(page))
+ cached++;
+ else if (PageSlab(page))
+ slab++;
+ else if (!page_count(page))
+ free++;
+ else
+ shared += page_count(page) - 1;
+ page++;
+ } while (page < end);
+ }
+
+ printk ("%d pages of RAM\n", total);
+ printk ("%d free pages\n", free);
+ printk ("%d reserved pages\n", reserved);
+ printk ("%d slab pages\n", slab);
+ printk ("%d pages shared\n", shared);
+ printk ("%d pages swap cached\n", cached);
+}
+
+static void __init print_memory_map(const char *what,
+ struct tag_mem_range *mem)
+{
+ printk ("%s:\n", what);
+ for (; mem; mem = mem->next) {
+ printk (" %08lx - %08lx\n",
+ (unsigned long)mem->addr,
+ (unsigned long)(mem->addr + mem->size));
+ }
+}
+
+#define MAX_LOWMEM HIGHMEM_START
+#define MAX_LOWMEM_PFN PFN_DOWN(MAX_LOWMEM)
+
+/*
+ * Sort a list of memory regions in-place by ascending address.
+ *
+ * We're using bubble sort because we only have singly linked lists
+ * with few elements.
+ */
+static void __init sort_mem_list(struct tag_mem_range **pmem)
+{
+ int done;
+ struct tag_mem_range **a, **b;
+
+ if (!*pmem)
+ return;
+
+ do {
+ done = 1;
+ a = pmem, b = &(*pmem)->next;
+ while (*b) {
+ if ((*a)->addr > (*b)->addr) {
+ struct tag_mem_range *tmp;
+ tmp = (*b)->next;
+ (*b)->next = *a;
+ *a = *b;
+ *b = tmp;
+ done = 0;
+ }
+ a = &(*a)->next;
+ b = &(*a)->next;
+ }
+ } while (!done);
+}
+
+/*
+ * Find a free memory region large enough for storing the
+ * bootmem bitmap.
+ */
+static unsigned long __init
+find_bootmap_pfn(const struct tag_mem_range *mem)
+{
+ unsigned long bootmap_pages, bootmap_len;
+ unsigned long node_pages = PFN_UP(mem->size);
+ unsigned long bootmap_addr = mem->addr;
+ struct tag_mem_range *reserved = mem_reserved;
+ struct tag_mem_range *ramdisk = mem_ramdisk;
+ unsigned long kern_start = virt_to_phys(_stext);
+ unsigned long kern_end = virt_to_phys(_end);
+
+ bootmap_pages = bootmem_bootmap_pages(node_pages);
+ bootmap_len = bootmap_pages << PAGE_SHIFT;
+
+ /*
+ * Find a large enough region without reserved pages for
+ * storing the bootmem bitmap. We can take advantage of the
+ * fact that all lists have been sorted.
+ *
+ * We have to check explicitly reserved regions as well as the
+ * kernel image and any RAMDISK images...
+ *
+ * Oh, and we have to make sure we don't overwrite the taglist
+ * since we're going to use it until the bootmem allocator is
+ * fully up and running.
+ */
+ while (1) {
+ if ((bootmap_addr < kern_end) &&
+ ((bootmap_addr + bootmap_len) > kern_start))
+ bootmap_addr = kern_end;
+
+ while (reserved &&
+ (bootmap_addr >= (reserved->addr + reserved->size)))
+ reserved = reserved->next;
+
+ if (reserved &&
+ ((bootmap_addr + bootmap_len) >= reserved->addr)) {
+ bootmap_addr = reserved->addr + reserved->size;
+ continue;
+ }
+
+ while (ramdisk &&
+ (bootmap_addr >= (ramdisk->addr + ramdisk->size)))
+ ramdisk = ramdisk->next;
+
+ if (!ramdisk ||
+ ((bootmap_addr + bootmap_len) < ramdisk->addr))
+ break;
+
+ bootmap_addr = ramdisk->addr + ramdisk->size;
+ }
+
+ if ((PFN_UP(bootmap_addr) + bootmap_len) >= (mem->addr + mem->size))
+ return ~0UL;
+
+ return PFN_UP(bootmap_addr);
+}
+
+void __init setup_bootmem(void)
+{
+ unsigned bootmap_size;
+ unsigned long first_pfn, bootmap_pfn, pages;
+ unsigned long max_pfn, max_low_pfn;
+ unsigned long kern_start = virt_to_phys(_stext);
+ unsigned long kern_end = virt_to_phys(_end);
+ unsigned node = 0;
+ struct tag_mem_range *bank, *res;
+
+ sort_mem_list(&mem_phys);
+ sort_mem_list(&mem_reserved);
+
+ print_memory_map("Physical memory", mem_phys);
+ print_memory_map("Reserved memory", mem_reserved);
+
+ nodes_clear(node_online_map);
+
+ if (mem_ramdisk) {
+#ifdef CONFIG_BLK_DEV_INITRD
+ initrd_start = __va(mem_ramdisk->addr);
+ initrd_end = initrd_start + mem_ramdisk->size;
+
+ print_memory_map("RAMDISK images", mem_ramdisk);
+ if (mem_ramdisk->next)
+ printk(KERN_WARNING
+ "Warning: Only the first RAMDISK image "
+ "will be used\n");
+ sort_mem_list(&mem_ramdisk);
+#else
+ printk(KERN_WARNING "RAM disk image present, but "
+ "no initrd support in kernel!\n");
+#endif
+ }
+
+ if (mem_phys->next)
+ printk(KERN_WARNING "Only using first memory bank\n");
+
+ for (bank = mem_phys; bank; bank = NULL) {
+ first_pfn = PFN_UP(bank->addr);
+ max_low_pfn = max_pfn = PFN_DOWN(bank->addr + bank->size);
+ bootmap_pfn = find_bootmap_pfn(bank);
+ if (bootmap_pfn > max_pfn)
+ panic("No space for bootmem bitmap!\n");
+
+ if (max_low_pfn > MAX_LOWMEM_PFN) {
+ max_low_pfn = MAX_LOWMEM_PFN;
+#ifndef CONFIG_HIGHMEM
+ /*
+ * Lowmem is memory that can be addressed
+ * directly through P1/P2
+ */
+ printk(KERN_WARNING
+ "Node %u: Only %ld MiB of memory will be used.\n",
+ node, MAX_LOWMEM >> 20);
+ printk(KERN_WARNING "Use a HIGHMEM enabled kernel.\n");
+#else
+#error HIGHMEM is not supported by AVR32 yet
+#endif
+ }
+
+ /* Initialize the boot-time allocator with low memory only. */
+ bootmap_size = init_bootmem_node(NODE_DATA(node), bootmap_pfn,
+ first_pfn, max_low_pfn);
+
+ printk("Node %u: bdata = %p, bdata->node_bootmem_map = %p\n",
+ node, NODE_DATA(node)->bdata,
+ NODE_DATA(node)->bdata->node_bootmem_map);
+
+ /*
+ * Register fully available RAM pages with the bootmem
+ * allocator.
+ */
+ pages = max_low_pfn - first_pfn;
+ free_bootmem_node (NODE_DATA(node), PFN_PHYS(first_pfn),
+ PFN_PHYS(pages));
+
+ /*
+ * Reserve space for the kernel image (if present in
+ * this node)...
+ */
+ if ((kern_start >= PFN_PHYS(first_pfn)) &&
+ (kern_start < PFN_PHYS(max_pfn))) {
+ printk("Node %u: Kernel image %08lx - %08lx\n",
+ node, kern_start, kern_end);
+ reserve_bootmem_node(NODE_DATA(node), kern_start,
+ kern_end - kern_start);
+ }
+
+ /* ...the bootmem bitmap... */
+ reserve_bootmem_node(NODE_DATA(node),
+ PFN_PHYS(bootmap_pfn),
+ bootmap_size);
+
+ /* ...any RAMDISK images... */
+ for (res = mem_ramdisk; res; res = res->next) {
+ if (res->addr > PFN_PHYS(max_pfn))
+ break;
+
+ if (res->addr >= PFN_PHYS(first_pfn)) {
+ printk("Node %u: RAMDISK %08lx - %08lx\n",
+ node,
+ (unsigned long)res->addr,
+ (unsigned long)(res->addr + res->size));
+ reserve_bootmem_node(NODE_DATA(node),
+ res->addr, res->size);
+ }
+ }
+
+ /* ...and any other reserved regions. */
+ for (res = mem_reserved; res; res = res->next) {
+ if (res->addr > PFN_PHYS(max_pfn))
+ break;
+
+ if (res->addr >= PFN_PHYS(first_pfn)) {
+ printk("Node %u: Reserved %08lx - %08lx\n",
+ node,
+ (unsigned long)res->addr,
+ (unsigned long)(res->addr + res->size));
+ reserve_bootmem_node(NODE_DATA(node),
+ res->addr, res->size);
+ }
+ }
+
+ node_set_online(node);
+ }
+}
+
+/*
+ * paging_init() sets up the page tables
+ *
+ * This routine also unmaps the page at virtual kernel address 0, so
+ * that we can trap those pesky NULL-reference errors in the kernel.
+ */
+void __init paging_init(void)
+{
+ extern unsigned long _evba;
+ void *zero_page;
+ int nid;
+
+ /*
+ * Make sure we can handle exceptions before enabling
+ * paging. Not that we should ever _get_ any exceptions this
+ * early, but you never know...
+ */
+ printk("Exception vectors start at %p\n", &_evba);
+ sysreg_write(EVBA, (unsigned long)&_evba);
+
+ /*
+ * Since we are ready to handle exceptions now, we should let
+ * the CPU generate them...
+ */
+ __asm__ __volatile__ ("csrf %0" : : "i"(SR_EM_BIT));
+
+ /*
+ * Allocate the zero page. The allocator will panic if it
+ * can't satisfy the request, so no need to check.
+ */
+ zero_page = alloc_bootmem_low_pages_node(NODE_DATA(0),
+ PAGE_SIZE);
+
+ {
+ pgd_t *pg_dir;
+ int i;
+
+ pg_dir = swapper_pg_dir;
+ sysreg_write(PTBR, (unsigned long)pg_dir);
+
+ for (i = 0; i < PTRS_PER_PGD; i++)
+ pgd_val(pg_dir[i]) = 0;
+
+ enable_mmu();
+ printk ("CPU: Paging enabled\n");
+ }
+
+ for_each_online_node(nid) {
+ pg_data_t *pgdat = NODE_DATA(nid);
+ unsigned long zones_size[MAX_NR_ZONES];
+ unsigned long low, start_pfn;
+
+ start_pfn = pgdat->bdata->node_boot_start;
+ start_pfn >>= PAGE_SHIFT;
+ low = pgdat->bdata->node_low_pfn;
+
+ memset(zones_size, 0, sizeof(zones_size));
+ zones_size[ZONE_NORMAL] = low - start_pfn;
+
+ printk("Node %u: start_pfn = 0x%lx, low = 0x%lx\n",
+ nid, start_pfn, low);
+
+ free_area_init_node(nid, pgdat, zones_size, start_pfn, NULL);
+
+ printk("Node %u: mem_map starts at %p\n",
+ pgdat->node_id, pgdat->node_mem_map);
+ }
+
+ mem_map = NODE_DATA(0)->node_mem_map;
+
+ memset(zero_page, 0, PAGE_SIZE);
+ empty_zero_page = virt_to_page(zero_page);
+ flush_dcache_page(empty_zero_page);
+}
+
+void __init mem_init(void)
+{
+ int codesize, reservedpages, datasize, initsize;
+ int nid, i;
+
+ reservedpages = 0;
+ high_memory = NULL;
+
+ /* this will put all low memory onto the freelists */
+ for_each_online_node(nid) {
+ pg_data_t *pgdat = NODE_DATA(nid);
+ unsigned long node_pages = 0;
+ void *node_high_memory;
+
+ num_physpages += pgdat->node_present_pages;
+
+ if (pgdat->node_spanned_pages != 0)
+ node_pages = free_all_bootmem_node(pgdat);
+
+ totalram_pages += node_pages;
+
+ for (i = 0; i < node_pages; i++)
+ if (PageReserved(pgdat->node_mem_map + i))
+ reservedpages++;
+
+ node_high_memory = (void *)((pgdat->node_start_pfn
+ + pgdat->node_spanned_pages)
+ << PAGE_SHIFT);
+ if (node_high_memory > high_memory)
+ high_memory = node_high_memory;
+ }
+
+ max_mapnr = MAP_NR(high_memory);
+
+ codesize = (unsigned long)_etext - (unsigned long)_text;
+ datasize = (unsigned long)_edata - (unsigned long)_data;
+ initsize = (unsigned long)__init_end - (unsigned long)__init_begin;
+
+ printk ("Memory: %luk/%luk available (%dk kernel code, "
+ "%dk reserved, %dk data, %dk init)\n",
+ (unsigned long)nr_free_pages() << (PAGE_SHIFT - 10),
+ totalram_pages << (PAGE_SHIFT - 10),
+ codesize >> 10,
+ reservedpages << (PAGE_SHIFT - 10),
+ datasize >> 10,
+ initsize >> 10);
+}
+
+static inline void free_area(unsigned long addr, unsigned long end, char *s)
+{
+ unsigned int size = (end - addr) >> 10;
+
+ for (; addr < end; addr += PAGE_SIZE) {
+ struct page *page = virt_to_page(addr);
+ ClearPageReserved(page);
+ init_page_count(page);
+ free_page(addr);
+ totalram_pages++;
+ }
+
+ if (size && s)
+ printk(KERN_INFO "Freeing %s memory: %dK (%lx - %lx)\n",
+ s, size, end - (size << 10), end);
+}
+
+void free_initmem(void)
+{
+ free_area((unsigned long)__init_begin, (unsigned long)__init_end,
+ "init");
+}
+
+#ifdef CONFIG_BLK_DEV_INITRD
+
+static int keep_initrd;
+
+void free_initrd_mem(unsigned long start, unsigned long end)
+{
+ if (!keep_initrd)
+ free_area(start, end, "initrd");
+}
+
+static int __init keepinitrd_setup(char *__unused)
+{
+ keep_initrd = 1;
+ return 1;
+}
+
+__setup("keepinitrd", keepinitrd_setup);
+#endif
diff --git a/arch/avr32/mm/ioremap.c b/arch/avr32/mm/ioremap.c
new file mode 100644
index 00000000000..536021877df
--- /dev/null
+++ b/arch/avr32/mm/ioremap.c
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2004-2006 Atmel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/vmalloc.h>
+#include <linux/module.h>
+
+#include <asm/io.h>
+#include <asm/pgtable.h>
+#include <asm/cacheflush.h>
+#include <asm/tlbflush.h>
+#include <asm/addrspace.h>
+
+static inline int remap_area_pte(pte_t *pte, unsigned long address,
+ unsigned long end, unsigned long phys_addr,
+ pgprot_t prot)
+{
+ unsigned long pfn;
+
+ pfn = phys_addr >> PAGE_SHIFT;
+ do {
+ WARN_ON(!pte_none(*pte));
+
+ set_pte(pte, pfn_pte(pfn, prot));
+ address += PAGE_SIZE;
+ pfn++;
+ pte++;
+ } while (address && (address < end));
+
+ return 0;
+}
+
+static inline int remap_area_pmd(pmd_t *pmd, unsigned long address,
+ unsigned long end, unsigned long phys_addr,
+ pgprot_t prot)
+{
+ unsigned long next;
+
+ phys_addr -= address;
+
+ do {
+ pte_t *pte = pte_alloc_kernel(pmd, address);
+ if (!pte)
+ return -ENOMEM;
+
+ next = (address + PMD_SIZE) & PMD_MASK;
+ if (remap_area_pte(pte, address, next,
+ address + phys_addr, prot))
+ return -ENOMEM;
+
+ address = next;
+ pmd++;
+ } while (address && (address < end));
+ return 0;
+}
+
+static int remap_area_pud(pud_t *pud, unsigned long address,
+ unsigned long end, unsigned long phys_addr,
+ pgprot_t prot)
+{
+ unsigned long next;
+
+ phys_addr -= address;
+
+ do {
+ pmd_t *pmd = pmd_alloc(&init_mm, pud, address);
+ if (!pmd)
+ return -ENOMEM;
+ next = (address + PUD_SIZE) & PUD_MASK;
+ if (remap_area_pmd(pmd, address, next,
+ phys_addr + address, prot))
+ return -ENOMEM;
+
+ address = next;
+ pud++;
+ } while (address && address < end);
+
+ return 0;
+}
+
+static int remap_area_pages(unsigned long address, unsigned long phys_addr,
+ size_t size, pgprot_t prot)
+{
+ unsigned long end = address + size;
+ unsigned long next;
+ pgd_t *pgd;
+ int err = 0;
+
+ phys_addr -= address;
+
+ pgd = pgd_offset_k(address);
+ flush_cache_all();
+ BUG_ON(address >= end);
+
+ spin_lock(&init_mm.page_table_lock);
+ do {
+ pud_t *pud = pud_alloc(&init_mm, pgd, address);
+
+ err = -ENOMEM;
+ if (!pud)
+ break;
+
+ next = (address + PGDIR_SIZE) & PGDIR_MASK;
+ if (next < address || next > end)
+ next = end;
+ err = remap_area_pud(pud, address, next,
+ phys_addr + address, prot);
+ if (err)
+ break;
+
+ address = next;
+ pgd++;
+ } while (address && (address < end));
+
+ spin_unlock(&init_mm.page_table_lock);
+ flush_tlb_all();
+ return err;
+}
+
+/*
+ * Re-map an arbitrary physical address space into the kernel virtual
+ * address space. Needed when the kernel wants to access physical
+ * memory directly.
+ */
+void __iomem *__ioremap(unsigned long phys_addr, size_t size,
+ unsigned long flags)
+{
+ void *addr;
+ struct vm_struct *area;
+ unsigned long offset, last_addr;
+ pgprot_t prot;
+
+ /*
+ * Check if we can simply use the P4 segment. This area is
+ * uncacheable, so if caching/buffering is requested, we can't
+ * use it.
+ */
+ if ((phys_addr >= P4SEG) && (flags == 0))
+ return (void __iomem *)phys_addr;
+
+ /* Don't allow wraparound or zero size */
+ last_addr = phys_addr + size - 1;
+ if (!size || last_addr < phys_addr)
+ return NULL;
+
+ /*
+ * XXX: When mapping regular RAM, we'd better make damn sure
+ * it's never used for anything else. But this is really the
+ * caller's responsibility...
+ */
+ if (PHYSADDR(P2SEGADDR(phys_addr)) == phys_addr)
+ return (void __iomem *)P2SEGADDR(phys_addr);
+
+ /* Mappings have to be page-aligned */
+ offset = phys_addr & ~PAGE_MASK;
+ phys_addr &= PAGE_MASK;
+ size = PAGE_ALIGN(last_addr + 1) - phys_addr;
+
+ prot = __pgprot(_PAGE_PRESENT | _PAGE_RW | _PAGE_DIRTY
+ | _PAGE_ACCESSED | _PAGE_TYPE_SMALL | flags);
+
+ /*
+ * Ok, go for it..
+ */
+ area = get_vm_area(size, VM_IOREMAP);
+ if (!area)
+ return NULL;
+ area->phys_addr = phys_addr;
+ addr = area->addr;
+ if (remap_area_pages((unsigned long)addr, phys_addr, size, prot)) {
+ vunmap(addr);
+ return NULL;
+ }
+
+ return (void __iomem *)(offset + (char *)addr);
+}
+EXPORT_SYMBOL(__ioremap);
+
+void __iounmap(void __iomem *addr)
+{
+ struct vm_struct *p;
+
+ if ((unsigned long)addr >= P4SEG)
+ return;
+
+ p = remove_vm_area((void *)(PAGE_MASK & (unsigned long __force)addr));
+ if (unlikely(!p)) {
+ printk (KERN_ERR "iounmap: bad address %p\n", addr);
+ return;
+ }
+
+ kfree (p);
+}
+EXPORT_SYMBOL(__iounmap);
diff --git a/arch/avr32/mm/tlb.c b/arch/avr32/mm/tlb.c
new file mode 100644
index 00000000000..5d0523bbe29
--- /dev/null
+++ b/arch/avr32/mm/tlb.c
@@ -0,0 +1,378 @@
+/*
+ * AVR32 TLB operations
+ *
+ * Copyright (C) 2004-2006 Atmel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/mm.h>
+
+#include <asm/mmu_context.h>
+
+#define _TLBEHI_I 0x100
+
+void show_dtlb_entry(unsigned int index)
+{
+ unsigned int tlbehi, tlbehi_save, tlbelo, mmucr, mmucr_save, flags;
+
+ local_irq_save(flags);
+ mmucr_save = sysreg_read(MMUCR);
+ tlbehi_save = sysreg_read(TLBEHI);
+ mmucr = mmucr_save & 0x13;
+ mmucr |= index << 14;
+ sysreg_write(MMUCR, mmucr);
+
+ asm volatile("tlbr" : : : "memory");
+ cpu_sync_pipeline();
+
+ tlbehi = sysreg_read(TLBEHI);
+ tlbelo = sysreg_read(TLBELO);
+
+ printk("%2u: %c %c %02x %05x %05x %o %o %c %c %c %c\n",
+ index,
+ (tlbehi & 0x200)?'1':'0',
+ (tlbelo & 0x100)?'1':'0',
+ (tlbehi & 0xff),
+ (tlbehi >> 12), (tlbelo >> 12),
+ (tlbelo >> 4) & 7, (tlbelo >> 2) & 3,
+ (tlbelo & 0x200)?'1':'0',
+ (tlbelo & 0x080)?'1':'0',
+ (tlbelo & 0x001)?'1':'0',
+ (tlbelo & 0x002)?'1':'0');
+
+ sysreg_write(MMUCR, mmucr_save);
+ sysreg_write(TLBEHI, tlbehi_save);
+ cpu_sync_pipeline();
+ local_irq_restore(flags);
+}
+
+void dump_dtlb(void)
+{
+ unsigned int i;
+
+ printk("ID V G ASID VPN PFN AP SZ C B W D\n");
+ for (i = 0; i < 32; i++)
+ show_dtlb_entry(i);
+}
+
+static unsigned long last_mmucr;
+
+static inline void set_replacement_pointer(unsigned shift)
+{
+ unsigned long mmucr, mmucr_save;
+
+ mmucr = mmucr_save = sysreg_read(MMUCR);
+
+ /* Does this mapping already exist? */
+ __asm__ __volatile__(
+ " tlbs\n"
+ " mfsr %0, %1"
+ : "=r"(mmucr)
+ : "i"(SYSREG_MMUCR));
+
+ if (mmucr & SYSREG_BIT(MMUCR_N)) {
+ /* Not found -- pick a not-recently-accessed entry */
+ unsigned long rp;
+ unsigned long tlbar = sysreg_read(TLBARLO);
+
+ rp = 32 - fls(tlbar);
+ if (rp == 32) {
+ rp = 0;
+ sysreg_write(TLBARLO, -1L);
+ }
+
+ mmucr &= 0x13;
+ mmucr |= (rp << shift);
+
+ sysreg_write(MMUCR, mmucr);
+ }
+
+ last_mmucr = mmucr;
+}
+
+static void update_dtlb(unsigned long address, pte_t pte, unsigned long asid)
+{
+ unsigned long vpn;
+
+ vpn = (address & MMU_VPN_MASK) | _TLBEHI_VALID | asid;
+ sysreg_write(TLBEHI, vpn);
+ cpu_sync_pipeline();
+
+ set_replacement_pointer(14);
+
+ sysreg_write(TLBELO, pte_val(pte) & _PAGE_FLAGS_HARDWARE_MASK);
+
+ /* Let's go */
+ asm volatile("nop\n\ttlbw" : : : "memory");
+ cpu_sync_pipeline();
+}
+
+void update_mmu_cache(struct vm_area_struct *vma,
+ unsigned long address, pte_t pte)
+{
+ unsigned long flags;
+
+ /* ptrace may call this routine */
+ if (vma && current->active_mm != vma->vm_mm)
+ return;
+
+ local_irq_save(flags);
+ update_dtlb(address, pte, get_asid());
+ local_irq_restore(flags);
+}
+
+void __flush_tlb_page(unsigned long asid, unsigned long page)
+{
+ unsigned long mmucr, tlbehi;
+
+ page |= asid;
+ sysreg_write(TLBEHI, page);
+ cpu_sync_pipeline();
+ asm volatile("tlbs");
+ mmucr = sysreg_read(MMUCR);
+
+ if (!(mmucr & SYSREG_BIT(MMUCR_N))) {
+ unsigned long tlbarlo;
+ unsigned long entry;
+
+ /* Clear the "valid" bit */
+ tlbehi = sysreg_read(TLBEHI);
+ tlbehi &= ~_TLBEHI_VALID;
+ sysreg_write(TLBEHI, tlbehi);
+ cpu_sync_pipeline();
+
+ /* mark the entry as "not accessed" */
+ entry = (mmucr >> 14) & 0x3f;
+ tlbarlo = sysreg_read(TLBARLO);
+ tlbarlo |= (0x80000000 >> entry);
+ sysreg_write(TLBARLO, tlbarlo);
+
+ /* update the entry with valid bit clear */
+ asm volatile("tlbw");
+ cpu_sync_pipeline();
+ }
+}
+
+void flush_tlb_page(struct vm_area_struct *vma, unsigned long page)
+{
+ if (vma->vm_mm && vma->vm_mm->context != NO_CONTEXT) {
+ unsigned long flags, asid;
+ unsigned long saved_asid = MMU_NO_ASID;
+
+ asid = vma->vm_mm->context & MMU_CONTEXT_ASID_MASK;
+ page &= PAGE_MASK;
+
+ local_irq_save(flags);
+ if (vma->vm_mm != current->mm) {
+ saved_asid = get_asid();
+ set_asid(asid);
+ }
+
+ __flush_tlb_page(asid, page);
+
+ if (saved_asid != MMU_NO_ASID)
+ set_asid(saved_asid);
+ local_irq_restore(flags);
+ }
+}
+
+void flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
+ unsigned long end)
+{
+ struct mm_struct *mm = vma->vm_mm;
+
+ if (mm->context != NO_CONTEXT) {
+ unsigned long flags;
+ int size;
+
+ local_irq_save(flags);
+ size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT;
+ if (size > (MMU_DTLB_ENTRIES / 4)) { /* Too many entries to flush */
+ mm->context = NO_CONTEXT;
+ if (mm == current->mm)
+ activate_context(mm);
+ } else {
+ unsigned long asid = mm->context & MMU_CONTEXT_ASID_MASK;
+ unsigned long saved_asid = MMU_NO_ASID;
+
+ start &= PAGE_MASK;
+ end += (PAGE_SIZE - 1);
+ end &= PAGE_MASK;
+ if (mm != current->mm) {
+ saved_asid = get_asid();
+ set_asid(asid);
+ }
+
+ while (start < end) {
+ __flush_tlb_page(asid, start);
+ start += PAGE_SIZE;
+ }
+ if (saved_asid != MMU_NO_ASID)
+ set_asid(saved_asid);
+ }
+ local_irq_restore(flags);
+ }
+}
+
+/*
+ * TODO: If this is only called for addresses > TASK_SIZE, we can probably
+ * skip the ASID stuff and just use the Global bit...
+ */
+void flush_tlb_kernel_range(unsigned long start, unsigned long end)
+{
+ unsigned long flags;
+ int size;
+
+ local_irq_save(flags);
+ size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT;
+ if (size > (MMU_DTLB_ENTRIES / 4)) { /* Too many entries to flush */
+ flush_tlb_all();
+ } else {
+ unsigned long asid = init_mm.context & MMU_CONTEXT_ASID_MASK;
+ unsigned long saved_asid = get_asid();
+
+ start &= PAGE_MASK;
+ end += (PAGE_SIZE - 1);
+ end &= PAGE_MASK;
+ set_asid(asid);
+ while (start < end) {
+ __flush_tlb_page(asid, start);
+ start += PAGE_SIZE;
+ }
+ set_asid(saved_asid);
+ }
+ local_irq_restore(flags);
+}
+
+void flush_tlb_mm(struct mm_struct *mm)
+{
+ /* Invalidate all TLB entries of this process by getting a new ASID */
+ if (mm->context != NO_CONTEXT) {
+ unsigned long flags;
+
+ local_irq_save(flags);
+ mm->context = NO_CONTEXT;
+ if (mm == current->mm)
+ activate_context(mm);
+ local_irq_restore(flags);
+ }
+}
+
+void flush_tlb_all(void)
+{
+ unsigned long flags;
+
+ local_irq_save(flags);
+ sysreg_write(MMUCR, sysreg_read(MMUCR) | SYSREG_BIT(MMUCR_I));
+ local_irq_restore(flags);
+}
+
+#ifdef CONFIG_PROC_FS
+
+#include <linux/seq_file.h>
+#include <linux/proc_fs.h>
+#include <linux/init.h>
+
+static void *tlb_start(struct seq_file *tlb, loff_t *pos)
+{
+ static unsigned long tlb_index;
+
+ if (*pos >= 32)
+ return NULL;
+
+ tlb_index = 0;
+ return &tlb_index;
+}
+
+static void *tlb_next(struct seq_file *tlb, void *v, loff_t *pos)
+{
+ unsigned long *index = v;
+
+ if (*index >= 31)
+ return NULL;
+
+ ++*pos;
+ ++*index;
+ return index;
+}
+
+static void tlb_stop(struct seq_file *tlb, void *v)
+{
+
+}
+
+static int tlb_show(struct seq_file *tlb, void *v)
+{
+ unsigned int tlbehi, tlbehi_save, tlbelo, mmucr, mmucr_save, flags;
+ unsigned long *index = v;
+
+ if (*index == 0)
+ seq_puts(tlb, "ID V G ASID VPN PFN AP SZ C B W D\n");
+
+ BUG_ON(*index >= 32);
+
+ local_irq_save(flags);
+ mmucr_save = sysreg_read(MMUCR);
+ tlbehi_save = sysreg_read(TLBEHI);
+ mmucr = mmucr_save & 0x13;
+ mmucr |= *index << 14;
+ sysreg_write(MMUCR, mmucr);
+
+ asm volatile("tlbr" : : : "memory");
+ cpu_sync_pipeline();
+
+ tlbehi = sysreg_read(TLBEHI);
+ tlbelo = sysreg_read(TLBELO);
+
+ sysreg_write(MMUCR, mmucr_save);
+ sysreg_write(TLBEHI, tlbehi_save);
+ cpu_sync_pipeline();
+ local_irq_restore(flags);
+
+ seq_printf(tlb, "%2lu: %c %c %02x %05x %05x %o %o %c %c %c %c\n",
+ *index,
+ (tlbehi & 0x200)?'1':'0',
+ (tlbelo & 0x100)?'1':'0',
+ (tlbehi & 0xff),
+ (tlbehi >> 12), (tlbelo >> 12),
+ (tlbelo >> 4) & 7, (tlbelo >> 2) & 3,
+ (tlbelo & 0x200)?'1':'0',
+ (tlbelo & 0x080)?'1':'0',
+ (tlbelo & 0x001)?'1':'0',
+ (tlbelo & 0x002)?'1':'0');
+
+ return 0;
+}
+
+static struct seq_operations tlb_ops = {
+ .start = tlb_start,
+ .next = tlb_next,
+ .stop = tlb_stop,
+ .show = tlb_show,
+};
+
+static int tlb_open(struct inode *inode, struct file *file)
+{
+ return seq_open(file, &tlb_ops);
+}
+
+static struct file_operations proc_tlb_operations = {
+ .open = tlb_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
+static int __init proctlb_init(void)
+{
+ struct proc_dir_entry *entry;
+
+ entry = create_proc_entry("tlb", 0, NULL);
+ if (entry)
+ entry->proc_fops = &proc_tlb_operations;
+ return 0;
+}
+late_initcall(proctlb_init);
+#endif /* CONFIG_PROC_FS */
diff --git a/arch/frv/Kconfig b/arch/frv/Kconfig
index a601a17cf56..f7b171b92ea 100644
--- a/arch/frv/Kconfig
+++ b/arch/frv/Kconfig
@@ -27,7 +27,11 @@ config GENERIC_CALIBRATE_DELAY
config GENERIC_HARDIRQS
bool
- default n
+ default y
+
+config GENERIC_HARDIRQS_NO__DO_IRQ
+ bool
+ default y
config GENERIC_TIME
bool
@@ -251,6 +255,12 @@ config MB93091_NO_MB
endchoice
endif
+config FUJITSU_MB93493
+ bool "MB93493 Multimedia chip"
+ help
+ Select this option if the MB93493 multimedia chip is going to be
+ used.
+
choice
prompt "GP-Relative data support"
default GPREL_DATA_8
diff --git a/arch/frv/kernel/Makefile b/arch/frv/kernel/Makefile
index 5a827b349b5..32db3499c46 100644
--- a/arch/frv/kernel/Makefile
+++ b/arch/frv/kernel/Makefile
@@ -10,15 +10,14 @@ extra-y:= head.o init_task.o vmlinux.lds
obj-y := $(heads-y) entry.o entry-table.o break.o switch_to.o kernel_thread.o \
process.o traps.o ptrace.o signal.o dma.o \
sys_frv.o time.o semaphore.o setup.o frv_ksyms.o \
- debug-stub.o irq.o irq-routing.o sleep.o uaccess.o
+ debug-stub.o irq.o sleep.o uaccess.o
obj-$(CONFIG_GDBSTUB) += gdb-stub.o gdb-io.o
obj-$(CONFIG_MB93091_VDK) += irq-mb93091.o
-obj-$(CONFIG_MB93093_PDK) += irq-mb93093.o
-obj-$(CONFIG_FUJITSU_MB93493) += irq-mb93493.o
obj-$(CONFIG_PM) += pm.o cmode.o
obj-$(CONFIG_MB93093_PDK) += pm-mb93093.o
+obj-$(CONFIG_FUJITSU_MB93493) += irq-mb93493.o
obj-$(CONFIG_SYSCTL) += sysctl.o
obj-$(CONFIG_FUTEX) += futex.o
obj-$(CONFIG_MODULES) += module.o
diff --git a/arch/frv/kernel/irq-mb93091.c b/arch/frv/kernel/irq-mb93091.c
index 1381abcd5cc..369bc0a7443 100644
--- a/arch/frv/kernel/irq-mb93091.c
+++ b/arch/frv/kernel/irq-mb93091.c
@@ -24,7 +24,6 @@
#include <asm/delay.h>
#include <asm/irq.h>
#include <asm/irc-regs.h>
-#include <asm/irq-routing.h>
#define __reg16(ADDR) (*(volatile unsigned short *)(ADDR))
@@ -33,83 +32,131 @@
#define __get_IFR() ({ __reg16(0xffc0000c); })
#define __clr_IFR(M) do { __reg16(0xffc0000c) = ~(M); wmb(); } while(0)
-static void frv_fpga_doirq(struct irq_source *source);
-static void frv_fpga_control(struct irq_group *group, int irq, int on);
-/*****************************************************************************/
/*
- * FPGA IRQ multiplexor
+ * on-motherboard FPGA PIC operations
*/
-static struct irq_source frv_fpga[4] = {
-#define __FPGA(X, M) \
- [X] = { \
- .muxname = "fpga."#X, \
- .irqmask = M, \
- .doirq = frv_fpga_doirq, \
- }
+static void frv_fpga_mask(unsigned int irq)
+{
+ uint16_t imr = __get_IMR();
- __FPGA(0, 0x0028),
- __FPGA(1, 0x0050),
- __FPGA(2, 0x1c00),
- __FPGA(3, 0x6386),
-};
+ imr |= 1 << (irq - IRQ_BASE_FPGA);
-static struct irq_group frv_fpga_irqs = {
- .first_irq = IRQ_BASE_FPGA,
- .control = frv_fpga_control,
- .sources = {
- [ 1] = &frv_fpga[3],
- [ 2] = &frv_fpga[3],
- [ 3] = &frv_fpga[0],
- [ 4] = &frv_fpga[1],
- [ 5] = &frv_fpga[0],
- [ 6] = &frv_fpga[1],
- [ 7] = &frv_fpga[3],
- [ 8] = &frv_fpga[3],
- [ 9] = &frv_fpga[3],
- [10] = &frv_fpga[2],
- [11] = &frv_fpga[2],
- [12] = &frv_fpga[2],
- [13] = &frv_fpga[3],
- [14] = &frv_fpga[3],
- },
-};
+ __set_IMR(imr);
+}
+static void frv_fpga_ack(unsigned int irq)
+{
+ __clr_IFR(1 << (irq - IRQ_BASE_FPGA));
+}
-static void frv_fpga_control(struct irq_group *group, int index, int on)
+static void frv_fpga_mask_ack(unsigned int irq)
{
uint16_t imr = __get_IMR();
- if (on)
- imr &= ~(1 << index);
- else
- imr |= 1 << index;
+ imr |= 1 << (irq - IRQ_BASE_FPGA);
+ __set_IMR(imr);
+
+ __clr_IFR(1 << (irq - IRQ_BASE_FPGA));
+}
+
+static void frv_fpga_unmask(unsigned int irq)
+{
+ uint16_t imr = __get_IMR();
+
+ imr &= ~(1 << (irq - IRQ_BASE_FPGA));
__set_IMR(imr);
}
-static void frv_fpga_doirq(struct irq_source *source)
+static struct irq_chip frv_fpga_pic = {
+ .name = "mb93091",
+ .ack = frv_fpga_ack,
+ .mask = frv_fpga_mask,
+ .mask_ack = frv_fpga_mask_ack,
+ .unmask = frv_fpga_unmask,
+};
+
+/*
+ * FPGA PIC interrupt handler
+ */
+static irqreturn_t fpga_interrupt(int irq, void *_mask, struct pt_regs *regs)
{
- uint16_t mask, imr;
+ uint16_t imr, mask = (unsigned long) _mask;
imr = __get_IMR();
- mask = source->irqmask & ~imr & __get_IFR();
- if (mask) {
- __set_IMR(imr | mask);
- __clr_IFR(mask);
- distribute_irqs(&frv_fpga_irqs, mask);
- __set_IMR(imr);
+ mask = mask & ~imr & __get_IFR();
+
+ /* poll all the triggered IRQs */
+ while (mask) {
+ int irq;
+
+ asm("scan %1,gr0,%0" : "=r"(irq) : "r"(mask));
+ irq = 31 - irq;
+ mask &= ~(1 << irq);
+
+ generic_handle_irq(IRQ_BASE_FPGA + irq, regs);
}
+
+ return IRQ_HANDLED;
}
+/*
+ * define an interrupt action for each FPGA PIC output
+ * - use dev_id to indicate the FPGA PIC input to output mappings
+ */
+static struct irqaction fpga_irq[4] = {
+ [0] = {
+ .handler = fpga_interrupt,
+ .flags = IRQF_DISABLED | IRQF_SHARED,
+ .mask = CPU_MASK_NONE,
+ .name = "fpga.0",
+ .dev_id = (void *) 0x0028UL,
+ },
+ [1] = {
+ .handler = fpga_interrupt,
+ .flags = IRQF_DISABLED | IRQF_SHARED,
+ .mask = CPU_MASK_NONE,
+ .name = "fpga.1",
+ .dev_id = (void *) 0x0050UL,
+ },
+ [2] = {
+ .handler = fpga_interrupt,
+ .flags = IRQF_DISABLED | IRQF_SHARED,
+ .mask = CPU_MASK_NONE,
+ .name = "fpga.2",
+ .dev_id = (void *) 0x1c00UL,
+ },
+ [3] = {
+ .handler = fpga_interrupt,
+ .flags = IRQF_DISABLED | IRQF_SHARED,
+ .mask = CPU_MASK_NONE,
+ .name = "fpga.3",
+ .dev_id = (void *) 0x6386UL,
+ }
+};
+
+/*
+ * initialise the motherboard FPGA's PIC
+ */
void __init fpga_init(void)
{
+ int irq;
+
+ /* all PIC inputs are all set to be low-level driven, apart from the
+ * NMI button (15) which is fixed at falling-edge
+ */
__set_IMR(0x7ffe);
__clr_IFR(0x0000);
- frv_irq_route_external(&frv_fpga[0], IRQ_CPU_EXTERNAL0);
- frv_irq_route_external(&frv_fpga[1], IRQ_CPU_EXTERNAL1);
- frv_irq_route_external(&frv_fpga[2], IRQ_CPU_EXTERNAL2);
- frv_irq_route_external(&frv_fpga[3], IRQ_CPU_EXTERNAL3);
- frv_irq_set_group(&frv_fpga_irqs);
+ for (irq = IRQ_BASE_FPGA + 1; irq <= IRQ_BASE_FPGA + 14; irq++)
+ set_irq_chip_and_handler(irq, &frv_fpga_pic, handle_level_irq);
+
+ set_irq_chip_and_handler(IRQ_FPGA_NMI, &frv_fpga_pic, handle_edge_irq);
+
+ /* the FPGA drives the first four external IRQ inputs on the CPU PIC */
+ setup_irq(IRQ_CPU_EXTERNAL0, &fpga_irq[0]);
+ setup_irq(IRQ_CPU_EXTERNAL1, &fpga_irq[1]);
+ setup_irq(IRQ_CPU_EXTERNAL2, &fpga_irq[2]);
+ setup_irq(IRQ_CPU_EXTERNAL3, &fpga_irq[3]);
}
diff --git a/arch/frv/kernel/irq-mb93093.c b/arch/frv/kernel/irq-mb93093.c
index 48b2a642088..a43a2215895 100644
--- a/arch/frv/kernel/irq-mb93093.c
+++ b/arch/frv/kernel/irq-mb93093.c
@@ -1,6 +1,6 @@
/* irq-mb93093.c: MB93093 FPGA interrupt handling
*
- * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Copyright (C) 2006 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
*
* This program is free software; you can redistribute it and/or
@@ -24,7 +24,6 @@
#include <asm/delay.h>
#include <asm/irq.h>
#include <asm/irc-regs.h>
-#include <asm/irq-routing.h>
#define __reg16(ADDR) (*(volatile unsigned short *)(__region_CS2 + (ADDR)))
@@ -33,66 +32,102 @@
#define __get_IFR() ({ __reg16(0x02); })
#define __clr_IFR(M) do { __reg16(0x02) = ~(M); wmb(); } while(0)
-static void frv_fpga_doirq(struct irq_source *source);
-static void frv_fpga_control(struct irq_group *group, int irq, int on);
-
-/*****************************************************************************/
/*
- * FPGA IRQ multiplexor
+ * off-CPU FPGA PIC operations
*/
-static struct irq_source frv_fpga[4] = {
-#define __FPGA(X, M) \
- [X] = { \
- .muxname = "fpga."#X, \
- .irqmask = M, \
- .doirq = frv_fpga_doirq, \
- }
+static void frv_fpga_mask(unsigned int irq)
+{
+ uint16_t imr = __get_IMR();
- __FPGA(0, 0x0700),
-};
+ imr |= 1 << (irq - IRQ_BASE_FPGA);
+ __set_IMR(imr);
+}
-static struct irq_group frv_fpga_irqs = {
- .first_irq = IRQ_BASE_FPGA,
- .control = frv_fpga_control,
- .sources = {
- [ 8] = &frv_fpga[0],
- [ 9] = &frv_fpga[0],
- [10] = &frv_fpga[0],
- },
-};
+static void frv_fpga_ack(unsigned int irq)
+{
+ __clr_IFR(1 << (irq - IRQ_BASE_FPGA));
+}
+
+static void frv_fpga_mask_ack(unsigned int irq)
+{
+ uint16_t imr = __get_IMR();
+ imr |= 1 << (irq - IRQ_BASE_FPGA);
+ __set_IMR(imr);
+
+ __clr_IFR(1 << (irq - IRQ_BASE_FPGA));
+}
-static void frv_fpga_control(struct irq_group *group, int index, int on)
+static void frv_fpga_unmask(unsigned int irq)
{
uint16_t imr = __get_IMR();
- if (on)
- imr &= ~(1 << index);
- else
- imr |= 1 << index;
+ imr &= ~(1 << (irq - IRQ_BASE_FPGA));
__set_IMR(imr);
}
-static void frv_fpga_doirq(struct irq_source *source)
+static struct irq_chip frv_fpga_pic = {
+ .name = "mb93093",
+ .ack = frv_fpga_ack,
+ .mask = frv_fpga_mask,
+ .mask_ack = frv_fpga_mask_ack,
+ .unmask = frv_fpga_unmask,
+ .end = frv_fpga_end,
+};
+
+/*
+ * FPGA PIC interrupt handler
+ */
+static irqreturn_t fpga_interrupt(int irq, void *_mask, struct pt_regs *regs)
{
- uint16_t mask, imr;
+ uint16_t imr, mask = (unsigned long) _mask;
imr = __get_IMR();
- mask = source->irqmask & ~imr & __get_IFR();
- if (mask) {
- __set_IMR(imr | mask);
- __clr_IFR(mask);
- distribute_irqs(&frv_fpga_irqs, mask);
- __set_IMR(imr);
+ mask = mask & ~imr & __get_IFR();
+
+ /* poll all the triggered IRQs */
+ while (mask) {
+ int irq;
+
+ asm("scan %1,gr0,%0" : "=r"(irq) : "r"(mask));
+ irq = 31 - irq;
+ mask &= ~(1 << irq);
+
+ generic_irq_handle(IRQ_BASE_FPGA + irq, regs);
}
+
+ return IRQ_HANDLED;
}
+/*
+ * define an interrupt action for each FPGA PIC output
+ * - use dev_id to indicate the FPGA PIC input to output mappings
+ */
+static struct irqaction fpga_irq[1] = {
+ [0] = {
+ .handler = fpga_interrupt,
+ .flags = IRQF_DISABLED,
+ .mask = CPU_MASK_NONE,
+ .name = "fpga.0",
+ .dev_id = (void *) 0x0700UL,
+ }
+};
+
+/*
+ * initialise the motherboard FPGA's PIC
+ */
void __init fpga_init(void)
{
+ int irq;
+
+ /* all PIC inputs are all set to be edge triggered */
__set_IMR(0x0700);
__clr_IFR(0x0000);
- frv_irq_route_external(&frv_fpga[0], IRQ_CPU_EXTERNAL2);
- frv_irq_set_group(&frv_fpga_irqs);
+ for (irq = IRQ_BASE_FPGA + 8; irq <= IRQ_BASE_FPGA + 10; irq++)
+ set_irq_chip_and_handler(irq, &frv_fpga_pic, handle_edge_irq);
+
+ /* the FPGA drives external IRQ input #2 on the CPU PIC */
+ setup_irq(IRQ_CPU_EXTERNAL2, &fpga_irq[0]);
}
diff --git a/arch/frv/kernel/irq-mb93493.c b/arch/frv/kernel/irq-mb93493.c
index 988d035640e..39c0188a349 100644
--- a/arch/frv/kernel/irq-mb93493.c
+++ b/arch/frv/kernel/irq-mb93493.c
@@ -1,6 +1,6 @@
/* irq-mb93493.c: MB93493 companion chip interrupt handler
*
- * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Copyright (C) 2006 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
*
* This program is free software; you can redistribute it and/or
@@ -24,84 +24,126 @@
#include <asm/delay.h>
#include <asm/irq.h>
#include <asm/irc-regs.h>
-#include <asm/irq-routing.h>
#include <asm/mb93493-irqs.h>
+#include <asm/mb93493-regs.h>
-static void frv_mb93493_doirq(struct irq_source *source);
+#define IRQ_ROUTE_ONE(X) (X##_ROUTE << (X - IRQ_BASE_MB93493))
+
+#define IRQ_ROUTING \
+ (IRQ_ROUTE_ONE(IRQ_MB93493_VDC) | \
+ IRQ_ROUTE_ONE(IRQ_MB93493_VCC) | \
+ IRQ_ROUTE_ONE(IRQ_MB93493_AUDIO_OUT) | \
+ IRQ_ROUTE_ONE(IRQ_MB93493_I2C_0) | \
+ IRQ_ROUTE_ONE(IRQ_MB93493_I2C_1) | \
+ IRQ_ROUTE_ONE(IRQ_MB93493_USB) | \
+ IRQ_ROUTE_ONE(IRQ_MB93493_LOCAL_BUS) | \
+ IRQ_ROUTE_ONE(IRQ_MB93493_PCMCIA) | \
+ IRQ_ROUTE_ONE(IRQ_MB93493_GPIO) | \
+ IRQ_ROUTE_ONE(IRQ_MB93493_AUDIO_IN))
-/*****************************************************************************/
/*
- * MB93493 companion chip IRQ multiplexor
+ * daughter board PIC operations
+ * - there is no way to ACK interrupts in the MB93493 chip
*/
-static struct irq_source frv_mb93493[2] = {
- [0] = {
- .muxname = "mb93493.0",
- .muxdata = __region_CS3 + 0x3d0,
- .doirq = frv_mb93493_doirq,
- .irqmask = 0x0000,
- },
- [1] = {
- .muxname = "mb93493.1",
- .muxdata = __region_CS3 + 0x3d4,
- .doirq = frv_mb93493_doirq,
- .irqmask = 0x0000,
- },
-};
-
-static void frv_mb93493_control(struct irq_group *group, int index, int on)
+static void frv_mb93493_mask(unsigned int irq)
{
- struct irq_source *source;
uint32_t iqsr;
+ volatile void *piqsr;
- if ((frv_mb93493[0].irqmask & (1 << index)))
- source = &frv_mb93493[0];
+ if (IRQ_ROUTING & (1 << (irq - IRQ_BASE_MB93493)))
+ piqsr = __addr_MB93493_IQSR(1);
else
- source = &frv_mb93493[1];
+ piqsr = __addr_MB93493_IQSR(0);
+
+ iqsr = readl(piqsr);
+ iqsr &= ~(1 << (irq - IRQ_BASE_MB93493 + 16));
+ writel(iqsr, piqsr);
+}
- iqsr = readl(source->muxdata);
- if (on)
- iqsr |= 1 << (index + 16);
+static void frv_mb93493_ack(unsigned int irq)
+{
+}
+
+static void frv_mb93493_unmask(unsigned int irq)
+{
+ uint32_t iqsr;
+ volatile void *piqsr;
+
+ if (IRQ_ROUTING & (1 << (irq - IRQ_BASE_MB93493)))
+ piqsr = __addr_MB93493_IQSR(1);
else
- iqsr &= ~(1 << (index + 16));
+ piqsr = __addr_MB93493_IQSR(0);
- writel(iqsr, source->muxdata);
+ iqsr = readl(piqsr);
+ iqsr |= 1 << (irq - IRQ_BASE_MB93493 + 16);
+ writel(iqsr, piqsr);
}
-static struct irq_group frv_mb93493_irqs = {
- .first_irq = IRQ_BASE_MB93493,
- .control = frv_mb93493_control,
+static struct irq_chip frv_mb93493_pic = {
+ .name = "mb93093",
+ .ack = frv_mb93493_ack,
+ .mask = frv_mb93493_mask,
+ .mask_ack = frv_mb93493_mask,
+ .unmask = frv_mb93493_unmask,
};
-static void frv_mb93493_doirq(struct irq_source *source)
+/*
+ * MB93493 PIC interrupt handler
+ */
+static irqreturn_t mb93493_interrupt(int irq, void *_piqsr, struct pt_regs *regs)
{
- uint32_t mask = readl(source->muxdata);
- mask = mask & (mask >> 16) & 0xffff;
+ volatile void *piqsr = _piqsr;
+ uint32_t iqsr;
- if (mask)
- distribute_irqs(&frv_mb93493_irqs, mask);
-}
+ iqsr = readl(piqsr);
+ iqsr = iqsr & (iqsr >> 16) & 0xffff;
-static void __init mb93493_irq_route(int irq, int source)
-{
- frv_mb93493[source].irqmask |= 1 << (irq - IRQ_BASE_MB93493);
- frv_mb93493_irqs.sources[irq - IRQ_BASE_MB93493] = &frv_mb93493[source];
+ /* poll all the triggered IRQs */
+ while (iqsr) {
+ int irq;
+
+ asm("scan %1,gr0,%0" : "=r"(irq) : "r"(iqsr));
+ irq = 31 - irq;
+ iqsr &= ~(1 << irq);
+
+ generic_handle_irq(IRQ_BASE_MB93493 + irq, regs);
+ }
+
+ return IRQ_HANDLED;
}
-void __init route_mb93493_irqs(void)
+/*
+ * define an interrupt action for each MB93493 PIC output
+ * - use dev_id to indicate the MB93493 PIC input to output mappings
+ */
+static struct irqaction mb93493_irq[2] = {
+ [0] = {
+ .handler = mb93493_interrupt,
+ .flags = IRQF_DISABLED | IRQF_SHARED,
+ .mask = CPU_MASK_NONE,
+ .name = "mb93493.0",
+ .dev_id = (void *) __addr_MB93493_IQSR(0),
+ },
+ [1] = {
+ .handler = mb93493_interrupt,
+ .flags = IRQF_DISABLED | IRQF_SHARED,
+ .mask = CPU_MASK_NONE,
+ .name = "mb93493.1",
+ .dev_id = (void *) __addr_MB93493_IQSR(1),
+ }
+};
+
+/*
+ * initialise the motherboard MB93493's PIC
+ */
+void __init mb93493_init(void)
{
- frv_irq_route_external(&frv_mb93493[0], IRQ_CPU_MB93493_0);
- frv_irq_route_external(&frv_mb93493[1], IRQ_CPU_MB93493_1);
-
- frv_irq_set_group(&frv_mb93493_irqs);
-
- mb93493_irq_route(IRQ_MB93493_VDC, IRQ_MB93493_VDC_ROUTE);
- mb93493_irq_route(IRQ_MB93493_VCC, IRQ_MB93493_VCC_ROUTE);
- mb93493_irq_route(IRQ_MB93493_AUDIO_IN, IRQ_MB93493_AUDIO_IN_ROUTE);
- mb93493_irq_route(IRQ_MB93493_I2C_0, IRQ_MB93493_I2C_0_ROUTE);
- mb93493_irq_route(IRQ_MB93493_I2C_1, IRQ_MB93493_I2C_1_ROUTE);
- mb93493_irq_route(IRQ_MB93493_USB, IRQ_MB93493_USB_ROUTE);
- mb93493_irq_route(IRQ_MB93493_LOCAL_BUS, IRQ_MB93493_LOCAL_BUS_ROUTE);
- mb93493_irq_route(IRQ_MB93493_PCMCIA, IRQ_MB93493_PCMCIA_ROUTE);
- mb93493_irq_route(IRQ_MB93493_GPIO, IRQ_MB93493_GPIO_ROUTE);
- mb93493_irq_route(IRQ_MB93493_AUDIO_OUT, IRQ_MB93493_AUDIO_OUT_ROUTE);
+ int irq;
+
+ for (irq = IRQ_BASE_MB93493 + 0; irq <= IRQ_BASE_MB93493 + 10; irq++)
+ set_irq_chip_and_handler(irq, &frv_mb93493_pic, handle_edge_irq);
+
+ /* the MB93493 drives external IRQ inputs on the CPU PIC */
+ setup_irq(IRQ_CPU_MB93493_0, &mb93493_irq[0]);
+ setup_irq(IRQ_CPU_MB93493_1, &mb93493_irq[1]);
}
diff --git a/arch/frv/kernel/irq-routing.c b/arch/frv/kernel/irq-routing.c
deleted file mode 100644
index 53886adf47d..00000000000
--- a/arch/frv/kernel/irq-routing.c
+++ /dev/null
@@ -1,291 +0,0 @@
-/* irq-routing.c: IRQ routing
- *
- * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
- * Written by David Howells (dhowells@redhat.com)
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
- */
-
-#include <linux/sched.h>
-#include <linux/random.h>
-#include <linux/init.h>
-#include <linux/serial_reg.h>
-#include <asm/io.h>
-#include <asm/irq-routing.h>
-#include <asm/irc-regs.h>
-#include <asm/serial-regs.h>
-#include <asm/dma.h>
-
-struct irq_level frv_irq_levels[16] = {
- [0 ... 15] = {
- .lock = SPIN_LOCK_UNLOCKED,
- }
-};
-
-struct irq_group *irq_groups[NR_IRQ_GROUPS];
-
-extern struct irq_group frv_cpu_irqs;
-
-void __init frv_irq_route(struct irq_source *source, int irqlevel)
-{
- source->level = &frv_irq_levels[irqlevel];
- source->next = frv_irq_levels[irqlevel].sources;
- frv_irq_levels[irqlevel].sources = source;
-}
-
-void __init frv_irq_route_external(struct irq_source *source, int irq)
-{
- int irqlevel = 0;
-
- switch (irq) {
- case IRQ_CPU_EXTERNAL0: irqlevel = IRQ_XIRQ0_LEVEL; break;
- case IRQ_CPU_EXTERNAL1: irqlevel = IRQ_XIRQ1_LEVEL; break;
- case IRQ_CPU_EXTERNAL2: irqlevel = IRQ_XIRQ2_LEVEL; break;
- case IRQ_CPU_EXTERNAL3: irqlevel = IRQ_XIRQ3_LEVEL; break;
- case IRQ_CPU_EXTERNAL4: irqlevel = IRQ_XIRQ4_LEVEL; break;
- case IRQ_CPU_EXTERNAL5: irqlevel = IRQ_XIRQ5_LEVEL; break;
- case IRQ_CPU_EXTERNAL6: irqlevel = IRQ_XIRQ6_LEVEL; break;
- case IRQ_CPU_EXTERNAL7: irqlevel = IRQ_XIRQ7_LEVEL; break;
- default: BUG();
- }
-
- source->level = &frv_irq_levels[irqlevel];
- source->next = frv_irq_levels[irqlevel].sources;
- frv_irq_levels[irqlevel].sources = source;
-}
-
-void __init frv_irq_set_group(struct irq_group *group)
-{
- irq_groups[group->first_irq >> NR_IRQ_LOG2_ACTIONS_PER_GROUP] = group;
-}
-
-void distribute_irqs(struct irq_group *group, unsigned long irqmask)
-{
- struct irqaction *action;
- int irq;
-
- while (irqmask) {
- asm("scan %1,gr0,%0" : "=r"(irq) : "r"(irqmask));
- if (irq < 0 || irq > 31)
- asm volatile("break");
- irq = 31 - irq;
-
- irqmask &= ~(1 << irq);
- action = group->actions[irq];
-
- irq += group->first_irq;
-
- if (action) {
- int status = 0;
-
-// if (!(action->flags & IRQF_DISABLED))
-// local_irq_enable();
-
- do {
- status |= action->flags;
- action->handler(irq, action->dev_id, __frame);
- action = action->next;
- } while (action);
-
- if (status & IRQF_SAMPLE_RANDOM)
- add_interrupt_randomness(irq);
- local_irq_disable();
- }
- }
-}
-
-/*****************************************************************************/
-/*
- * CPU UART interrupts
- */
-static void frv_cpuuart_doirq(struct irq_source *source)
-{
-// uint8_t iir = readb(source->muxdata + UART_IIR * 8);
-// if ((iir & 0x0f) != UART_IIR_NO_INT)
- distribute_irqs(&frv_cpu_irqs, source->irqmask);
-}
-
-struct irq_source frv_cpuuart[2] = {
-#define __CPUUART(X, A) \
- [X] = { \
- .muxname = "uart", \
- .muxdata = (volatile void __iomem *)(unsigned long)A,\
- .irqmask = 1 << IRQ_CPU_UART##X, \
- .doirq = frv_cpuuart_doirq, \
- }
-
- __CPUUART(0, UART0_BASE),
- __CPUUART(1, UART1_BASE),
-};
-
-/*****************************************************************************/
-/*
- * CPU DMA interrupts
- */
-static void frv_cpudma_doirq(struct irq_source *source)
-{
- uint32_t cstr = readl(source->muxdata + DMAC_CSTRx);
- if (cstr & DMAC_CSTRx_INT)
- distribute_irqs(&frv_cpu_irqs, source->irqmask);
-}
-
-struct irq_source frv_cpudma[8] = {
-#define __CPUDMA(X, A) \
- [X] = { \
- .muxname = "dma", \
- .muxdata = (volatile void __iomem *)(unsigned long)A,\
- .irqmask = 1 << IRQ_CPU_DMA##X, \
- .doirq = frv_cpudma_doirq, \
- }
-
- __CPUDMA(0, 0xfe000900),
- __CPUDMA(1, 0xfe000980),
- __CPUDMA(2, 0xfe000a00),
- __CPUDMA(3, 0xfe000a80),
- __CPUDMA(4, 0xfe001000),
- __CPUDMA(5, 0xfe001080),
- __CPUDMA(6, 0xfe001100),
- __CPUDMA(7, 0xfe001180),
-};
-
-/*****************************************************************************/
-/*
- * CPU timer interrupts - can't tell whether they've generated an interrupt or not
- */
-static void frv_cputimer_doirq(struct irq_source *source)
-{
- distribute_irqs(&frv_cpu_irqs, source->irqmask);
-}
-
-struct irq_source frv_cputimer[3] = {
-#define __CPUTIMER(X) \
- [X] = { \
- .muxname = "timer", \
- .muxdata = NULL, \
- .irqmask = 1 << IRQ_CPU_TIMER##X, \
- .doirq = frv_cputimer_doirq, \
- }
-
- __CPUTIMER(0),
- __CPUTIMER(1),
- __CPUTIMER(2),
-};
-
-/*****************************************************************************/
-/*
- * external CPU interrupts - can't tell directly whether they've generated an interrupt or not
- */
-static void frv_cpuexternal_doirq(struct irq_source *source)
-{
- distribute_irqs(&frv_cpu_irqs, source->irqmask);
-}
-
-struct irq_source frv_cpuexternal[8] = {
-#define __CPUEXTERNAL(X) \
- [X] = { \
- .muxname = "ext", \
- .muxdata = NULL, \
- .irqmask = 1 << IRQ_CPU_EXTERNAL##X, \
- .doirq = frv_cpuexternal_doirq, \
- }
-
- __CPUEXTERNAL(0),
- __CPUEXTERNAL(1),
- __CPUEXTERNAL(2),
- __CPUEXTERNAL(3),
- __CPUEXTERNAL(4),
- __CPUEXTERNAL(5),
- __CPUEXTERNAL(6),
- __CPUEXTERNAL(7),
-};
-
-#define set_IRR(N,A,B,C,D) __set_IRR(N, (A << 28) | (B << 24) | (C << 20) | (D << 16))
-
-struct irq_group frv_cpu_irqs = {
- .sources = {
- [IRQ_CPU_UART0] = &frv_cpuuart[0],
- [IRQ_CPU_UART1] = &frv_cpuuart[1],
- [IRQ_CPU_TIMER0] = &frv_cputimer[0],
- [IRQ_CPU_TIMER1] = &frv_cputimer[1],
- [IRQ_CPU_TIMER2] = &frv_cputimer[2],
- [IRQ_CPU_DMA0] = &frv_cpudma[0],
- [IRQ_CPU_DMA1] = &frv_cpudma[1],
- [IRQ_CPU_DMA2] = &frv_cpudma[2],
- [IRQ_CPU_DMA3] = &frv_cpudma[3],
- [IRQ_CPU_DMA4] = &frv_cpudma[4],
- [IRQ_CPU_DMA5] = &frv_cpudma[5],
- [IRQ_CPU_DMA6] = &frv_cpudma[6],
- [IRQ_CPU_DMA7] = &frv_cpudma[7],
- [IRQ_CPU_EXTERNAL0] = &frv_cpuexternal[0],
- [IRQ_CPU_EXTERNAL1] = &frv_cpuexternal[1],
- [IRQ_CPU_EXTERNAL2] = &frv_cpuexternal[2],
- [IRQ_CPU_EXTERNAL3] = &frv_cpuexternal[3],
- [IRQ_CPU_EXTERNAL4] = &frv_cpuexternal[4],
- [IRQ_CPU_EXTERNAL5] = &frv_cpuexternal[5],
- [IRQ_CPU_EXTERNAL6] = &frv_cpuexternal[6],
- [IRQ_CPU_EXTERNAL7] = &frv_cpuexternal[7],
- },
-};
-
-/*****************************************************************************/
-/*
- * route the CPU's interrupt sources
- */
-void __init route_cpu_irqs(void)
-{
- frv_irq_set_group(&frv_cpu_irqs);
-
- __set_IITMR(0, 0x003f0000); /* DMA0-3, TIMER0-2 IRQ detect levels */
- __set_IITMR(1, 0x20000000); /* ERR0-1, UART0-1, DMA4-7 IRQ detect levels */
-
- /* route UART and error interrupts */
- frv_irq_route(&frv_cpuuart[0], IRQ_UART0_LEVEL);
- frv_irq_route(&frv_cpuuart[1], IRQ_UART1_LEVEL);
-
- set_IRR(6, IRQ_GDBSTUB_LEVEL, IRQ_GDBSTUB_LEVEL, IRQ_UART1_LEVEL, IRQ_UART0_LEVEL);
-
- /* route DMA channel interrupts */
- frv_irq_route(&frv_cpudma[0], IRQ_DMA0_LEVEL);
- frv_irq_route(&frv_cpudma[1], IRQ_DMA1_LEVEL);
- frv_irq_route(&frv_cpudma[2], IRQ_DMA2_LEVEL);
- frv_irq_route(&frv_cpudma[3], IRQ_DMA3_LEVEL);
- frv_irq_route(&frv_cpudma[4], IRQ_DMA4_LEVEL);
- frv_irq_route(&frv_cpudma[5], IRQ_DMA5_LEVEL);
- frv_irq_route(&frv_cpudma[6], IRQ_DMA6_LEVEL);
- frv_irq_route(&frv_cpudma[7], IRQ_DMA7_LEVEL);
-
- set_IRR(4, IRQ_DMA3_LEVEL, IRQ_DMA2_LEVEL, IRQ_DMA1_LEVEL, IRQ_DMA0_LEVEL);
- set_IRR(7, IRQ_DMA7_LEVEL, IRQ_DMA6_LEVEL, IRQ_DMA5_LEVEL, IRQ_DMA4_LEVEL);
-
- /* route timer interrupts */
- frv_irq_route(&frv_cputimer[0], IRQ_TIMER0_LEVEL);
- frv_irq_route(&frv_cputimer[1], IRQ_TIMER1_LEVEL);
- frv_irq_route(&frv_cputimer[2], IRQ_TIMER2_LEVEL);
-
- set_IRR(5, 0, IRQ_TIMER2_LEVEL, IRQ_TIMER1_LEVEL, IRQ_TIMER0_LEVEL);
-
- /* route external interrupts */
- frv_irq_route(&frv_cpuexternal[0], IRQ_XIRQ0_LEVEL);
- frv_irq_route(&frv_cpuexternal[1], IRQ_XIRQ1_LEVEL);
- frv_irq_route(&frv_cpuexternal[2], IRQ_XIRQ2_LEVEL);
- frv_irq_route(&frv_cpuexternal[3], IRQ_XIRQ3_LEVEL);
- frv_irq_route(&frv_cpuexternal[4], IRQ_XIRQ4_LEVEL);
- frv_irq_route(&frv_cpuexternal[5], IRQ_XIRQ5_LEVEL);
- frv_irq_route(&frv_cpuexternal[6], IRQ_XIRQ6_LEVEL);
- frv_irq_route(&frv_cpuexternal[7], IRQ_XIRQ7_LEVEL);
-
- set_IRR(2, IRQ_XIRQ7_LEVEL, IRQ_XIRQ6_LEVEL, IRQ_XIRQ5_LEVEL, IRQ_XIRQ4_LEVEL);
- set_IRR(3, IRQ_XIRQ3_LEVEL, IRQ_XIRQ2_LEVEL, IRQ_XIRQ1_LEVEL, IRQ_XIRQ0_LEVEL);
-
-#if defined(CONFIG_MB93091_VDK)
- __set_TM1(0x55550000); /* XIRQ7-0 all active low */
-#elif defined(CONFIG_MB93093_PDK)
- __set_TM1(0x15550000); /* XIRQ7 active high, 6-0 all active low */
-#else
-#error dont know external IRQ trigger levels for this setup
-#endif
-
-} /* end route_cpu_irqs() */
diff --git a/arch/frv/kernel/irq.c b/arch/frv/kernel/irq.c
index 08967010be0..5ac041c7c0a 100644
--- a/arch/frv/kernel/irq.c
+++ b/arch/frv/kernel/irq.c
@@ -1,6 +1,6 @@
/* irq.c: FRV IRQ handling
*
- * Copyright (C) 2003, 2004 Red Hat, Inc. All Rights Reserved.
+ * Copyright (C) 2003, 2004, 2006 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
*
* This program is free software; you can redistribute it and/or
@@ -9,13 +9,6 @@
* 2 of the License, or (at your option) any later version.
*/
-/*
- * (mostly architecture independent, will move to kernel/irq.c in 2.5.)
- *
- * IRQs are in fact implemented a bit like signal handlers for the kernel.
- * Naturally it's not a 1:1 relation, but there are similarities.
- */
-
#include <linux/ptrace.h>
#include <linux/errno.h>
#include <linux/signal.h>
@@ -43,19 +36,16 @@
#include <asm/delay.h>
#include <asm/irq.h>
#include <asm/irc-regs.h>
-#include <asm/irq-routing.h>
#include <asm/gdb-stub.h>
-extern void __init fpga_init(void);
-extern void __init route_mb93493_irqs(void);
-
-static void register_irq_proc (unsigned int irq);
+#define set_IRR(N,A,B,C,D) __set_IRR(N, (A << 28) | (B << 24) | (C << 20) | (D << 16))
-/*
- * Special irq handlers.
- */
+extern void __init fpga_init(void);
+#ifdef CONFIG_FUJITSU_MB93493
+extern void __init mb93493_init(void);
+#endif
-irqreturn_t no_action(int cpl, void *dev_id, struct pt_regs *regs) { return IRQ_HANDLED; }
+#define __reg16(ADDR) (*(volatile unsigned short *)(ADDR))
atomic_t irq_err_count;
@@ -64,215 +54,86 @@ atomic_t irq_err_count;
*/
int show_interrupts(struct seq_file *p, void *v)
{
- struct irqaction *action;
- struct irq_group *group;
+ int i = *(loff_t *) v, cpu;
+ struct irqaction * action;
unsigned long flags;
- int level, grp, ix, i, j;
-
- i = *(loff_t *) v;
-
- switch (i) {
- case 0:
- seq_printf(p, " ");
- for_each_online_cpu(j)
- seq_printf(p, "CPU%d ",j);
-
- seq_putc(p, '\n');
- break;
- case 1 ... NR_IRQ_GROUPS * NR_IRQ_ACTIONS_PER_GROUP:
- local_irq_save(flags);
-
- grp = (i - 1) / NR_IRQ_ACTIONS_PER_GROUP;
- group = irq_groups[grp];
- if (!group)
- goto skip;
-
- ix = (i - 1) % NR_IRQ_ACTIONS_PER_GROUP;
- action = group->actions[ix];
- if (!action)
- goto skip;
-
- seq_printf(p, "%3d: ", i - 1);
-
-#ifndef CONFIG_SMP
- seq_printf(p, "%10u ", kstat_irqs(i));
-#else
- for_each_online_cpu(j)
- seq_printf(p, "%10u ", kstat_cpu(j).irqs[i - 1]);
-#endif
-
- level = group->sources[ix]->level - frv_irq_levels;
-
- seq_printf(p, " %12s@%x", group->sources[ix]->muxname, level);
- seq_printf(p, " %s", action->name);
-
- for (action = action->next; action; action = action->next)
- seq_printf(p, ", %s", action->name);
+ if (i == 0) {
+ char cpuname[12];
+ seq_printf(p, " ");
+ for_each_present_cpu(cpu) {
+ sprintf(cpuname, "CPU%d", cpu);
+ seq_printf(p, " %10s", cpuname);
+ }
seq_putc(p, '\n');
-skip:
- local_irq_restore(flags);
- break;
+ }
- case NR_IRQ_GROUPS * NR_IRQ_ACTIONS_PER_GROUP + 1:
- seq_printf(p, "ERR: %10u\n", atomic_read(&irq_err_count));
- break;
+ if (i < NR_IRQS) {
+ spin_lock_irqsave(&irq_desc[i].lock, flags);
+ action = irq_desc[i].action;
+ if (action) {
+ seq_printf(p, "%3d: ", i);
+ for_each_present_cpu(cpu)
+ seq_printf(p, "%10u ", kstat_cpu(cpu).irqs[i]);
+ seq_printf(p, " %10s", irq_desc[i].chip->name ? : "-");
+ seq_printf(p, " %s", action->name);
+ for (action = action->next;
+ action;
+ action = action->next)
+ seq_printf(p, ", %s", action->name);
+
+ seq_putc(p, '\n');
+ }
- default:
- break;
+ spin_unlock_irqrestore(&irq_desc[i].lock, flags);
+ } else if (i == NR_IRQS) {
+ seq_printf(p, "Err: %10u\n", atomic_read(&irq_err_count));
}
return 0;
}
-
/*
- * Generic enable/disable code: this just calls
- * down into the PIC-specific version for the actual
- * hardware disable after having gotten the irq
- * controller lock.
+ * on-CPU PIC operations
*/
-
-/**
- * disable_irq_nosync - disable an irq without waiting
- * @irq: Interrupt to disable
- *
- * Disable the selected interrupt line. Disables and Enables are
- * nested.
- * Unlike disable_irq(), this function does not ensure existing
- * instances of the IRQ handler have completed before returning.
- *
- * This function may be called from IRQ context.
- */
-
-void disable_irq_nosync(unsigned int irq)
+static void frv_cpupic_ack(unsigned int irqlevel)
{
- struct irq_source *source;
- struct irq_group *group;
- struct irq_level *level;
- unsigned long flags;
- int idx = irq & (NR_IRQ_ACTIONS_PER_GROUP - 1);
-
- group = irq_groups[irq >> NR_IRQ_LOG2_ACTIONS_PER_GROUP];
- if (!group)
- BUG();
-
- source = group->sources[idx];
- if (!source)
- BUG();
-
- level = source->level;
-
- spin_lock_irqsave(&level->lock, flags);
-
- if (group->control) {
- if (!group->disable_cnt[idx]++)
- group->control(group, idx, 0);
- } else if (!level->disable_count++) {
- __set_MASK(level - frv_irq_levels);
- }
-
- spin_unlock_irqrestore(&level->lock, flags);
+ __clr_RC(irqlevel);
+ __clr_IRL();
}
-EXPORT_SYMBOL(disable_irq_nosync);
-
-/**
- * disable_irq - disable an irq and wait for completion
- * @irq: Interrupt to disable
- *
- * Disable the selected interrupt line. Enables and Disables are
- * nested.
- * This function waits for any pending IRQ handlers for this interrupt
- * to complete before returning. If you use this function while
- * holding a resource the IRQ handler may need you will deadlock.
- *
- * This function may be called - with care - from IRQ context.
- */
-
-void disable_irq(unsigned int irq)
+static void frv_cpupic_mask(unsigned int irqlevel)
{
- disable_irq_nosync(irq);
-
-#ifdef CONFIG_SMP
- if (!local_irq_count(smp_processor_id())) {
- do {
- barrier();
- } while (irq_desc[irq].status & IRQ_INPROGRESS);
- }
-#endif
+ __set_MASK(irqlevel);
}
-EXPORT_SYMBOL(disable_irq);
-
-/**
- * enable_irq - enable handling of an irq
- * @irq: Interrupt to enable
- *
- * Undoes the effect of one call to disable_irq(). If this
- * matches the last disable, processing of interrupts on this
- * IRQ line is re-enabled.
- *
- * This function may be called from IRQ context.
- */
-
-void enable_irq(unsigned int irq)
+static void frv_cpupic_mask_ack(unsigned int irqlevel)
{
- struct irq_source *source;
- struct irq_group *group;
- struct irq_level *level;
- unsigned long flags;
- int idx = irq & (NR_IRQ_ACTIONS_PER_GROUP - 1);
- int count;
-
- group = irq_groups[irq >> NR_IRQ_LOG2_ACTIONS_PER_GROUP];
- if (!group)
- BUG();
-
- source = group->sources[idx];
- if (!source)
- BUG();
-
- level = source->level;
-
- spin_lock_irqsave(&level->lock, flags);
-
- if (group->control)
- count = group->disable_cnt[idx];
- else
- count = level->disable_count;
-
- switch (count) {
- case 1:
- if (group->control) {
- if (group->actions[idx])
- group->control(group, idx, 1);
- } else {
- if (level->usage)
- __clr_MASK(level - frv_irq_levels);
- }
- /* fall-through */
-
- default:
- count--;
- break;
-
- case 0:
- printk("enable_irq(%u) unbalanced from %p\n", irq, __builtin_return_address(0));
- }
+ __set_MASK(irqlevel);
+ __clr_RC(irqlevel);
+ __clr_IRL();
+}
- if (group->control)
- group->disable_cnt[idx] = count;
- else
- level->disable_count = count;
+static void frv_cpupic_unmask(unsigned int irqlevel)
+{
+ __clr_MASK(irqlevel);
+}
- spin_unlock_irqrestore(&level->lock, flags);
+static void frv_cpupic_end(unsigned int irqlevel)
+{
+ __clr_MASK(irqlevel);
}
-EXPORT_SYMBOL(enable_irq);
+static struct irq_chip frv_cpu_pic = {
+ .name = "cpu",
+ .ack = frv_cpupic_ack,
+ .mask = frv_cpupic_mask,
+ .mask_ack = frv_cpupic_mask_ack,
+ .unmask = frv_cpupic_unmask,
+ .end = frv_cpupic_end,
+};
-/*****************************************************************************/
/*
* handles all normal device IRQ's
* - registers are referred to by the __frame variable (GR28)
@@ -281,463 +142,65 @@ EXPORT_SYMBOL(enable_irq);
*/
asmlinkage void do_IRQ(void)
{
- struct irq_source *source;
- int level, cpu;
-
irq_enter();
-
- level = (__frame->tbr >> 4) & 0xf;
- cpu = smp_processor_id();
-
- if ((unsigned long) __frame - (unsigned long) (current + 1) < 512)
- BUG();
-
- __set_MASK(level);
- __clr_RC(level);
- __clr_IRL();
-
- kstat_this_cpu.irqs[level]++;
-
- for (source = frv_irq_levels[level].sources; source; source = source->next)
- source->doirq(source);
-
- __clr_MASK(level);
-
+ generic_handle_irq(__get_IRL(), __frame);
irq_exit();
+}
-} /* end do_IRQ() */
-
-/*****************************************************************************/
/*
* handles all NMIs when not co-opted by the debugger
* - registers are referred to by the __frame variable (GR28)
*/
asmlinkage void do_NMI(void)
{
-} /* end do_NMI() */
-
-/*****************************************************************************/
-/**
- * request_irq - allocate an interrupt line
- * @irq: Interrupt line to allocate
- * @handler: Function to be called when the IRQ occurs
- * @irqflags: Interrupt type flags
- * @devname: An ascii name for the claiming device
- * @dev_id: A cookie passed back to the handler function
- *
- * This call allocates interrupt resources and enables the
- * interrupt line and IRQ handling. From the point this
- * call is made your handler function may be invoked. Since
- * your handler function must clear any interrupt the board
- * raises, you must take care both to initialise your hardware
- * and to set up the interrupt handler in the right order.
- *
- * Dev_id must be globally unique. Normally the address of the
- * device data structure is used as the cookie. Since the handler
- * receives this value it makes sense to use it.
- *
- * If your interrupt is shared you must pass a non NULL dev_id
- * as this is required when freeing the interrupt.
- *
- * Flags:
- *
- * IRQF_SHARED Interrupt is shared
- *
- * IRQF_DISABLED Disable local interrupts while processing
- *
- * IRQF_SAMPLE_RANDOM The interrupt can be used for entropy
- *
- */
-
-int request_irq(unsigned int irq,
- irqreturn_t (*handler)(int, void *, struct pt_regs *),
- unsigned long irqflags,
- const char * devname,
- void *dev_id)
-{
- int retval;
- struct irqaction *action;
-
-#if 1
- /*
- * Sanity-check: shared interrupts should REALLY pass in
- * a real dev-ID, otherwise we'll have trouble later trying
- * to figure out which interrupt is which (messes up the
- * interrupt freeing logic etc).
- */
- if (irqflags & IRQF_SHARED) {
- if (!dev_id)
- printk("Bad boy: %s (at 0x%x) called us without a dev_id!\n",
- devname, (&irq)[-1]);
- }
-#endif
-
- if ((irq >> NR_IRQ_LOG2_ACTIONS_PER_GROUP) >= NR_IRQ_GROUPS)
- return -EINVAL;
- if (!handler)
- return -EINVAL;
-
- action = (struct irqaction *) kmalloc(sizeof(struct irqaction), GFP_KERNEL);
- if (!action)
- return -ENOMEM;
-
- action->handler = handler;
- action->flags = irqflags;
- action->mask = CPU_MASK_NONE;
- action->name = devname;
- action->next = NULL;
- action->dev_id = dev_id;
-
- retval = setup_irq(irq, action);
- if (retval)
- kfree(action);
- return retval;
-}
-
-EXPORT_SYMBOL(request_irq);
-
-/**
- * free_irq - free an interrupt
- * @irq: Interrupt line to free
- * @dev_id: Device identity to free
- *
- * Remove an interrupt handler. The handler is removed and if the
- * interrupt line is no longer in use by any driver it is disabled.
- * On a shared IRQ the caller must ensure the interrupt is disabled
- * on the card it drives before calling this function. The function
- * does not return until any executing interrupts for this IRQ
- * have completed.
- *
- * This function may be called from interrupt context.
- *
- * Bugs: Attempting to free an irq in a handler for the same irq hangs
- * the machine.
- */
-
-void free_irq(unsigned int irq, void *dev_id)
-{
- struct irq_source *source;
- struct irq_group *group;
- struct irq_level *level;
- struct irqaction **p, **pp;
- unsigned long flags;
-
- if ((irq >> NR_IRQ_LOG2_ACTIONS_PER_GROUP) >= NR_IRQ_GROUPS)
- return;
-
- group = irq_groups[irq >> NR_IRQ_LOG2_ACTIONS_PER_GROUP];
- if (!group)
- BUG();
-
- source = group->sources[irq & (NR_IRQ_ACTIONS_PER_GROUP - 1)];
- if (!source)
- BUG();
-
- level = source->level;
- p = &group->actions[irq & (NR_IRQ_ACTIONS_PER_GROUP - 1)];
-
- spin_lock_irqsave(&level->lock, flags);
-
- for (pp = p; *pp; pp = &(*pp)->next) {
- struct irqaction *action = *pp;
-
- if (action->dev_id != dev_id)
- continue;
-
- /* found it - remove from the list of entries */
- *pp = action->next;
-
- level->usage--;
-
- if (p == pp && group->control)
- group->control(group, irq & (NR_IRQ_ACTIONS_PER_GROUP - 1), 0);
-
- if (level->usage == 0)
- __set_MASK(level - frv_irq_levels);
-
- spin_unlock_irqrestore(&level->lock,flags);
-
-#ifdef CONFIG_SMP
- /* Wait to make sure it's not being used on another CPU */
- while (desc->status & IRQ_INPROGRESS)
- barrier();
-#endif
- kfree(action);
- return;
- }
-}
-
-EXPORT_SYMBOL(free_irq);
-
-/*
- * IRQ autodetection code..
- *
- * This depends on the fact that any interrupt that comes in on to an
- * unassigned IRQ will cause GxICR_DETECT to be set
- */
-
-static DECLARE_MUTEX(probe_sem);
-
-/**
- * probe_irq_on - begin an interrupt autodetect
- *
- * Commence probing for an interrupt. The interrupts are scanned
- * and a mask of potential interrupt lines is returned.
- *
- */
-
-unsigned long probe_irq_on(void)
-{
- down(&probe_sem);
- return 0;
}
-EXPORT_SYMBOL(probe_irq_on);
-
/*
- * Return a mask of triggered interrupts (this
- * can handle only legacy ISA interrupts).
- */
-
-/**
- * probe_irq_mask - scan a bitmap of interrupt lines
- * @val: mask of interrupts to consider
- *
- * Scan the ISA bus interrupt lines and return a bitmap of
- * active interrupts. The interrupt probe logic state is then
- * returned to its previous value.
- *
- * Note: we need to scan all the irq's even though we will
- * only return ISA irq numbers - just so that we reset them
- * all to a known state.
- */
-unsigned int probe_irq_mask(unsigned long xmask)
-{
- up(&probe_sem);
- return 0;
-}
-
-EXPORT_SYMBOL(probe_irq_mask);
-
-/*
- * Return the one interrupt that triggered (this can
- * handle any interrupt source).
- */
-
-/**
- * probe_irq_off - end an interrupt autodetect
- * @xmask: mask of potential interrupts (unused)
- *
- * Scans the unused interrupt lines and returns the line which
- * appears to have triggered the interrupt. If no interrupt was
- * found then zero is returned. If more than one interrupt is
- * found then minus the first candidate is returned to indicate
- * their is doubt.
- *
- * The interrupt probe logic state is returned to its previous
- * value.
- *
- * BUGS: When used in a module (which arguably shouldnt happen)
- * nothing prevents two IRQ probe callers from overlapping. The
- * results of this are non-optimal.
+ * initialise the interrupt system
*/
-
-int probe_irq_off(unsigned long xmask)
-{
- up(&probe_sem);
- return -1;
-}
-
-EXPORT_SYMBOL(probe_irq_off);
-
-/* this was setup_x86_irq but it seems pretty generic */
-int setup_irq(unsigned int irq, struct irqaction *new)
-{
- struct irq_source *source;
- struct irq_group *group;
- struct irq_level *level;
- struct irqaction **p, **pp;
- unsigned long flags;
-
- group = irq_groups[irq >> NR_IRQ_LOG2_ACTIONS_PER_GROUP];
- if (!group)
- BUG();
-
- source = group->sources[irq & (NR_IRQ_ACTIONS_PER_GROUP - 1)];
- if (!source)
- BUG();
-
- level = source->level;
-
- p = &group->actions[irq & (NR_IRQ_ACTIONS_PER_GROUP - 1)];
-
- /*
- * Some drivers like serial.c use request_irq() heavily,
- * so we have to be careful not to interfere with a
- * running system.
- */
- if (new->flags & IRQF_SAMPLE_RANDOM) {
- /*
- * This function might sleep, we want to call it first,
- * outside of the atomic block.
- * Yes, this might clear the entropy pool if the wrong
- * driver is attempted to be loaded, without actually
- * installing a new handler, but is this really a problem,
- * only the sysadmin is able to do this.
- */
- rand_initialize_irq(irq);
- }
-
- /* must juggle the interrupt processing stuff with interrupts disabled */
- spin_lock_irqsave(&level->lock, flags);
-
- /* can't share interrupts unless all parties agree to */
- if (level->usage != 0 && !(level->flags & new->flags & IRQF_SHARED)) {
- spin_unlock_irqrestore(&level->lock,flags);
- return -EBUSY;
- }
-
- /* add new interrupt at end of irq queue */
- pp = p;
- while (*pp)
- pp = &(*pp)->next;
-
- *pp = new;
-
- level->usage++;
- level->flags = new->flags;
-
- /* turn the interrupts on */
- if (level->usage == 1)
- __clr_MASK(level - frv_irq_levels);
-
- if (p == pp && group->control)
- group->control(group, irq & (NR_IRQ_ACTIONS_PER_GROUP - 1), 1);
-
- spin_unlock_irqrestore(&level->lock, flags);
- register_irq_proc(irq);
- return 0;
-}
-
-static struct proc_dir_entry * root_irq_dir;
-static struct proc_dir_entry * irq_dir [NR_IRQS];
-
-#define HEX_DIGITS 8
-
-static unsigned int parse_hex_value (const char __user *buffer,
- unsigned long count, unsigned long *ret)
-{
- unsigned char hexnum [HEX_DIGITS];
- unsigned long value;
- int i;
-
- if (!count)
- return -EINVAL;
- if (count > HEX_DIGITS)
- count = HEX_DIGITS;
- if (copy_from_user(hexnum, buffer, count))
- return -EFAULT;
-
- /*
- * Parse the first 8 characters as a hex string, any non-hex char
- * is end-of-string. '00e1', 'e1', '00E1', 'E1' are all the same.
- */
- value = 0;
-
- for (i = 0; i < count; i++) {
- unsigned int c = hexnum[i];
-
- switch (c) {
- case '0' ... '9': c -= '0'; break;
- case 'a' ... 'f': c -= 'a'-10; break;
- case 'A' ... 'F': c -= 'A'-10; break;
- default:
- goto out;
- }
- value = (value << 4) | c;
- }
-out:
- *ret = value;
- return 0;
-}
-
-
-static int prof_cpu_mask_read_proc (char *page, char **start, off_t off,
- int count, int *eof, void *data)
-{
- unsigned long *mask = (unsigned long *) data;
- if (count < HEX_DIGITS+1)
- return -EINVAL;
- return sprintf (page, "%08lx\n", *mask);
-}
-
-static int prof_cpu_mask_write_proc (struct file *file, const char __user *buffer,
- unsigned long count, void *data)
-{
- unsigned long *mask = (unsigned long *) data, full_count = count, err;
- unsigned long new_value;
-
- show_state();
- err = parse_hex_value(buffer, count, &new_value);
- if (err)
- return err;
-
- *mask = new_value;
- return full_count;
-}
-
-#define MAX_NAMELEN 10
-
-static void register_irq_proc (unsigned int irq)
-{
- char name [MAX_NAMELEN];
-
- if (!root_irq_dir || irq_dir[irq])
- return;
-
- memset(name, 0, MAX_NAMELEN);
- sprintf(name, "%d", irq);
-
- /* create /proc/irq/1234 */
- irq_dir[irq] = proc_mkdir(name, root_irq_dir);
-}
-
-unsigned long prof_cpu_mask = -1;
-
-void init_irq_proc (void)
+void __init init_IRQ(void)
{
- struct proc_dir_entry *entry;
- int i;
+ int level;
- /* create /proc/irq */
- root_irq_dir = proc_mkdir("irq", NULL);
+ for (level = 1; level <= 14; level++)
+ set_irq_chip_and_handler(level, &frv_cpu_pic,
+ handle_level_irq);
- /* create /proc/irq/prof_cpu_mask */
- entry = create_proc_entry("prof_cpu_mask", 0600, root_irq_dir);
- if (!entry)
- return;
+ set_irq_handler(IRQ_CPU_TIMER0, handle_edge_irq);
- entry->nlink = 1;
- entry->data = (void *)&prof_cpu_mask;
- entry->read_proc = prof_cpu_mask_read_proc;
- entry->write_proc = prof_cpu_mask_write_proc;
-
- /*
- * Create entries for all existing IRQs.
+ /* set the trigger levels for internal interrupt sources
+ * - timers all falling-edge
+ * - ERR0 is rising-edge
+ * - all others are high-level
*/
- for (i = 0; i < NR_IRQS; i++)
- register_irq_proc(i);
-}
+ __set_IITMR(0, 0x003f0000); /* DMA0-3, TIMER0-2 */
+ __set_IITMR(1, 0x20000000); /* ERR0-1, UART0-1, DMA4-7 */
+
+ /* route internal interrupts */
+ set_IRR(4, IRQ_DMA3_LEVEL, IRQ_DMA2_LEVEL, IRQ_DMA1_LEVEL,
+ IRQ_DMA0_LEVEL);
+ set_IRR(5, 0, IRQ_TIMER2_LEVEL, IRQ_TIMER1_LEVEL, IRQ_TIMER0_LEVEL);
+ set_IRR(6, IRQ_GDBSTUB_LEVEL, IRQ_GDBSTUB_LEVEL,
+ IRQ_UART1_LEVEL, IRQ_UART0_LEVEL);
+ set_IRR(7, IRQ_DMA7_LEVEL, IRQ_DMA6_LEVEL, IRQ_DMA5_LEVEL,
+ IRQ_DMA4_LEVEL);
+
+ /* route external interrupts */
+ set_IRR(2, IRQ_XIRQ7_LEVEL, IRQ_XIRQ6_LEVEL, IRQ_XIRQ5_LEVEL,
+ IRQ_XIRQ4_LEVEL);
+ set_IRR(3, IRQ_XIRQ3_LEVEL, IRQ_XIRQ2_LEVEL, IRQ_XIRQ1_LEVEL,
+ IRQ_XIRQ0_LEVEL);
+
+#if defined(CONFIG_MB93091_VDK)
+ __set_TM1(0x55550000); /* XIRQ7-0 all active low */
+#elif defined(CONFIG_MB93093_PDK)
+ __set_TM1(0x15550000); /* XIRQ7 active high, 6-0 all active low */
+#else
+#error dont know external IRQ trigger levels for this setup
+#endif
-/*****************************************************************************/
-/*
- * initialise the interrupt system
- */
-void __init init_IRQ(void)
-{
- route_cpu_irqs();
fpga_init();
#ifdef CONFIG_FUJITSU_MB93493
- route_mb93493_irqs();
+ mb93493_init();
#endif
-} /* end init_IRQ() */
+}
diff --git a/arch/frv/kernel/setup.c b/arch/frv/kernel/setup.c
index af08ccd4ed6..d96a57e5f03 100644
--- a/arch/frv/kernel/setup.c
+++ b/arch/frv/kernel/setup.c
@@ -43,7 +43,6 @@
#include <asm/mb-regs.h>
#include <asm/mb93493-regs.h>
#include <asm/gdb-stub.h>
-#include <asm/irq-routing.h>
#include <asm/io.h>
#ifdef CONFIG_BLK_DEV_INITRD
diff --git a/arch/frv/kernel/time.c b/arch/frv/kernel/time.c
index 68a77fe3bb4..3d0284bccb9 100644
--- a/arch/frv/kernel/time.c
+++ b/arch/frv/kernel/time.c
@@ -26,7 +26,6 @@
#include <asm/timer-regs.h>
#include <asm/mb-regs.h>
#include <asm/mb86943a.h>
-#include <asm/irq-routing.h>
#include <linux/timex.h>
diff --git a/arch/frv/mb93090-mb00/pci-irq.c b/arch/frv/mb93090-mb00/pci-irq.c
index 2278c80bd88..ba587523c01 100644
--- a/arch/frv/mb93090-mb00/pci-irq.c
+++ b/arch/frv/mb93090-mb00/pci-irq.c
@@ -15,7 +15,6 @@
#include <asm/io.h>
#include <asm/smp.h>
-#include <asm/irq-routing.h>
#include "pci-frv.h"
diff --git a/arch/frv/mm/init.c b/arch/frv/mm/init.c
index b5b4286f9dd..3f3a0ed3539 100644
--- a/arch/frv/mm/init.c
+++ b/arch/frv/mm/init.c
@@ -98,7 +98,7 @@ void show_mem(void)
*/
void __init paging_init(void)
{
- unsigned long zones_size[MAX_NR_ZONES] = {0, 0, 0};
+ unsigned long zones_size[MAX_NR_ZONES] = {0, };
/* allocate some pages for kernel housekeeping tasks */
empty_bad_page_table = (unsigned long) alloc_bootmem_pages(PAGE_SIZE);
diff --git a/arch/h8300/mm/init.c b/arch/h8300/mm/init.c
index d3d40bdc2d6..e4f4199f97a 100644
--- a/arch/h8300/mm/init.c
+++ b/arch/h8300/mm/init.c
@@ -138,7 +138,7 @@ void paging_init(void)
#endif
{
- unsigned long zones_size[MAX_NR_ZONES] = {0, 0, 0};
+ unsigned long zones_size[MAX_NR_ZONES] = {0, };
zones_size[ZONE_DMA] = 0 >> PAGE_SHIFT;
zones_size[ZONE_NORMAL] = (end_mem - PAGE_OFFSET) >> PAGE_SHIFT;
diff --git a/arch/i386/Kconfig b/arch/i386/Kconfig
index b2751eadbc5..6189b0c28d6 100644
--- a/arch/i386/Kconfig
+++ b/arch/i386/Kconfig
@@ -494,7 +494,7 @@ config HIGHMEM64G
endchoice
choice
- depends on EXPERIMENTAL && !X86_PAE
+ depends on EXPERIMENTAL
prompt "Memory split" if EMBEDDED
default VMSPLIT_3G
help
@@ -516,6 +516,7 @@ choice
config VMSPLIT_3G
bool "3G/1G user/kernel split"
config VMSPLIT_3G_OPT
+ depends on !HIGHMEM
bool "3G/1G user/kernel split (for full 1G low memory)"
config VMSPLIT_2G
bool "2G/2G user/kernel split"
@@ -794,6 +795,7 @@ config HOTPLUG_CPU
config COMPAT_VDSO
bool "Compat VDSO support"
default y
+ depends on !PARAVIRT
help
Map the VDSO to the predictable old-style address too.
---help---
diff --git a/arch/i386/kernel/apm.c b/arch/i386/kernel/apm.c
index 8591f2fa920..ff9ce4b5eaa 100644
--- a/arch/i386/kernel/apm.c
+++ b/arch/i386/kernel/apm.c
@@ -1154,9 +1154,11 @@ out:
static void set_time(void)
{
+ struct timespec ts;
if (got_clock_diff) { /* Must know time zone in order to set clock */
- xtime.tv_sec = get_cmos_time() + clock_cmos_diff;
- xtime.tv_nsec = 0;
+ ts.tv_sec = get_cmos_time() + clock_cmos_diff;
+ ts.tv_nsec = 0;
+ do_settimeofday(&ts);
}
}
@@ -1232,13 +1234,8 @@ static int suspend(int vetoable)
restore_processor_state();
local_irq_disable();
- write_seqlock(&xtime_lock);
- spin_lock(&i8253_lock);
- reinit_timer();
set_time();
-
- spin_unlock(&i8253_lock);
- write_sequnlock(&xtime_lock);
+ reinit_timer();
if (err == APM_NO_ERROR)
err = APM_SUCCESS;
@@ -1365,9 +1362,7 @@ static void check_events(void)
ignore_bounce = 1;
if ((event != APM_NORMAL_RESUME)
|| (ignore_normal_resume == 0)) {
- write_seqlock_irq(&xtime_lock);
set_time();
- write_sequnlock_irq(&xtime_lock);
device_resume();
pm_send_all(PM_RESUME, (void *)0);
queue_event(event, NULL);
@@ -1383,9 +1378,7 @@ static void check_events(void)
break;
case APM_UPDATE_TIME:
- write_seqlock_irq(&xtime_lock);
set_time();
- write_sequnlock_irq(&xtime_lock);
break;
case APM_CRITICAL_SUSPEND:
@@ -2339,6 +2332,7 @@ static int __init apm_init(void)
ret = kernel_thread(apm, NULL, CLONE_KERNEL | SIGCHLD);
if (ret < 0) {
printk(KERN_ERR "apm: disabled - Unable to start kernel thread.\n");
+ remove_proc_entry("apm", NULL);
return -ENOMEM;
}
@@ -2348,7 +2342,13 @@ static int __init apm_init(void)
return 0;
}
- misc_register(&apm_device);
+ /*
+ * Note we don't actually care if the misc_device cannot be registered.
+ * this driver can do its job without it, even if userspace can't
+ * control it. just log the error
+ */
+ if (misc_register(&apm_device))
+ printk(KERN_WARNING "apm: Could not register misc device.\n");
if (HZ != 100)
idle_period = (idle_period * HZ) / 100;
diff --git a/arch/i386/kernel/cpu/mtrr/generic.c b/arch/i386/kernel/cpu/mtrr/generic.c
index 169ac8e0db6..0b61eed8bbd 100644
--- a/arch/i386/kernel/cpu/mtrr/generic.c
+++ b/arch/i386/kernel/cpu/mtrr/generic.c
@@ -243,7 +243,7 @@ static DEFINE_SPINLOCK(set_atomicity_lock);
* has been called.
*/
-static void prepare_set(void)
+static void prepare_set(void) __acquires(set_atomicity_lock)
{
unsigned long cr0;
@@ -274,7 +274,7 @@ static void prepare_set(void)
mtrr_wrmsr(MTRRdefType_MSR, deftype_lo & 0xf300UL, deftype_hi);
}
-static void post_set(void)
+static void post_set(void) __releases(set_atomicity_lock)
{
/* Flush TLBs (no need to flush caches - they are disabled) */
__flush_tlb();
diff --git a/arch/i386/kernel/efi_stub.S b/arch/i386/kernel/efi_stub.S
index d3ee73a3eee..ef00bb77d7e 100644
--- a/arch/i386/kernel/efi_stub.S
+++ b/arch/i386/kernel/efi_stub.S
@@ -7,7 +7,6 @@
#include <linux/linkage.h>
#include <asm/page.h>
-#include <asm/pgtable.h>
/*
* efi_call_phys(void *, ...) is a function with variable parameters.
diff --git a/arch/i386/kernel/reboot.c b/arch/i386/kernel/reboot.c
index 54cfeabbc5e..84278e0093a 100644
--- a/arch/i386/kernel/reboot.c
+++ b/arch/i386/kernel/reboot.c
@@ -145,14 +145,10 @@ real_mode_gdt_entries [3] =
0x000092000100ffffULL /* 16-bit real-mode 64k data at 0x00000100 */
};
-static struct
-{
- unsigned short size __attribute__ ((packed));
- unsigned long long * base __attribute__ ((packed));
-}
-real_mode_gdt = { sizeof (real_mode_gdt_entries) - 1, real_mode_gdt_entries },
-real_mode_idt = { 0x3ff, NULL },
-no_idt = { 0, NULL };
+static struct Xgt_desc_struct
+real_mode_gdt = { sizeof (real_mode_gdt_entries) - 1, (long)real_mode_gdt_entries },
+real_mode_idt = { 0x3ff, 0 },
+no_idt = { 0, 0 };
/* This is 16-bit protected mode code to disable paging and the cache,
diff --git a/arch/i386/kernel/setup.c b/arch/i386/kernel/setup.c
index f1682206d30..16d99444cf6 100644
--- a/arch/i386/kernel/setup.c
+++ b/arch/i386/kernel/setup.c
@@ -53,6 +53,7 @@
#include <asm/apic.h>
#include <asm/e820.h>
#include <asm/mpspec.h>
+#include <asm/mmzone.h>
#include <asm/setup.h>
#include <asm/arch_hooks.h>
#include <asm/sections.h>
@@ -934,6 +935,24 @@ static void __init parse_cmdline_early (char ** cmdline_p)
}
/*
+ * reservetop=size reserves a hole at the top of the kernel address space which
+ * a hypervisor can load into later. Needed for dynamically loaded hypervisors,
+ * so relocating the fixmap can be done before paging initialization.
+ */
+static int __init parse_reservetop(char *arg)
+{
+ unsigned long address;
+
+ if (!arg)
+ return -EINVAL;
+
+ address = memparse(arg, &arg);
+ reserve_top_address(address);
+ return 0;
+}
+early_param("reservetop", parse_reservetop);
+
+/*
* Callback for efi_memory_walk.
*/
static int __init
@@ -1181,7 +1200,7 @@ static unsigned long __init setup_memory(void)
void __init zone_sizes_init(void)
{
- unsigned long zones_size[MAX_NR_ZONES] = {0, 0, 0};
+ unsigned long zones_size[MAX_NR_ZONES] = { 0, };
unsigned int max_dma, low;
max_dma = virt_to_phys((char *)MAX_DMA_ADDRESS) >> PAGE_SHIFT;
@@ -1258,7 +1277,7 @@ void __init setup_bootmem_allocator(void)
*/
find_smp_config();
#endif
-
+ numa_kva_reserve();
#ifdef CONFIG_BLK_DEV_INITRD
if (LOADER_TYPE && INITRD_START) {
if (INITRD_START + INITRD_SIZE <= (max_low_pfn << PAGE_SHIFT)) {
diff --git a/arch/i386/kernel/smp.c b/arch/i386/kernel/smp.c
index c10789d7a9d..465188e2d70 100644
--- a/arch/i386/kernel/smp.c
+++ b/arch/i386/kernel/smp.c
@@ -634,3 +634,69 @@ fastcall void smp_call_function_interrupt(struct pt_regs *regs)
}
}
+/*
+ * this function sends a 'generic call function' IPI to one other CPU
+ * in the system.
+ *
+ * cpu is a standard Linux logical CPU number.
+ */
+static void
+__smp_call_function_single(int cpu, void (*func) (void *info), void *info,
+ int nonatomic, int wait)
+{
+ struct call_data_struct data;
+ int cpus = 1;
+
+ data.func = func;
+ data.info = info;
+ atomic_set(&data.started, 0);
+ data.wait = wait;
+ if (wait)
+ atomic_set(&data.finished, 0);
+
+ call_data = &data;
+ wmb();
+ /* Send a message to all other CPUs and wait for them to respond */
+ send_IPI_mask(cpumask_of_cpu(cpu), CALL_FUNCTION_VECTOR);
+
+ /* Wait for response */
+ while (atomic_read(&data.started) != cpus)
+ cpu_relax();
+
+ if (!wait)
+ return;
+
+ while (atomic_read(&data.finished) != cpus)
+ cpu_relax();
+}
+
+/*
+ * smp_call_function_single - Run a function on another CPU
+ * @func: The function to run. This must be fast and non-blocking.
+ * @info: An arbitrary pointer to pass to the function.
+ * @nonatomic: Currently unused.
+ * @wait: If true, wait until function has completed on other CPUs.
+ *
+ * Retrurns 0 on success, else a negative status code.
+ *
+ * Does not return until the remote CPU is nearly ready to execute <func>
+ * or is or has executed.
+ */
+
+int smp_call_function_single(int cpu, void (*func) (void *info), void *info,
+ int nonatomic, int wait)
+{
+ /* prevent preemption and reschedule on another processor */
+ int me = get_cpu();
+ if (cpu == me) {
+ WARN_ON(1);
+ put_cpu();
+ return -EBUSY;
+ }
+ spin_lock_bh(&call_lock);
+ __smp_call_function_single(cpu, func, info, nonatomic, wait);
+ spin_unlock_bh(&call_lock);
+ put_cpu();
+ return 0;
+}
+EXPORT_SYMBOL(smp_call_function_single);
diff --git a/arch/i386/kernel/smpboot.c b/arch/i386/kernel/smpboot.c
index f948419c888..efe07990e7f 100644
--- a/arch/i386/kernel/smpboot.c
+++ b/arch/i386/kernel/smpboot.c
@@ -642,9 +642,13 @@ static void map_cpu_to_logical_apicid(void)
{
int cpu = smp_processor_id();
int apicid = logical_smp_processor_id();
+ int node = apicid_to_node(apicid);
+
+ if (!node_online(node))
+ node = first_online_node;
cpu_2_logical_apicid[cpu] = apicid;
- map_cpu_to_node(cpu, apicid_to_node(apicid));
+ map_cpu_to_node(cpu, node);
}
static void unmap_cpu_to_logical_apicid(int cpu)
diff --git a/arch/i386/kernel/srat.c b/arch/i386/kernel/srat.c
index b1809c9a089..83db411b3aa 100644
--- a/arch/i386/kernel/srat.c
+++ b/arch/i386/kernel/srat.c
@@ -42,7 +42,7 @@
#define PXM_BITMAP_LEN (MAX_PXM_DOMAINS / 8)
static u8 pxm_bitmap[PXM_BITMAP_LEN]; /* bitmap of proximity domains */
-#define MAX_CHUNKS_PER_NODE 4
+#define MAX_CHUNKS_PER_NODE 3
#define MAXCHUNKS (MAX_CHUNKS_PER_NODE * MAX_NUMNODES)
struct node_memory_chunk_s {
unsigned long start_pfn;
@@ -135,9 +135,6 @@ static void __init parse_memory_affinity_structure (char *sratp)
"enabled and removable" : "enabled" ) );
}
-#if MAX_NR_ZONES != 4
-#error "MAX_NR_ZONES != 4, chunk_to_zone requires review"
-#endif
/* Take a chunk of pages from page frame cstart to cend and count the number
* of pages in each zone, returned via zones[].
*/
diff --git a/arch/i386/kernel/time.c b/arch/i386/kernel/time.c
index edd00f6cee3..1302e4ab3c4 100644
--- a/arch/i386/kernel/time.c
+++ b/arch/i386/kernel/time.c
@@ -270,16 +270,19 @@ void notify_arch_cmos_timer(void)
mod_timer(&sync_cmos_timer, jiffies + 1);
}
-static long clock_cmos_diff, sleep_start;
+static long clock_cmos_diff;
+static unsigned long sleep_start;
static int timer_suspend(struct sys_device *dev, pm_message_t state)
{
/*
* Estimate time zone so that set_time can update the clock
*/
- clock_cmos_diff = -get_cmos_time();
+ unsigned long ctime = get_cmos_time();
+
+ clock_cmos_diff = -ctime;
clock_cmos_diff += get_seconds();
- sleep_start = get_cmos_time();
+ sleep_start = ctime;
return 0;
}
@@ -287,18 +290,29 @@ static int timer_resume(struct sys_device *dev)
{
unsigned long flags;
unsigned long sec;
- unsigned long sleep_length;
-
+ unsigned long ctime = get_cmos_time();
+ long sleep_length = (ctime - sleep_start) * HZ;
+ struct timespec ts;
+
+ if (sleep_length < 0) {
+ printk(KERN_WARNING "CMOS clock skew detected in timer resume!\n");
+ /* The time after the resume must not be earlier than the time
+ * before the suspend or some nasty things will happen
+ */
+ sleep_length = 0;
+ ctime = sleep_start;
+ }
#ifdef CONFIG_HPET_TIMER
if (is_hpet_enabled())
hpet_reenable();
#endif
setup_pit_timer();
- sec = get_cmos_time() + clock_cmos_diff;
- sleep_length = (get_cmos_time() - sleep_start) * HZ;
+
+ sec = ctime + clock_cmos_diff;
+ ts.tv_sec = sec;
+ ts.tv_nsec = 0;
+ do_settimeofday(&ts);
write_seqlock_irqsave(&xtime_lock, flags);
- xtime.tv_sec = sec;
- xtime.tv_nsec = 0;
jiffies_64 += sleep_length;
wall_jiffies += sleep_length;
write_sequnlock_irqrestore(&xtime_lock, flags);
@@ -334,10 +348,11 @@ extern void (*late_time_init)(void);
/* Duplicate of time_init() below, with hpet_enable part added */
static void __init hpet_time_init(void)
{
- xtime.tv_sec = get_cmos_time();
- xtime.tv_nsec = (INITIAL_JIFFIES % HZ) * (NSEC_PER_SEC / HZ);
- set_normalized_timespec(&wall_to_monotonic,
- -xtime.tv_sec, -xtime.tv_nsec);
+ struct timespec ts;
+ ts.tv_sec = get_cmos_time();
+ ts.tv_nsec = (INITIAL_JIFFIES % HZ) * (NSEC_PER_SEC / HZ);
+
+ do_settimeofday(&ts);
if ((hpet_enable() >= 0) && hpet_use_timer) {
printk("Using HPET for base-timer\n");
@@ -349,6 +364,7 @@ static void __init hpet_time_init(void)
void __init time_init(void)
{
+ struct timespec ts;
#ifdef CONFIG_HPET_TIMER
if (is_hpet_capable()) {
/*
@@ -359,10 +375,10 @@ void __init time_init(void)
return;
}
#endif
- xtime.tv_sec = get_cmos_time();
- xtime.tv_nsec = (INITIAL_JIFFIES % HZ) * (NSEC_PER_SEC / HZ);
- set_normalized_timespec(&wall_to_monotonic,
- -xtime.tv_sec, -xtime.tv_nsec);
+ ts.tv_sec = get_cmos_time();
+ ts.tv_nsec = (INITIAL_JIFFIES % HZ) * (NSEC_PER_SEC / HZ);
+
+ do_settimeofday(&ts);
time_init_hook();
}
diff --git a/arch/i386/kernel/time_hpet.c b/arch/i386/kernel/time_hpet.c
index 14a1376fedd..6bf14a4e995 100644
--- a/arch/i386/kernel/time_hpet.c
+++ b/arch/i386/kernel/time_hpet.c
@@ -301,23 +301,25 @@ int hpet_rtc_timer_init(void)
hpet_rtc_int_freq = DEFAULT_RTC_INT_FREQ;
local_irq_save(flags);
+
cnt = hpet_readl(HPET_COUNTER);
cnt += ((hpet_tick*HZ)/hpet_rtc_int_freq);
hpet_writel(cnt, HPET_T1_CMP);
hpet_t1_cmp = cnt;
- local_irq_restore(flags);
cfg = hpet_readl(HPET_T1_CFG);
cfg &= ~HPET_TN_PERIODIC;
cfg |= HPET_TN_ENABLE | HPET_TN_32BIT;
hpet_writel(cfg, HPET_T1_CFG);
+ local_irq_restore(flags);
+
return 1;
}
static void hpet_rtc_timer_reinit(void)
{
- unsigned int cfg, cnt;
+ unsigned int cfg, cnt, ticks_per_int, lost_ints;
if (unlikely(!(PIE_on | AIE_on | UIE_on))) {
cfg = hpet_readl(HPET_T1_CFG);
@@ -332,10 +334,33 @@ static void hpet_rtc_timer_reinit(void)
hpet_rtc_int_freq = DEFAULT_RTC_INT_FREQ;
/* It is more accurate to use the comparator value than current count.*/
- cnt = hpet_t1_cmp;
- cnt += hpet_tick*HZ/hpet_rtc_int_freq;
- hpet_writel(cnt, HPET_T1_CMP);
- hpet_t1_cmp = cnt;
+ ticks_per_int = hpet_tick * HZ / hpet_rtc_int_freq;
+ hpet_t1_cmp += ticks_per_int;
+ hpet_writel(hpet_t1_cmp, HPET_T1_CMP);
+
+ /*
+ * If the interrupt handler was delayed too long, the write above tries
+ * to schedule the next interrupt in the past and the hardware would
+ * not interrupt until the counter had wrapped around.
+ * So we have to check that the comparator wasn't set to a past time.
+ */
+ cnt = hpet_readl(HPET_COUNTER);
+ if (unlikely((int)(cnt - hpet_t1_cmp) > 0)) {
+ lost_ints = (cnt - hpet_t1_cmp) / ticks_per_int + 1;
+ /* Make sure that, even with the time needed to execute
+ * this code, the next scheduled interrupt has been moved
+ * back to the future: */
+ lost_ints++;
+
+ hpet_t1_cmp += lost_ints * ticks_per_int;
+ hpet_writel(hpet_t1_cmp, HPET_T1_CMP);
+
+ if (PIE_on)
+ PIE_count += lost_ints;
+
+ printk(KERN_WARNING "rtc: lost some interrupts at %ldHz.\n",
+ hpet_rtc_int_freq);
+ }
}
/*
diff --git a/arch/i386/kernel/traps.c b/arch/i386/kernel/traps.c
index 7e9edafffd8..4fcc6690be9 100644
--- a/arch/i386/kernel/traps.c
+++ b/arch/i386/kernel/traps.c
@@ -313,6 +313,8 @@ void show_registers(struct pt_regs *regs)
*/
if (in_kernel) {
u8 __user *eip;
+ int code_bytes = 64;
+ unsigned char c;
printk("\n" KERN_EMERG "Stack: ");
show_stack_log_lvl(NULL, regs, (unsigned long *)esp, KERN_EMERG);
@@ -320,9 +322,12 @@ void show_registers(struct pt_regs *regs)
printk(KERN_EMERG "Code: ");
eip = (u8 __user *)regs->eip - 43;
- for (i = 0; i < 64; i++, eip++) {
- unsigned char c;
-
+ if (eip < (u8 __user *)PAGE_OFFSET || __get_user(c, eip)) {
+ /* try starting at EIP */
+ eip = (u8 __user *)regs->eip;
+ code_bytes = 32;
+ }
+ for (i = 0; i < code_bytes; i++, eip++) {
if (eip < (u8 __user *)PAGE_OFFSET || __get_user(c, eip)) {
printk(" Bad EIP value.");
break;
diff --git a/arch/i386/kernel/vmlinux.lds.S b/arch/i386/kernel/vmlinux.lds.S
index 2d4f1386e2b..1e7ac1c44dd 100644
--- a/arch/i386/kernel/vmlinux.lds.S
+++ b/arch/i386/kernel/vmlinux.lds.S
@@ -13,6 +13,12 @@ OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
OUTPUT_ARCH(i386)
ENTRY(phys_startup_32)
jiffies = jiffies_64;
+
+PHDRS {
+ text PT_LOAD FLAGS(5); /* R_E */
+ data PT_LOAD FLAGS(7); /* RWE */
+ note PT_NOTE FLAGS(4); /* R__ */
+}
SECTIONS
{
. = __KERNEL_START;
@@ -26,7 +32,7 @@ SECTIONS
KPROBES_TEXT
*(.fixup)
*(.gnu.warning)
- } = 0x9090
+ } :text = 0x9090
_etext = .; /* End of text section */
@@ -48,7 +54,7 @@ SECTIONS
.data : AT(ADDR(.data) - LOAD_OFFSET) { /* Data */
*(.data)
CONSTRUCTORS
- }
+ } :data
. = ALIGN(4096);
__nosave_begin = .;
@@ -184,4 +190,6 @@ SECTIONS
STABS_DEBUG
DWARF_DEBUG
+
+ NOTES
}
diff --git a/arch/i386/mach-voyager/voyager_thread.c b/arch/i386/mach-voyager/voyager_thread.c
index 50f6de6ff64..f39887359e8 100644
--- a/arch/i386/mach-voyager/voyager_thread.c
+++ b/arch/i386/mach-voyager/voyager_thread.c
@@ -130,7 +130,6 @@ thread(void *unused)
init_timer(&wakeup_timer);
sigfillset(&current->blocked);
- current->signal->tty = NULL;
printk(KERN_NOTICE "Voyager starting monitor thread\n");
diff --git a/arch/i386/mm/boot_ioremap.c b/arch/i386/mm/boot_ioremap.c
index 5d44f4f5ff5..4de11f508c3 100644
--- a/arch/i386/mm/boot_ioremap.c
+++ b/arch/i386/mm/boot_ioremap.c
@@ -29,8 +29,11 @@
*/
#define BOOT_PTE_PTRS (PTRS_PER_PTE*2)
-#define boot_pte_index(address) \
- (((address) >> PAGE_SHIFT) & (BOOT_PTE_PTRS - 1))
+
+static unsigned long boot_pte_index(unsigned long vaddr)
+{
+ return __pa(vaddr) >> PAGE_SHIFT;
+}
static inline boot_pte_t* boot_vaddr_to_pte(void *address)
{
diff --git a/arch/i386/mm/discontig.c b/arch/i386/mm/discontig.c
index 7c392dc553b..fb5d8b747de 100644
--- a/arch/i386/mm/discontig.c
+++ b/arch/i386/mm/discontig.c
@@ -117,7 +117,8 @@ void set_pmd_pfn(unsigned long vaddr, unsigned long pfn, pgprot_t flags);
void *node_remap_end_vaddr[MAX_NUMNODES];
void *node_remap_alloc_vaddr[MAX_NUMNODES];
-
+static unsigned long kva_start_pfn;
+static unsigned long kva_pages;
/*
* FLAT - support for basic PC memory model with discontig enabled, essentially
* a single node with all available processors in it with a flat
@@ -286,7 +287,6 @@ unsigned long __init setup_memory(void)
{
int nid;
unsigned long system_start_pfn, system_max_low_pfn;
- unsigned long reserve_pages;
/*
* When mapping a NUMA machine we allocate the node_mem_map arrays
@@ -298,14 +298,23 @@ unsigned long __init setup_memory(void)
find_max_pfn();
get_memcfg_numa();
- reserve_pages = calculate_numa_remap_pages();
+ kva_pages = calculate_numa_remap_pages();
/* partially used pages are not usable - thus round upwards */
system_start_pfn = min_low_pfn = PFN_UP(init_pg_tables_end);
- system_max_low_pfn = max_low_pfn = find_max_low_pfn() - reserve_pages;
- printk("reserve_pages = %ld find_max_low_pfn() ~ %ld\n",
- reserve_pages, max_low_pfn + reserve_pages);
+ kva_start_pfn = find_max_low_pfn() - kva_pages;
+
+#ifdef CONFIG_BLK_DEV_INITRD
+ /* Numa kva area is below the initrd */
+ if (LOADER_TYPE && INITRD_START)
+ kva_start_pfn = PFN_DOWN(INITRD_START) - kva_pages;
+#endif
+ kva_start_pfn -= kva_start_pfn & (PTRS_PER_PTE-1);
+
+ system_max_low_pfn = max_low_pfn = find_max_low_pfn();
+ printk("kva_start_pfn ~ %ld find_max_low_pfn() ~ %ld\n",
+ kva_start_pfn, max_low_pfn);
printk("max_pfn = %ld\n", max_pfn);
#ifdef CONFIG_HIGHMEM
highstart_pfn = highend_pfn = max_pfn;
@@ -323,7 +332,7 @@ unsigned long __init setup_memory(void)
(ulong) pfn_to_kaddr(max_low_pfn));
for_each_online_node(nid) {
node_remap_start_vaddr[nid] = pfn_to_kaddr(
- highstart_pfn + node_remap_offset[nid]);
+ kva_start_pfn + node_remap_offset[nid]);
/* Init the node remap allocator */
node_remap_end_vaddr[nid] = node_remap_start_vaddr[nid] +
(node_remap_size[nid] * PAGE_SIZE);
@@ -338,7 +347,6 @@ unsigned long __init setup_memory(void)
}
printk("High memory starts at vaddr %08lx\n",
(ulong) pfn_to_kaddr(highstart_pfn));
- vmalloc_earlyreserve = reserve_pages * PAGE_SIZE;
for_each_online_node(nid)
find_max_pfn_node(nid);
@@ -348,13 +356,18 @@ unsigned long __init setup_memory(void)
return max_low_pfn;
}
+void __init numa_kva_reserve(void)
+{
+ reserve_bootmem(PFN_PHYS(kva_start_pfn),PFN_PHYS(kva_pages));
+}
+
void __init zone_sizes_init(void)
{
int nid;
for_each_online_node(nid) {
- unsigned long zones_size[MAX_NR_ZONES] = {0, 0, 0};
+ unsigned long zones_size[MAX_NR_ZONES] = {0, };
unsigned long *zholes_size;
unsigned int max_dma;
@@ -409,7 +422,7 @@ void __init set_highmem_pages_init(int bad_ppro)
zone_end_pfn = zone_start_pfn + zone->spanned_pages;
printk("Initializing %s for node %d (%08lx:%08lx)\n",
- zone->name, zone->zone_pgdat->node_id,
+ zone->name, zone_to_nid(zone),
zone_start_pfn, zone_end_pfn);
for (node_pfn = zone_start_pfn; node_pfn < zone_end_pfn; node_pfn++) {
diff --git a/arch/i386/mm/init.c b/arch/i386/mm/init.c
index 89e8486aac3..efd0bcdac65 100644
--- a/arch/i386/mm/init.c
+++ b/arch/i386/mm/init.c
@@ -629,6 +629,48 @@ void __init mem_init(void)
(unsigned long) (totalhigh_pages << (PAGE_SHIFT-10))
);
+#if 1 /* double-sanity-check paranoia */
+ printk("virtual kernel memory layout:\n"
+ " fixmap : 0x%08lx - 0x%08lx (%4ld kB)\n"
+#ifdef CONFIG_HIGHMEM
+ " pkmap : 0x%08lx - 0x%08lx (%4ld kB)\n"
+#endif
+ " vmalloc : 0x%08lx - 0x%08lx (%4ld MB)\n"
+ " lowmem : 0x%08lx - 0x%08lx (%4ld MB)\n"
+ " .init : 0x%08lx - 0x%08lx (%4ld kB)\n"
+ " .data : 0x%08lx - 0x%08lx (%4ld kB)\n"
+ " .text : 0x%08lx - 0x%08lx (%4ld kB)\n",
+ FIXADDR_START, FIXADDR_TOP,
+ (FIXADDR_TOP - FIXADDR_START) >> 10,
+
+#ifdef CONFIG_HIGHMEM
+ PKMAP_BASE, PKMAP_BASE+LAST_PKMAP*PAGE_SIZE,
+ (LAST_PKMAP*PAGE_SIZE) >> 10,
+#endif
+
+ VMALLOC_START, VMALLOC_END,
+ (VMALLOC_END - VMALLOC_START) >> 20,
+
+ (unsigned long)__va(0), (unsigned long)high_memory,
+ ((unsigned long)high_memory - (unsigned long)__va(0)) >> 20,
+
+ (unsigned long)&__init_begin, (unsigned long)&__init_end,
+ ((unsigned long)&__init_end - (unsigned long)&__init_begin) >> 10,
+
+ (unsigned long)&_etext, (unsigned long)&_edata,
+ ((unsigned long)&_edata - (unsigned long)&_etext) >> 10,
+
+ (unsigned long)&_text, (unsigned long)&_etext,
+ ((unsigned long)&_etext - (unsigned long)&_text) >> 10);
+
+#ifdef CONFIG_HIGHMEM
+ BUG_ON(PKMAP_BASE+LAST_PKMAP*PAGE_SIZE > FIXADDR_START);
+ BUG_ON(VMALLOC_END > PKMAP_BASE);
+#endif
+ BUG_ON(VMALLOC_START > VMALLOC_END);
+ BUG_ON((unsigned long)high_memory > VMALLOC_START);
+#endif /* double-sanity-check paranoia */
+
#ifdef CONFIG_X86_PAE
if (!cpu_has_pae)
panic("cannot execute a PAE-enabled kernel on a PAE-less CPU!");
@@ -657,7 +699,7 @@ void __init mem_init(void)
int arch_add_memory(int nid, u64 start, u64 size)
{
struct pglist_data *pgdata = &contig_page_data;
- struct zone *zone = pgdata->node_zones + MAX_NR_ZONES-1;
+ struct zone *zone = pgdata->node_zones + ZONE_HIGHMEM;
unsigned long start_pfn = start >> PAGE_SHIFT;
unsigned long nr_pages = size >> PAGE_SHIFT;
diff --git a/arch/i386/mm/pgtable.c b/arch/i386/mm/pgtable.c
index bd98768d876..10126e3f817 100644
--- a/arch/i386/mm/pgtable.c
+++ b/arch/i386/mm/pgtable.c
@@ -12,6 +12,7 @@
#include <linux/slab.h>
#include <linux/pagemap.h>
#include <linux/spinlock.h>
+#include <linux/module.h>
#include <asm/system.h>
#include <asm/pgtable.h>
@@ -60,7 +61,9 @@ void show_mem(void)
printk(KERN_INFO "%lu pages writeback\n",
global_page_state(NR_WRITEBACK));
printk(KERN_INFO "%lu pages mapped\n", global_page_state(NR_FILE_MAPPED));
- printk(KERN_INFO "%lu pages slab\n", global_page_state(NR_SLAB));
+ printk(KERN_INFO "%lu pages slab\n",
+ global_page_state(NR_SLAB_RECLAIMABLE) +
+ global_page_state(NR_SLAB_UNRECLAIMABLE));
printk(KERN_INFO "%lu pages pagetables\n",
global_page_state(NR_PAGETABLE));
}
@@ -137,6 +140,12 @@ void set_pmd_pfn(unsigned long vaddr, unsigned long pfn, pgprot_t flags)
__flush_tlb_one(vaddr);
}
+static int fixmaps;
+#ifndef CONFIG_COMPAT_VDSO
+unsigned long __FIXADDR_TOP = 0xfffff000;
+EXPORT_SYMBOL(__FIXADDR_TOP);
+#endif
+
void __set_fixmap (enum fixed_addresses idx, unsigned long phys, pgprot_t flags)
{
unsigned long address = __fix_to_virt(idx);
@@ -146,6 +155,25 @@ void __set_fixmap (enum fixed_addresses idx, unsigned long phys, pgprot_t flags)
return;
}
set_pte_pfn(address, phys >> PAGE_SHIFT, flags);
+ fixmaps++;
+}
+
+/**
+ * reserve_top_address - reserves a hole in the top of kernel address space
+ * @reserve - size of hole to reserve
+ *
+ * Can be used to relocate the fixmap area and poke a hole in the top
+ * of kernel address space to make room for a hypervisor.
+ */
+void reserve_top_address(unsigned long reserve)
+{
+ BUG_ON(fixmaps > 0);
+#ifdef CONFIG_COMPAT_VDSO
+ BUG_ON(reserve != 0);
+#else
+ __FIXADDR_TOP = -reserve - PAGE_SIZE;
+ __VMALLOC_RESERVE += reserve;
+#endif
}
pte_t *pte_alloc_one_kernel(struct mm_struct *mm, unsigned long address)
diff --git a/arch/i386/power/swsusp.S b/arch/i386/power/swsusp.S
index c893b897217..8a2b50a0aaa 100644
--- a/arch/i386/power/swsusp.S
+++ b/arch/i386/power/swsusp.S
@@ -32,7 +32,7 @@ ENTRY(swsusp_arch_resume)
movl $swsusp_pg_dir-__PAGE_OFFSET, %ecx
movl %ecx, %cr3
- movl pagedir_nosave, %edx
+ movl restore_pblist, %edx
.p2align 4,,7
copy_loop:
diff --git a/arch/ia64/Kconfig b/arch/ia64/Kconfig
index db274da7dba..f521f2f60a7 100644
--- a/arch/ia64/Kconfig
+++ b/arch/ia64/Kconfig
@@ -66,15 +66,6 @@ config IA64_UNCACHED_ALLOCATOR
bool
select GENERIC_ALLOCATOR
-config DMA_IS_DMA32
- bool
- default y
-
-config DMA_IS_NORMAL
- bool
- depends on IA64_SGI_SN2
- default y
-
config AUDIT_ARCH
bool
default y
diff --git a/arch/ia64/kernel/acpi.c b/arch/ia64/kernel/acpi.c
index 0176556aeec..32c3abededc 100644
--- a/arch/ia64/kernel/acpi.c
+++ b/arch/ia64/kernel/acpi.c
@@ -771,16 +771,19 @@ int acpi_map_cpu2node(acpi_handle handle, int cpu, long physid)
{
#ifdef CONFIG_ACPI_NUMA
int pxm_id;
+ int nid;
pxm_id = acpi_get_pxm(handle);
-
/*
- * Assuming that the container driver would have set the proximity
- * domain and would have initialized pxm_to_node(pxm_id) && pxm_flag
+ * We don't have cpu-only-node hotadd. But if the system equips
+ * SRAT table, pxm is already found and node is ready.
+ * So, just pxm_to_nid(pxm) is OK.
+ * This code here is for the system which doesn't have full SRAT
+ * table for possible cpus.
*/
- node_cpuid[cpu].nid = (pxm_id < 0) ? 0 : pxm_to_node(pxm_id);
-
+ nid = acpi_map_pxm_to_node(pxm_id);
node_cpuid[cpu].phys_id = physid;
+ node_cpuid[cpu].nid = nid;
#endif
return (0);
}
diff --git a/arch/ia64/kernel/numa.c b/arch/ia64/kernel/numa.c
index 1cc360c83e7..20340631179 100644
--- a/arch/ia64/kernel/numa.c
+++ b/arch/ia64/kernel/numa.c
@@ -29,6 +29,36 @@ EXPORT_SYMBOL(cpu_to_node_map);
cpumask_t node_to_cpu_mask[MAX_NUMNODES] __cacheline_aligned;
+void __cpuinit map_cpu_to_node(int cpu, int nid)
+{
+ int oldnid;
+ if (nid < 0) { /* just initialize by zero */
+ cpu_to_node_map[cpu] = 0;
+ return;
+ }
+ /* sanity check first */
+ oldnid = cpu_to_node_map[cpu];
+ if (cpu_isset(cpu, node_to_cpu_mask[oldnid])) {
+ return; /* nothing to do */
+ }
+ /* we don't have cpu-driven node hot add yet...
+ In usual case, node is created from SRAT at boot time. */
+ if (!node_online(nid))
+ nid = first_online_node;
+ cpu_to_node_map[cpu] = nid;
+ cpu_set(cpu, node_to_cpu_mask[nid]);
+ return;
+}
+
+void __cpuinit unmap_cpu_from_node(int cpu, int nid)
+{
+ WARN_ON(!cpu_isset(cpu, node_to_cpu_mask[nid]));
+ WARN_ON(cpu_to_node_map[cpu] != nid);
+ cpu_to_node_map[cpu] = 0;
+ cpu_clear(cpu, node_to_cpu_mask[nid]);
+}
+
+
/**
* build_cpu_to_node_map - setup cpu to node and node to cpumask arrays
*
@@ -49,8 +79,6 @@ void __init build_cpu_to_node_map(void)
node = node_cpuid[i].nid;
break;
}
- cpu_to_node_map[cpu] = (node >= 0) ? node : 0;
- if (node >= 0)
- cpu_set(cpu, node_to_cpu_mask[node]);
+ map_cpu_to_node(cpu, node);
}
}
diff --git a/arch/ia64/kernel/perfmon.c b/arch/ia64/kernel/perfmon.c
index 84a7e52f56f..7bb7696e4ce 100644
--- a/arch/ia64/kernel/perfmon.c
+++ b/arch/ia64/kernel/perfmon.c
@@ -34,6 +34,7 @@
#include <linux/file.h>
#include <linux/poll.h>
#include <linux/vfs.h>
+#include <linux/smp.h>
#include <linux/pagemap.h>
#include <linux/mount.h>
#include <linux/bitops.h>
diff --git a/arch/ia64/kernel/topology.c b/arch/ia64/kernel/topology.c
index f648c610b10..05bdf7affb4 100644
--- a/arch/ia64/kernel/topology.c
+++ b/arch/ia64/kernel/topology.c
@@ -36,6 +36,9 @@ int arch_register_cpu(int num)
*/
if (!can_cpei_retarget() && is_cpu_cpei_target(num))
sysfs_cpus[num].cpu.no_control = 1;
+#ifdef CONFIG_NUMA
+ map_cpu_to_node(num, node_cpuid[num].nid);
+#endif
#endif
return register_cpu(&sysfs_cpus[num].cpu, num);
@@ -45,7 +48,8 @@ int arch_register_cpu(int num)
void arch_unregister_cpu(int num)
{
- return unregister_cpu(&sysfs_cpus[num].cpu);
+ unregister_cpu(&sysfs_cpus[num].cpu);
+ unmap_cpu_from_node(num, cpu_to_node(num));
}
EXPORT_SYMBOL(arch_register_cpu);
EXPORT_SYMBOL(arch_unregister_cpu);
diff --git a/arch/ia64/kernel/uncached.c b/arch/ia64/kernel/uncached.c
index 4c73a676366..c58e933694d 100644
--- a/arch/ia64/kernel/uncached.c
+++ b/arch/ia64/kernel/uncached.c
@@ -98,7 +98,7 @@ static int uncached_add_chunk(struct uncached_pool *uc_pool, int nid)
/* attempt to allocate a granule's worth of cached memory pages */
- page = alloc_pages_node(nid, GFP_KERNEL | __GFP_ZERO,
+ page = alloc_pages_node(nid, GFP_KERNEL | __GFP_ZERO | GFP_THISNODE,
IA64_GRANULE_SHIFT-PAGE_SHIFT);
if (!page) {
mutex_unlock(&uc_pool->add_chunk_mutex);
diff --git a/arch/ia64/sn/kernel/sn2/sn_hwperf.c b/arch/ia64/sn/kernel/sn2/sn_hwperf.c
index 9a8a29339d2..b632b9c1e3b 100644
--- a/arch/ia64/sn/kernel/sn2/sn_hwperf.c
+++ b/arch/ia64/sn/kernel/sn2/sn_hwperf.c
@@ -32,9 +32,10 @@
#include <linux/cpumask.h>
#include <linux/smp_lock.h>
#include <linux/nodemask.h>
+#include <linux/smp.h>
+
#include <asm/processor.h>
#include <asm/topology.h>
-#include <asm/smp.h>
#include <asm/semaphore.h>
#include <asm/uaccess.h>
#include <asm/sal.h>
diff --git a/arch/m32r/mm/init.c b/arch/m32r/mm/init.c
index b71348fec1f..bbd97c85bc5 100644
--- a/arch/m32r/mm/init.c
+++ b/arch/m32r/mm/init.c
@@ -100,7 +100,7 @@ void free_initrd_mem(unsigned long, unsigned long);
#ifndef CONFIG_DISCONTIGMEM
unsigned long __init zone_sizes_init(void)
{
- unsigned long zones_size[MAX_NR_ZONES] = {0, 0, 0};
+ unsigned long zones_size[MAX_NR_ZONES] = {0, };
unsigned long max_dma;
unsigned long low;
unsigned long start_pfn;
diff --git a/arch/m68knommu/mm/init.c b/arch/m68knommu/mm/init.c
index e4c233eef19..06e538d1be3 100644
--- a/arch/m68knommu/mm/init.c
+++ b/arch/m68knommu/mm/init.c
@@ -136,7 +136,7 @@ void paging_init(void)
#endif
{
- unsigned long zones_size[MAX_NR_ZONES] = {0, 0, 0};
+ unsigned long zones_size[MAX_NR_ZONES] = {0, };
zones_size[ZONE_DMA] = 0 >> PAGE_SHIFT;
zones_size[ZONE_NORMAL] = (end_mem - PAGE_OFFSET) >> PAGE_SHIFT;
diff --git a/arch/mips/au1000/common/dbdma.c b/arch/mips/au1000/common/dbdma.c
index 98244d51c15..c4fae8ff467 100644
--- a/arch/mips/au1000/common/dbdma.c
+++ b/arch/mips/au1000/common/dbdma.c
@@ -230,7 +230,7 @@ EXPORT_SYMBOL(au1xxx_ddma_add_device);
*/
u32
au1xxx_dbdma_chan_alloc(u32 srcid, u32 destid,
- void (*callback)(int, void *, struct pt_regs *), void *callparam)
+ void (*callback)(int, void *), void *callparam)
{
unsigned long flags;
u32 used, chan, rv;
@@ -248,8 +248,10 @@ au1xxx_dbdma_chan_alloc(u32 srcid, u32 destid,
au1xxx_dbdma_init();
dbdma_initialized = 1;
- if ((stp = find_dbdev_id(srcid)) == NULL) return 0;
- if ((dtp = find_dbdev_id(destid)) == NULL) return 0;
+ if ((stp = find_dbdev_id(srcid)) == NULL)
+ return 0;
+ if ((dtp = find_dbdev_id(destid)) == NULL)
+ return 0;
used = 0;
rv = 0;
@@ -869,7 +871,7 @@ dbdma_interrupt(int irq, void *dev_id, struct pt_regs *regs)
au_sync();
if (ctp->chan_callback)
- (ctp->chan_callback)(irq, ctp->chan_callparam, regs);
+ (ctp->chan_callback)(irq, ctp->chan_callparam);
ctp->cur_ptr = phys_to_virt(DSCR_GET_NXTPTR(dp->dscr_nxtptr));
return IRQ_RETVAL(1);
diff --git a/arch/mips/mm/init.c b/arch/mips/mm/init.c
index c52497bb102..5b06349af2d 100644
--- a/arch/mips/mm/init.c
+++ b/arch/mips/mm/init.c
@@ -163,10 +163,10 @@ static int __init page_is_ram(unsigned long pagenr)
void __init paging_init(void)
{
- unsigned long zones_size[] = { [0 ... MAX_NR_ZONES - 1] = 0 };
+ unsigned long zones_size[] = { 0, };
unsigned long max_dma, high, low;
#ifndef CONFIG_FLATMEM
- unsigned long zholes_size[] = { [0 ... MAX_NR_ZONES - 1] = 0 };
+ unsigned long zholes_size[] = { 0, };
unsigned long i, j, pfn;
#endif
diff --git a/arch/mips/sgi-ip27/ip27-memory.c b/arch/mips/sgi-ip27/ip27-memory.c
index efe6971fc80..16e5682b01f 100644
--- a/arch/mips/sgi-ip27/ip27-memory.c
+++ b/arch/mips/sgi-ip27/ip27-memory.c
@@ -19,6 +19,7 @@
#include <linux/swap.h>
#include <linux/bootmem.h>
#include <linux/pfn.h>
+#include <linux/highmem.h>
#include <asm/page.h>
#include <asm/sections.h>
@@ -508,7 +509,7 @@ extern unsigned long setup_zero_pages(void);
void __init paging_init(void)
{
- unsigned long zones_size[MAX_NR_ZONES] = {0, 0, 0};
+ unsigned long zones_size[MAX_NR_ZONES] = {0, };
unsigned node;
pagetable_init();
diff --git a/arch/parisc/mm/init.c b/arch/parisc/mm/init.c
index f2b96f1e0da..25ad28d63e8 100644
--- a/arch/parisc/mm/init.c
+++ b/arch/parisc/mm/init.c
@@ -551,7 +551,7 @@ void show_mem(void)
printk("Zone list for zone %d on node %d: ", j, i);
for (k = 0; zl->zones[k] != NULL; k++)
- printk("[%d/%s] ", zl->zones[k]->zone_pgdat->node_id, zl->zones[k]->name);
+ printk("[%d/%s] ", zone_to_nid(zl->zones[k]), zl->zones[k]->name);
printk("\n");
}
}
@@ -809,7 +809,7 @@ void __init paging_init(void)
flush_tlb_all_local(NULL);
for (i = 0; i < npmem_ranges; i++) {
- unsigned long zones_size[MAX_NR_ZONES] = { 0, 0, 0 };
+ unsigned long zones_size[MAX_NR_ZONES] = { 0, };
/* We have an IOMMU, so all memory can go into a single
ZONE_DMA zone. */
diff --git a/arch/powerpc/kernel/swsusp_32.S b/arch/powerpc/kernel/swsusp_32.S
index 7369f9a6ad2..69e8f86aa4f 100644
--- a/arch/powerpc/kernel/swsusp_32.S
+++ b/arch/powerpc/kernel/swsusp_32.S
@@ -159,8 +159,8 @@ END_FTR_SECTION_IFSET(CPU_FTR_ALTIVEC)
isync
/* Load ptr the list of pages to copy in r3 */
- lis r11,(pagedir_nosave - KERNELBASE)@h
- ori r11,r11,pagedir_nosave@l
+ lis r11,(restore_pblist - KERNELBASE)@h
+ ori r11,r11,restore_pblist@l
lwz r10,0(r11)
/* Copy the pages. This is a very basic implementation, to
diff --git a/arch/s390/appldata/appldata_mem.c b/arch/s390/appldata/appldata_mem.c
index ab3b0765a64..8aea3698a77 100644
--- a/arch/s390/appldata/appldata_mem.c
+++ b/arch/s390/appldata/appldata_mem.c
@@ -117,8 +117,7 @@ static void appldata_get_mem_data(void *data)
mem_data->pgpgout = ev[PGPGOUT] >> 1;
mem_data->pswpin = ev[PSWPIN];
mem_data->pswpout = ev[PSWPOUT];
- mem_data->pgalloc = ev[PGALLOC_HIGH] + ev[PGALLOC_NORMAL] +
- ev[PGALLOC_DMA];
+ mem_data->pgalloc = ev[PGALLOC_NORMAL] + ev[PGALLOC_DMA];
mem_data->pgfault = ev[PGFAULT];
mem_data->pgmajfault = ev[PGMAJFAULT];
diff --git a/arch/s390/mm/cmm.c b/arch/s390/mm/cmm.c
index 786a44dba5b..607f50ead1f 100644
--- a/arch/s390/mm/cmm.c
+++ b/arch/s390/mm/cmm.c
@@ -15,6 +15,8 @@
#include <linux/sched.h>
#include <linux/sysctl.h>
#include <linux/ctype.h>
+#include <linux/swap.h>
+#include <linux/kthread.h>
#include <asm/pgalloc.h>
#include <asm/uaccess.h>
@@ -34,18 +36,18 @@ struct cmm_page_array {
unsigned long pages[CMM_NR_PAGES];
};
-static long cmm_pages = 0;
-static long cmm_timed_pages = 0;
-static volatile long cmm_pages_target = 0;
-static volatile long cmm_timed_pages_target = 0;
-static long cmm_timeout_pages = 0;
-static long cmm_timeout_seconds = 0;
+static long cmm_pages;
+static long cmm_timed_pages;
+static volatile long cmm_pages_target;
+static volatile long cmm_timed_pages_target;
+static long cmm_timeout_pages;
+static long cmm_timeout_seconds;
-static struct cmm_page_array *cmm_page_list = NULL;
-static struct cmm_page_array *cmm_timed_page_list = NULL;
+static struct cmm_page_array *cmm_page_list;
+static struct cmm_page_array *cmm_timed_page_list;
+static DEFINE_SPINLOCK(cmm_lock);
-static unsigned long cmm_thread_active = 0;
-static struct work_struct cmm_thread_starter;
+static struct task_struct *cmm_thread_ptr;
static wait_queue_head_t cmm_thread_wait;
static struct timer_list cmm_timer;
@@ -53,71 +55,100 @@ static void cmm_timer_fn(unsigned long);
static void cmm_set_timer(void);
static long
-cmm_alloc_pages(long pages, long *counter, struct cmm_page_array **list)
+cmm_alloc_pages(long nr, long *counter, struct cmm_page_array **list)
{
- struct cmm_page_array *pa;
- unsigned long page;
+ struct cmm_page_array *pa, *npa;
+ unsigned long addr;
- pa = *list;
- while (pages) {
- page = __get_free_page(GFP_NOIO);
- if (!page)
+ while (nr) {
+ addr = __get_free_page(GFP_NOIO);
+ if (!addr)
break;
+ spin_lock(&cmm_lock);
+ pa = *list;
if (!pa || pa->index >= CMM_NR_PAGES) {
/* Need a new page for the page list. */
- pa = (struct cmm_page_array *)
+ spin_unlock(&cmm_lock);
+ npa = (struct cmm_page_array *)
__get_free_page(GFP_NOIO);
- if (!pa) {
- free_page(page);
+ if (!npa) {
+ free_page(addr);
break;
}
- pa->next = *list;
- pa->index = 0;
- *list = pa;
+ spin_lock(&cmm_lock);
+ pa = *list;
+ if (!pa || pa->index >= CMM_NR_PAGES) {
+ npa->next = pa;
+ npa->index = 0;
+ pa = npa;
+ *list = pa;
+ } else
+ free_page((unsigned long) npa);
}
- diag10(page);
- pa->pages[pa->index++] = page;
+ diag10(addr);
+ pa->pages[pa->index++] = addr;
(*counter)++;
- pages--;
+ spin_unlock(&cmm_lock);
+ nr--;
}
- return pages;
+ return nr;
}
-static void
-cmm_free_pages(long pages, long *counter, struct cmm_page_array **list)
+static long
+cmm_free_pages(long nr, long *counter, struct cmm_page_array **list)
{
struct cmm_page_array *pa;
- unsigned long page;
+ unsigned long addr;
+ spin_lock(&cmm_lock);
pa = *list;
- while (pages) {
+ while (nr) {
if (!pa || pa->index <= 0)
break;
- page = pa->pages[--pa->index];
+ addr = pa->pages[--pa->index];
if (pa->index == 0) {
pa = pa->next;
free_page((unsigned long) *list);
*list = pa;
}
- free_page(page);
+ free_page(addr);
(*counter)--;
- pages--;
+ nr--;
}
+ spin_unlock(&cmm_lock);
+ return nr;
}
+static int cmm_oom_notify(struct notifier_block *self,
+ unsigned long dummy, void *parm)
+{
+ unsigned long *freed = parm;
+ long nr = 256;
+
+ nr = cmm_free_pages(nr, &cmm_timed_pages, &cmm_timed_page_list);
+ if (nr > 0)
+ nr = cmm_free_pages(nr, &cmm_pages, &cmm_page_list);
+ cmm_pages_target = cmm_pages;
+ cmm_timed_pages_target = cmm_timed_pages;
+ *freed += 256 - nr;
+ return NOTIFY_OK;
+}
+
+static struct notifier_block cmm_oom_nb = {
+ .notifier_call = cmm_oom_notify
+};
+
static int
cmm_thread(void *dummy)
{
int rc;
- daemonize("cmmthread");
while (1) {
rc = wait_event_interruptible(cmm_thread_wait,
(cmm_pages != cmm_pages_target ||
- cmm_timed_pages != cmm_timed_pages_target));
- if (rc == -ERESTARTSYS) {
- /* Got kill signal. End thread. */
- clear_bit(0, &cmm_thread_active);
+ cmm_timed_pages != cmm_timed_pages_target ||
+ kthread_should_stop()));
+ if (kthread_should_stop() || rc == -ERESTARTSYS) {
cmm_pages_target = cmm_pages;
cmm_timed_pages_target = cmm_timed_pages;
break;
@@ -143,16 +174,8 @@ cmm_thread(void *dummy)
}
static void
-cmm_start_thread(void)
-{
- kernel_thread(cmm_thread, NULL, 0);
-}
-
-static void
cmm_kick_thread(void)
{
- if (!test_and_set_bit(0, &cmm_thread_active))
- schedule_work(&cmm_thread_starter);
wake_up(&cmm_thread_wait);
}
@@ -177,21 +200,21 @@ cmm_set_timer(void)
static void
cmm_timer_fn(unsigned long ignored)
{
- long pages;
+ long nr;
- pages = cmm_timed_pages_target - cmm_timeout_pages;
- if (pages < 0)
+ nr = cmm_timed_pages_target - cmm_timeout_pages;
+ if (nr < 0)
cmm_timed_pages_target = 0;
else
- cmm_timed_pages_target = pages;
+ cmm_timed_pages_target = nr;
cmm_kick_thread();
cmm_set_timer();
}
void
-cmm_set_pages(long pages)
+cmm_set_pages(long nr)
{
- cmm_pages_target = pages;
+ cmm_pages_target = nr;
cmm_kick_thread();
}
@@ -202,9 +225,9 @@ cmm_get_pages(void)
}
void
-cmm_add_timed_pages(long pages)
+cmm_add_timed_pages(long nr)
{
- cmm_timed_pages_target += pages;
+ cmm_timed_pages_target += nr;
cmm_kick_thread();
}
@@ -215,9 +238,9 @@ cmm_get_timed_pages(void)
}
void
-cmm_set_timeout(long pages, long seconds)
+cmm_set_timeout(long nr, long seconds)
{
- cmm_timeout_pages = pages;
+ cmm_timeout_pages = nr;
cmm_timeout_seconds = seconds;
cmm_set_timer();
}
@@ -245,7 +268,7 @@ cmm_pages_handler(ctl_table *ctl, int write, struct file *filp,
void __user *buffer, size_t *lenp, loff_t *ppos)
{
char buf[16], *p;
- long pages;
+ long nr;
int len;
if (!*lenp || (*ppos && !write)) {
@@ -260,17 +283,17 @@ cmm_pages_handler(ctl_table *ctl, int write, struct file *filp,
return -EFAULT;
buf[sizeof(buf) - 1] = '\0';
cmm_skip_blanks(buf, &p);
- pages = simple_strtoul(p, &p, 0);
+ nr = simple_strtoul(p, &p, 0);
if (ctl == &cmm_table[0])
- cmm_set_pages(pages);
+ cmm_set_pages(nr);
else
- cmm_add_timed_pages(pages);
+ cmm_add_timed_pages(nr);
} else {
if (ctl == &cmm_table[0])
- pages = cmm_get_pages();
+ nr = cmm_get_pages();
else
- pages = cmm_get_timed_pages();
- len = sprintf(buf, "%ld\n", pages);
+ nr = cmm_get_timed_pages();
+ len = sprintf(buf, "%ld\n", nr);
if (len > *lenp)
len = *lenp;
if (copy_to_user(buffer, buf, len))
@@ -286,7 +309,7 @@ cmm_timeout_handler(ctl_table *ctl, int write, struct file *filp,
void __user *buffer, size_t *lenp, loff_t *ppos)
{
char buf[64], *p;
- long pages, seconds;
+ long nr, seconds;
int len;
if (!*lenp || (*ppos && !write)) {
@@ -301,10 +324,10 @@ cmm_timeout_handler(ctl_table *ctl, int write, struct file *filp,
return -EFAULT;
buf[sizeof(buf) - 1] = '\0';
cmm_skip_blanks(buf, &p);
- pages = simple_strtoul(p, &p, 0);
+ nr = simple_strtoul(p, &p, 0);
cmm_skip_blanks(p, &p);
seconds = simple_strtoul(p, &p, 0);
- cmm_set_timeout(pages, seconds);
+ cmm_set_timeout(nr, seconds);
} else {
len = sprintf(buf, "%ld %ld\n",
cmm_timeout_pages, cmm_timeout_seconds);
@@ -357,7 +380,7 @@ static struct ctl_table cmm_dir_table[] = {
static void
cmm_smsg_target(char *from, char *msg)
{
- long pages, seconds;
+ long nr, seconds;
if (strlen(sender) > 0 && strcmp(from, sender) != 0)
return;
@@ -366,27 +389,27 @@ cmm_smsg_target(char *from, char *msg)
if (strncmp(msg, "SHRINK", 6) == 0) {
if (!cmm_skip_blanks(msg + 6, &msg))
return;
- pages = simple_strtoul(msg, &msg, 0);
+ nr = simple_strtoul(msg, &msg, 0);
cmm_skip_blanks(msg, &msg);
if (*msg == '\0')
- cmm_set_pages(pages);
+ cmm_set_pages(nr);
} else if (strncmp(msg, "RELEASE", 7) == 0) {
if (!cmm_skip_blanks(msg + 7, &msg))
return;
- pages = simple_strtoul(msg, &msg, 0);
+ nr = simple_strtoul(msg, &msg, 0);
cmm_skip_blanks(msg, &msg);
if (*msg == '\0')
- cmm_add_timed_pages(pages);
+ cmm_add_timed_pages(nr);
} else if (strncmp(msg, "REUSE", 5) == 0) {
if (!cmm_skip_blanks(msg + 5, &msg))
return;
- pages = simple_strtoul(msg, &msg, 0);
+ nr = simple_strtoul(msg, &msg, 0);
if (!cmm_skip_blanks(msg, &msg))
return;
seconds = simple_strtoul(msg, &msg, 0);
cmm_skip_blanks(msg, &msg);
if (*msg == '\0')
- cmm_set_timeout(pages, seconds);
+ cmm_set_timeout(nr, seconds);
}
}
#endif
@@ -396,21 +419,49 @@ struct ctl_table_header *cmm_sysctl_header;
static int
cmm_init (void)
{
+ int rc = -ENOMEM;
+
#ifdef CONFIG_CMM_PROC
cmm_sysctl_header = register_sysctl_table(cmm_dir_table, 1);
+ if (!cmm_sysctl_header)
+ goto out;
#endif
#ifdef CONFIG_CMM_IUCV
- smsg_register_callback(SMSG_PREFIX, cmm_smsg_target);
+ rc = smsg_register_callback(SMSG_PREFIX, cmm_smsg_target);
+ if (rc < 0)
+ goto out_smsg;
#endif
- INIT_WORK(&cmm_thread_starter, (void *) cmm_start_thread, NULL);
+ rc = register_oom_notifier(&cmm_oom_nb);
+ if (rc < 0)
+ goto out_oom_notify;
init_waitqueue_head(&cmm_thread_wait);
init_timer(&cmm_timer);
- return 0;
+ cmm_thread_ptr = kthread_run(cmm_thread, NULL, "cmmthread");
+ rc = IS_ERR(cmm_thread_ptr) ? PTR_ERR(cmm_thread_ptr) : 0;
+ if (!rc)
+ goto out;
+ /*
+ * kthread_create failed. undo all the stuff from above again.
+ */
+ unregister_oom_notifier(&cmm_oom_nb);
+
+out_oom_notify:
+#ifdef CONFIG_CMM_IUCV
+ smsg_unregister_callback(SMSG_PREFIX, cmm_smsg_target);
+out_smsg:
+#endif
+#ifdef CONFIG_CMM_PROC
+ unregister_sysctl_table(cmm_sysctl_header);
+#endif
+out:
+ return rc;
}
static void
cmm_exit(void)
{
+ kthread_stop(cmm_thread_ptr);
+ unregister_oom_notifier(&cmm_oom_nb);
cmm_free_pages(cmm_pages, &cmm_pages, &cmm_page_list);
cmm_free_pages(cmm_timed_pages, &cmm_timed_pages, &cmm_timed_page_list);
#ifdef CONFIG_CMM_PROC
diff --git a/arch/sh/mm/cache-sh7705.c b/arch/sh/mm/cache-sh7705.c
index ad8ed7d41e1..bf94eedb0a8 100644
--- a/arch/sh/mm/cache-sh7705.c
+++ b/arch/sh/mm/cache-sh7705.c
@@ -30,7 +30,7 @@
#define __pte_offset(address) \
((address >> PAGE_SHIFT) & (PTRS_PER_PTE - 1))
-#define pte_offset(dir, address) ((pte_t *) pmd_page_kernel(*(dir)) + \
+#define pte_offset(dir, address) ((pte_t *) pmd_page_vaddr(*(dir)) + \
__pte_offset(address))
static inline void cache_wback_all(void)
diff --git a/arch/sh64/mm/init.c b/arch/sh64/mm/init.c
index 1169757fb38..83295bd21aa 100644
--- a/arch/sh64/mm/init.c
+++ b/arch/sh64/mm/init.c
@@ -110,7 +110,7 @@ void show_mem(void)
*/
void __init paging_init(void)
{
- unsigned long zones_size[MAX_NR_ZONES] = {0, 0, 0};
+ unsigned long zones_size[MAX_NR_ZONES] = {0, };
pgd_init((unsigned long)swapper_pg_dir);
pgd_init((unsigned long)swapper_pg_dir +
diff --git a/arch/sparc/mm/srmmu.c b/arch/sparc/mm/srmmu.c
index 16e13f663ab..b27a506309e 100644
--- a/arch/sparc/mm/srmmu.c
+++ b/arch/sparc/mm/srmmu.c
@@ -2175,7 +2175,7 @@ void __init ld_mmu_srmmu(void)
BTFIXUPSET_CALL(pte_pfn, srmmu_pte_pfn, BTFIXUPCALL_NORM);
BTFIXUPSET_CALL(pmd_page, srmmu_pmd_page, BTFIXUPCALL_NORM);
- BTFIXUPSET_CALL(pgd_page, srmmu_pgd_page, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(pgd_page_vaddr, srmmu_pgd_page, BTFIXUPCALL_NORM);
BTFIXUPSET_SETHI(none_mask, 0xF0000000);
diff --git a/arch/sparc/mm/sun4c.c b/arch/sparc/mm/sun4c.c
index 7fdddf3c7e1..436021ceb2e 100644
--- a/arch/sparc/mm/sun4c.c
+++ b/arch/sparc/mm/sun4c.c
@@ -2280,5 +2280,5 @@ void __init ld_mmu_sun4c(void)
/* These should _never_ get called with two level tables. */
BTFIXUPSET_CALL(pgd_set, sun4c_pgd_set, BTFIXUPCALL_NOP);
- BTFIXUPSET_CALL(pgd_page, sun4c_pgd_page, BTFIXUPCALL_RETO0);
+ BTFIXUPSET_CALL(pgd_page_vaddr, sun4c_pgd_page, BTFIXUPCALL_RETO0);
}
diff --git a/arch/sparc64/solaris/misc.c b/arch/sparc64/solaris/misc.c
index 8135ec322c9..642541769a1 100644
--- a/arch/sparc64/solaris/misc.c
+++ b/arch/sparc64/solaris/misc.c
@@ -736,20 +736,15 @@ struct exec_domain solaris_exec_domain = {
extern int init_socksys(void);
-#ifdef MODULE
-
MODULE_AUTHOR("Jakub Jelinek (jj@ultra.linux.cz), Patrik Rak (prak3264@ss1000.ms.mff.cuni.cz)");
MODULE_DESCRIPTION("Solaris binary emulation module");
MODULE_LICENSE("GPL");
-#ifdef __sparc_v9__
extern u32 tl0_solaris[8];
#define update_ttable(x) \
tl0_solaris[3] = (((long)(x) - (long)tl0_solaris - 3) >> 2) | 0x40000000; \
wmb(); \
__asm__ __volatile__ ("flush %0" : : "r" (&tl0_solaris[3]))
-#else
-#endif
extern u32 solaris_sparc_syscall[];
extern u32 solaris_syscall[];
@@ -757,7 +752,7 @@ extern void cleanup_socksys(void);
extern u32 entry64_personality_patch;
-int init_module(void)
+static int __init solaris_init(void)
{
int ret;
@@ -777,19 +772,12 @@ int init_module(void)
return 0;
}
-void cleanup_module(void)
+static void __exit solaris_exit(void)
{
update_ttable(solaris_syscall);
cleanup_socksys();
unregister_exec_domain(&solaris_exec_domain);
}
-#else
-int init_solaris_emul(void)
-{
- register_exec_domain(&solaris_exec_domain);
- init_socksys();
- return 0;
-}
-#endif
-
+module_init(solaris_init);
+module_exit(solaris_exit);
diff --git a/arch/sparc64/solaris/socksys.c b/arch/sparc64/solaris/socksys.c
index bc3df95bc05..7c90e41fd3b 100644
--- a/arch/sparc64/solaris/socksys.c
+++ b/arch/sparc64/solaris/socksys.c
@@ -168,8 +168,7 @@ static struct file_operations socksys_fops = {
.release = socksys_release,
};
-int __init
-init_socksys(void)
+int __init init_socksys(void)
{
int ret;
struct file * file;
@@ -199,8 +198,7 @@ init_socksys(void)
return 0;
}
-void
-cleanup_socksys(void)
+void __exit cleanup_socksys(void)
{
if (unregister_chrdev(30, "socksys"))
printk ("Couldn't unregister socksys character device\n");
diff --git a/arch/um/drivers/chan_kern.c b/arch/um/drivers/chan_kern.c
index 7218c754505..e82764f75e7 100644
--- a/arch/um/drivers/chan_kern.c
+++ b/arch/um/drivers/chan_kern.c
@@ -544,7 +544,7 @@ static struct chan *parse_chan(struct line *line, char *str, int device,
ops = NULL;
data = NULL;
- for(i = 0; i < sizeof(chan_table)/sizeof(chan_table[0]); i++){
+ for(i = 0; i < ARRAY_SIZE(chan_table); i++){
entry = &chan_table[i];
if(!strncmp(str, entry->key, strlen(entry->key))){
ops = entry->ops;
diff --git a/arch/um/drivers/mconsole_kern.c b/arch/um/drivers/mconsole_kern.c
index b414522f768..79610b5ce67 100644
--- a/arch/um/drivers/mconsole_kern.c
+++ b/arch/um/drivers/mconsole_kern.c
@@ -497,7 +497,7 @@ static void mconsole_get_config(int (*get_config)(char *, char *, int,
}
error = NULL;
- size = sizeof(default_buf)/sizeof(default_buf[0]);
+ size = ARRAY_SIZE(default_buf);
buf = default_buf;
while(1){
diff --git a/arch/um/drivers/mconsole_user.c b/arch/um/drivers/mconsole_user.c
index 9bfd405c3bd..5b2f5fe9e42 100644
--- a/arch/um/drivers/mconsole_user.c
+++ b/arch/um/drivers/mconsole_user.c
@@ -16,6 +16,7 @@
#include "user.h"
#include "mconsole.h"
#include "umid.h"
+#include "user_util.h"
static struct mconsole_command commands[] = {
/* With uts namespaces, uts information becomes process-specific, so
@@ -65,14 +66,14 @@ static struct mconsole_command *mconsole_parse(struct mc_request *req)
struct mconsole_command *cmd;
int i;
- for(i=0;i<sizeof(commands)/sizeof(commands[0]);i++){
+ for(i = 0; i < ARRAY_SIZE(commands); i++){
cmd = &commands[i];
if(!strncmp(req->request.data, cmd->command,
strlen(cmd->command))){
- return(cmd);
+ return cmd;
}
}
- return(NULL);
+ return NULL;
}
#define MIN(a,b) ((a)<(b) ? (a):(b))
diff --git a/arch/um/drivers/net_kern.c b/arch/um/drivers/net_kern.c
index 501f95675d8..4a7966b2193 100644
--- a/arch/um/drivers/net_kern.c
+++ b/arch/um/drivers/net_kern.c
@@ -31,6 +31,11 @@
#include "irq_user.h"
#include "irq_kern.h"
+static inline void set_ether_mac(struct net_device *dev, unsigned char *addr)
+{
+ memcpy(dev->dev_addr, addr, ETH_ALEN);
+}
+
#define DRIVER_NAME "uml-netdev"
static DEFINE_SPINLOCK(opened_lock);
@@ -242,7 +247,7 @@ static int uml_net_set_mac(struct net_device *dev, void *addr)
struct sockaddr *hwaddr = addr;
spin_lock(&lp->lock);
- memcpy(dev->dev_addr, hwaddr->sa_data, ETH_ALEN);
+ set_ether_mac(dev, hwaddr->sa_data);
spin_unlock(&lp->lock);
return(0);
@@ -790,13 +795,6 @@ void dev_ip_addr(void *d, unsigned char *bin_buf)
memcpy(bin_buf, &in->ifa_address, sizeof(in->ifa_address));
}
-void set_ether_mac(void *d, unsigned char *addr)
-{
- struct net_device *dev = d;
-
- memcpy(dev->dev_addr, addr, ETH_ALEN);
-}
-
struct sk_buff *ether_adjust_skb(struct sk_buff *skb, int extra)
{
if((skb != NULL) && (skb_tailroom(skb) < extra)){
diff --git a/arch/um/drivers/pcap_kern.c b/arch/um/drivers/pcap_kern.c
index 466ff2c2f91..4c767c7adb9 100644
--- a/arch/um/drivers/pcap_kern.c
+++ b/arch/um/drivers/pcap_kern.c
@@ -76,7 +76,7 @@ int pcap_setup(char *str, char **mac_out, void *data)
if(host_if != NULL)
init->host_if = host_if;
- for(i = 0; i < sizeof(options)/sizeof(options[0]); i++){
+ for(i = 0; i < ARRAY_SIZE(options); i++){
if(options[i] == NULL)
continue;
if(!strcmp(options[i], "promisc"))
diff --git a/arch/um/include/kern_util.h b/arch/um/include/kern_util.h
index b98bdd8e052..89e1dc835a5 100644
--- a/arch/um/include/kern_util.h
+++ b/arch/um/include/kern_util.h
@@ -27,7 +27,6 @@ extern int ncpus;
extern char *linux_prog;
extern char *gdb_init;
extern int kmalloc_ok;
-extern int timer_irq_inited;
extern int jail;
extern int nsyscalls;
diff --git a/arch/um/include/longjmp.h b/arch/um/include/longjmp.h
index 1b5c0131a12..e93c6d3e893 100644
--- a/arch/um/include/longjmp.h
+++ b/arch/um/include/longjmp.h
@@ -1,9 +1,12 @@
#ifndef __UML_LONGJMP_H
#define __UML_LONGJMP_H
-#include <setjmp.h>
+#include "sysdep/archsetjmp.h"
#include "os.h"
+extern int setjmp(jmp_buf);
+extern void longjmp(jmp_buf, int);
+
#define UML_LONGJMP(buf, val) do { \
longjmp(*buf, val); \
} while(0)
diff --git a/arch/um/include/net_user.h b/arch/um/include/net_user.h
index 800c403920b..47ef7cb49a8 100644
--- a/arch/um/include/net_user.h
+++ b/arch/um/include/net_user.h
@@ -26,7 +26,6 @@ struct net_user_info {
extern void ether_user_init(void *data, void *dev);
extern void dev_ip_addr(void *d, unsigned char *bin_buf);
-extern void set_ether_mac(void *d, unsigned char *addr);
extern void iter_addresses(void *d, void (*cb)(unsigned char *,
unsigned char *, void *),
void *arg);
diff --git a/arch/um/include/os.h b/arch/um/include/os.h
index 5316e8a4a4f..24fb6d8680e 100644
--- a/arch/um/include/os.h
+++ b/arch/um/include/os.h
@@ -276,9 +276,11 @@ extern int setjmp_wrapper(void (*proc)(void *, void *), ...);
extern void switch_timers(int to_real);
extern void idle_sleep(int secs);
+extern int set_interval(int is_virtual);
+#ifdef CONFIG_MODE_TT
extern void enable_timer(void);
+#endif
extern void disable_timer(void);
-extern void user_time_init(void);
extern void uml_idle_timer(void);
extern unsigned long long os_nsecs(void);
@@ -329,6 +331,7 @@ extern void os_set_ioignore(void);
extern void init_irq_signals(int on_sigstack);
/* sigio.c */
+extern int add_sigio_fd(int fd);
extern int ignore_sigio_fd(int fd);
extern void maybe_sigio_broken(int fd, int read);
diff --git a/arch/um/include/registers.h b/arch/um/include/registers.h
index 83b688ca198..f845b3629a6 100644
--- a/arch/um/include/registers.h
+++ b/arch/um/include/registers.h
@@ -7,6 +7,7 @@
#define __REGISTERS_H
#include "sysdep/ptrace.h"
+#include "sysdep/archsetjmp.h"
extern void init_thread_registers(union uml_pt_regs *to);
extern int save_fp_registers(int pid, unsigned long *fp_regs);
@@ -15,6 +16,6 @@ extern void save_registers(int pid, union uml_pt_regs *regs);
extern void restore_registers(int pid, union uml_pt_regs *regs);
extern void init_registers(int pid);
extern void get_safe_registers(unsigned long * regs, unsigned long * fp_regs);
-extern void get_thread_regs(union uml_pt_regs *uml_regs, void *buffer);
+extern unsigned long get_thread_reg(int reg, jmp_buf *buf);
#endif
diff --git a/arch/um/include/sysdep-i386/archsetjmp.h b/arch/um/include/sysdep-i386/archsetjmp.h
new file mode 100644
index 00000000000..ea1ba3d42ae
--- /dev/null
+++ b/arch/um/include/sysdep-i386/archsetjmp.h
@@ -0,0 +1,19 @@
+/*
+ * arch/i386/include/klibc/archsetjmp.h
+ */
+
+#ifndef _KLIBC_ARCHSETJMP_H
+#define _KLIBC_ARCHSETJMP_H
+
+struct __jmp_buf {
+ unsigned int __ebx;
+ unsigned int __esp;
+ unsigned int __ebp;
+ unsigned int __esi;
+ unsigned int __edi;
+ unsigned int __eip;
+};
+
+typedef struct __jmp_buf jmp_buf[1];
+
+#endif /* _SETJMP_H */
diff --git a/arch/um/include/sysdep-i386/signal.h b/arch/um/include/sysdep-i386/signal.h
deleted file mode 100644
index 07518b16213..00000000000
--- a/arch/um/include/sysdep-i386/signal.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2004 PathScale, Inc
- * Licensed under the GPL
- */
-
-#ifndef __I386_SIGNAL_H_
-#define __I386_SIGNAL_H_
-
-#include <signal.h>
-
-#define ARCH_SIGHDLR_PARAM int sig
-
-#define ARCH_GET_SIGCONTEXT(sc, sig) \
- do sc = (struct sigcontext *) (&sig + 1); while(0)
-
-#endif
-
-/*
- * Overrides for Emacs so that we follow Linus's tabbing style.
- * Emacs will notice this stuff at the end of the file and automatically
- * adjust the settings for this buffer only. This must remain at the end
- * of the file.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-file-style: "linux"
- * End:
- */
diff --git a/arch/um/include/sysdep-x86_64/archsetjmp.h b/arch/um/include/sysdep-x86_64/archsetjmp.h
new file mode 100644
index 00000000000..454fc60aff6
--- /dev/null
+++ b/arch/um/include/sysdep-x86_64/archsetjmp.h
@@ -0,0 +1,21 @@
+/*
+ * arch/x86_64/include/klibc/archsetjmp.h
+ */
+
+#ifndef _KLIBC_ARCHSETJMP_H
+#define _KLIBC_ARCHSETJMP_H
+
+struct __jmp_buf {
+ unsigned long __rbx;
+ unsigned long __rsp;
+ unsigned long __rbp;
+ unsigned long __r12;
+ unsigned long __r13;
+ unsigned long __r14;
+ unsigned long __r15;
+ unsigned long __rip;
+};
+
+typedef struct __jmp_buf jmp_buf[1];
+
+#endif /* _SETJMP_H */
diff --git a/arch/um/include/sysdep-x86_64/signal.h b/arch/um/include/sysdep-x86_64/signal.h
deleted file mode 100644
index 6142897af3d..00000000000
--- a/arch/um/include/sysdep-x86_64/signal.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (C) 2004 PathScale, Inc
- * Licensed under the GPL
- */
-
-#ifndef __X86_64_SIGNAL_H_
-#define __X86_64_SIGNAL_H_
-
-#define ARCH_SIGHDLR_PARAM int sig
-
-#define ARCH_GET_SIGCONTEXT(sc, sig_addr) \
- do { \
- struct ucontext *__uc; \
- asm("movq %%rdx, %0" : "=r" (__uc)); \
- sc = (struct sigcontext *) &__uc->uc_mcontext; \
- } while(0)
-
-#endif
-
-/*
- * Overrides for Emacs so that we follow Linus's tabbing style.
- * Emacs will notice this stuff at the end of the file and automatically
- * adjust the settings for this buffer only. This must remain at the end
- * of the file.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-file-style: "linux"
- * End:
- */
diff --git a/arch/um/kernel/exec.c b/arch/um/kernel/exec.c
index fc38a6d5906..0561c43b468 100644
--- a/arch/um/kernel/exec.c
+++ b/arch/um/kernel/exec.c
@@ -41,9 +41,11 @@ static long execve1(char *file, char __user * __user *argv,
long error;
#ifdef CONFIG_TTY_LOG
- task_lock(current);
+ mutex_lock(&tty_mutex);
+ task_lock(current); /* FIXME: is this needed ? */
log_exec(argv, current->signal->tty);
task_unlock(current);
+ mutex_unlock(&tty_mutex);
#endif
error = do_execve(file, argv, env, &current->thread.regs);
if (error == 0){
diff --git a/arch/um/kernel/irq.c b/arch/um/kernel/irq.c
index 589c69a7504..ce7f233fc49 100644
--- a/arch/um/kernel/irq.c
+++ b/arch/um/kernel/irq.c
@@ -142,19 +142,6 @@ int activate_fd(int irq, int fd, int type, void *dev_id)
.events = events,
.current_events = 0 } );
- /* Critical section - locked by a spinlock because this stuff can
- * be changed from interrupt handlers. The stuff above is done
- * outside the lock because it allocates memory.
- */
-
- /* Actually, it only looks like it can be called from interrupt
- * context. The culprit is reactivate_fd, which calls
- * maybe_sigio_broken, which calls write_sigio_workaround,
- * which calls activate_fd. However, write_sigio_workaround should
- * only be called once, at boot time. That would make it clear that
- * this is called only from process context, and can be locked with
- * a semaphore.
- */
spin_lock_irqsave(&irq_lock, flags);
for (irq_fd = active_fds; irq_fd != NULL; irq_fd = irq_fd->next) {
if ((irq_fd->fd == fd) && (irq_fd->type == type)) {
@@ -165,7 +152,6 @@ int activate_fd(int irq, int fd, int type, void *dev_id)
}
}
- /*-------------*/
if (type == IRQ_WRITE)
fd = -1;
@@ -198,7 +184,6 @@ int activate_fd(int irq, int fd, int type, void *dev_id)
spin_lock_irqsave(&irq_lock, flags);
}
- /*-------------*/
*last_irq_ptr = new_fd;
last_irq_ptr = &new_fd->next;
@@ -210,14 +195,14 @@ int activate_fd(int irq, int fd, int type, void *dev_id)
*/
maybe_sigio_broken(fd, (type == IRQ_READ));
- return(0);
+ return 0;
out_unlock:
spin_unlock_irqrestore(&irq_lock, flags);
out_kfree:
kfree(new_fd);
out:
- return(err);
+ return err;
}
static void free_irq_by_cb(int (*test)(struct irq_fd *, void *), void *arg)
@@ -302,10 +287,7 @@ void reactivate_fd(int fd, int irqnum)
os_set_pollfd(i, irq->fd);
spin_unlock_irqrestore(&irq_lock, flags);
- /* This calls activate_fd, so it has to be outside the critical
- * section.
- */
- maybe_sigio_broken(fd, (irq->type == IRQ_READ));
+ add_sigio_fd(fd);
}
void deactivate_fd(int fd, int irqnum)
@@ -316,11 +298,15 @@ void deactivate_fd(int fd, int irqnum)
spin_lock_irqsave(&irq_lock, flags);
irq = find_irq_by_fd(fd, irqnum, &i);
- if (irq == NULL)
- goto out;
+ if(irq == NULL){
+ spin_unlock_irqrestore(&irq_lock, flags);
+ return;
+ }
+
os_set_pollfd(i, -1);
- out:
spin_unlock_irqrestore(&irq_lock, flags);
+
+ ignore_sigio_fd(fd);
}
int deactivate_all_fds(void)
diff --git a/arch/um/kernel/mem.c b/arch/um/kernel/mem.c
index 61280167c56..93121c6d26e 100644
--- a/arch/um/kernel/mem.c
+++ b/arch/um/kernel/mem.c
@@ -79,8 +79,10 @@ void mem_init(void)
/* this will put all low memory onto the freelists */
totalram_pages = free_all_bootmem();
+#ifdef CONFIG_HIGHMEM
totalhigh_pages = highmem >> PAGE_SHIFT;
totalram_pages += totalhigh_pages;
+#endif
num_physpages = totalram_pages;
max_pfn = totalram_pages;
printk(KERN_INFO "Memory: %luk available\n",
@@ -221,10 +223,13 @@ void paging_init(void)
empty_zero_page = (unsigned long *) alloc_bootmem_low_pages(PAGE_SIZE);
empty_bad_page = (unsigned long *) alloc_bootmem_low_pages(PAGE_SIZE);
- for(i=0;i<sizeof(zones_size)/sizeof(zones_size[0]);i++)
+ for(i = 0; i < ARRAY_SIZE(zones_size); i++)
zones_size[i] = 0;
+
zones_size[ZONE_DMA] = (end_iomem >> PAGE_SHIFT) - (uml_physmem >> PAGE_SHIFT);
+#ifdef CONFIG_HIGHMEM
zones_size[ZONE_HIGHMEM] = highmem >> PAGE_SHIFT;
+#endif
free_area_init(zones_size);
/*
diff --git a/arch/um/kernel/process_kern.c b/arch/um/kernel/process_kern.c
index f6a5a502120..537895d68ad 100644
--- a/arch/um/kernel/process_kern.c
+++ b/arch/um/kernel/process_kern.c
@@ -23,6 +23,7 @@
#include "linux/proc_fs.h"
#include "linux/ptrace.h"
#include "linux/random.h"
+#include "linux/personality.h"
#include "asm/unistd.h"
#include "asm/mman.h"
#include "asm/segment.h"
@@ -476,7 +477,7 @@ int singlestepping(void * t)
#ifndef arch_align_stack
unsigned long arch_align_stack(unsigned long sp)
{
- if (randomize_va_space)
+ if (!(current->personality & ADDR_NO_RANDOMIZE) && randomize_va_space)
sp -= get_random_int() % 8192;
return sp & ~0xf;
}
diff --git a/arch/um/kernel/reboot.c b/arch/um/kernel/reboot.c
index 3ef73bf2e78..f602623644a 100644
--- a/arch/um/kernel/reboot.c
+++ b/arch/um/kernel/reboot.c
@@ -22,7 +22,7 @@ static void kill_idlers(int me)
struct task_struct *p;
int i;
- for(i = 0; i < sizeof(idle_threads)/sizeof(idle_threads[0]); i++){
+ for(i = 0; i < ARRAY_SIZE(idle_threads); i++){
p = idle_threads[i];
if((p != NULL) && (p->thread.mode.tt.extern_pid != me))
os_kill_process(p->thread.mode.tt.extern_pid, 0);
@@ -62,14 +62,3 @@ void machine_halt(void)
{
machine_power_off();
}
-
-/*
- * Overrides for Emacs so that we follow Linus's tabbing style.
- * Emacs will notice this stuff at the end of the file and automatically
- * adjust the settings for this buffer only. This must remain at the end
- * of the file.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-file-style: "linux"
- * End:
- */
diff --git a/arch/um/kernel/skas/mmu.c b/arch/um/kernel/skas/mmu.c
index 624ca238d1f..79c22707a63 100644
--- a/arch/um/kernel/skas/mmu.c
+++ b/arch/um/kernel/skas/mmu.c
@@ -55,7 +55,7 @@ static int init_stub_pte(struct mm_struct *mm, unsigned long proc,
* destroy_context_skas.
*/
- mm->context.skas.last_page_table = pmd_page_kernel(*pmd);
+ mm->context.skas.last_page_table = pmd_page_vaddr(*pmd);
#ifdef CONFIG_3_LEVEL_PGTABLES
mm->context.skas.last_pmd = (unsigned long) __va(pud_val(*pud));
#endif
diff --git a/arch/um/kernel/time.c b/arch/um/kernel/time.c
index 552ca1cb984..2454bbd9555 100644
--- a/arch/um/kernel/time.c
+++ b/arch/um/kernel/time.c
@@ -35,9 +35,6 @@ unsigned long long sched_clock(void)
return (unsigned long long)jiffies_64 * (1000000000 / HZ);
}
-/* Changed at early boot */
-int timer_irq_inited = 0;
-
static unsigned long long prev_nsecs;
#ifdef CONFIG_UML_REAL_TIME_CLOCK
static long long delta; /* Deviation per interval */
@@ -113,12 +110,13 @@ static void register_timer(void)
err = request_irq(TIMER_IRQ, um_timer, IRQF_DISABLED, "timer", NULL);
if(err != 0)
- printk(KERN_ERR "timer_init : request_irq failed - "
+ printk(KERN_ERR "register_timer : request_irq failed - "
"errno = %d\n", -err);
- timer_irq_inited = 1;
-
- user_time_init();
+ err = set_interval(1);
+ if(err != 0)
+ printk(KERN_ERR "register_timer : set_interval failed - "
+ "errno = %d\n", -err);
}
extern void (*late_time_init)(void);
diff --git a/arch/um/kernel/tlb.c b/arch/um/kernel/tlb.c
index f5b0636f9ad..54a5ff25645 100644
--- a/arch/um/kernel/tlb.c
+++ b/arch/um/kernel/tlb.c
@@ -1,4 +1,4 @@
-/*
+/*
* Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com)
* Licensed under the GPL
*/
@@ -16,12 +16,12 @@
#include "os.h"
static int add_mmap(unsigned long virt, unsigned long phys, unsigned long len,
- int r, int w, int x, struct host_vm_op *ops, int *index,
+ int r, int w, int x, struct host_vm_op *ops, int *index,
int last_filled, union mm_context *mmu, void **flush,
int (*do_ops)(union mm_context *, struct host_vm_op *,
int, int, void **))
{
- __u64 offset;
+ __u64 offset;
struct host_vm_op *last;
int fd, ret = 0;
@@ -89,7 +89,7 @@ static int add_munmap(unsigned long addr, unsigned long len,
static int add_mprotect(unsigned long addr, unsigned long len, int r, int w,
int x, struct host_vm_op *ops, int *index,
int last_filled, union mm_context *mmu, void **flush,
- int (*do_ops)(union mm_context *, struct host_vm_op *,
+ int (*do_ops)(union mm_context *, struct host_vm_op *,
int, int, void **))
{
struct host_vm_op *last;
@@ -124,105 +124,105 @@ static int add_mprotect(unsigned long addr, unsigned long len, int r, int w,
#define ADD_ROUND(n, inc) (((n) + (inc)) & ~((inc) - 1))
void fix_range_common(struct mm_struct *mm, unsigned long start_addr,
- unsigned long end_addr, int force,
+ unsigned long end_addr, int force,
int (*do_ops)(union mm_context *, struct host_vm_op *,
int, int, void **))
{
- pgd_t *npgd;
- pud_t *npud;
- pmd_t *npmd;
- pte_t *npte;
- union mm_context *mmu = &mm->context;
- unsigned long addr, end;
- int r, w, x;
- struct host_vm_op ops[1];
- void *flush = NULL;
- int op_index = -1, last_op = sizeof(ops) / sizeof(ops[0]) - 1;
- int ret = 0;
-
- if(mm == NULL) return;
-
- ops[0].type = NONE;
- for(addr = start_addr; addr < end_addr && !ret;){
- npgd = pgd_offset(mm, addr);
- if(!pgd_present(*npgd)){
- end = ADD_ROUND(addr, PGDIR_SIZE);
- if(end > end_addr)
- end = end_addr;
- if(force || pgd_newpage(*npgd)){
- ret = add_munmap(addr, end - addr, ops,
- &op_index, last_op, mmu,
- &flush, do_ops);
- pgd_mkuptodate(*npgd);
- }
- addr = end;
- continue;
- }
-
- npud = pud_offset(npgd, addr);
- if(!pud_present(*npud)){
- end = ADD_ROUND(addr, PUD_SIZE);
- if(end > end_addr)
- end = end_addr;
- if(force || pud_newpage(*npud)){
- ret = add_munmap(addr, end - addr, ops,
- &op_index, last_op, mmu,
- &flush, do_ops);
- pud_mkuptodate(*npud);
- }
- addr = end;
- continue;
- }
-
- npmd = pmd_offset(npud, addr);
- if(!pmd_present(*npmd)){
- end = ADD_ROUND(addr, PMD_SIZE);
- if(end > end_addr)
- end = end_addr;
- if(force || pmd_newpage(*npmd)){
- ret = add_munmap(addr, end - addr, ops,
- &op_index, last_op, mmu,
- &flush, do_ops);
- pmd_mkuptodate(*npmd);
- }
- addr = end;
- continue;
- }
-
- npte = pte_offset_kernel(npmd, addr);
- r = pte_read(*npte);
- w = pte_write(*npte);
- x = pte_exec(*npte);
+ pgd_t *npgd;
+ pud_t *npud;
+ pmd_t *npmd;
+ pte_t *npte;
+ union mm_context *mmu = &mm->context;
+ unsigned long addr, end;
+ int r, w, x;
+ struct host_vm_op ops[1];
+ void *flush = NULL;
+ int op_index = -1, last_op = ARRAY_SIZE(ops) - 1;
+ int ret = 0;
+
+ if(mm == NULL)
+ return;
+
+ ops[0].type = NONE;
+ for(addr = start_addr; addr < end_addr && !ret;){
+ npgd = pgd_offset(mm, addr);
+ if(!pgd_present(*npgd)){
+ end = ADD_ROUND(addr, PGDIR_SIZE);
+ if(end > end_addr)
+ end = end_addr;
+ if(force || pgd_newpage(*npgd)){
+ ret = add_munmap(addr, end - addr, ops,
+ &op_index, last_op, mmu,
+ &flush, do_ops);
+ pgd_mkuptodate(*npgd);
+ }
+ addr = end;
+ continue;
+ }
+
+ npud = pud_offset(npgd, addr);
+ if(!pud_present(*npud)){
+ end = ADD_ROUND(addr, PUD_SIZE);
+ if(end > end_addr)
+ end = end_addr;
+ if(force || pud_newpage(*npud)){
+ ret = add_munmap(addr, end - addr, ops,
+ &op_index, last_op, mmu,
+ &flush, do_ops);
+ pud_mkuptodate(*npud);
+ }
+ addr = end;
+ continue;
+ }
+
+ npmd = pmd_offset(npud, addr);
+ if(!pmd_present(*npmd)){
+ end = ADD_ROUND(addr, PMD_SIZE);
+ if(end > end_addr)
+ end = end_addr;
+ if(force || pmd_newpage(*npmd)){
+ ret = add_munmap(addr, end - addr, ops,
+ &op_index, last_op, mmu,
+ &flush, do_ops);
+ pmd_mkuptodate(*npmd);
+ }
+ addr = end;
+ continue;
+ }
+
+ npte = pte_offset_kernel(npmd, addr);
+ r = pte_read(*npte);
+ w = pte_write(*npte);
+ x = pte_exec(*npte);
if (!pte_young(*npte)) {
r = 0;
w = 0;
} else if (!pte_dirty(*npte)) {
w = 0;
}
- if(force || pte_newpage(*npte)){
- if(pte_present(*npte))
- ret = add_mmap(addr,
- pte_val(*npte) & PAGE_MASK,
- PAGE_SIZE, r, w, x, ops,
- &op_index, last_op, mmu,
- &flush, do_ops);
+ if(force || pte_newpage(*npte)){
+ if(pte_present(*npte))
+ ret = add_mmap(addr,
+ pte_val(*npte) & PAGE_MASK,
+ PAGE_SIZE, r, w, x, ops,
+ &op_index, last_op, mmu,
+ &flush, do_ops);
else ret = add_munmap(addr, PAGE_SIZE, ops,
&op_index, last_op, mmu,
&flush, do_ops);
- }
- else if(pte_newprot(*npte))
+ }
+ else if(pte_newprot(*npte))
ret = add_mprotect(addr, PAGE_SIZE, r, w, x, ops,
&op_index, last_op, mmu,
&flush, do_ops);
- *npte = pte_mkuptodate(*npte);
- addr += PAGE_SIZE;
- }
-
+ *npte = pte_mkuptodate(*npte);
+ addr += PAGE_SIZE;
+ }
if(!ret)
ret = (*do_ops)(mmu, ops, op_index, 1, &flush);
- /* This is not an else because ret is modified above */
+/* This is not an else because ret is modified above */
if(ret) {
printk("fix_range_common: failed, killing current process\n");
force_sig(SIGKILL, current);
@@ -231,160 +231,160 @@ void fix_range_common(struct mm_struct *mm, unsigned long start_addr,
int flush_tlb_kernel_range_common(unsigned long start, unsigned long end)
{
- struct mm_struct *mm;
- pgd_t *pgd;
- pud_t *pud;
- pmd_t *pmd;
- pte_t *pte;
- unsigned long addr, last;
- int updated = 0, err;
-
- mm = &init_mm;
- for(addr = start; addr < end;){
- pgd = pgd_offset(mm, addr);
- if(!pgd_present(*pgd)){
- last = ADD_ROUND(addr, PGDIR_SIZE);
- if(last > end)
- last = end;
- if(pgd_newpage(*pgd)){
- updated = 1;
- err = os_unmap_memory((void *) addr,
- last - addr);
- if(err < 0)
- panic("munmap failed, errno = %d\n",
- -err);
- }
- addr = last;
- continue;
- }
-
- pud = pud_offset(pgd, addr);
- if(!pud_present(*pud)){
- last = ADD_ROUND(addr, PUD_SIZE);
- if(last > end)
- last = end;
- if(pud_newpage(*pud)){
- updated = 1;
- err = os_unmap_memory((void *) addr,
- last - addr);
- if(err < 0)
- panic("munmap failed, errno = %d\n",
- -err);
- }
- addr = last;
- continue;
- }
-
- pmd = pmd_offset(pud, addr);
- if(!pmd_present(*pmd)){
- last = ADD_ROUND(addr, PMD_SIZE);
- if(last > end)
- last = end;
- if(pmd_newpage(*pmd)){
- updated = 1;
- err = os_unmap_memory((void *) addr,
- last - addr);
- if(err < 0)
- panic("munmap failed, errno = %d\n",
- -err);
- }
- addr = last;
- continue;
- }
-
- pte = pte_offset_kernel(pmd, addr);
- if(!pte_present(*pte) || pte_newpage(*pte)){
- updated = 1;
- err = os_unmap_memory((void *) addr,
- PAGE_SIZE);
- if(err < 0)
- panic("munmap failed, errno = %d\n",
- -err);
- if(pte_present(*pte))
- map_memory(addr,
- pte_val(*pte) & PAGE_MASK,
- PAGE_SIZE, 1, 1, 1);
- }
- else if(pte_newprot(*pte)){
- updated = 1;
- os_protect_memory((void *) addr, PAGE_SIZE, 1, 1, 1);
- }
- addr += PAGE_SIZE;
- }
- return(updated);
+ struct mm_struct *mm;
+ pgd_t *pgd;
+ pud_t *pud;
+ pmd_t *pmd;
+ pte_t *pte;
+ unsigned long addr, last;
+ int updated = 0, err;
+
+ mm = &init_mm;
+ for(addr = start; addr < end;){
+ pgd = pgd_offset(mm, addr);
+ if(!pgd_present(*pgd)){
+ last = ADD_ROUND(addr, PGDIR_SIZE);
+ if(last > end)
+ last = end;
+ if(pgd_newpage(*pgd)){
+ updated = 1;
+ err = os_unmap_memory((void *) addr,
+ last - addr);
+ if(err < 0)
+ panic("munmap failed, errno = %d\n",
+ -err);
+ }
+ addr = last;
+ continue;
+ }
+
+ pud = pud_offset(pgd, addr);
+ if(!pud_present(*pud)){
+ last = ADD_ROUND(addr, PUD_SIZE);
+ if(last > end)
+ last = end;
+ if(pud_newpage(*pud)){
+ updated = 1;
+ err = os_unmap_memory((void *) addr,
+ last - addr);
+ if(err < 0)
+ panic("munmap failed, errno = %d\n",
+ -err);
+ }
+ addr = last;
+ continue;
+ }
+
+ pmd = pmd_offset(pud, addr);
+ if(!pmd_present(*pmd)){
+ last = ADD_ROUND(addr, PMD_SIZE);
+ if(last > end)
+ last = end;
+ if(pmd_newpage(*pmd)){
+ updated = 1;
+ err = os_unmap_memory((void *) addr,
+ last - addr);
+ if(err < 0)
+ panic("munmap failed, errno = %d\n",
+ -err);
+ }
+ addr = last;
+ continue;
+ }
+
+ pte = pte_offset_kernel(pmd, addr);
+ if(!pte_present(*pte) || pte_newpage(*pte)){
+ updated = 1;
+ err = os_unmap_memory((void *) addr,
+ PAGE_SIZE);
+ if(err < 0)
+ panic("munmap failed, errno = %d\n",
+ -err);
+ if(pte_present(*pte))
+ map_memory(addr,
+ pte_val(*pte) & PAGE_MASK,
+ PAGE_SIZE, 1, 1, 1);
+ }
+ else if(pte_newprot(*pte)){
+ updated = 1;
+ os_protect_memory((void *) addr, PAGE_SIZE, 1, 1, 1);
+ }
+ addr += PAGE_SIZE;
+ }
+ return(updated);
}
pgd_t *pgd_offset_proc(struct mm_struct *mm, unsigned long address)
{
- return(pgd_offset(mm, address));
+ return(pgd_offset(mm, address));
}
pud_t *pud_offset_proc(pgd_t *pgd, unsigned long address)
{
- return(pud_offset(pgd, address));
+ return(pud_offset(pgd, address));
}
pmd_t *pmd_offset_proc(pud_t *pud, unsigned long address)
{
- return(pmd_offset(pud, address));
+ return(pmd_offset(pud, address));
}
pte_t *pte_offset_proc(pmd_t *pmd, unsigned long address)
{
- return(pte_offset_kernel(pmd, address));
+ return(pte_offset_kernel(pmd, address));
}
pte_t *addr_pte(struct task_struct *task, unsigned long addr)
{
- pgd_t *pgd = pgd_offset(task->mm, addr);
- pud_t *pud = pud_offset(pgd, addr);
- pmd_t *pmd = pmd_offset(pud, addr);
+ pgd_t *pgd = pgd_offset(task->mm, addr);
+ pud_t *pud = pud_offset(pgd, addr);
+ pmd_t *pmd = pmd_offset(pud, addr);
- return(pte_offset_map(pmd, addr));
+ return(pte_offset_map(pmd, addr));
}
void flush_tlb_page(struct vm_area_struct *vma, unsigned long address)
{
- address &= PAGE_MASK;
- flush_tlb_range(vma, address, address + PAGE_SIZE);
+ address &= PAGE_MASK;
+ flush_tlb_range(vma, address, address + PAGE_SIZE);
}
void flush_tlb_all(void)
{
- flush_tlb_mm(current->mm);
+ flush_tlb_mm(current->mm);
}
void flush_tlb_kernel_range(unsigned long start, unsigned long end)
{
- CHOOSE_MODE_PROC(flush_tlb_kernel_range_tt,
- flush_tlb_kernel_range_common, start, end);
+ CHOOSE_MODE_PROC(flush_tlb_kernel_range_tt,
+ flush_tlb_kernel_range_common, start, end);
}
void flush_tlb_kernel_vm(void)
{
- CHOOSE_MODE(flush_tlb_kernel_vm_tt(),
- flush_tlb_kernel_range_common(start_vm, end_vm));
+ CHOOSE_MODE(flush_tlb_kernel_vm_tt(),
+ flush_tlb_kernel_range_common(start_vm, end_vm));
}
void __flush_tlb_one(unsigned long addr)
{
- CHOOSE_MODE_PROC(__flush_tlb_one_tt, __flush_tlb_one_skas, addr);
+ CHOOSE_MODE_PROC(__flush_tlb_one_tt, __flush_tlb_one_skas, addr);
}
void flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
unsigned long end)
{
- CHOOSE_MODE_PROC(flush_tlb_range_tt, flush_tlb_range_skas, vma, start,
- end);
+ CHOOSE_MODE_PROC(flush_tlb_range_tt, flush_tlb_range_skas, vma, start,
+ end);
}
void flush_tlb_mm(struct mm_struct *mm)
{
- CHOOSE_MODE_PROC(flush_tlb_mm_tt, flush_tlb_mm_skas, mm);
+ CHOOSE_MODE_PROC(flush_tlb_mm_tt, flush_tlb_mm_skas, mm);
}
void force_flush_all(void)
{
- CHOOSE_MODE(force_flush_all_tt(), force_flush_all_skas());
+ CHOOSE_MODE(force_flush_all_tt(), force_flush_all_skas());
}
diff --git a/arch/um/kernel/trap.c b/arch/um/kernel/trap.c
index ac70fa5a2e2..e5eeaf2b6af 100644
--- a/arch/um/kernel/trap.c
+++ b/arch/um/kernel/trap.c
@@ -227,9 +227,16 @@ void bad_segv(struct faultinfo fi, unsigned long ip)
void relay_signal(int sig, union uml_pt_regs *regs)
{
- if(arch_handle_signal(sig, regs)) return;
- if(!UPT_IS_USER(regs))
+ if(arch_handle_signal(sig, regs))
+ return;
+
+ if(!UPT_IS_USER(regs)){
+ if(sig == SIGBUS)
+ printk("Bus error - the /dev/shm or /tmp mount likely "
+ "just ran out of space\n");
panic("Kernel mode signal %d", sig);
+ }
+
current->thread.arch.faultinfo = *UPT_FAULTINFO(regs);
force_sig(sig, current);
}
diff --git a/arch/um/os-Linux/helper.c b/arch/um/os-Linux/helper.c
index 6987d1d247a..cd15b9df5b5 100644
--- a/arch/um/os-Linux/helper.c
+++ b/arch/um/os-Linux/helper.c
@@ -42,7 +42,7 @@ static int helper_child(void *arg)
if(data->pre_exec != NULL)
(*data->pre_exec)(data->pre_data);
execvp(argv[0], argv);
- errval = errno;
+ errval = -errno;
printk("helper_child - execve of '%s' failed - errno = %d\n", argv[0], errno);
os_write_file(data->fd, &errval, sizeof(errval));
kill(os_getpid(), SIGKILL);
@@ -62,7 +62,7 @@ int run_helper(void (*pre_exec)(void *), void *pre_data, char **argv,
stack = *stack_out;
else stack = alloc_stack(0, __cant_sleep());
if(stack == 0)
- return(-ENOMEM);
+ return -ENOMEM;
ret = os_pipe(fds, 1, 0);
if(ret < 0){
@@ -95,16 +95,16 @@ int run_helper(void (*pre_exec)(void *), void *pre_data, char **argv,
/* Read the errno value from the child, if the exec failed, or get 0 if
* the exec succeeded because the pipe fd was set as close-on-exec. */
n = os_read_file(fds[0], &ret, sizeof(ret));
- if (n < 0) {
- printk("run_helper : read on pipe failed, ret = %d\n", -n);
- ret = n;
- kill(pid, SIGKILL);
- CATCH_EINTR(waitpid(pid, NULL, 0));
- } else if(n != 0){
- CATCH_EINTR(n = waitpid(pid, NULL, 0));
- ret = -errno;
- } else {
+ if(n == 0)
ret = pid;
+ else {
+ if(n < 0){
+ printk("run_helper : read on pipe failed, ret = %d\n",
+ -n);
+ ret = n;
+ kill(pid, SIGKILL);
+ }
+ CATCH_EINTR(waitpid(pid, NULL, 0));
}
out_close:
diff --git a/arch/um/os-Linux/irq.c b/arch/um/os-Linux/irq.c
index 7555bf9c33d..a97206df5b5 100644
--- a/arch/um/os-Linux/irq.c
+++ b/arch/um/os-Linux/irq.c
@@ -132,7 +132,7 @@ void os_set_pollfd(int i, int fd)
void os_set_ioignore(void)
{
- set_handler(SIGIO, SIG_IGN, 0, -1);
+ signal(SIGIO, SIG_IGN);
}
void init_irq_signals(int on_sigstack)
diff --git a/arch/um/os-Linux/main.c b/arch/um/os-Linux/main.c
index 90912aaca7a..d1c5670787d 100644
--- a/arch/um/os-Linux/main.c
+++ b/arch/um/os-Linux/main.c
@@ -67,13 +67,32 @@ static __init void do_uml_initcalls(void)
static void last_ditch_exit(int sig)
{
- signal(SIGINT, SIG_DFL);
- signal(SIGTERM, SIG_DFL);
- signal(SIGHUP, SIG_DFL);
uml_cleanup();
exit(1);
}
+static void install_fatal_handler(int sig)
+{
+ struct sigaction action;
+
+ /* All signals are enabled in this handler ... */
+ sigemptyset(&action.sa_mask);
+
+ /* ... including the signal being handled, plus we want the
+ * handler reset to the default behavior, so that if an exit
+ * handler is hanging for some reason, the UML will just die
+ * after this signal is sent a second time.
+ */
+ action.sa_flags = SA_RESETHAND | SA_NODEFER;
+ action.sa_restorer = NULL;
+ action.sa_handler = last_ditch_exit;
+ if(sigaction(sig, &action, NULL) < 0){
+ printf("failed to install handler for signal %d - errno = %d\n",
+ errno);
+ exit(1);
+ }
+}
+
#define UML_LIB_PATH ":/usr/lib/uml"
static void setup_env_path(void)
@@ -158,9 +177,12 @@ int main(int argc, char **argv, char **envp)
}
new_argv[argc] = NULL;
- set_handler(SIGINT, last_ditch_exit, SA_ONESHOT | SA_NODEFER, -1);
- set_handler(SIGTERM, last_ditch_exit, SA_ONESHOT | SA_NODEFER, -1);
- set_handler(SIGHUP, last_ditch_exit, SA_ONESHOT | SA_NODEFER, -1);
+ /* Allow these signals to bring down a UML if all other
+ * methods of control fail.
+ */
+ install_fatal_handler(SIGINT);
+ install_fatal_handler(SIGTERM);
+ install_fatal_handler(SIGHUP);
scan_elf_aux( envp);
diff --git a/arch/um/os-Linux/mem.c b/arch/um/os-Linux/mem.c
index 560c8063c77..b170b4704dc 100644
--- a/arch/um/os-Linux/mem.c
+++ b/arch/um/os-Linux/mem.c
@@ -114,14 +114,14 @@ static void which_tmpdir(void)
}
while(1){
- found = next(fd, buf, sizeof(buf) / sizeof(buf[0]), ' ');
+ found = next(fd, buf, ARRAY_SIZE(buf), ' ');
if(found != 1)
break;
if(!strncmp(buf, "/dev/shm", strlen("/dev/shm")))
goto found;
- found = next(fd, buf, sizeof(buf) / sizeof(buf[0]), '\n');
+ found = next(fd, buf, ARRAY_SIZE(buf), '\n');
if(found != 1)
break;
}
@@ -135,7 +135,7 @@ err:
return;
found:
- found = next(fd, buf, sizeof(buf) / sizeof(buf[0]), ' ');
+ found = next(fd, buf, ARRAY_SIZE(buf), ' ');
if(found != 1)
goto err;
diff --git a/arch/um/os-Linux/process.c b/arch/um/os-Linux/process.c
index b98d3ca2cd1..ff203625a4b 100644
--- a/arch/um/os-Linux/process.c
+++ b/arch/um/os-Linux/process.c
@@ -7,7 +7,6 @@
#include <stdio.h>
#include <errno.h>
#include <signal.h>
-#include <setjmp.h>
#include <linux/unistd.h>
#include <sys/mman.h>
#include <sys/wait.h>
@@ -247,7 +246,17 @@ void init_new_thread_stack(void *sig_stack, void (*usr1_handler)(int))
set_sigstack(sig_stack, pages * page_size());
flags = SA_ONSTACK;
}
- if(usr1_handler) set_handler(SIGUSR1, usr1_handler, flags, -1);
+ if(usr1_handler){
+ struct sigaction sa;
+
+ sa.sa_handler = usr1_handler;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = flags;
+ sa.sa_restorer = NULL;
+ if(sigaction(SIGUSR1, &sa, NULL) < 0)
+ panic("init_new_thread_stack - sigaction failed - "
+ "errno = %d\n", errno);
+ }
}
void init_new_thread_signals(void)
diff --git a/arch/um/os-Linux/sigio.c b/arch/um/os-Linux/sigio.c
index 0ecac563c7b..f6457765b17 100644
--- a/arch/um/os-Linux/sigio.c
+++ b/arch/um/os-Linux/sigio.c
@@ -43,17 +43,9 @@ struct pollfds {
/* Protected by sigio_lock(). Used by the sigio thread, but the UML thread
* synchronizes with it.
*/
-static struct pollfds current_poll = {
- .poll = NULL,
- .size = 0,
- .used = 0
-};
-
-static struct pollfds next_poll = {
- .poll = NULL,
- .size = 0,
- .used = 0
-};
+static struct pollfds current_poll;
+static struct pollfds next_poll;
+static struct pollfds all_sigio_fds;
static int write_sigio_thread(void *unused)
{
@@ -78,7 +70,8 @@ static int write_sigio_thread(void *unused)
n = os_read_file(sigio_private[1], &c, sizeof(c));
if(n != sizeof(c))
printk("write_sigio_thread : "
- "read failed, err = %d\n", -n);
+ "read on socket failed, "
+ "err = %d\n", -n);
tmp = current_poll;
current_poll = next_poll;
next_poll = tmp;
@@ -93,35 +86,36 @@ static int write_sigio_thread(void *unused)
n = os_write_file(respond_fd, &c, sizeof(c));
if(n != sizeof(c))
- printk("write_sigio_thread : write failed, "
- "err = %d\n", -n);
+ printk("write_sigio_thread : write on socket "
+ "failed, err = %d\n", -n);
}
}
return 0;
}
-static int need_poll(int n)
+static int need_poll(struct pollfds *polls, int n)
{
- if(n <= next_poll.size){
- next_poll.used = n;
- return(0);
+ if(n <= polls->size){
+ polls->used = n;
+ return 0;
}
- kfree(next_poll.poll);
- next_poll.poll = um_kmalloc_atomic(n * sizeof(struct pollfd));
- if(next_poll.poll == NULL){
+ kfree(polls->poll);
+ polls->poll = um_kmalloc_atomic(n * sizeof(struct pollfd));
+ if(polls->poll == NULL){
printk("need_poll : failed to allocate new pollfds\n");
- next_poll.size = 0;
- next_poll.used = 0;
- return(-1);
+ polls->size = 0;
+ polls->used = 0;
+ return -ENOMEM;
}
- next_poll.size = n;
- next_poll.used = n;
- return(0);
+ polls->size = n;
+ polls->used = n;
+ return 0;
}
/* Must be called with sigio_lock held, because it's needed by the marked
- * critical section. */
+ * critical section.
+ */
static void update_thread(void)
{
unsigned long flags;
@@ -156,34 +150,39 @@ static void update_thread(void)
set_signals(flags);
}
-static int add_sigio_fd(int fd, int read)
+int add_sigio_fd(int fd)
{
- int err = 0, i, n, events;
+ struct pollfd *p;
+ int err = 0, i, n;
sigio_lock();
+ for(i = 0; i < all_sigio_fds.used; i++){
+ if(all_sigio_fds.poll[i].fd == fd)
+ break;
+ }
+ if(i == all_sigio_fds.used)
+ goto out;
+
+ p = &all_sigio_fds.poll[i];
+
for(i = 0; i < current_poll.used; i++){
if(current_poll.poll[i].fd == fd)
goto out;
}
n = current_poll.used + 1;
- err = need_poll(n);
+ err = need_poll(&next_poll, n);
if(err)
goto out;
for(i = 0; i < current_poll.used; i++)
next_poll.poll[i] = current_poll.poll[i];
- if(read) events = POLLIN;
- else events = POLLOUT;
-
- next_poll.poll[n - 1] = ((struct pollfd) { .fd = fd,
- .events = events,
- .revents = 0 });
+ next_poll.poll[n - 1] = *p;
update_thread();
out:
sigio_unlock();
- return(err);
+ return err;
}
int ignore_sigio_fd(int fd)
@@ -205,18 +204,14 @@ int ignore_sigio_fd(int fd)
if(i == current_poll.used)
goto out;
- err = need_poll(current_poll.used - 1);
+ err = need_poll(&next_poll, current_poll.used - 1);
if(err)
goto out;
for(i = 0; i < current_poll.used; i++){
p = &current_poll.poll[i];
- if(p->fd != fd) next_poll.poll[n++] = current_poll.poll[i];
- }
- if(n == i){
- printk("ignore_sigio_fd : fd %d not found\n", fd);
- err = -1;
- goto out;
+ if(p->fd != fd)
+ next_poll.poll[n++] = *p;
}
update_thread();
@@ -234,7 +229,7 @@ static struct pollfd *setup_initial_poll(int fd)
printk("setup_initial_poll : failed to allocate poll\n");
return NULL;
}
- *p = ((struct pollfd) { .fd = fd,
+ *p = ((struct pollfd) { .fd = fd,
.events = POLLIN,
.revents = 0 });
return p;
@@ -323,6 +318,8 @@ out_close1:
void maybe_sigio_broken(int fd, int read)
{
+ int err;
+
if(!isatty(fd))
return;
@@ -330,7 +327,19 @@ void maybe_sigio_broken(int fd, int read)
return;
write_sigio_workaround();
- add_sigio_fd(fd, read);
+
+ sigio_lock();
+ err = need_poll(&all_sigio_fds, all_sigio_fds.used + 1);
+ if(err){
+ printk("maybe_sigio_broken - failed to add pollfd\n");
+ goto out;
+ }
+ all_sigio_fds.poll[all_sigio_fds.used++] =
+ ((struct pollfd) { .fd = fd,
+ .events = read ? POLLIN : POLLOUT,
+ .revents = 0 });
+out:
+ sigio_unlock();
}
static void sigio_cleanup(void)
diff --git a/arch/um/os-Linux/signal.c b/arch/um/os-Linux/signal.c
index 60e4faedf25..6b81739279d 100644
--- a/arch/um/os-Linux/signal.c
+++ b/arch/um/os-Linux/signal.c
@@ -15,7 +15,6 @@
#include "user.h"
#include "signal_kern.h"
#include "sysdep/sigcontext.h"
-#include "sysdep/signal.h"
#include "sigcontext.h"
#include "mode.h"
#include "os.h"
@@ -38,18 +37,10 @@
static int signals_enabled = 1;
static int pending = 0;
-void sig_handler(ARCH_SIGHDLR_PARAM)
+void sig_handler(int sig, struct sigcontext *sc)
{
- struct sigcontext *sc;
int enabled;
- /* Must be the first thing that this handler does - x86_64 stores
- * the sigcontext in %rdx, and we need to save it before it has a
- * chance to get trashed.
- */
-
- ARCH_GET_SIGCONTEXT(sc, sig);
-
enabled = signals_enabled;
if(!enabled && (sig == SIGIO)){
pending |= SIGIO_MASK;
@@ -64,15 +55,8 @@ void sig_handler(ARCH_SIGHDLR_PARAM)
set_signals(enabled);
}
-extern int timer_irq_inited;
-
static void real_alarm_handler(int sig, struct sigcontext *sc)
{
- if(!timer_irq_inited){
- signals_enabled = 1;
- return;
- }
-
if(sig == SIGALRM)
switch_timers(0);
@@ -84,13 +68,10 @@ static void real_alarm_handler(int sig, struct sigcontext *sc)
}
-void alarm_handler(ARCH_SIGHDLR_PARAM)
+void alarm_handler(int sig, struct sigcontext *sc)
{
- struct sigcontext *sc;
int enabled;
- ARCH_GET_SIGCONTEXT(sc, sig);
-
enabled = signals_enabled;
if(!signals_enabled){
if(sig == SIGVTALRM)
@@ -126,6 +107,10 @@ void remove_sigstack(void)
panic("disabling signal stack failed, errno = %d\n", errno);
}
+void (*handlers[_NSIG])(int sig, struct sigcontext *sc);
+
+extern void hard_handler(int sig);
+
void set_handler(int sig, void (*handler)(int), int flags, ...)
{
struct sigaction action;
@@ -133,13 +118,16 @@ void set_handler(int sig, void (*handler)(int), int flags, ...)
sigset_t sig_mask;
int mask;
- va_start(ap, flags);
- action.sa_handler = handler;
+ handlers[sig] = (void (*)(int, struct sigcontext *)) handler;
+ action.sa_handler = hard_handler;
+
sigemptyset(&action.sa_mask);
- while((mask = va_arg(ap, int)) != -1){
+
+ va_start(ap, flags);
+ while((mask = va_arg(ap, int)) != -1)
sigaddset(&action.sa_mask, mask);
- }
va_end(ap);
+
action.sa_flags = flags;
action.sa_restorer = NULL;
if(sigaction(sig, &action, NULL) < 0)
diff --git a/arch/um/os-Linux/skas/process.c b/arch/um/os-Linux/skas/process.c
index 7baf90fda58..42e3d1ed802 100644
--- a/arch/um/os-Linux/skas/process.c
+++ b/arch/um/os-Linux/skas/process.c
@@ -8,7 +8,6 @@
#include <unistd.h>
#include <errno.h>
#include <signal.h>
-#include <setjmp.h>
#include <sched.h>
#include "ptrace_user.h"
#include <sys/wait.h>
@@ -156,11 +155,15 @@ extern int __syscall_stub_start;
static int userspace_tramp(void *stack)
{
void *addr;
+ int err;
ptrace(PTRACE_TRACEME, 0, 0, 0);
init_new_thread_signals();
- enable_timer();
+ err = set_interval(1);
+ if(err)
+ panic("userspace_tramp - setting timer failed, errno = %d\n",
+ err);
if(!proc_mm){
/* This has a pte, but it can't be mapped in with the usual
@@ -190,14 +193,25 @@ static int userspace_tramp(void *stack)
}
}
if(!ptrace_faultinfo && (stack != NULL)){
+ struct sigaction sa;
+
unsigned long v = UML_CONFIG_STUB_CODE +
(unsigned long) stub_segv_handler -
(unsigned long) &__syscall_stub_start;
set_sigstack((void *) UML_CONFIG_STUB_DATA, page_size());
- set_handler(SIGSEGV, (void *) v, SA_ONSTACK,
- SIGIO, SIGWINCH, SIGALRM, SIGVTALRM,
- SIGUSR1, -1);
+ sigemptyset(&sa.sa_mask);
+ sigaddset(&sa.sa_mask, SIGIO);
+ sigaddset(&sa.sa_mask, SIGWINCH);
+ sigaddset(&sa.sa_mask, SIGALRM);
+ sigaddset(&sa.sa_mask, SIGVTALRM);
+ sigaddset(&sa.sa_mask, SIGUSR1);
+ sa.sa_flags = SA_ONSTACK;
+ sa.sa_handler = (void *) v;
+ sa.sa_restorer = NULL;
+ if(sigaction(SIGSEGV, &sa, NULL) < 0)
+ panic("userspace_tramp - setting SIGSEGV handler "
+ "failed - errno = %d\n", errno);
}
os_stop_process(os_getpid());
@@ -470,7 +484,7 @@ void thread_wait(void *sw, void *fb)
*switch_buf = &buf;
fork_buf = fb;
if(UML_SETJMP(&buf) == 0)
- siglongjmp(*fork_buf, INIT_JMP_REMOVE_SIGSTACK);
+ UML_LONGJMP(fork_buf, INIT_JMP_REMOVE_SIGSTACK);
}
void switch_threads(void *me, void *next)
diff --git a/arch/um/os-Linux/start_up.c b/arch/um/os-Linux/start_up.c
index 50314850400..7fe92680c7d 100644
--- a/arch/um/os-Linux/start_up.c
+++ b/arch/um/os-Linux/start_up.c
@@ -14,7 +14,6 @@
#include <sched.h>
#include <fcntl.h>
#include <errno.h>
-#include <setjmp.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <sys/mman.h>
diff --git a/arch/um/os-Linux/sys-i386/Makefile b/arch/um/os-Linux/sys-i386/Makefile
index b3213613c41..37806621b25 100644
--- a/arch/um/os-Linux/sys-i386/Makefile
+++ b/arch/um/os-Linux/sys-i386/Makefile
@@ -3,7 +3,7 @@
# Licensed under the GPL
#
-obj-$(CONFIG_MODE_SKAS) = registers.o tls.o
+obj-$(CONFIG_MODE_SKAS) = registers.o signal.o tls.o
USER_OBJS := $(obj-y)
diff --git a/arch/um/os-Linux/sys-i386/registers.c b/arch/um/os-Linux/sys-i386/registers.c
index 516f66dd87e..7cd0369e02b 100644
--- a/arch/um/os-Linux/sys-i386/registers.c
+++ b/arch/um/os-Linux/sys-i386/registers.c
@@ -5,12 +5,12 @@
#include <errno.h>
#include <string.h>
-#include <setjmp.h>
#include "sysdep/ptrace_user.h"
#include "sysdep/ptrace.h"
#include "uml-config.h"
#include "skas_ptregs.h"
#include "registers.h"
+#include "longjmp.h"
#include "user.h"
/* These are set once at boot time and not changed thereafter */
@@ -130,11 +130,14 @@ void get_safe_registers(unsigned long *regs, unsigned long *fp_regs)
HOST_FP_SIZE * sizeof(unsigned long));
}
-void get_thread_regs(union uml_pt_regs *uml_regs, void *buffer)
+unsigned long get_thread_reg(int reg, jmp_buf *buf)
{
- struct __jmp_buf_tag *jmpbuf = buffer;
-
- UPT_SET(uml_regs, EIP, jmpbuf->__jmpbuf[JB_PC]);
- UPT_SET(uml_regs, UESP, jmpbuf->__jmpbuf[JB_SP]);
- UPT_SET(uml_regs, EBP, jmpbuf->__jmpbuf[JB_BP]);
+ switch(reg){
+ case EIP: return buf[0]->__eip;
+ case UESP: return buf[0]->__esp;
+ case EBP: return buf[0]->__ebp;
+ default:
+ printk("get_thread_regs - unknown register %d\n", reg);
+ return 0;
+ }
}
diff --git a/arch/um/os-Linux/sys-i386/signal.c b/arch/um/os-Linux/sys-i386/signal.c
new file mode 100644
index 00000000000..0d3eae51835
--- /dev/null
+++ b/arch/um/os-Linux/sys-i386/signal.c
@@ -0,0 +1,15 @@
+/*
+ * Copyright (C) 2006 Jeff Dike (jdike@addtoit.com)
+ * Licensed under the GPL
+ */
+
+#include <signal.h>
+
+extern void (*handlers[])(int sig, struct sigcontext *sc);
+
+void hard_handler(int sig)
+{
+ struct sigcontext *sc = (struct sigcontext *) (&sig + 1);
+
+ (*handlers[sig])(sig, sc);
+}
diff --git a/arch/um/os-Linux/sys-x86_64/Makefile b/arch/um/os-Linux/sys-x86_64/Makefile
index 340ef26f594..f67842a7735 100644
--- a/arch/um/os-Linux/sys-x86_64/Makefile
+++ b/arch/um/os-Linux/sys-x86_64/Makefile
@@ -3,7 +3,7 @@
# Licensed under the GPL
#
-obj-$(CONFIG_MODE_SKAS) = registers.o
+obj-$(CONFIG_MODE_SKAS) = registers.o signal.o
USER_OBJS := $(obj-y)
diff --git a/arch/um/os-Linux/sys-x86_64/registers.c b/arch/um/os-Linux/sys-x86_64/registers.c
index becd898d939..cb8e8a26328 100644
--- a/arch/um/os-Linux/sys-x86_64/registers.c
+++ b/arch/um/os-Linux/sys-x86_64/registers.c
@@ -5,11 +5,11 @@
#include <errno.h>
#include <string.h>
-#include <setjmp.h>
#include "ptrace_user.h"
#include "uml-config.h"
#include "skas_ptregs.h"
#include "registers.h"
+#include "longjmp.h"
#include "user.h"
/* These are set once at boot time and not changed thereafter */
@@ -78,11 +78,14 @@ void get_safe_registers(unsigned long *regs, unsigned long *fp_regs)
HOST_FP_SIZE * sizeof(unsigned long));
}
-void get_thread_regs(union uml_pt_regs *uml_regs, void *buffer)
+unsigned long get_thread_reg(int reg, jmp_buf *buf)
{
- struct __jmp_buf_tag *jmpbuf = buffer;
-
- UPT_SET(uml_regs, RIP, jmpbuf->__jmpbuf[JB_PC]);
- UPT_SET(uml_regs, RSP, jmpbuf->__jmpbuf[JB_RSP]);
- UPT_SET(uml_regs, RBP, jmpbuf->__jmpbuf[JB_RBP]);
+ switch(reg){
+ case RIP: return buf[0]->__rip;
+ case RSP: return buf[0]->__rsp;
+ case RBP: return buf[0]->__rbp;
+ default:
+ printk("get_thread_regs - unknown register %d\n", reg);
+ return 0;
+ }
}
diff --git a/arch/um/os-Linux/sys-x86_64/signal.c b/arch/um/os-Linux/sys-x86_64/signal.c
new file mode 100644
index 00000000000..3f369e5f976
--- /dev/null
+++ b/arch/um/os-Linux/sys-x86_64/signal.c
@@ -0,0 +1,16 @@
+/*
+ * Copyright (C) 2006 Jeff Dike (jdike@addtoit.com)
+ * Licensed under the GPL
+ */
+
+#include <signal.h>
+
+extern void (*handlers[])(int sig, struct sigcontext *sc);
+
+void hard_handler(int sig)
+{
+ struct ucontext *uc;
+ asm("movq %%rdx, %0" : "=r" (uc));
+
+ (*handlers[sig])(sig, (struct sigcontext *) &uc->uc_mcontext);
+}
diff --git a/arch/um/os-Linux/time.c b/arch/um/os-Linux/time.c
index 4ae73c0e548..38be096e750 100644
--- a/arch/um/os-Linux/time.c
+++ b/arch/um/os-Linux/time.c
@@ -17,20 +17,25 @@
#include "kern_constants.h"
#include "os.h"
-static void set_interval(int timer_type)
+int set_interval(int is_virtual)
{
int usec = 1000000/hz();
+ int timer_type = is_virtual ? ITIMER_VIRTUAL : ITIMER_REAL;
struct itimerval interval = ((struct itimerval) { { 0, usec },
{ 0, usec } });
if(setitimer(timer_type, &interval, NULL) == -1)
- panic("setitimer failed - errno = %d\n", errno);
+ return -errno;
+
+ return 0;
}
+#ifdef CONFIG_MODE_TT
void enable_timer(void)
{
- set_interval(ITIMER_VIRTUAL);
+ set_interval(1);
}
+#endif
void disable_timer(void)
{
@@ -40,8 +45,8 @@ void disable_timer(void)
printk("disnable_timer - setitimer failed, errno = %d\n",
errno);
/* If there are signals already queued, after unblocking ignore them */
- set_handler(SIGALRM, SIG_IGN, 0, -1);
- set_handler(SIGVTALRM, SIG_IGN, 0, -1);
+ signal(SIGALRM, SIG_IGN);
+ signal(SIGVTALRM, SIG_IGN);
}
void switch_timers(int to_real)
@@ -74,7 +79,7 @@ void uml_idle_timer(void)
set_handler(SIGALRM, (__sighandler_t) alarm_handler,
SA_RESTART, SIGUSR1, SIGIO, SIGWINCH, SIGVTALRM, -1);
- set_interval(ITIMER_REAL);
+ set_interval(0);
}
#endif
@@ -94,8 +99,3 @@ void idle_sleep(int secs)
ts.tv_nsec = 0;
nanosleep(&ts, NULL);
}
-
-void user_time_init(void)
-{
- set_interval(ITIMER_VIRTUAL);
-}
diff --git a/arch/um/os-Linux/trap.c b/arch/um/os-Linux/trap.c
index 90b29ae9af4..1df231a2624 100644
--- a/arch/um/os-Linux/trap.c
+++ b/arch/um/os-Linux/trap.c
@@ -5,7 +5,6 @@
#include <stdlib.h>
#include <signal.h>
-#include <setjmp.h>
#include "kern_util.h"
#include "user_util.h"
#include "os.h"
diff --git a/arch/um/os-Linux/uaccess.c b/arch/um/os-Linux/uaccess.c
index 865f6a6a259..bbb73a65037 100644
--- a/arch/um/os-Linux/uaccess.c
+++ b/arch/um/os-Linux/uaccess.c
@@ -4,8 +4,7 @@
* Licensed under the GPL
*/
-#include <setjmp.h>
-#include <string.h>
+#include <stddef.h>
#include "longjmp.h"
unsigned long __do_user_copy(void *to, const void *from, int n,
diff --git a/arch/um/os-Linux/util.c b/arch/um/os-Linux/util.c
index c47a2a7ce70..3f5b1514e8a 100644
--- a/arch/um/os-Linux/util.c
+++ b/arch/um/os-Linux/util.c
@@ -7,7 +7,6 @@
#include <stdlib.h>
#include <unistd.h>
#include <limits.h>
-#include <setjmp.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/utsname.h>
@@ -107,11 +106,11 @@ int setjmp_wrapper(void (*proc)(void *, void *), ...)
jmp_buf buf;
int n;
- n = sigsetjmp(buf, 1);
+ n = UML_SETJMP(&buf);
if(n == 0){
va_start(args, proc);
(*proc)(&buf, &args);
}
va_end(args);
- return(n);
+ return n;
}
diff --git a/arch/um/sys-i386/Makefile b/arch/um/sys-i386/Makefile
index 374d61a1943..59cc7027575 100644
--- a/arch/um/sys-i386/Makefile
+++ b/arch/um/sys-i386/Makefile
@@ -1,5 +1,5 @@
obj-y = bugs.o checksum.o delay.o fault.o ksyms.o ldt.o ptrace.o \
- ptrace_user.o signal.o sigcontext.o syscalls.o sysrq.o \
+ ptrace_user.o setjmp.o signal.o sigcontext.o syscalls.o sysrq.o \
sys_call_table.o tls.o
obj-$(CONFIG_MODE_SKAS) += stub.o stub_segv.o
diff --git a/arch/um/sys-i386/bugs.c b/arch/um/sys-i386/bugs.c
index 41b0ab2fe83..f1bcd399ac9 100644
--- a/arch/um/sys-i386/bugs.c
+++ b/arch/um/sys-i386/bugs.c
@@ -13,6 +13,7 @@
#include "sysdep/ptrace.h"
#include "task.h"
#include "os.h"
+#include "user_util.h"
#define MAXTOKEN 64
@@ -104,17 +105,17 @@ int cpu_feature(char *what, char *buf, int len)
static int check_cpu_flag(char *feature, int *have_it)
{
char buf[MAXTOKEN], c;
- int fd, len = sizeof(buf)/sizeof(buf[0]);
+ int fd, len = ARRAY_SIZE(buf);
printk("Checking for host processor %s support...", feature);
fd = os_open_file("/proc/cpuinfo", of_read(OPENFLAGS()), 0);
if(fd < 0){
printk("Couldn't open /proc/cpuinfo, err = %d\n", -fd);
- return(0);
+ return 0;
}
*have_it = 0;
- if(!find_cpuinfo_line(fd, "flags", buf, sizeof(buf) / sizeof(buf[0])))
+ if(!find_cpuinfo_line(fd, "flags", buf, ARRAY_SIZE(buf)))
goto out;
c = token(fd, buf, len - 1, ' ');
@@ -138,7 +139,7 @@ static int check_cpu_flag(char *feature, int *have_it)
if(*have_it == 0) printk("No\n");
else if(*have_it == 1) printk("Yes\n");
os_close_file(fd);
- return(1);
+ return 1;
}
#if 0 /* This doesn't work in tt mode, plus it's causing compilation problems
diff --git a/arch/um/sys-i386/ldt.c b/arch/um/sys-i386/ldt.c
index fe0877b3509..69971b78bea 100644
--- a/arch/um/sys-i386/ldt.c
+++ b/arch/um/sys-i386/ldt.c
@@ -424,9 +424,8 @@ void ldt_get_host_info(void)
size++;
}
- if(size < sizeof(dummy_list)/sizeof(dummy_list[0])) {
+ if(size < ARRAY_SIZE(dummy_list))
host_ldt_entries = dummy_list;
- }
else {
size = (size + 1) * sizeof(dummy_list[0]);
host_ldt_entries = (short *)kmalloc(size, GFP_KERNEL);
diff --git a/arch/um/sys-i386/ptrace_user.c b/arch/um/sys-i386/ptrace_user.c
index 40aa8853144..5f3cc668582 100644
--- a/arch/um/sys-i386/ptrace_user.c
+++ b/arch/um/sys-i386/ptrace_user.c
@@ -15,6 +15,7 @@
#include "user.h"
#include "os.h"
#include "uml-config.h"
+#include "user_util.h"
int ptrace_getregs(long pid, unsigned long *regs_out)
{
@@ -51,7 +52,7 @@ static void write_debugregs(int pid, unsigned long *regs)
int nregs, i;
dummy = NULL;
- nregs = sizeof(dummy->u_debugreg)/sizeof(dummy->u_debugreg[0]);
+ nregs = ARRAY_SIZE(dummy->u_debugreg);
for(i = 0; i < nregs; i++){
if((i == 4) || (i == 5)) continue;
if(ptrace(PTRACE_POKEUSR, pid, &dummy->u_debugreg[i],
@@ -68,7 +69,7 @@ static void read_debugregs(int pid, unsigned long *regs)
int nregs, i;
dummy = NULL;
- nregs = sizeof(dummy->u_debugreg)/sizeof(dummy->u_debugreg[0]);
+ nregs = ARRAY_SIZE(dummy->u_debugreg);
for(i = 0; i < nregs; i++){
regs[i] = ptrace(PTRACE_PEEKUSR, pid,
&dummy->u_debugreg[i], 0);
diff --git a/arch/um/sys-i386/setjmp.S b/arch/um/sys-i386/setjmp.S
new file mode 100644
index 00000000000..b766792c993
--- /dev/null
+++ b/arch/um/sys-i386/setjmp.S
@@ -0,0 +1,58 @@
+#
+# arch/i386/setjmp.S
+#
+# setjmp/longjmp for the i386 architecture
+#
+
+#
+# The jmp_buf is assumed to contain the following, in order:
+# %ebx
+# %esp
+# %ebp
+# %esi
+# %edi
+# <return address>
+#
+
+ .text
+ .align 4
+ .globl setjmp
+ .type setjmp, @function
+setjmp:
+#ifdef _REGPARM
+ movl %eax,%edx
+#else
+ movl 4(%esp),%edx
+#endif
+ popl %ecx # Return address, and adjust the stack
+ xorl %eax,%eax # Return value
+ movl %ebx,(%edx)
+ movl %esp,4(%edx) # Post-return %esp!
+ pushl %ecx # Make the call/return stack happy
+ movl %ebp,8(%edx)
+ movl %esi,12(%edx)
+ movl %edi,16(%edx)
+ movl %ecx,20(%edx) # Return address
+ ret
+
+ .size setjmp,.-setjmp
+
+ .text
+ .align 4
+ .globl longjmp
+ .type longjmp, @function
+longjmp:
+#ifdef _REGPARM
+ xchgl %eax,%edx
+#else
+ movl 4(%esp),%edx # jmp_ptr address
+ movl 8(%esp),%eax # Return value
+#endif
+ movl (%edx),%ebx
+ movl 4(%edx),%esp
+ movl 8(%edx),%ebp
+ movl 12(%edx),%esi
+ movl 16(%edx),%edi
+ jmp *20(%edx)
+
+ .size longjmp,.-longjmp
diff --git a/arch/um/sys-x86_64/Makefile b/arch/um/sys-x86_64/Makefile
index c19794d435d..f41768b8e25 100644
--- a/arch/um/sys-x86_64/Makefile
+++ b/arch/um/sys-x86_64/Makefile
@@ -5,8 +5,8 @@
#
obj-y = bugs.o delay.o fault.o ldt.o mem.o ptrace.o ptrace_user.o \
- sigcontext.o signal.o syscalls.o syscall_table.o sysrq.o ksyms.o \
- tls.o
+ setjmp.o sigcontext.o signal.o syscalls.o syscall_table.o sysrq.o \
+ ksyms.o tls.o
obj-$(CONFIG_MODE_SKAS) += stub.o stub_segv.o
obj-$(CONFIG_MODULES) += um_module.o
diff --git a/arch/um/sys-x86_64/setjmp.S b/arch/um/sys-x86_64/setjmp.S
new file mode 100644
index 00000000000..45f547b4043
--- /dev/null
+++ b/arch/um/sys-x86_64/setjmp.S
@@ -0,0 +1,54 @@
+#
+# arch/x86_64/setjmp.S
+#
+# setjmp/longjmp for the x86-64 architecture
+#
+
+#
+# The jmp_buf is assumed to contain the following, in order:
+# %rbx
+# %rsp (post-return)
+# %rbp
+# %r12
+# %r13
+# %r14
+# %r15
+# <return address>
+#
+
+ .text
+ .align 4
+ .globl setjmp
+ .type setjmp, @function
+setjmp:
+ pop %rsi # Return address, and adjust the stack
+ xorl %eax,%eax # Return value
+ movq %rbx,(%rdi)
+ movq %rsp,8(%rdi) # Post-return %rsp!
+ push %rsi # Make the call/return stack happy
+ movq %rbp,16(%rdi)
+ movq %r12,24(%rdi)
+ movq %r13,32(%rdi)
+ movq %r14,40(%rdi)
+ movq %r15,48(%rdi)
+ movq %rsi,56(%rdi) # Return address
+ ret
+
+ .size setjmp,.-setjmp
+
+ .text
+ .align 4
+ .globl longjmp
+ .type longjmp, @function
+longjmp:
+ movl %esi,%eax # Return value (int)
+ movq (%rdi),%rbx
+ movq 8(%rdi),%rsp
+ movq 16(%rdi),%rbp
+ movq 24(%rdi),%r12
+ movq 32(%rdi),%r13
+ movq 40(%rdi),%r14
+ movq 48(%rdi),%r15
+ jmp *56(%rdi)
+
+ .size longjmp,.-longjmp
diff --git a/arch/x86_64/Kconfig b/arch/x86_64/Kconfig
index 6cd4878625f..581ce9af0ec 100644
--- a/arch/x86_64/Kconfig
+++ b/arch/x86_64/Kconfig
@@ -24,6 +24,10 @@ config X86
bool
default y
+config ZONE_DMA32
+ bool
+ default y
+
config LOCKDEP_SUPPORT
bool
default y
diff --git a/arch/x86_64/kernel/e820.c b/arch/x86_64/kernel/e820.c
index d6d7f731f6f..708a3cd9a27 100644
--- a/arch/x86_64/kernel/e820.c
+++ b/arch/x86_64/kernel/e820.c
@@ -16,6 +16,7 @@
#include <linux/string.h>
#include <linux/kexec.h>
#include <linux/module.h>
+#include <linux/mm.h>
#include <asm/pgtable.h>
#include <asm/page.h>
@@ -297,6 +298,53 @@ void __init e820_reserve_resources(void)
}
}
+/* Mark pages corresponding to given address range as nosave */
+static void __init
+e820_mark_nosave_range(unsigned long start, unsigned long end)
+{
+ unsigned long pfn, max_pfn;
+
+ if (start >= end)
+ return;
+
+ printk("Nosave address range: %016lx - %016lx\n", start, end);
+ max_pfn = end >> PAGE_SHIFT;
+ for (pfn = start >> PAGE_SHIFT; pfn < max_pfn; pfn++)
+ if (pfn_valid(pfn))
+ SetPageNosave(pfn_to_page(pfn));
+}
+
+/*
+ * Find the ranges of physical addresses that do not correspond to
+ * e820 RAM areas and mark the corresponding pages as nosave for software
+ * suspend and suspend to RAM.
+ *
+ * This function requires the e820 map to be sorted and without any
+ * overlapping entries and assumes the first e820 area to be RAM.
+ */
+void __init e820_mark_nosave_regions(void)
+{
+ int i;
+ unsigned long paddr;
+
+ paddr = round_down(e820.map[0].addr + e820.map[0].size, PAGE_SIZE);
+ for (i = 1; i < e820.nr_map; i++) {
+ struct e820entry *ei = &e820.map[i];
+
+ if (paddr < ei->addr)
+ e820_mark_nosave_range(paddr,
+ round_up(ei->addr, PAGE_SIZE));
+
+ paddr = round_down(ei->addr + ei->size, PAGE_SIZE);
+ if (ei->type != E820_RAM)
+ e820_mark_nosave_range(round_up(ei->addr, PAGE_SIZE),
+ paddr);
+
+ if (paddr >= (end_pfn << PAGE_SHIFT))
+ break;
+ }
+}
+
/*
* Add a memory region to the kernel e820 map.
*/
diff --git a/arch/x86_64/kernel/setup.c b/arch/x86_64/kernel/setup.c
index 34afad70482..4b39f0da17f 100644
--- a/arch/x86_64/kernel/setup.c
+++ b/arch/x86_64/kernel/setup.c
@@ -689,6 +689,7 @@ void __init setup_arch(char **cmdline_p)
*/
probe_roms();
e820_reserve_resources();
+ e820_mark_nosave_regions();
request_resource(&iomem_resource, &video_ram_resource);
diff --git a/arch/x86_64/kernel/smpboot.c b/arch/x86_64/kernel/smpboot.c
index 975380207b4..3ae9ffddddc 100644
--- a/arch/x86_64/kernel/smpboot.c
+++ b/arch/x86_64/kernel/smpboot.c
@@ -46,9 +46,10 @@
#include <linux/bootmem.h>
#include <linux/thread_info.h>
#include <linux/module.h>
-
#include <linux/delay.h>
#include <linux/mc146818rtc.h>
+#include <linux/smp.h>
+
#include <asm/mtrr.h>
#include <asm/pgalloc.h>
#include <asm/desc.h>
diff --git a/arch/x86_64/kernel/suspend_asm.S b/arch/x86_64/kernel/suspend_asm.S
index 320b6fb00cc..bfbe00763c6 100644
--- a/arch/x86_64/kernel/suspend_asm.S
+++ b/arch/x86_64/kernel/suspend_asm.S
@@ -54,7 +54,7 @@ ENTRY(restore_image)
movq %rcx, %cr3;
movq %rax, %cr4; # turn PGE back on
- movq pagedir_nosave(%rip), %rdx
+ movq restore_pblist(%rip), %rdx
loop:
testq %rdx, %rdx
jz done
diff --git a/arch/x86_64/kernel/time.c b/arch/x86_64/kernel/time.c
index 7a9b1822418..7700e6cd2bd 100644
--- a/arch/x86_64/kernel/time.c
+++ b/arch/x86_64/kernel/time.c
@@ -1148,23 +1148,25 @@ int hpet_rtc_timer_init(void)
hpet_rtc_int_freq = DEFAULT_RTC_INT_FREQ;
local_irq_save(flags);
+
cnt = hpet_readl(HPET_COUNTER);
cnt += ((hpet_tick*HZ)/hpet_rtc_int_freq);
hpet_writel(cnt, HPET_T1_CMP);
hpet_t1_cmp = cnt;
- local_irq_restore(flags);
cfg = hpet_readl(HPET_T1_CFG);
cfg &= ~HPET_TN_PERIODIC;
cfg |= HPET_TN_ENABLE | HPET_TN_32BIT;
hpet_writel(cfg, HPET_T1_CFG);
+ local_irq_restore(flags);
+
return 1;
}
static void hpet_rtc_timer_reinit(void)
{
- unsigned int cfg, cnt;
+ unsigned int cfg, cnt, ticks_per_int, lost_ints;
if (unlikely(!(PIE_on | AIE_on | UIE_on))) {
cfg = hpet_readl(HPET_T1_CFG);
@@ -1179,10 +1181,33 @@ static void hpet_rtc_timer_reinit(void)
hpet_rtc_int_freq = DEFAULT_RTC_INT_FREQ;
/* It is more accurate to use the comparator value than current count.*/
- cnt = hpet_t1_cmp;
- cnt += hpet_tick*HZ/hpet_rtc_int_freq;
- hpet_writel(cnt, HPET_T1_CMP);
- hpet_t1_cmp = cnt;
+ ticks_per_int = hpet_tick * HZ / hpet_rtc_int_freq;
+ hpet_t1_cmp += ticks_per_int;
+ hpet_writel(hpet_t1_cmp, HPET_T1_CMP);
+
+ /*
+ * If the interrupt handler was delayed too long, the write above tries
+ * to schedule the next interrupt in the past and the hardware would
+ * not interrupt until the counter had wrapped around.
+ * So we have to check that the comparator wasn't set to a past time.
+ */
+ cnt = hpet_readl(HPET_COUNTER);
+ if (unlikely((int)(cnt - hpet_t1_cmp) > 0)) {
+ lost_ints = (cnt - hpet_t1_cmp) / ticks_per_int + 1;
+ /* Make sure that, even with the time needed to execute
+ * this code, the next scheduled interrupt has been moved
+ * back to the future: */
+ lost_ints++;
+
+ hpet_t1_cmp += lost_ints * ticks_per_int;
+ hpet_writel(hpet_t1_cmp, HPET_T1_CMP);
+
+ if (PIE_on)
+ PIE_count += lost_ints;
+
+ printk(KERN_WARNING "rtc: lost some interrupts at %ldHz.\n",
+ hpet_rtc_int_freq);
+ }
}
/*
diff --git a/arch/x86_64/mm/fault.c b/arch/x86_64/mm/fault.c
index ac8ea66ccb9..4198798e146 100644
--- a/arch/x86_64/mm/fault.c
+++ b/arch/x86_64/mm/fault.c
@@ -299,7 +299,7 @@ static int vmalloc_fault(unsigned long address)
if (pgd_none(*pgd))
set_pgd(pgd, *pgd_ref);
else
- BUG_ON(pgd_page(*pgd) != pgd_page(*pgd_ref));
+ BUG_ON(pgd_page_vaddr(*pgd) != pgd_page_vaddr(*pgd_ref));
/* Below here mismatches are bugs because these lower tables
are shared */
@@ -308,7 +308,7 @@ static int vmalloc_fault(unsigned long address)
pud_ref = pud_offset(pgd_ref, address);
if (pud_none(*pud_ref))
return -1;
- if (pud_none(*pud) || pud_page(*pud) != pud_page(*pud_ref))
+ if (pud_none(*pud) || pud_page_vaddr(*pud) != pud_page_vaddr(*pud_ref))
BUG();
pmd = pmd_offset(pud, address);
pmd_ref = pmd_offset(pud_ref, address);
@@ -641,7 +641,7 @@ void vmalloc_sync_all(void)
if (pgd_none(*pgd))
set_pgd(pgd, *pgd_ref);
else
- BUG_ON(pgd_page(*pgd) != pgd_page(*pgd_ref));
+ BUG_ON(pgd_page_vaddr(*pgd) != pgd_page_vaddr(*pgd_ref));
}
spin_unlock(&pgd_lock);
set_bit(pgd_index(address), insync);
diff --git a/arch/x86_64/mm/init.c b/arch/x86_64/mm/init.c
index d14fb2dfbfc..52fd42c40c8 100644
--- a/arch/x86_64/mm/init.c
+++ b/arch/x86_64/mm/init.c
@@ -536,7 +536,7 @@ int memory_add_physaddr_to_nid(u64 start)
int arch_add_memory(int nid, u64 start, u64 size)
{
struct pglist_data *pgdat = NODE_DATA(nid);
- struct zone *zone = pgdat->node_zones + MAX_NR_ZONES-2;
+ struct zone *zone = pgdat->node_zones + ZONE_NORMAL;
unsigned long start_pfn = start >> PAGE_SHIFT;
unsigned long nr_pages = size >> PAGE_SHIFT;
int ret;