[PATCH] avr32 architecture

This adds support for the Atmel AVR32 architecture as well as the AT32AP7000
CPU and the AT32STK1000 development board.

AVR32 is a new high-performance 32-bit RISC microprocessor core, designed for
cost-sensitive embedded applications, with particular emphasis on low power
consumption and high code density.  The AVR32 architecture is not binary
compatible with earlier 8-bit AVR architectures.

The AVR32 architecture, including the instruction set, is described by the
AVR32 Architecture Manual, available from

http://www.atmel.com/dyn/resources/prod_documents/doc32000.pdf

The Atmel AT32AP7000 is the first CPU implementing the AVR32 architecture.  It
features a 7-stage pipeline, 16KB instruction and data caches and a full
Memory Management Unit.  It also comes with a large set of integrated
peripherals, many of which are shared with the AT91 ARM-based controllers from
Atmel.

Full data sheet is available from

http://www.atmel.com/dyn/resources/prod_documents/doc32003.pdf

while the CPU core implementation including caches and MMU is documented by
the AVR32 AP Technical Reference, available from

http://www.atmel.com/dyn/resources/prod_documents/doc32001.pdf

Information about the AT32STK1000 development board can be found at

http://www.atmel.com/dyn/products/tools_card.asp?tool_id=3918

including a BSP CD image with an earlier version of this patch, development
tools (binaries and source/patches) and a root filesystem image suitable for
booting from SD card.

Alternatively, there's a preliminary "getting started" guide available at
http://avr32linux.org/twiki/bin/view/Main/GettingStarted which provides links
to the sources and patches you will need in order to set up a cross-compiling
environment for avr32-linux.

This patch, as well as the other patches included with the BSP and the
toolchain patches, is actively supported by Atmel Corporation.

[dmccr@us.ibm.com: Fix more pxx_page macro locations]
[bunk@stusta.de: fix `make defconfig']
Signed-off-by: Haavard Skinnemoen <hskinnemoen@atmel.com>
Signed-off-by: Adrian Bunk <bunk@stusta.de>
Signed-off-by: Dave McCracken <dmccr@us.ibm.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
diff --git a/arch/avr32/kernel/traps.c b/arch/avr32/kernel/traps.c
new file mode 100644
index 0000000..7e803f4
--- /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)
+{
+
+}