Initial revision
diff --git a/cpu/74xx_7xx/cache.S b/cpu/74xx_7xx/cache.S
new file mode 100644
index 0000000..f35966c
--- /dev/null
+++ b/cpu/74xx_7xx/cache.S
@@ -0,0 +1,381 @@
+#include <config.h>
+#include <mpc74xx.h>
+#include <version.h>
+
+#include <ppc_asm.tmpl>
+#include <ppc_defs.h>
+
+#include <asm/cache.h>
+#include <asm/mmu.h>
+
+#ifndef CACHE_LINE_SIZE
+# define CACHE_LINE_SIZE L1_CACHE_BYTES
+#endif
+
+#if CACHE_LINE_SIZE == 128
+#define LG_CACHE_LINE_SIZE 7
+#elif CACHE_LINE_SIZE == 32
+#define LG_CACHE_LINE_SIZE 5
+#elif CACHE_LINE_SIZE == 16
+#define LG_CACHE_LINE_SIZE 4
+#elif CACHE_LINE_SIZE == 8
+#define LG_CACHE_LINE_SIZE 3
+#else
+# error "Invalid cache line size!"
+#endif
+
+/*
+ * Invalidate L1 instruction cache.
+ */
+_GLOBAL(invalidate_l1_instruction_cache)
+	mfspr	r3,PVR
+	rlwinm	r3,r3,16,16,31
+	cmpi	0,r3,1
+	beqlr			/* for 601, do nothing */
+	/* 603/604 processor - use invalidate-all bit in HID0 */
+	mfspr	r3,HID0
+	ori	r3,r3,HID0_ICFI
+	mtspr	HID0,r3
+	isync
+	blr
+
+/*
+ * Invalidate L1 data cache.
+ */
+_GLOBAL(invalidate_l1_data_cache)
+	mfspr	r3,HID0
+	ori	r3,r3,HID0_DCFI
+	mtspr	HID0,r3
+	isync
+	blr
+
+/*
+ * Flush data cache.
+ */
+_GLOBAL(flush_data_cache)
+	lis	r3,0
+	lis	r5,CACHE_LINE_SIZE
+flush:
+	cmp	0,1,r3,r5
+	bge	done
+	lwz	r5,0(r3)
+	lis	r5,CACHE_LINE_SIZE
+	addi	r3,r3,0x4
+	b	flush
+done:
+	blr
+/*
+ * Write any modified data cache blocks out to memory
+ * and invalidate the corresponding instruction cache blocks.
+ * This is a no-op on the 601.
+ *
+ * flush_icache_range(unsigned long start, unsigned long stop)
+ */
+_GLOBAL(flush_icache_range)
+	mfspr	r5,PVR
+	rlwinm	r5,r5,16,16,31
+	cmpi	0,r5,1
+	beqlr				/* for 601, do nothing */
+	li	r5,CACHE_LINE_SIZE-1
+	andc	r3,r3,r5
+	subf	r4,r3,r4
+	add	r4,r4,r5
+	srwi.	r4,r4,LG_CACHE_LINE_SIZE
+	beqlr
+	mtctr	r4
+	mr	r6,r3
+1:	dcbst	0,r3
+	addi	r3,r3,CACHE_LINE_SIZE
+	bdnz	1b
+	sync				/* wait for dcbst's to get to ram */
+	mtctr	r4
+2:	icbi	0,r6
+	addi	r6,r6,CACHE_LINE_SIZE
+	bdnz	2b
+	sync				/* additional sync needed on g4 */
+	isync
+	blr
+/*
+ * Write any modified data cache blocks out to memory.
+ * Does not invalidate the corresponding cache lines (especially for
+ * any corresponding instruction cache).
+ *
+ * clean_dcache_range(unsigned long start, unsigned long stop)
+ */
+_GLOBAL(clean_dcache_range)
+	li	r5,CACHE_LINE_SIZE-1
+	andc	r3,r3,r5	/* align r3 down to cache line */
+	subf	r4,r3,r4	/* r4 = offset of stop from start of cache line */
+	add	r4,r4,r5	/* r4 += cache_line_size-1 */
+	srwi.	r4,r4,LG_CACHE_LINE_SIZE  /* r4 = number of cache lines to flush */
+	beqlr				  /* if r4 == 0 return */
+	mtctr	r4			  /* ctr = r4 */
+
+	sync
+1:	dcbst	0,r3
+	addi	r3,r3,CACHE_LINE_SIZE
+	bdnz	1b
+	sync				/* wait for dcbst's to get to ram */
+	blr
+
+/*
+ * Write any modified data cache blocks out to memory
+ * and invalidate the corresponding instruction cache blocks.
+ *
+ * flush_dcache_range(unsigned long start, unsigned long stop)
+ */
+_GLOBAL(flush_dcache_range)
+	li	r5,CACHE_LINE_SIZE-1
+	andc	r3,r3,r5
+	subf	r4,r3,r4
+	add	r4,r4,r5
+	srwi.	r4,r4,LG_CACHE_LINE_SIZE
+	beqlr
+	mtctr	r4
+
+	sync
+1:	dcbf	0,r3
+	addi	r3,r3,CACHE_LINE_SIZE
+	bdnz	1b
+	sync				/* wait for dcbf's to get to ram */
+	blr
+
+/*
+ * Like above, but invalidate the D-cache.  This is used by the 8xx
+ * to invalidate the cache so the PPC core doesn't get stale data
+ * from the CPM (no cache snooping here :-).
+ *
+ * invalidate_dcache_range(unsigned long start, unsigned long stop)
+ */
+_GLOBAL(invalidate_dcache_range)
+	li	r5,CACHE_LINE_SIZE-1
+	andc	r3,r3,r5
+	subf	r4,r3,r4
+	add	r4,r4,r5
+	srwi.	r4,r4,LG_CACHE_LINE_SIZE
+	beqlr
+	mtctr	r4
+
+	sync
+1:	dcbi	0,r3
+	addi	r3,r3,CACHE_LINE_SIZE
+	bdnz	1b
+	sync				/* wait for dcbi's to get to ram */
+	blr
+
+/*
+ * Flush a particular page from the data cache to RAM.
+ * Note: this is necessary because the instruction cache does *not*
+ * snoop from the data cache.
+ * This is a no-op on the 601 which has a unified cache.
+ *
+ *	void __flush_page_to_ram(void *page)
+ */
+_GLOBAL(__flush_page_to_ram)
+	mfspr	r5,PVR
+	rlwinm	r5,r5,16,16,31
+	cmpi	0,r5,1
+	beqlr				/* for 601, do nothing */
+	rlwinm	r3,r3,0,0,19		/* Get page base address */
+	li	r4,4096/CACHE_LINE_SIZE	/* Number of lines in a page */
+	mtctr	r4
+	mr	r6,r3
+0:	dcbst	0,r3			/* Write line to ram */
+	addi	r3,r3,CACHE_LINE_SIZE
+	bdnz	0b
+	sync
+	mtctr	r4
+1:	icbi	0,r6
+	addi	r6,r6,CACHE_LINE_SIZE
+	bdnz	1b
+	sync
+	isync
+	blr
+
+/*
+ * Flush a particular page from the instruction cache.
+ * Note: this is necessary because the instruction cache does *not*
+ * snoop from the data cache.
+ * This is a no-op on the 601 which has a unified cache.
+ *
+ *	void __flush_icache_page(void *page)
+ */
+_GLOBAL(__flush_icache_page)
+	mfspr	r5,PVR
+	rlwinm	r5,r5,16,16,31
+	cmpi	0,r5,1
+	beqlr				/* for 601, do nothing */
+	li	r4,4096/CACHE_LINE_SIZE	/* Number of lines in a page */
+	mtctr	r4
+1:	icbi	0,r3
+	addi	r3,r3,CACHE_LINE_SIZE
+	bdnz	1b
+	sync
+	isync
+	blr
+
+/*
+ * Clear a page using the dcbz instruction, which doesn't cause any
+ * memory traffic (except to write out any cache lines which get
+ * displaced).  This only works on cacheable memory.
+ */
+_GLOBAL(clear_page)
+	li	r0,4096/CACHE_LINE_SIZE
+	mtctr	r0
+1:	dcbz	0,r3
+	addi	r3,r3,CACHE_LINE_SIZE
+	bdnz	1b
+	blr
+
+/*
+ * Enable L1 Instruction cache
+ */
+_GLOBAL(icache_enable)
+	mfspr	r3, HID0
+	li	r5, HID0_ICFI|HID0_ILOCK
+	andc	r3, r3, r5
+	ori	r3, r3, HID0_ICE
+	ori	r5, r3, HID0_ICFI
+	mtspr	HID0, r5
+	mtspr	HID0, r3
+	isync
+	blr
+
+/*
+ * Disable L1 Instruction cache
+ */
+_GLOBAL(icache_disable)
+	mfspr	r3, HID0
+	li	r5, 0
+	ori	r5, r5, HID0_ICE
+	andc	r3, r3, r5
+	mtspr	HID0, r3
+	isync
+	blr
+
+/*
+ * Is instruction cache enabled?
+ */
+_GLOBAL(icache_status)
+	mfspr	r3, HID0
+	andi.	r3, r3, HID0_ICE
+	blr
+
+
+_GLOBAL(l1dcache_enable)
+	mfspr	r3, HID0
+	li	r5, HID0_DCFI|HID0_DLOCK
+	andc	r3, r3, r5
+	mtspr	HID0, r3		/* no invalidate, unlock */
+	ori	r3, r3, HID0_DCE
+	ori	r5, r3, HID0_DCFI
+	mtspr	HID0, r5		/* enable + invalidate */
+	mtspr	HID0, r3		/* enable */
+	sync
+	blr
+
+/*
+ * Enable data cache(s) - L1 and optionally L2
+ * Calls l2cache_enable. LR saved in r5
+ */
+_GLOBAL(dcache_enable)
+	mfspr	r3, HID0
+	li	r5, HID0_DCFI|HID0_DLOCK
+	andc	r3, r3, r5
+	mtspr	HID0, r3		/* no invalidate, unlock */
+	ori	r3, r3, HID0_DCE
+	ori	r5, r3, HID0_DCFI
+	mtspr	HID0, r5		/* enable + invalidate */
+	mtspr	HID0, r3		/* enable */
+	sync
+#ifdef CFG_L2
+	mflr	r5
+	bl	l2cache_enable		/* uses r3 and r4 */
+	sync
+	mtlr	r5
+#endif
+	blr
+
+
+/*
+ * Disable data cache(s) - L1 and optionally L2
+ * Calls flush_data_cache and l2cache_disable_no_flush.
+ * LR saved in r4
+ */
+_GLOBAL(dcache_disable)
+	mflr	r4			/* save link register */
+	bl	flush_data_cache	/* uses r3 and r5 */
+	sync
+	mfspr	r3, HID0
+	li	r5, HID0_DCFI|HID0_DLOCK
+	andc	r3, r3, r5
+	mtspr	HID0, r3		/* no invalidate, unlock */
+	li	r5, HID0_DCE|HID0_DCFI
+	andc	r3, r3, r5		/* no enable, no invalidate */
+	mtspr	HID0, r3
+	sync
+#ifdef CFG_L2
+	bl	l2cache_disable_no_flush /* uses r3 */
+#endif
+	mtlr	r4			/* restore link register */
+	blr
+
+/*
+ * Is data cache enabled?
+ */
+_GLOBAL(dcache_status)
+	mfspr	r3, HID0
+	andi.	r3, r3, HID0_DCE
+	blr
+
+/*
+ * Invalidate L2 cache using L2I and polling L2IP
+ */
+_GLOBAL(l2cache_invalidate)
+	sync
+	oris	r3, r3, L2CR_L2I@h
+	sync
+	mtspr	l2cr, r3
+	sync
+invl2:
+	mfspr	r3, l2cr
+	andi.	r3, r3, L2CR_L2IP
+	bne	invl2
+	/* turn off the global invalidate bit */
+	mfspr	r3, l2cr
+	rlwinm	r3, r3, 0, 11, 9
+	sync
+	mtspr	l2cr, r3
+	sync
+	blr
+
+/*
+ * Enable L2 cache
+ * Calls l2cache_invalidate. LR is saved in r4
+ */
+_GLOBAL(l2cache_enable)
+	mflr	r4			/* save link register */
+	bl	l2cache_invalidate	/* uses r3 */
+	sync
+	lis	r3, L2_ENABLE@h
+	ori	r3, r3, L2_ENABLE@l
+	mtspr	l2cr, r3
+	isync
+	mtlr	r4			/* restore link register */
+	blr
+
+/*
+ * Disable L2 cache
+ * Calls flush_data_cache. LR is saved in r4
+ */
+_GLOBAL(l2cache_disable)
+	mflr	r4			/* save link register */
+	bl	flush_data_cache	/* uses r3 and r5 */
+	sync
+	mtlr	r4			/* restore link register */
+l2cache_disable_no_flush:		/* provide way to disable L2 w/o flushing */
+	lis	r3, L2_INIT@h
+	ori	r3, r3, L2_INIT@l
+	mtspr	l2cr, r3
+	isync
+	blr
diff --git a/cpu/arm720t/start.S b/cpu/arm720t/start.S
new file mode 100644
index 0000000..e9899a9
--- /dev/null
+++ b/cpu/arm720t/start.S
@@ -0,0 +1,429 @@
+/*
+ *  armboot - Startup Code for ARM720 CPU-core
+ *
+ *  Copyright (c) 2001	Marius Gröger <mag@sysgo.de>
+ *  Copyright (c) 2002	Alex Züpke <azu@sysgo.de>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * 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.
+ *
+ * This program 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
+ */
+
+
+
+#include <config.h>
+#include <version.h>
+
+
+/*
+ *************************************************************************
+ *
+ * Jump vector table as in table 3.1 in [1]
+ *
+ *************************************************************************
+ */
+
+
+.globl _start
+_start:	b       reset
+	ldr	pc, _undefined_instruction
+	ldr	pc, _software_interrupt
+	ldr	pc, _prefetch_abort
+	ldr	pc, _data_abort
+	ldr	pc, _not_used
+	ldr	pc, _irq
+	ldr	pc, _fiq
+
+_undefined_instruction:	.word undefined_instruction
+_software_interrupt:	.word software_interrupt
+_prefetch_abort:	.word prefetch_abort
+_data_abort:		.word data_abort
+_not_used:		.word not_used
+_irq:			.word irq
+_fiq:			.word fiq
+
+	.balignl 16,0xdeadbeef
+
+
+/*
+ *************************************************************************
+ *
+ * Startup Code (reset vector)
+ *
+ * do important init only if we don't start from memory!
+ * relocate armboot to ram
+ * setup stack
+ * jump to second stage
+ *
+ *************************************************************************
+ */
+
+/*
+ * CFG_MEM_END is in the board dependent config-file (configs/config_BOARD.h)
+ */
+_TEXT_BASE:
+	.word	TEXT_BASE
+
+.globl _armboot_start
+_armboot_start:
+	.word _start
+
+/*
+ * Note: _armboot_end_data and _armboot_end are defined
+ * by the (board-dependent) linker script.
+ * _armboot_end_data is the first usable FLASH address after armboot
+ */
+.globl _armboot_end_data
+_armboot_end_data:
+	.word armboot_end_data
+.globl _armboot_end
+_armboot_end:
+	.word armboot_end
+
+/*
+ * _armboot_real_end is the first usable RAM address behind armboot
+ * and the various stacks
+ */
+.globl _armboot_real_end
+_armboot_real_end:
+	.word 0x0badc0de
+
+#ifdef CONFIG_USE_IRQ
+/* IRQ stack memory (calculated at run-time) */
+.globl IRQ_STACK_START
+IRQ_STACK_START:
+	.word	0x0badc0de
+
+/* IRQ stack memory (calculated at run-time) */
+.globl FIQ_STACK_START
+FIQ_STACK_START:
+	.word 0x0badc0de
+#endif
+
+
+/*
+ * the actual reset code
+ */
+
+reset:
+	/*
+	 * set the cpu to SVC32 mode
+	 */
+	mrs	r0,cpsr
+	bic	r0,r0,#0x1f
+	orr	r0,r0,#0x13
+	msr	cpsr,r0
+
+	/*
+	 * we do sys-critical inits only at reboot,
+	 * not when booting from ram!
+	 */
+#ifdef CONFIG_INIT_CRITICAL
+	bl	cpu_init_crit
+#endif
+
+relocate:
+	/*
+	 * relocate armboot to RAM
+	 */
+	adr	r0, _start		/* r0 <- current position of code */
+	ldr	r2, _armboot_start
+	ldr	r3, _armboot_end
+	sub	r2, r3, r2		/* r2 <- size of armboot */
+	ldr	r1, _TEXT_BASE		/* r1 <- destination address */
+	add	r2, r0, r2		/* r2 <- source end address */
+
+	/*
+	 * r0 = source address
+	 * r1 = target address
+	 * r2 = source end address
+	 */
+copy_loop:
+	ldmia	r0!, {r3-r10}
+	stmia	r1!, {r3-r10}
+	cmp	r0, r2
+	ble	copy_loop
+
+	/* set up the stack */
+	ldr	r0, _armboot_end
+	add	r0, r0, #CONFIG_STACKSIZE
+	sub	sp, r0, #12		/* leave 3 words for abort-stack */
+
+	ldr	pc, _start_armboot
+
+_start_armboot:	.word start_armboot
+
+
+/*
+ *************************************************************************
+ *
+ * CPU_init_critical registers
+ *
+ * setup important registers
+ * setup memory timing
+ *
+ *************************************************************************
+ */
+
+
+/* Interupt-Controller base addresses */
+INTMR1:		.word	0x80000280 @ 32 bit size
+INTMR2:		.word	0x80001280 @ 16 bit size
+INTMR3:		.word	0x80002280 @  8 bit size
+
+/* SYSCONs */
+SYSCON1:	.word	0x80000100
+SYSCON2:	.word	0x80001100
+SYSCON3:	.word	0x80002200
+
+#define CLKCTL	       0x6  /* mask */
+#define CLKCTL_18      0x0  /* 18.432 MHz */
+#define CLKCTL_36      0x2  /* 36.864 MHz */
+#define CLKCTL_49      0x4  /* 49.152 MHz */
+#define CLKCTL_73      0x6  /* 73.728 MHz */
+
+cpu_init_crit:
+	/*
+	 * mask all IRQs by clearing all bits in the INTMRs
+	 */
+	mov	r1, #0x00
+	ldr	r0, INTMR1
+	str	r1, [r0]
+	ldr	r0, INTMR2
+	str	r1, [r0]
+	ldr	r0, INTMR3
+	str	r1, [r0]
+
+	/*
+	 * flush v4 I/D caches
+	 */
+	mov	r0, #0
+	mcr	p15, 0, r0, c7, c7, 0	/* flush v3/v4 cache */
+	mcr	p15, 0, r0, c8, c7, 0	/* flush v4 TLB */
+
+	/*
+	 * disable MMU stuff and caches
+	 */
+	mrc	p15,0,r0,c1,c0
+	bic	r0, r0, #0x00002300	@ clear bits 13, 9:8 (--V- --RS)
+	bic	r0, r0, #0x0000008f	@ clear bits 7, 3:0 (B--- WCAM)
+	orr	r0, r0, #0x00000002	@ set bit 2 (A) Align
+	mcr	p15,0,r0,c1,c0
+
+#ifdef CONFIG_ARM7_REVD
+	/* set clock speed */
+	/* !!! we run @ 36 MHz due to a hardware flaw in Rev. D processors */
+	/* !!! not doing DRAM refresh properly! */
+	ldr	r0, SYSCON3
+	ldr	r1, [r0]
+	bic	r1, r1, #CLKCTL
+	orr	r1, r1, #CLKCTL_36
+	str	r1, [r0]
+#endif
+
+	/*
+	 * before relocating, we have to setup RAM timing
+	 * because memory timing is board-dependend, you will
+	 * find a memsetup.S in your board directory.
+	 */
+	mov	ip, lr
+	bl	memsetup
+	mov	lr, ip
+
+	mov	pc, lr
+
+
+
+
+/*
+ *************************************************************************
+ *
+ * Interrupt handling
+ *
+ *************************************************************************
+ */
+
+@
+@ IRQ stack frame.
+@
+#define S_FRAME_SIZE	72
+
+#define S_OLD_R0	68
+#define S_PSR		64
+#define S_PC		60
+#define S_LR		56
+#define S_SP		52
+
+#define S_IP		48
+#define S_FP		44
+#define S_R10		40
+#define S_R9		36
+#define S_R8		32
+#define S_R7		28
+#define S_R6		24
+#define S_R5		20
+#define S_R4		16
+#define S_R3		12
+#define S_R2		8
+#define S_R1		4
+#define S_R0		0
+
+#define MODE_SVC 0x13
+#define I_BIT	 0x80
+
+/*
+ * use bad_save_user_regs for abort/prefetch/undef/swi ...
+ * use irq_save_user_regs / irq_restore_user_regs for IRQ/FIQ handling
+ */
+
+	.macro	bad_save_user_regs
+	sub	sp, sp, #S_FRAME_SIZE
+	stmia	sp, {r0 - r12}			@ Calling r0-r12
+	add     r8, sp, #S_PC
+
+	ldr	r2, _armboot_end
+	add	r2, r2, #CONFIG_STACKSIZE
+	sub	r2, r2, #8
+	ldmia	r2, {r2 - r4}                   @ get pc, cpsr, old_r0
+	add	r0, sp, #S_FRAME_SIZE		@ restore sp_SVC
+
+	add	r5, sp, #S_SP
+	mov	r1, lr
+	stmia	r5, {r0 - r4}                   @ save sp_SVC, lr_SVC, pc, cpsr, old_r
+	mov	r0, sp
+	.endm
+
+	.macro	irq_save_user_regs
+	sub	sp, sp, #S_FRAME_SIZE
+	stmia	sp, {r0 - r12}			@ Calling r0-r12
+	add     r8, sp, #S_PC
+	stmdb   r8, {sp, lr}^                   @ Calling SP, LR
+	str     lr, [r8, #0]                    @ Save calling PC
+	mrs     r6, spsr
+	str     r6, [r8, #4]                    @ Save CPSR
+	str     r0, [r8, #8]                    @ Save OLD_R0
+	mov	r0, sp
+	.endm
+
+	.macro	irq_restore_user_regs
+	ldmia	sp, {r0 - lr}^			@ Calling r0 - lr
+	mov	r0, r0
+	ldr	lr, [sp, #S_PC]			@ Get PC
+	add	sp, sp, #S_FRAME_SIZE
+	subs	pc, lr, #4			@ return & move spsr_svc into cpsr
+	.endm
+
+	.macro get_bad_stack
+	ldr	r13, _armboot_end		@ setup our mode stack
+	add	r13, r13, #CONFIG_STACKSIZE	@ resides at top of normal stack
+	sub	r13, r13, #8
+
+	str	lr, [r13]			@ save caller lr / spsr
+	mrs	lr, spsr
+	str     lr, [r13, #4]
+
+	mov	r13, #MODE_SVC			@ prepare SVC-Mode
+	msr	spsr_c, r13
+	mov	lr, pc
+	movs	pc, lr
+	.endm
+
+	.macro get_irq_stack			@ setup IRQ stack
+	ldr	sp, IRQ_STACK_START
+	.endm
+
+	.macro get_fiq_stack			@ setup FIQ stack
+	ldr	sp, FIQ_STACK_START
+	.endm
+
+/*
+ * exception handlers
+ */
+	.align  5
+undefined_instruction:
+	get_bad_stack
+	bad_save_user_regs
+	bl 	do_undefined_instruction
+
+	.align	5
+software_interrupt:
+	get_bad_stack
+	bad_save_user_regs
+	bl 	do_software_interrupt
+
+	.align	5
+prefetch_abort:
+	get_bad_stack
+	bad_save_user_regs
+	bl 	do_prefetch_abort
+
+	.align	5
+data_abort:
+	get_bad_stack
+	bad_save_user_regs
+	bl 	do_data_abort
+
+	.align	5
+not_used:
+	get_bad_stack
+	bad_save_user_regs
+	bl 	do_not_used
+
+#ifdef CONFIG_USE_IRQ
+
+	.align	5
+irq:
+	get_irq_stack
+	irq_save_user_regs
+	bl 	do_irq
+	irq_restore_user_regs
+
+	.align	5
+fiq:
+	get_fiq_stack
+	/* someone ought to write a more effiction fiq_save_user_regs */
+	irq_save_user_regs
+	bl 	do_fiq
+	irq_restore_user_regs
+
+#else
+
+	.align	5
+irq:
+	get_bad_stack
+	bad_save_user_regs
+	bl 	do_irq
+
+	.align	5
+fiq:
+	get_bad_stack
+	bad_save_user_regs
+	bl 	do_fiq
+
+#endif
+
+	.align	5
+.globl reset_cpu
+reset_cpu:
+	mov     ip, #0
+	mcr     p15, 0, ip, c7, c7, 0           @ invalidate cache
+	mcr     p15, 0, ip, c8, c7, 0           @ flush TLB (v4)
+	mrc     p15, 0, ip, c1, c0, 0           @ get ctrl register
+	bic     ip, ip, #0x000f                 @ ............wcam
+	bic     ip, ip, #0x2100                 @ ..v....s........
+	mcr     p15, 0, ip, c1, c0, 0           @ ctrl register
+	mov     pc, r0
diff --git a/cpu/arm920t/start.S b/cpu/arm920t/start.S
new file mode 100644
index 0000000..a858dfa
--- /dev/null
+++ b/cpu/arm920t/start.S
@@ -0,0 +1,475 @@
+/*
+ *  armboot - Startup Code for ARM920 CPU-core
+ *
+ *  Copyright (c) 2001	Marius Gröger <mag@sysgo.de>
+ *  Copyright (c) 2002	Alex Züpke <azu@sysgo.de>
+ *  Copyright (c) 2002	Gary Jennejohn <gj@denx.de>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * 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.
+ *
+ * This program 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
+ */
+
+
+
+#include <config.h>
+#include <version.h>
+
+
+/*
+ *************************************************************************
+ *
+ * Jump vector table as in table 3.1 in [1]
+ *
+ *************************************************************************
+ */
+
+
+.globl _start
+_start:	b       reset
+	ldr	pc, _undefined_instruction
+	ldr	pc, _software_interrupt
+	ldr	pc, _prefetch_abort
+	ldr	pc, _data_abort
+	ldr	pc, _not_used
+	ldr	pc, _irq
+	ldr	pc, _fiq
+
+_undefined_instruction:	.word undefined_instruction
+_software_interrupt:	.word software_interrupt
+_prefetch_abort:	.word prefetch_abort
+_data_abort:		.word data_abort
+_not_used:		.word not_used
+_irq:			.word irq
+_fiq:			.word fiq
+
+	.balignl 16,0xdeadbeef
+
+
+/*
+ *************************************************************************
+ *
+ * Startup Code (reset vector)
+ *
+ * do important init only if we don't start from memory!
+ * relocate armboot to ram
+ * setup stack
+ * jump to second stage
+ *
+ *************************************************************************
+ */
+
+/*
+ * CFG_MEM_END is in the board dependent config-file (configs/config_BOARD.h)
+ */
+_TEXT_BASE:
+	.word	TEXT_BASE
+
+.globl _armboot_start
+_armboot_start:
+	.word _start
+
+/*
+ * Note: _armboot_end_data and _armboot_end are defined
+ * by the (board-dependent) linker script.
+ * _armboot_end_data is the first usable FLASH address after armboot
+ */
+.globl _armboot_end_data
+_armboot_end_data:
+	.word armboot_end_data
+.globl _armboot_end
+_armboot_end:
+	.word armboot_end
+
+/*
+ * _armboot_real_end is the first usable RAM address behind armboot
+ * and the various stacks
+ */
+.globl _armboot_real_end
+_armboot_real_end:
+	.word 0x0badc0de
+
+#ifdef CONFIG_USE_IRQ
+/* IRQ stack memory (calculated at run-time) */
+.globl IRQ_STACK_START
+IRQ_STACK_START:
+	.word	0x0badc0de
+
+/* IRQ stack memory (calculated at run-time) */
+.globl FIQ_STACK_START
+FIQ_STACK_START:
+	.word 0x0badc0de
+#endif
+
+
+/*
+ * the actual reset code
+ */
+
+reset:
+	/*
+	 * set the cpu to SVC32 mode
+	 */
+	mrs	r0,cpsr
+	bic	r0,r0,#0x1f
+	orr	r0,r0,#0xd3
+	msr	cpsr,r0
+
+/* turn off the watchdog */
+#if defined(CONFIG_S3C2400)
+#define pWTCON		0x15300000
+/* Interupt-Controller base addresses */
+#define INTMSK		0x14400008
+/* clock divisor register */
+#define CLKDIVN		0x14800014
+#elif defined(CONFIG_S3C2410)
+#define pWTCON		0x53000000
+/* Interupt-Controller base addresses */
+#define INTMSK		0x4A000008
+#define INTSUBMSK	0x4A00001C
+/* clock divisor register */
+#define CLKDIVN		0x4C000014
+#endif
+
+	ldr     r0, =pWTCON
+	mov     r1, #0x0
+	str     r1, [r0]
+
+	/*
+	 * mask all IRQs by setting all bits in the INTMR - default
+	 */
+	mov	r1, #0xffffffff
+	ldr	r0, =INTMSK
+	str	r1, [r0]
+#if defined(CONFIG_S3C2410)
+	ldr	r1, =0x3ff
+	ldr	r0, =INTSUBMSK
+	str	r1, [r0]
+#endif
+
+	/* FCLK:HCLK:PCLK = 1:2:4 */
+	/* default FCLK is 120 MHz ! */
+	ldr	r0, =CLKDIVN
+	mov	r1, #3
+	str	r1, [r0]
+
+	/*
+	 * we do sys-critical inits only at reboot,
+	 * not when booting from ram!
+	 */
+#ifdef CONFIG_INIT_CRITICAL
+	bl	cpu_init_crit
+#endif
+
+relocate:
+	/*
+	 * relocate armboot to RAM
+	 */
+	adr	r0, _start		/* r0 <- current position of code */
+	ldr	r2, _armboot_start
+	ldr	r3, _armboot_end
+	sub	r2, r3, r2		/* r2 <- size of armboot */
+	ldr	r1, _TEXT_BASE		/* r1 <- destination address */
+	add	r2, r0, r2		/* r2 <- source end address */
+
+	/*
+	 * r0 = source address
+	 * r1 = target address
+	 * r2 = source end address
+	 */
+copy_loop:
+	ldmia	r0!, {r3-r10}
+	stmia	r1!, {r3-r10}
+	cmp	r0, r2
+	ble	copy_loop
+
+#if 0
+	/* try doing this stuff after the relocation */
+	ldr     r0, =pWTCON
+	mov     r1, #0x0
+	str     r1, [r0]
+
+	/*
+	 * mask all IRQs by setting all bits in the INTMR - default
+	 */
+	mov	r1, #0xffffffff
+	ldr	r0, =INTMR
+	str	r1, [r0]
+
+	/* FCLK:HCLK:PCLK = 1:2:4 */
+	/* default FCLK is 120 MHz ! */
+	ldr	r0, =CLKDIVN
+	mov	r1, #3
+	str	r1, [r0]
+	/* END stuff after relocation */
+#endif
+
+	/* set up the stack */
+	ldr	r0, _armboot_end
+	add	r0, r0, #CONFIG_STACKSIZE
+	sub	sp, r0, #12		/* leave 3 words for abort-stack */
+
+	ldr	pc, _start_armboot
+
+_start_armboot:	.word start_armboot
+
+
+/*
+ *************************************************************************
+ *
+ * CPU_init_critical registers
+ *
+ * setup important registers
+ * setup memory timing
+ *
+ *************************************************************************
+ */
+
+
+cpu_init_crit:
+	/*
+	 * flush v4 I/D caches
+	 */
+	mov	r0, #0
+	mcr	p15, 0, r0, c7, c7, 0	/* flush v3/v4 cache */
+	mcr	p15, 0, r0, c8, c7, 0	/* flush v4 TLB */
+
+	/*
+	 * disable MMU stuff and caches
+	 */
+	mrc	p15, 0, r0, c1, c0, 0
+	bic	r0, r0, #0x00002300	@ clear bits 13, 9:8 (--V- --RS)
+	bic	r0, r0, #0x00000087	@ clear bits 7, 2:0 (B--- -CAM)
+	orr	r0, r0, #0x00000002	@ set bit 2 (A) Align
+	orr	r0, r0, #0x00001000	@ set bit 12 (I) I-Cache
+	mcr	p15, 0, r0, c1, c0, 0
+
+
+	/*
+	 * before relocating, we have to setup RAM timing
+	 * because memory timing is board-dependend, you will
+	 * find a memsetup.S in your board directory.
+	 */
+	mov	ip, lr
+	bl	memsetup
+	mov	lr, ip
+
+	mov	pc, lr
+
+
+
+
+/*
+ *************************************************************************
+ *
+ * Interrupt handling
+ *
+ *************************************************************************
+ */
+
+@
+@ IRQ stack frame.
+@
+#define S_FRAME_SIZE	72
+
+#define S_OLD_R0	68
+#define S_PSR		64
+#define S_PC		60
+#define S_LR		56
+#define S_SP		52
+
+#define S_IP		48
+#define S_FP		44
+#define S_R10		40
+#define S_R9		36
+#define S_R8		32
+#define S_R7		28
+#define S_R6		24
+#define S_R5		20
+#define S_R4		16
+#define S_R3		12
+#define S_R2		8
+#define S_R1		4
+#define S_R0		0
+
+#define MODE_SVC 0x13
+#define I_BIT	 0x80
+
+/*
+ * use bad_save_user_regs for abort/prefetch/undef/swi ...
+ * use irq_save_user_regs / irq_restore_user_regs for IRQ/FIQ handling
+ */
+
+	.macro	bad_save_user_regs
+	sub	sp, sp, #S_FRAME_SIZE
+	stmia	sp, {r0 - r12}			@ Calling r0-r12
+	add     r8, sp, #S_PC
+
+	ldr	r2, _armboot_end
+	add	r2, r2, #CONFIG_STACKSIZE
+	sub	r2, r2, #8
+	ldmia	r2, {r2 - r4}                   @ get pc, cpsr, old_r0
+	add	r0, sp, #S_FRAME_SIZE		@ restore sp_SVC
+
+	add	r5, sp, #S_SP
+	mov	r1, lr
+	stmia	r5, {r0 - r4}                   @ save sp_SVC, lr_SVC, pc, cpsr, old_r
+	mov	r0, sp
+	.endm
+
+	.macro	irq_save_user_regs
+	sub	sp, sp, #S_FRAME_SIZE
+	stmia	sp, {r0 - r12}			@ Calling r0-r12
+	add     r8, sp, #S_PC
+	stmdb   r8, {sp, lr}^                   @ Calling SP, LR
+	str     lr, [r8, #0]                    @ Save calling PC
+	mrs     r6, spsr
+	str     r6, [r8, #4]                    @ Save CPSR
+	str     r0, [r8, #8]                    @ Save OLD_R0
+	mov	r0, sp
+	.endm
+
+	.macro	irq_restore_user_regs
+	ldmia	sp, {r0 - lr}^			@ Calling r0 - lr
+	mov	r0, r0
+	ldr	lr, [sp, #S_PC]			@ Get PC
+	add	sp, sp, #S_FRAME_SIZE
+	subs	pc, lr, #4			@ return & move spsr_svc into cpsr
+	.endm
+
+	.macro get_bad_stack
+	ldr	r13, _armboot_end		@ setup our mode stack
+	add	r13, r13, #CONFIG_STACKSIZE	@ resides at top of normal stack
+	sub	r13, r13, #8
+
+	str	lr, [r13]			@ save caller lr / spsr
+	mrs	lr, spsr
+	str     lr, [r13, #4]
+
+	mov	r13, #MODE_SVC			@ prepare SVC-Mode
+	@ msr	spsr_c, r13
+	msr	spsr, r13
+	mov	lr, pc
+	movs	pc, lr
+	.endm
+
+	.macro get_irq_stack			@ setup IRQ stack
+	ldr	sp, IRQ_STACK_START
+	.endm
+
+	.macro get_fiq_stack			@ setup FIQ stack
+	ldr	sp, FIQ_STACK_START
+	.endm
+
+/*
+ * exception handlers
+ */
+	.align  5
+undefined_instruction:
+	get_bad_stack
+	bad_save_user_regs
+	bl 	do_undefined_instruction
+
+	.align	5
+software_interrupt:
+	get_bad_stack
+	bad_save_user_regs
+	bl 	do_software_interrupt
+
+	.align	5
+prefetch_abort:
+	get_bad_stack
+	bad_save_user_regs
+	bl 	do_prefetch_abort
+
+	.align	5
+data_abort:
+	get_bad_stack
+	bad_save_user_regs
+	bl 	do_data_abort
+
+	.align	5
+not_used:
+	get_bad_stack
+	bad_save_user_regs
+	bl 	do_not_used
+
+#ifdef CONFIG_USE_IRQ
+
+	.align	5
+irq:
+	get_irq_stack
+	irq_save_user_regs
+	bl 	do_irq
+	irq_restore_user_regs
+
+	.align	5
+fiq:
+	get_fiq_stack
+	/* someone ought to write a more effiction fiq_save_user_regs */
+	irq_save_user_regs
+	bl 	do_fiq
+	irq_restore_user_regs
+
+#else
+
+	.align	5
+irq:
+	get_bad_stack
+	bad_save_user_regs
+	bl 	do_irq
+
+	.align	5
+fiq:
+	get_bad_stack
+	bad_save_user_regs
+	bl 	do_fiq
+
+#endif
+
+	.align	5
+.globl reset_cpu
+reset_cpu:
+#ifdef CONFIG_S3C2400
+	bl	disable_interrupts
+	ldr	r1, _rWTCON
+	ldr	r2, _rWTCNT
+	/* Disable watchdog */
+	mov	r3, #0x0000
+	str	r3, [r1]
+	/* Initialize watchdog timer count register */
+	mov	r3, #0x0001
+	str	r3, [r2]
+	/* Enable watchdog timer; assert reset at timer timeout */
+	mov	r3, #0x0021
+	str	r3, [r1]
+_loop_forever:
+	b	_loop_forever
+_rWTCON:
+	.word	0x15300000
+_rWTCNT:
+	.word	0x15300008
+#else /* ! CONFIG_S3C2400 */
+	mov     ip, #0
+	mcr     p15, 0, ip, c7, c7, 0           @ invalidate cache
+	mcr     p15, 0, ip, c8, c7, 0           @ flush TLB (v4)
+	mrc     p15, 0, ip, c1, c0, 0           @ get ctrl register
+	bic     ip, ip, #0x000f                 @ ............wcam
+	bic     ip, ip, #0x2100                 @ ..v....s........
+	mcr     p15, 0, ip, c1, c0, 0           @ ctrl register
+	mov     pc, r0
+#endif /* CONFIG_S3C2400 */
diff --git a/cpu/mpc8260/ether_scc.c b/cpu/mpc8260/ether_scc.c
new file mode 100644
index 0000000..ff164fe
--- /dev/null
+++ b/cpu/mpc8260/ether_scc.c
@@ -0,0 +1,358 @@
+/*
+ * MPC8260 SCC Ethernet
+ *
+ * Copyright (c) 2000 MontaVista Software, Inc.   Dan Malek (dmalek@jlc.net)
+ *
+ * (C) Copyright 2000 Sysgo Real-Time Solutions, GmbH <www.elinos.com>
+ * Marius Groeger <mgroeger@sysgo.de>
+ *
+ * (C) Copyright (c) 2001
+ * Advent Networks, Inc. <http://www.adventnetworks.com>
+ * Jay Monkman <jtm@smoothsmoothie.com>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * 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.
+ *
+ * This program 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
+ */
+
+#include <common.h>
+#include <asm/cpm_8260.h>
+#include <mpc8260.h>
+#include <net.h>
+#include <command.h>
+#include <config.h>
+
+#if defined(CONFIG_ETHER_ON_SCC) && (CONFIG_COMMANDS & CFG_CMD_NET)
+
+#if (CONFIG_ETHER_INDEX == 1)
+#  define PROFF_ENET            PROFF_SCC1
+#  define CPM_CR_ENET_PAGE      CPM_CR_SCC1_PAGE
+#  define CPM_CR_ENET_SBLOCK    CPM_CR_SCC1_SBLOCK
+#  define CMXSCR_MASK          (CMXSCR_SC1          |\
+                                CMXSCR_RS1CS_MSK    |\
+                                CMXSCR_TS1CS_MSK)
+
+#elif (CONFIG_ETHER_INDEX == 2)
+#  define PROFF_ENET            PROFF_SCC2
+#  define CPM_CR_ENET_PAGE      CPM_CR_SCC2_PAGE
+#  define CPM_CR_ENET_SBLOCK    CPM_CR_SCC2_SBLOCK
+#  define CMXSCR_MASK          (CMXSCR_SC2          |\
+                                CMXSCR_RS2CS_MSK    |\
+                                CMXSCR_TS2CS_MSK)
+
+#elif (CONFIG_ETHER_INDEX == 3)
+#  define PROFF_ENET            PROFF_SCC3
+#  define CPM_CR_ENET_PAGE      CPM_CR_SCC3_PAGE
+#  define CPM_CR_ENET_SBLOCK    CPM_CR_SCC3_SBLOCK
+#  define CMXSCR_MASK          (CMXSCR_SC3          |\
+                                CMXSCR_RS3CS_MSK    |\
+                                CMXSCR_TS3CS_MSK)
+#elif (CONFIG_ETHER_INDEX == 4)
+#  define PROFF_ENET            PROFF_SCC4
+#  define CPM_CR_ENET_PAGE      CPM_CR_SCC4_PAGE
+#  define CPM_CR_ENET_SBLOCK    CPM_CR_SCC4_SBLOCK
+#  define CMXSCR_MASK          (CMXSCR_SC4          |\
+                                CMXSCR_RS4CS_MSK    |\
+                                CMXSCR_TS4CS_MSK)
+
+#endif
+
+
+/* Ethernet Transmit and Receive Buffers */
+#define DBUF_LENGTH  1520
+
+#define TX_BUF_CNT 2
+
+#define TOUT_LOOP 1000000
+
+static char txbuf[TX_BUF_CNT][ DBUF_LENGTH ];
+
+static uint rxIdx;      /* index of the current RX buffer */
+static uint txIdx;      /* index of the current TX buffer */
+
+/*
+ * SCC Ethernet Tx and Rx buffer descriptors allocated at the
+ *  immr->udata_bd address on Dual-Port RAM
+ * Provide for Double Buffering
+ */
+
+typedef volatile struct CommonBufferDescriptor {
+    cbd_t rxbd[PKTBUFSRX];         /* Rx BD */
+    cbd_t txbd[TX_BUF_CNT];        /* Tx BD */
+} RTXBD;
+
+static RTXBD *rtx;
+
+
+int eth_send(volatile void *packet, int length)
+{
+    int i;
+    int result = 0;
+
+    if (length <= 0) {
+        printf("scc: bad packet size: %d\n", length);
+        goto out;
+    }
+
+    for(i=0; rtx->txbd[txIdx].cbd_sc & BD_ENET_TX_READY; i++) {
+        if (i >= TOUT_LOOP) {
+            printf("scc: tx buffer not ready\n");
+            goto out;
+        }
+    }
+
+    rtx->txbd[txIdx].cbd_bufaddr = (uint)packet;
+    rtx->txbd[txIdx].cbd_datlen = length;
+    rtx->txbd[txIdx].cbd_sc |= (BD_ENET_TX_READY | BD_ENET_TX_LAST |
+                                BD_ENET_TX_WRAP);
+
+    for(i=0; rtx->txbd[txIdx].cbd_sc & BD_ENET_TX_READY; i++) {
+        if (i >= TOUT_LOOP) {
+            printf("scc: tx error\n");
+            goto out;
+        }
+    }
+
+    /* return only status bits */
+    result = rtx->txbd[txIdx].cbd_sc & BD_ENET_TX_STATS;
+
+ out:
+    return result;
+}
+
+
+int eth_rx(void)
+{
+    int length;
+
+    for (;;)
+    {
+        if (rtx->rxbd[rxIdx].cbd_sc & BD_ENET_RX_EMPTY) {
+            length = -1;
+            break;     /* nothing received - leave for() loop */
+        }
+
+        length = rtx->rxbd[rxIdx].cbd_datlen;
+
+        if (rtx->rxbd[rxIdx].cbd_sc & 0x003f)
+        {
+            printf("err: %x\n", rtx->rxbd[rxIdx].cbd_sc);
+        }
+        else
+        {
+            /* Pass the packet up to the protocol layers. */
+            NetReceive(NetRxPackets[rxIdx], length - 4);
+        }
+
+
+        /* Give the buffer back to the SCC. */
+        rtx->rxbd[rxIdx].cbd_datlen = 0;
+
+        /* wrap around buffer index when necessary */
+        if ((rxIdx + 1) >= PKTBUFSRX) {
+            rtx->rxbd[PKTBUFSRX - 1].cbd_sc = (BD_ENET_RX_WRAP |
+                                               BD_ENET_RX_EMPTY);
+            rxIdx = 0;
+        }
+        else {
+            rtx->rxbd[rxIdx].cbd_sc = BD_ENET_RX_EMPTY;
+            rxIdx++;
+        }
+    }
+    return length;
+}
+
+/**************************************************************
+ *
+ * SCC Ethernet Initialization Routine
+ *
+ *************************************************************/
+
+int eth_init(bd_t *bis)
+{
+    int i;
+    volatile immap_t *immr = (immap_t *)CFG_IMMR;
+    scc_enet_t *pram_ptr;
+    uint dpaddr;
+
+    rxIdx = 0;
+    txIdx = 0;
+
+    /* assign static pointer to BD area */
+    dpaddr = m8260_cpm_dpalloc(sizeof(RTXBD) + 2, 16);
+    rtx = (RTXBD *)&immr->im_dprambase[dpaddr];
+
+    /* 24.21 - (1-3): ioports have been set up already */
+
+    /* 24.21 - (4,5): connect SCC's tx and rx clocks, use NMSI for SCC */
+    immr->im_cpmux.cmx_uar = 0;
+    immr->im_cpmux.cmx_scr = ( (immr->im_cpmux.cmx_scr & ~CMXSCR_MASK) |
+                               CFG_CMXSCR_VALUE);
+
+
+    /* 24.21 (6) write RBASE and TBASE to parameter RAM */
+    pram_ptr = (scc_enet_t *)&(immr->im_dprambase[PROFF_ENET]);
+    pram_ptr->sen_genscc.scc_rbase = (unsigned int)(&rtx->rxbd[0]);
+    pram_ptr->sen_genscc.scc_tbase = (unsigned int)(&rtx->txbd[0]);
+
+    pram_ptr->sen_genscc.scc_rfcr = 0x18;  /* Nrml Ops and Mot byte ordering */
+    pram_ptr->sen_genscc.scc_tfcr = 0x18;  /* Mot byte ordering, Nrml access */
+
+    pram_ptr->sen_genscc.scc_mrblr = DBUF_LENGTH; /* max. package len 1520 */
+
+    pram_ptr->sen_cpres  = ~(0x0);        /* Preset CRC */
+    pram_ptr->sen_cmask  = 0xdebb20e3;    /* Constant Mask for CRC */
+
+
+    /* 24.21 - (7): Write INIT RX AND TX PARAMETERS to CPCR */
+    while(immr->im_cpm.cp_cpcr & CPM_CR_FLG);
+    immr->im_cpm.cp_cpcr = mk_cr_cmd(CPM_CR_ENET_PAGE,
+                                     CPM_CR_ENET_SBLOCK,
+                                     0x0c,
+                                     CPM_CR_INIT_TRX) | CPM_CR_FLG;
+
+    /* 24.21 - (8-18): Set up parameter RAM */
+    pram_ptr->sen_crcec  = 0x0;           /* Error Counter CRC (unused) */
+    pram_ptr->sen_alec   = 0x0;           /* Align Error Counter (unused) */
+    pram_ptr->sen_disfc  = 0x0;           /* Discard Frame Counter (unused) */
+
+    pram_ptr->sen_pads   = 0x8888;        /* Short Frame PAD Characters */
+
+    pram_ptr->sen_retlim = 15;            /* Retry Limit Threshold */
+
+    pram_ptr->sen_maxflr = 1518;  /* MAX Frame Length Register */
+    pram_ptr->sen_minflr = 64;            /* MIN Frame Length Register */
+
+    pram_ptr->sen_maxd1  = DBUF_LENGTH;   /* MAX DMA1 Length Register */
+    pram_ptr->sen_maxd2  = DBUF_LENGTH;   /* MAX DMA2 Length Register */
+
+    pram_ptr->sen_gaddr1 = 0x0;   /* Group Address Filter 1 (unused) */
+    pram_ptr->sen_gaddr2 = 0x0;   /* Group Address Filter 2 (unused) */
+    pram_ptr->sen_gaddr3 = 0x0;   /* Group Address Filter 3 (unused) */
+    pram_ptr->sen_gaddr4 = 0x0;   /* Group Address Filter 4 (unused) */
+
+#  define ea bis->bi_enetaddr
+    pram_ptr->sen_paddrh = (ea[5] << 8) + ea[4];
+    pram_ptr->sen_paddrm = (ea[3] << 8) + ea[2];
+    pram_ptr->sen_paddrl = (ea[1] << 8) + ea[0];
+#  undef ea
+
+    pram_ptr->sen_pper   = 0x0;   /* Persistence (unused) */
+
+    pram_ptr->sen_iaddr1 = 0x0;   /* Individual Address Filter 1 (unused) */
+    pram_ptr->sen_iaddr2 = 0x0;   /* Individual Address Filter 2 (unused) */
+    pram_ptr->sen_iaddr3 = 0x0;   /* Individual Address Filter 3 (unused) */
+    pram_ptr->sen_iaddr4 = 0x0;   /* Individual Address Filter 4 (unused) */
+
+    pram_ptr->sen_taddrh = 0x0;   /* Tmp Address (MSB) (unused) */
+    pram_ptr->sen_taddrm = 0x0;   /* Tmp Address (unused) */
+    pram_ptr->sen_taddrl = 0x0;   /* Tmp Address (LSB) (unused) */
+
+
+    /* 24.21 - (19): Initialize RxBD */
+    for (i = 0; i < PKTBUFSRX; i++)
+    {
+        rtx->rxbd[i].cbd_sc = BD_ENET_RX_EMPTY;
+        rtx->rxbd[i].cbd_datlen = 0;                  /* Reset */
+        rtx->rxbd[i].cbd_bufaddr = (uint)NetRxPackets[i];
+    }
+
+    rtx->rxbd[PKTBUFSRX - 1].cbd_sc |= BD_ENET_RX_WRAP;
+
+    /* 24.21 - (20): Initialize TxBD */
+    for (i = 0; i < TX_BUF_CNT; i++)
+    {
+        rtx->txbd[i].cbd_sc = (BD_ENET_TX_PAD  |
+                               BD_ENET_TX_LAST |
+                               BD_ENET_TX_TC);
+        rtx->txbd[i].cbd_datlen = 0;                  /* Reset */
+        rtx->txbd[i].cbd_bufaddr = (uint)&txbuf[i][0];
+    }
+
+    rtx->txbd[TX_BUF_CNT - 1].cbd_sc |= BD_ENET_TX_WRAP;
+
+    /* 24.21 - (21): Write 0xffff to SCCE */
+    immr->im_scc[CONFIG_ETHER_INDEX-1].scc_scce = ~(0x0);
+
+    /* 24.21 - (22): Write to SCCM to enable TXE, RXF, TXB events */
+    immr->im_scc[CONFIG_ETHER_INDEX-1].scc_sccm = (SCCE_ENET_TXE |
+                                                   SCCE_ENET_RXF |
+                                                   SCCE_ENET_TXB);
+
+    /* 24.21 - (23): we don't use ethernet interrupts */
+
+    /* 24.21 - (24): Clear GSMR_H to enable normal operations */
+    immr->im_scc[CONFIG_ETHER_INDEX-1].scc_gsmrh = 0;
+
+    /* 24.21 - (25): Clear GSMR_L to enable normal operations */
+    immr->im_scc[CONFIG_ETHER_INDEX-1].scc_gsmrl = (SCC_GSMRL_TCI        |
+                                                    SCC_GSMRL_TPL_48     |
+                                                    SCC_GSMRL_TPP_10     |
+                                                    SCC_GSMRL_MODE_ENET);
+
+    /* 24.21 - (26): Initialize DSR */
+    immr->im_scc[CONFIG_ETHER_INDEX-1].scc_dsr = 0xd555;
+
+    /* 24.21 - (27): Initialize PSMR2
+     *
+     * Settings:
+     *	CRC = 32-Bit CCITT
+     *	NIB = Begin searching for SFD 22 bits after RENA
+     *	FDE = Full Duplex Enable
+     *	BRO = Reject broadcast packets
+     *	PROMISCOUS = Catch all packets regardless of dest. MAC adress
+     */
+    immr->im_scc[CONFIG_ETHER_INDEX-1].scc_psmr   =	SCC_PSMR_ENCRC	|
+							SCC_PSMR_NIB22	|
+#if defined(CONFIG_SCC_ENET_FULL_DUPLEX)
+							SCC_PSMR_FDE	|
+#endif
+#if defined(CONFIG_SCC_ENET_NO_BROADCAST)
+							SCC_PSMR_BRO	|
+#endif
+#if defined(CONFIG_SCC_ENET_PROMISCOUS)
+							SCC_PSMR_PRO	|
+#endif
+							0;
+
+    /* 24.21 - (28): Write to GSMR_L to enable SCC */
+    immr->im_scc[CONFIG_ETHER_INDEX-1].scc_gsmrl |= (SCC_GSMRL_ENR |
+                                                     SCC_GSMRL_ENT);
+
+    return 1;
+}
+
+
+
+void eth_halt(void)
+{
+    volatile immap_t *immr = (immap_t *)CFG_IMMR;
+    immr->im_scc[CONFIG_ETHER_INDEX-1].scc_gsmrl &= ~(SCC_GSMRL_ENR |
+                                                      SCC_GSMRL_ENT);
+}
+
+#if 0
+void restart(void)
+{
+    volatile immap_t *immr = (immap_t *)CFG_IMMR;
+    immr->im_cpm.cp_scc[CONFIG_ETHER_INDEX-1].scc_gsmrl |= (SCC_GSMRL_ENR |
+                                                            SCC_GSMRL_ENT);
+}
+#endif
+
+#endif  /* CONFIG_ETHER_ON_SCC && CFG_CMD_NET */
+
diff --git a/cpu/ppc4xx/kgdb.S b/cpu/ppc4xx/kgdb.S
new file mode 100644
index 0000000..78681cd
--- /dev/null
+++ b/cpu/ppc4xx/kgdb.S
@@ -0,0 +1,78 @@
+/*
+ *  Copyright (C) 2000	Murray Jensen <Murray.Jensen@cmst.csiro.au>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * 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.
+ *
+ * This program 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
+ */
+
+#include <config.h>
+#include <command.h>
+#include <ppc4xx.h>
+#include <version.h>
+
+#define CONFIG_405GP 1		/* needed for Linux kernel header files */
+#define _LINUX_CONFIG_H 1	/* avoid reading Linux autoconf.h file	*/
+
+#include <ppc_asm.tmpl>
+#include <ppc_defs.h>
+
+#include <asm/cache.h>
+#include <asm/mmu.h>
+
+#if (CONFIG_COMMANDS & CFG_CMD_KGDB)
+ /*
+ * cache flushing routines for kgdb
+ */
+
+	.globl	kgdb_flush_cache_all
+kgdb_flush_cache_all:
+	/* icache */
+	iccci   r0,r0		/* iccci invalidates the entire I cache */
+	/* dcache */
+	addi    r6,0,0x0000     /* clear GPR 6 */
+        addi    r7,r0, 128 	/* do loop for # of dcache lines */
+				/* NOTE: dccci invalidates both */
+        mtctr   r7              /* ways in the D cache */
+..dcloop:
+        dccci   0,r6            /* invalidate line */
+        addi    r6,r6, 32	/* bump to next line */
+        bdnz    ..dcloop
+	blr
+
+	.globl	kgdb_flush_cache_range
+kgdb_flush_cache_range:
+	li	r5,CFG_CACHELINE_SIZE-1
+	andc	r3,r3,r5
+	subf	r4,r3,r4
+	add	r4,r4,r5
+	srwi.	r4,r4,CFG_CACHELINE_SHIFT
+	beqlr
+	mtctr	r4
+	mr	r6,r3
+1:	dcbst	0,r3
+	addi	r3,r3,CFG_CACHELINE_SIZE
+	bdnz	1b
+	sync			/* wait for dcbst's to get to ram */
+	mtctr	r4
+2:	icbi	0,r6
+	addi	r6,r6,CFG_CACHELINE_SIZE
+	bdnz	2b
+	SYNC
+	blr
+
+#endif /* CFG_CMD_KGDB */
diff --git a/cpu/ppc4xx/resetvec.S b/cpu/ppc4xx/resetvec.S
new file mode 100644
index 0000000..5350225
--- /dev/null
+++ b/cpu/ppc4xx/resetvec.S
@@ -0,0 +1,10 @@
+/* Copyright MontaVista Software Incorporated, 2000 */
+
+
+	.section .resetvec,"ax"
+#if defined(CONFIG_440)
+	b _start_440
+#else
+	b _start
+#endif
+
diff --git a/cpu/ppc4xx/serial.c b/cpu/ppc4xx/serial.c
new file mode 100644
index 0000000..d02ff2f
--- /dev/null
+++ b/cpu/ppc4xx/serial.c
@@ -0,0 +1,808 @@
+/*
+ * (C) Copyright 2000
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * 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.
+ *
+ * This program 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
+ */
+/*------------------------------------------------------------------------------+ */
+/*
+ * This source code has been made available to you by IBM on an AS-IS
+ * basis.  Anyone receiving this source is licensed under IBM
+ * copyrights to use it in any way he or she deems fit, including
+ * copying it, modifying it, compiling it, and redistributing it either
+ * with or without modifications.  No license under IBM patents or
+ * patent applications is to be implied by the copyright license.
+ *
+ * Any user of this software should understand that IBM cannot provide
+ * technical support for this software and will not be responsible for
+ * any consequences resulting from the use of this software.
+ *
+ * Any person who transfers this source code or any derivative work
+ * must include the IBM copyright notice, this paragraph, and the
+ * preceding two paragraphs in the transferred software.
+ *
+ * COPYRIGHT   I B M   CORPORATION 1995
+ * LICENSED MATERIAL  -  PROGRAM PROPERTY OF I B M
+ */
+/*------------------------------------------------------------------------------- */
+
+#include <common.h>
+#include <commproc.h>
+#include <asm/processor.h>
+#include <watchdog.h>
+#include "vecnum.h"
+
+#if CONFIG_SERIAL_SOFTWARE_FIFO
+#include <malloc.h>
+#endif
+
+/*****************************************************************************/
+#ifdef CONFIG_IOP480
+
+#define SPU_BASE         0x40000000
+
+#define spu_LineStat_rc  0x00	/* Line Status Register (Read/Clear) */
+#define spu_LineStat_w   0x04	/* Line Status Register (Set) */
+#define spu_Handshk_rc   0x08	/* Handshake Status Register (Read/Clear) */
+#define spu_Handshk_w    0x0c	/* Handshake Status Register (Set) */
+#define spu_BRateDivh    0x10	/* Baud rate divisor high */
+#define spu_BRateDivl    0x14	/* Baud rate divisor low */
+#define spu_CtlReg       0x18	/* Control Register */
+#define spu_RxCmd        0x1c	/* Rx Command Register */
+#define spu_TxCmd        0x20	/* Tx Command Register */
+#define spu_RxBuff       0x24	/* Rx data buffer */
+#define spu_TxBuff       0x24	/* Tx data buffer */
+
+/*-----------------------------------------------------------------------------+
+  | Line Status Register.
+  +-----------------------------------------------------------------------------*/
+#define asyncLSRport1           0x40000000
+#define asyncLSRport1set        0x40000004
+#define asyncLSRDataReady             0x80
+#define asyncLSRFramingError          0x40
+#define asyncLSROverrunError          0x20
+#define asyncLSRParityError           0x10
+#define asyncLSRBreakInterrupt        0x08
+#define asyncLSRTxHoldEmpty           0x04
+#define asyncLSRTxShiftEmpty          0x02
+
+/*-----------------------------------------------------------------------------+
+  | Handshake Status Register.
+  +-----------------------------------------------------------------------------*/
+#define asyncHSRport1           0x40000008
+#define asyncHSRport1set        0x4000000c
+#define asyncHSRDsr                   0x80
+#define asyncLSRCts                   0x40
+
+/*-----------------------------------------------------------------------------+
+  | Control Register.
+  +-----------------------------------------------------------------------------*/
+#define asyncCRport1            0x40000018
+#define asyncCRNormal                 0x00
+#define asyncCRLoopback               0x40
+#define asyncCRAutoEcho               0x80
+#define asyncCRDtr                    0x20
+#define asyncCRRts                    0x10
+#define asyncCRWordLength7            0x00
+#define asyncCRWordLength8            0x08
+#define asyncCRParityDisable          0x00
+#define asyncCRParityEnable           0x04
+#define asyncCREvenParity             0x00
+#define asyncCROddParity              0x02
+#define asyncCRStopBitsOne            0x00
+#define asyncCRStopBitsTwo            0x01
+#define asyncCRDisableDtrRts          0x00
+
+/*-----------------------------------------------------------------------------+
+  | Receiver Command Register.
+  +-----------------------------------------------------------------------------*/
+#define asyncRCRport1           0x4000001c
+#define asyncRCRDisable               0x00
+#define asyncRCREnable                0x80
+#define asyncRCRIntDisable            0x00
+#define asyncRCRIntEnabled            0x20
+#define asyncRCRDMACh2                0x40
+#define asyncRCRDMACh3                0x60
+#define asyncRCRErrorInt              0x10
+#define asyncRCRPauseEnable           0x08
+
+/*-----------------------------------------------------------------------------+
+  | Transmitter Command Register.
+  +-----------------------------------------------------------------------------*/
+#define asyncTCRport1           0x40000020
+#define asyncTCRDisable               0x00
+#define asyncTCREnable                0x80
+#define asyncTCRIntDisable            0x00
+#define asyncTCRIntEnabled            0x20
+#define asyncTCRDMACh2                0x40
+#define asyncTCRDMACh3                0x60
+#define asyncTCRTxEmpty               0x10
+#define asyncTCRErrorInt              0x08
+#define asyncTCRStopPause             0x04
+#define asyncTCRBreakGen              0x02
+
+/*-----------------------------------------------------------------------------+
+  | Miscellanies defines.
+  +-----------------------------------------------------------------------------*/
+#define asyncTxBufferport1      0x40000024
+#define asyncRxBufferport1      0x40000024
+#define asyncDLABLsbport1       0x40000014
+#define asyncDLABMsbport1       0x40000010
+#define asyncXOFFchar                 0x13
+#define asyncXONchar                  0x11
+
+
+/*
+ * Minimal serial functions needed to use one of the SMC ports
+ * as serial console interface.
+ */
+
+int serial_init (void)
+{
+	DECLARE_GLOBAL_DATA_PTR;
+
+	volatile char val;
+	unsigned short br_reg;
+
+	br_reg = ((((CONFIG_CPUCLOCK * 1000000) / 16) / gd->baudrate) - 1);
+
+	/*
+	 * Init onboard UART
+	 */
+	out8 (SPU_BASE + spu_LineStat_rc, 0x78); /* Clear all bits in Line Status Reg */
+	out8 (SPU_BASE + spu_BRateDivl, (br_reg & 0x00ff)); /* Set baud rate divisor... */
+	out8 (SPU_BASE + spu_BRateDivh, ((br_reg & 0xff00) >> 8)); /* ... */
+	out8 (SPU_BASE + spu_CtlReg, 0x08);	/* Set 8 bits, no parity and 1 stop bit */
+	out8 (SPU_BASE + spu_RxCmd, 0xb0);	/* Enable Rx */
+	out8 (SPU_BASE + spu_TxCmd, 0x9c);	/* Enable Tx */
+	out8 (SPU_BASE + spu_Handshk_rc, 0xff);	/* Clear Handshake */
+	val = in8 (SPU_BASE + spu_RxBuff);	/* Dummy read, to clear receiver */
+
+	return (0);
+}
+
+
+void serial_setbrg (void)
+{
+	DECLARE_GLOBAL_DATA_PTR;
+
+	unsigned short br_reg;
+
+	br_reg = ((((CONFIG_CPUCLOCK * 1000000) / 16) / gd->baudrate) - 1);
+
+	out8 (SPU_BASE + spu_BRateDivl, (br_reg & 0x00ff)); /* Set baud rate divisor... */
+	out8 (SPU_BASE + spu_BRateDivh, ((br_reg & 0xff00) >> 8)); /* ... */
+}
+
+
+void serial_putc (const char c)
+{
+	if (c == '\n')
+		serial_putc ('\r');
+
+	/* load status from handshake register */
+	if (in8 (SPU_BASE + spu_Handshk_rc) != 00)
+		out8 (SPU_BASE + spu_Handshk_rc, 0xff);	/* Clear Handshake */
+
+	out8 (SPU_BASE + spu_TxBuff, c);	/* Put char */
+
+	while ((in8 (SPU_BASE + spu_LineStat_rc) & 04) != 04) {
+		if (in8 (SPU_BASE + spu_Handshk_rc) != 00)
+			out8 (SPU_BASE + spu_Handshk_rc, 0xff);	/* Clear Handshake */
+	}
+}
+
+
+void serial_puts (const char *s)
+{
+	while (*s) {
+		serial_putc (*s++);
+	}
+}
+
+
+int serial_getc ()
+{
+	unsigned char status = 0;
+
+	while (1) {
+		status = in8 (asyncLSRport1);
+		if ((status & asyncLSRDataReady) != 0x0) {
+			break;
+		}
+		if ((status & ( asyncLSRFramingError |
+				asyncLSROverrunError |
+				asyncLSRParityError  |
+				asyncLSRBreakInterrupt )) != 0) {
+			(void) out8 (asyncLSRport1,
+				     asyncLSRFramingError |
+				     asyncLSROverrunError |
+				     asyncLSRParityError  |
+				     asyncLSRBreakInterrupt );
+		}
+	}
+	return (0x000000ff & (int) in8 (asyncRxBufferport1));
+}
+
+
+int serial_tstc ()
+{
+	unsigned char status;
+
+	status = in8 (asyncLSRport1);
+	if ((status & asyncLSRDataReady) != 0x0) {
+		return (1);
+	}
+	if ((status & ( asyncLSRFramingError |
+			asyncLSROverrunError |
+			asyncLSRParityError  |
+			asyncLSRBreakInterrupt )) != 0) {
+		(void) out8 (asyncLSRport1,
+			     asyncLSRFramingError |
+			     asyncLSROverrunError |
+			     asyncLSRParityError  |
+			     asyncLSRBreakInterrupt);
+	}
+	return 0;
+}
+
+#endif	/* CONFIG_IOP480 */
+
+
+/*****************************************************************************/
+#if defined(CONFIG_405GP) || defined(CONFIG_405CR) || defined(CONFIG_440)
+
+#if defined(CONFIG_440)
+#define UART0_BASE  CFG_PERIPHERAL_BASE + 0x00000200
+#define UART1_BASE  CFG_PERIPHERAL_BASE + 0x00000300
+#define CR0_MASK        0x3fff0000
+#define CR0_EXTCLK_ENA  0x00600000
+#define CR0_UDIV_POS    16
+#else
+#define UART_BASE_PTR   0xF800FFFC;	/* pointer to uart base */
+#define UART0_BASE      0xef600300
+#define UART1_BASE      0xef600400
+#define CR0_MASK        0x00001fff
+#define CR0_EXTCLK_ENA  0x00000c00
+#define CR0_UDIV_POS    1
+#endif
+
+#define UART_RBR    0x00
+#define UART_THR    0x00
+#define UART_IER    0x01
+#define UART_IIR    0x02
+#define UART_FCR    0x02
+#define UART_LCR    0x03
+#define UART_MCR    0x04
+#define UART_LSR    0x05
+#define UART_MSR    0x06
+#define UART_SCR    0x07
+#define UART_DLL    0x00
+#define UART_DLM    0x01
+
+/*-----------------------------------------------------------------------------+
+  | Line Status Register.
+  +-----------------------------------------------------------------------------*/
+/*#define asyncLSRport1           UART0_BASE+0x05 */
+#define asyncLSRDataReady1            0x01
+#define asyncLSROverrunError1         0x02
+#define asyncLSRParityError1          0x04
+#define asyncLSRFramingError1         0x08
+#define asyncLSRBreakInterrupt1       0x10
+#define asyncLSRTxHoldEmpty1          0x20
+#define asyncLSRTxShiftEmpty1         0x40
+#define asyncLSRRxFifoError1          0x80
+
+/*-----------------------------------------------------------------------------+
+  | Miscellanies defines.
+  +-----------------------------------------------------------------------------*/
+/*#define asyncTxBufferport1      UART0_BASE+0x00 */
+/*#define asyncRxBufferport1      UART0_BASE+0x00 */
+
+
+#if CONFIG_SERIAL_SOFTWARE_FIFO
+/*-----------------------------------------------------------------------------+
+  | Fifo
+  +-----------------------------------------------------------------------------*/
+typedef struct {
+	char *rx_buffer;
+	ulong rx_put;
+	ulong rx_get;
+} serial_buffer_t;
+
+volatile static serial_buffer_t buf_info;
+#endif
+
+
+#if defined(CONFIG_440) && !defined(CFG_EXT_SERIAL_CLOCK)
+static void serial_divs (int baudrate, unsigned long *pudiv,
+			 unsigned short *pbdiv )
+{
+	sys_info_t	sysinfo;
+	unsigned long div;		/* total divisor udiv * bdiv */
+	unsigned long umin;		/* minimum udiv	*/
+	unsigned short diff;    /* smallest diff */
+	unsigned long udiv;     /* best udiv */
+
+	unsigned short idiff;   /* current diff */
+	unsigned short ibdiv;   /* current bdiv */
+	unsigned long i;
+	unsigned long est;      /* current estimate */
+
+	get_sys_info( &sysinfo );
+
+	udiv = 32;     /* Assume lowest possible serial clk */
+	div = sysinfo.freqPLB/(16*baudrate); /* total divisor */
+	umin = sysinfo.pllOpbDiv<<1; /* 2 x OPB divisor */
+	diff = 32;      /* highest possible */
+
+	/* i is the test udiv value -- start with the largest
+	 * possible (32) to minimize serial clock and constrain
+	 * search to umin.
+	 */
+	for( i = 32; i > umin; i-- ){
+		ibdiv = div/i;
+		est = i * ibdiv;
+		idiff = (est > div) ? (est-div) : (div-est);
+		if( idiff == 0 ){
+			udiv = i;
+			break;      /* can't do better */
+		}
+		else if( idiff < diff ){
+			udiv = i;       /* best so far */
+			diff = idiff;   /* update lowest diff*/
+		}
+	}
+
+	*pudiv = udiv;
+	*pbdiv = div/udiv;
+
+}
+#endif /* defined(CONFIG_440) && !defined(CFG_EXT_SERIAL_CLK */
+
+
+/*
+ * Minimal serial functions needed to use one of the SMC ports
+ * as serial console interface.
+ */
+
+#if defined(CONFIG_440)
+int serial_init (void)
+{
+	DECLARE_GLOBAL_DATA_PTR;
+
+	unsigned long reg;
+	unsigned long udiv;
+	unsigned short bdiv;
+	volatile char val;
+#ifdef CFG_EXT_SERIAL_CLOCK
+	unsigned long tmp;
+#endif
+
+	reg = mfdcr(cntrl0) & ~CR0_MASK;
+#ifdef CFG_EXT_SERIAL_CLOCK
+	reg |= CR0_EXTCLK_ENA;
+	udiv = 1;
+	tmp  = gd->baudrate * 16;
+	bdiv = (CFG_EXT_SERIAL_CLOCK + tmp / 2) / tmp;
+#else
+	/* For 440, the cpu clock is on divider chain A, UART on divider
+	 * chain B ... so cpu clock is irrelevant. Get the "optimized"
+	 * values that are subject to the 1/2 opb clock constraint
+	 */
+	serial_divs (gd->baudrate, &udiv, &bdiv);
+#endif
+
+	reg |= (udiv - 1) << CR0_UDIV_POS;	/* set the UART divisor */
+	mtdcr (cntrl0, reg);
+
+	out8 (UART0_BASE + UART_LCR, 0x80);	/* set DLAB bit */
+	out8 (UART0_BASE + UART_DLL, bdiv);	/* set baudrate divisor */
+	out8 (UART0_BASE + UART_DLM, bdiv >> 8);/* set baudrate divisor */
+	out8 (UART0_BASE + UART_LCR, 0x03);	/* clear DLAB; set 8 bits, no parity */
+	out8 (UART0_BASE + UART_FCR, 0x00);	/* disable FIFO */
+	out8 (UART0_BASE + UART_MCR, 0x00);	/* no modem control DTR RTS */
+	val = in8 (UART0_BASE + UART_LSR);	/* clear line status */
+	val = in8 (UART0_BASE + UART_RBR);	/* read receive buffer */
+	out8 (UART0_BASE + UART_SCR, 0x00);	/* set scratchpad */
+	out8 (UART0_BASE + UART_IER, 0x00);	/* set interrupt enable reg */
+
+	return (0);
+}
+
+#else /* !defined(CONFIG_440) */
+
+int serial_init (void)
+{
+	DECLARE_GLOBAL_DATA_PTR;
+
+	unsigned long reg;
+	unsigned long tmp;
+	unsigned long clk;
+	unsigned long udiv;
+	unsigned short bdiv;
+	volatile char val;
+
+	reg = mfdcr(cntrl0) & ~CR0_MASK;
+#ifdef CFG_EXT_SERIAL_CLOCK
+	clk = CFG_EXT_SERIAL_CLOCK;
+	udiv = 1;
+	reg |= CR0_EXTCLK_ENA;
+#else
+	clk = gd->cpu_clk;
+#ifdef CFG_405_UART_ERRATA_59
+	udiv = 31;			/* Errata 59: stuck at 31 */
+#else
+	tmp = CFG_BASE_BAUD * 16;
+	udiv = (clk + tmp / 2) / tmp;
+#endif
+#endif
+
+	reg |= (udiv - 1) << CR0_UDIV_POS;	/* set the UART divisor */
+	mtdcr (cntrl0, reg);
+
+	tmp = gd->baudrate * udiv * 16;
+	bdiv = (clk + tmp / 2) / tmp;
+
+	out8 (UART0_BASE + UART_LCR, 0x80);	/* set DLAB bit */
+	out8 (UART0_BASE + UART_DLL, bdiv);	/* set baudrate divisor */
+	out8 (UART0_BASE + UART_DLM, bdiv >> 8);/* set baudrate divisor */
+	out8 (UART0_BASE + UART_LCR, 0x03);	/* clear DLAB; set 8 bits, no parity */
+	out8 (UART0_BASE + UART_FCR, 0x00);	/* disable FIFO */
+	out8 (UART0_BASE + UART_MCR, 0x00);	/* no modem control DTR RTS */
+	val = in8 (UART0_BASE + UART_LSR);	/* clear line status */
+	val = in8 (UART0_BASE + UART_RBR);	/* read receive buffer */
+	out8 (UART0_BASE + UART_SCR, 0x00);	/* set scratchpad */
+	out8 (UART0_BASE + UART_IER, 0x00);	/* set interrupt enable reg */
+
+	return (0);
+}
+
+#endif /* if defined(CONFIG_440) */
+
+void serial_setbrg (void)
+{
+	DECLARE_GLOBAL_DATA_PTR;
+
+	unsigned long tmp;
+	unsigned long clk;
+	unsigned long udiv;
+	unsigned short bdiv;
+
+#ifdef CFG_EXT_SERIAL_CLOCK
+	clk = CFG_EXT_SERIAL_CLOCK;
+#else
+	clk = gd->cpu_clk;
+#endif
+	udiv = ((mfdcr (cntrl0) & 0x3e) >> 1) + 1;
+	tmp = gd->baudrate * udiv * 16;
+	bdiv = (clk + tmp / 2) / tmp;
+
+	out8 (UART0_BASE + UART_LCR, 0x80);	/* set DLAB bit */
+	out8 (UART0_BASE + UART_DLL, bdiv);	/* set baudrate divisor */
+	out8 (UART0_BASE + UART_DLM, bdiv >> 8);/* set baudrate divisor */
+	out8 (UART0_BASE + UART_LCR, 0x03);	/* clear DLAB; set 8 bits, no parity */
+}
+
+
+void serial_putc (const char c)
+{
+	int i;
+
+	if (c == '\n')
+		serial_putc ('\r');
+
+	/* check THRE bit, wait for transmiter available */
+	for (i = 1; i < 3500; i++) {
+		if ((in8 (UART0_BASE + UART_LSR) & 0x20) == 0x20)
+			break;
+		udelay (100);
+	}
+	out8 (UART0_BASE + UART_THR, c);	/* put character out */
+}
+
+
+void serial_puts (const char *s)
+{
+	while (*s) {
+		serial_putc (*s++);
+	}
+}
+
+
+int serial_getc ()
+{
+	unsigned char status = 0;
+
+	while (1) {
+#if defined(CONFIG_HW_WATCHDOG)
+		WATCHDOG_RESET ();	/* Reset HW Watchdog, if needed */
+#endif	/* CONFIG_HW_WATCHDOG */
+		status = in8 (UART0_BASE + UART_LSR);
+		if ((status & asyncLSRDataReady1) != 0x0) {
+			break;
+		}
+		if ((status & ( asyncLSRFramingError1 |
+				asyncLSROverrunError1 |
+				asyncLSRParityError1  |
+				asyncLSRBreakInterrupt1 )) != 0) {
+			out8 (UART0_BASE + UART_LSR,
+			      asyncLSRFramingError1 |
+			      asyncLSROverrunError1 |
+			      asyncLSRParityError1  |
+			      asyncLSRBreakInterrupt1);
+		}
+	}
+	return (0x000000ff & (int) in8 (UART0_BASE));
+}
+
+
+int serial_tstc ()
+{
+	unsigned char status;
+
+	status = in8 (UART0_BASE + UART_LSR);
+	if ((status & asyncLSRDataReady1) != 0x0) {
+		return (1);
+	}
+	if ((status & ( asyncLSRFramingError1 |
+			asyncLSROverrunError1 |
+			asyncLSRParityError1  |
+			asyncLSRBreakInterrupt1 )) != 0) {
+		out8 (UART0_BASE + UART_LSR,
+		      asyncLSRFramingError1 |
+		      asyncLSROverrunError1 |
+		      asyncLSRParityError1  |
+		      asyncLSRBreakInterrupt1);
+	}
+	return 0;
+}
+
+
+#if CONFIG_SERIAL_SOFTWARE_FIFO
+
+void serial_isr (void *arg)
+{
+	int space;
+	int c;
+	const int rx_get = buf_info.rx_get;
+	int rx_put = buf_info.rx_put;
+
+	if (rx_get <= rx_put) {
+		space = CONFIG_SERIAL_SOFTWARE_FIFO - (rx_put - rx_get);
+	} else {
+		space = rx_get - rx_put;
+	}
+	while (serial_tstc ()) {
+		c = serial_getc ();
+		if (space) {
+			buf_info.rx_buffer[rx_put++] = c;
+			space--;
+		}
+		if (rx_put == CONFIG_SERIAL_SOFTWARE_FIFO)
+			rx_put = 0;
+		if (space < CONFIG_SERIAL_SOFTWARE_FIFO / 4) {
+			/* Stop flow by setting RTS inactive */
+			out8 (UART0_BASE + UART_MCR,
+			      in8 (UART0_BASE + UART_MCR) & (0xFF ^ 0x02));
+		}
+	}
+	buf_info.rx_put = rx_put;
+}
+
+void serial_buffered_init (void)
+{
+	serial_puts ("Switching to interrupt driven serial input mode.\n");
+	buf_info.rx_buffer = malloc (CONFIG_SERIAL_SOFTWARE_FIFO);
+	buf_info.rx_put = 0;
+	buf_info.rx_get = 0;
+
+	if (in8 (UART0_BASE + UART_MSR) & 0x10) {
+		serial_puts ("Check CTS signal present on serial port: OK.\n");
+	} else {
+		serial_puts ("WARNING: CTS signal not present on serial port.\n");
+	}
+
+	irq_install_handler ( VECNUM_U0 /*UART0 */ /*int vec */ ,
+			      serial_isr /*interrupt_handler_t *handler */ ,
+			      (void *) &buf_info /*void *arg */ );
+
+	/* Enable "RX Data Available" Interrupt on UART */
+	/* out8(UART0_BASE + UART_IER, in8(UART0_BASE + UART_IER) |0x01); */
+	out8 (UART0_BASE + UART_IER, 0x01);
+	/* Set DTR active */
+	out8 (UART0_BASE + UART_MCR, in8 (UART0_BASE + UART_MCR) | 0x01);
+	/* Start flow by setting RTS active */
+	out8 (UART0_BASE + UART_MCR, in8 (UART0_BASE + UART_MCR) | 0x02);
+	/* Setup UART FIFO: RX trigger level: 4 byte, Enable FIFO */
+	out8 (UART0_BASE + UART_FCR, (1 << 6) | 1);
+}
+
+void serial_buffered_putc (const char c)
+{
+	/* Wait for CTS */
+#if defined(CONFIG_HW_WATCHDOG)
+	while (!(in8 (UART0_BASE + UART_MSR) & 0x10))
+		WATCHDOG_RESET ();
+#else
+	while (!(in8 (UART0_BASE + UART_MSR) & 0x10));
+#endif
+	serial_putc (c);
+}
+
+void serial_buffered_puts (const char *s)
+{
+	serial_puts (s);
+}
+
+int serial_buffered_getc (void)
+{
+	int space;
+	int c;
+	int rx_get = buf_info.rx_get;
+	int rx_put;
+
+#if defined(CONFIG_HW_WATCHDOG)
+	while (rx_get == buf_info.rx_put)
+		WATCHDOG_RESET ();
+#else
+	while (rx_get == buf_info.rx_put);
+#endif
+	c = buf_info.rx_buffer[rx_get++];
+	if (rx_get == CONFIG_SERIAL_SOFTWARE_FIFO)
+		rx_get = 0;
+	buf_info.rx_get = rx_get;
+
+	rx_put = buf_info.rx_put;
+	if (rx_get <= rx_put) {
+		space = CONFIG_SERIAL_SOFTWARE_FIFO - (rx_put - rx_get);
+	} else {
+		space = rx_get - rx_put;
+	}
+	if (space > CONFIG_SERIAL_SOFTWARE_FIFO / 2) {
+		/* Start flow by setting RTS active */
+		out8 (UART0_BASE + UART_MCR, in8 (UART0_BASE + UART_MCR) | 0x02);
+	}
+
+	return c;
+}
+
+int serial_buffered_tstc (void)
+{
+	return (buf_info.rx_get != buf_info.rx_put) ? 1 : 0;
+}
+
+#endif	/* CONFIG_SERIAL_SOFTWARE_FIFO */
+
+
+#if (CONFIG_COMMANDS & CFG_CMD_KGDB)
+/*
+  AS HARNOIS : according to CONFIG_KGDB_SER_INDEX kgdb uses serial port
+  number 0 or number 1
+  - if CONFIG_KGDB_SER_INDEX = 1 => serial port number 0 :
+  configuration has been already done
+  - if CONFIG_KGDB_SER_INDEX = 2 => serial port number 1 :
+  configure port 1 for serial I/O with rate = CONFIG_KGDB_BAUDRATE
+*/
+#if (CONFIG_KGDB_SER_INDEX & 2)
+void kgdb_serial_init (void)
+{
+	DECLARE_GLOBAL_DATA_PTR;
+
+	volatile char val;
+	unsigned short br_reg;
+
+	get_clocks ();
+	br_reg = (((((gd->cpu_clk / 16) / 18) * 10) / CONFIG_KGDB_BAUDRATE) +
+		  5) / 10;
+	/*
+	 * Init onboard 16550 UART
+	 */
+	out8 (UART1_BASE + UART_LCR, 0x80);	/* set DLAB bit */
+	out8 (UART1_BASE + UART_DLL, (br_reg & 0x00ff));	/* set divisor for 9600 baud */
+	out8 (UART1_BASE + UART_DLM, ((br_reg & 0xff00) >> 8));	/* set divisor for 9600 baud */
+	out8 (UART1_BASE + UART_LCR, 0x03);	/* line control 8 bits no parity */
+	out8 (UART1_BASE + UART_FCR, 0x00);	/* disable FIFO */
+	out8 (UART1_BASE + UART_MCR, 0x00);	/* no modem control DTR RTS */
+	val = in8 (UART1_BASE + UART_LSR);	/* clear line status */
+	val = in8 (UART1_BASE + UART_RBR);	/* read receive buffer */
+	out8 (UART1_BASE + UART_SCR, 0x00);	/* set scratchpad */
+	out8 (UART1_BASE + UART_IER, 0x00);	/* set interrupt enable reg */
+}
+
+
+void putDebugChar (const char c)
+{
+	if (c == '\n')
+		serial_putc ('\r');
+
+	out8 (UART1_BASE + UART_THR, c);	/* put character out */
+
+	/* check THRE bit, wait for transfer done */
+	while ((in8 (UART1_BASE + UART_LSR) & 0x20) != 0x20);
+}
+
+
+void putDebugStr (const char *s)
+{
+	while (*s) {
+		serial_putc (*s++);
+	}
+}
+
+
+int getDebugChar (void)
+{
+	unsigned char status = 0;
+
+	while (1) {
+		status = in8 (UART1_BASE + UART_LSR);
+		if ((status & asyncLSRDataReady1) != 0x0) {
+			break;
+		}
+		if ((status & ( asyncLSRFramingError1 |
+				asyncLSROverrunError1 |
+				asyncLSRParityError1  |
+				asyncLSRBreakInterrupt1 )) != 0) {
+			out8 (UART1_BASE + UART_LSR,
+			      asyncLSRFramingError1 |
+			      asyncLSROverrunError1 |
+			      asyncLSRParityError1  |
+			      asyncLSRBreakInterrupt1);
+		}
+	}
+	return (0x000000ff & (int) in8 (UART1_BASE));
+}
+
+
+void kgdb_interruptible (int yes)
+{
+	return;
+}
+
+#else	/* ! (CONFIG_KGDB_SER_INDEX & 2) */
+
+void kgdb_serial_init (void)
+{
+	serial_printf ("[on serial] ");
+}
+
+void putDebugChar (int c)
+{
+	serial_putc (c);
+}
+
+void putDebugStr (const char *str)
+{
+	serial_puts (str);
+}
+
+int getDebugChar (void)
+{
+	return serial_getc ();
+}
+
+void kgdb_interruptible (int yes)
+{
+	return;
+}
+#endif	/* (CONFIG_KGDB_SER_INDEX & 2) */
+#endif	/* CFG_CMD_KGDB */
+
+#endif	/* CONFIG_405GP || CONFIG_405CR */
diff --git a/cpu/ppc4xx/spd_sdram.c b/cpu/ppc4xx/spd_sdram.c
new file mode 100644
index 0000000..fc0c980
--- /dev/null
+++ b/cpu/ppc4xx/spd_sdram.c
@@ -0,0 +1,1764 @@
+/*
+ * (C) Copyright 2001
+ * Bill Hunter, Wave 7 Optics, williamhunter@attbi.com
+ *
+ * Based on code by:
+ *
+ * Kenneth Johansson ,Ericsson Business Innovation.
+ * kenneth.johansson@inn.ericsson.se
+ *
+ * hacked up by bill hunter. fixed so we could run before
+ * serial_init and console_init. previous version avoided this by
+ * running out of cache memory during serial/console init, then running
+ * this code later.
+ *
+ * (C) Copyright 2002
+ * Jun Gu, Artesyn Technology, jung@artesyncp.com
+ * Support for IBM 440 based on OpenBIOS draminit.c from IBM.
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * 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.
+ *
+ * This program 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
+ */
+
+#include <common.h>
+#include <asm/processor.h>
+#include <i2c.h>
+#include <ppc4xx.h>
+
+#ifdef CONFIG_SPD_EEPROM
+
+/*
+ * Set default values
+ */
+#ifndef	CFG_I2C_SPEED
+#define	CFG_I2C_SPEED	50000
+#endif
+
+#ifndef	CFG_I2C_SLAVE
+#define	CFG_I2C_SLAVE	0xFE
+#endif
+
+#ifndef  CONFIG_440              /* for 405 WALNUT board */
+
+#define  SDRAM0_CFG_DCE          0x80000000
+#define  SDRAM0_CFG_SRE          0x40000000
+#define  SDRAM0_CFG_PME          0x20000000
+#define  SDRAM0_CFG_MEMCHK       0x10000000
+#define  SDRAM0_CFG_REGEN        0x08000000
+#define  SDRAM0_CFG_ECCDD        0x00400000
+#define  SDRAM0_CFG_EMDULR       0x00200000
+#define  SDRAM0_CFG_DRW_SHIFT    (31-6)
+#define  SDRAM0_CFG_BRPF_SHIFT   (31-8)
+
+#define  SDRAM0_TR_CASL_SHIFT    (31-8)
+#define  SDRAM0_TR_PTA_SHIFT     (31-13)
+#define  SDRAM0_TR_CTP_SHIFT     (31-15)
+#define  SDRAM0_TR_LDF_SHIFT     (31-17)
+#define  SDRAM0_TR_RFTA_SHIFT    (31-29)
+#define  SDRAM0_TR_RCD_SHIFT     (31-31)
+
+#define  SDRAM0_RTR_SHIFT        (31-15)
+#define  SDRAM0_ECCCFG_SHIFT     (31-11)
+
+/* SDRAM0_CFG enable macro  */
+#define SDRAM0_CFG_BRPF(x) ( ( x & 0x3)<< SDRAM0_CFG_BRPF_SHIFT )
+
+#define SDRAM0_BXCR_SZ_MASK  0x000e0000
+#define SDRAM0_BXCR_AM_MASK  0x0000e000
+
+#define SDRAM0_BXCR_SZ_SHIFT (31-14)
+#define SDRAM0_BXCR_AM_SHIFT (31-18)
+
+#define SDRAM0_BXCR_SZ(x)  ( (( x << SDRAM0_BXCR_SZ_SHIFT) & SDRAM0_BXCR_SZ_MASK) )
+#define SDRAM0_BXCR_AM(x)  ( (( x << SDRAM0_BXCR_AM_SHIFT) & SDRAM0_BXCR_AM_MASK) )
+
+#ifdef CONFIG_W7O
+# define SPD_ERR(x) do { return 0; } while (0)
+#else
+# define SPD_ERR(x) do { printf(x); hang(); } while (0)
+#endif
+
+/*
+ * what we really want is
+ * (1/hertz) but we don't want to use floats so multiply with 10E9
+ *
+ * The error needs to be on the safe side so we want the floor function.
+ * This means we get an exact value or we calculate that our bus frequency is
+ * a bit faster than it really is and thus we don't progam the sdram controller
+ * to run to fast
+ */
+#define sdram_HZ_to_ns(hertz) (1000000000/(hertz))
+
+/* function prototypes */
+int spd_read(uint addr);			/* prototype */
+
+
+/*
+ * This function is reading data from the DIMM module EEPROM over the SPD bus
+ * and uses that to program the sdram controller.
+ *
+ * This works on boards that has the same schematics that the IBM walnut has.
+ *
+ * BUG: Don't handle ECC memory
+ * BUG: A few values in the TR register is currently hardcoded
+ */
+
+long int spd_sdram(void)
+{
+	int bus_period,tmp,row,col;
+	int total_size,bank_size,bank_code;
+	int ecc_on;
+	int mode = 4;
+	int bank_cnt = 1;
+
+	int sdram0_pmit=0x07c00000;
+	int sdram0_besr0=-1;
+	int sdram0_besr1=-1;
+	int sdram0_eccesr=-1;
+	int sdram0_ecccfg;
+
+	int sdram0_rtr=0;
+	int sdram0_tr=0;
+
+	int sdram0_b0cr;
+	int sdram0_b1cr;
+	int sdram0_b2cr;
+	int sdram0_b3cr;
+
+	int sdram0_cfg=0;
+
+	int t_rp;
+	int t_rcd;
+	int t_rc = 70; /* This value not available in SPD_EEPROM */
+	int min_cas = 2;
+
+	/*
+	 * Make sure I2C controller is initialized
+	 * before continuing.
+	 */
+	i2c_init(CFG_I2C_SPEED, CFG_I2C_SLAVE);
+
+	/*
+	 * Calculate the bus period, we do it this
+	 * way to minimize stack utilization.
+	 */
+    	tmp = (mfdcr(pllmd) >> (31-6)) & 0xf;	/* get FBDV bits */
+	tmp = CONFIG_SYS_CLK_FREQ * tmp;	/* get plb freq */
+	bus_period = sdram_HZ_to_ns(tmp);	/* get sdram speed */
+
+     	/* Make shure we are using SDRAM */
+	if (spd_read(2) != 0x04){
+          SPD_ERR("SDRAM - non SDRAM memory module found\n");
+     	  }
+
+/*------------------------------------------------------------------
+  configure memory timing register
+
+  data from DIMM:
+  27	IN Row Precharge Time ( t RP)
+  29	MIN RAS to CAS Delay ( t RCD)
+  127   Component and Clock Detail ,clk0-clk3, junction temp, CAS
+  -------------------------------------------------------------------*/
+
+     /*
+      * first figure out which cas latency mode to use
+      * use the min supported mode
+      */
+
+	tmp = spd_read(127) & 0x6;
+     if(tmp == 0x02){      	   /* only cas = 2 supported */
+     	  min_cas = 2;
+/*     	  t_ck = spd_read(9); */
+/*     	  t_ac = spd_read(10); */
+	  }
+     else if (tmp == 0x04){         /* only cas = 3 supported */
+     	  min_cas = 3;
+/*     	  t_ck = spd_read(9); */
+/*     	  t_ac = spd_read(10); */
+	  }
+     else if (tmp == 0x06){         /* 2,3 supported, so use 2 */
+     	  min_cas = 2;
+/*     	  t_ck = spd_read(23); */
+/*     	  t_ac = spd_read(24); */
+	  }
+     else {
+	     SPD_ERR("SDRAM - unsupported CAS latency \n");
+	}
+
+     /* get some timing values, t_rp,t_rcd
+     */
+     t_rp = spd_read(27);
+     t_rcd = spd_read(29);
+
+
+     /* The following timing calcs subtract 1 before deviding.
+      * this has effect of using ceiling intead of floor rounding,
+      * and also subtracting 1 to convert number to reg value
+      */
+     /* set up CASL */
+     sdram0_tr = (min_cas - 1) << SDRAM0_TR_CASL_SHIFT;
+     /* set up PTA */
+     sdram0_tr |= (((t_rp - 1)/bus_period) & 0x3) << SDRAM0_TR_PTA_SHIFT;
+     /* set up CTP */
+     tmp = ((t_rc - t_rcd - t_rp -1) / bus_period) & 0x3;
+     if(tmp<1) SPD_ERR("SDRAM - unsupported prech to act time (Trp)\n");
+     sdram0_tr |= tmp << SDRAM0_TR_CTP_SHIFT;
+     /* set LDF	= 2 cycles, reg value = 1 */
+     sdram0_tr |= 1 << SDRAM0_TR_LDF_SHIFT;
+     /* set RFTA = t_rfc/bus_period, use t_rfc = t_rc */
+	tmp = ((t_rc - 1) / bus_period)-4;
+	if(tmp<0)tmp=0;
+	if(tmp>6)tmp=6;
+	sdram0_tr |= tmp << SDRAM0_TR_RFTA_SHIFT;
+     /* set RCD = t_rcd/bus_period*/
+     sdram0_tr |= (((t_rcd - 1) / bus_period) &0x3) << SDRAM0_TR_RCD_SHIFT ;
+
+
+/*------------------------------------------------------------------
+  configure RTR register
+  -------------------------------------------------------------------*/
+     row = spd_read(3);
+     col = spd_read(4);
+     tmp = spd_read(12) & 0x7f ; /* refresh type less self refresh bit */
+     switch(tmp){
+	case 0x00:
+	  tmp=15625;
+	  break;
+	case 0x01:
+	  tmp=15625/4;
+	  break;
+	case 0x02:
+	  tmp=15625/2;
+	  break;
+	case 0x03:
+	  tmp=15625*2;
+	  break;
+	case 0x04:
+	  tmp=15625*4;
+	  break;
+	case 0x05:
+	  tmp=15625*8;
+	  break;
+	default:
+     	  SPD_ERR("SDRAM - Bad refresh period \n");
+	}
+	/* convert from nsec to bus cycles */
+	tmp = tmp/bus_period;
+	sdram0_rtr = (tmp & 0x3ff8)<<  SDRAM0_RTR_SHIFT;
+
+/*------------------------------------------------------------------
+  determine the number of banks used
+  -------------------------------------------------------------------*/
+	/* byte 7:6 is module data width */
+	if(spd_read(7) != 0)
+	    SPD_ERR("SDRAM - unsupported module width\n");
+	tmp = spd_read(6);
+	if (tmp < 32)
+	    SPD_ERR("SDRAM - unsupported module width\n");
+	else if (tmp < 64)
+	    bank_cnt=1;		/* one bank per sdram side */
+	else if (tmp < 73)
+	    bank_cnt=2;	/* need two banks per side */
+	else if (tmp < 161)
+	    bank_cnt=4;	/* need four banks per side */
+	else
+	    SPD_ERR("SDRAM - unsupported module width\n");
+
+	/* byte 5 is the module row count (refered to as dimm "sides") */
+	tmp = spd_read(5);
+	if(tmp==1);
+	else if(tmp==2) bank_cnt *=2;
+	else if(tmp==4) bank_cnt *=4;
+	else bank_cnt = 8; 		/* 8 is an error code */
+
+	if(bank_cnt > 4)	/* we only have 4 banks to work with */
+	    SPD_ERR("SDRAM - unsupported module rows for this width\n");
+
+	/* now check for ECC ability of module. We only support ECC
+	 *   on 32 bit wide devices with 8 bit ECC.
+	 */
+	if ( (spd_read(11)==2) && ((spd_read(6)==40) || (spd_read(14)==8)) ){
+	   sdram0_ecccfg=0xf<<SDRAM0_ECCCFG_SHIFT;
+	   ecc_on = 1;
+   	}
+	else{
+	   sdram0_ecccfg=0;
+	   ecc_on = 0;
+   	}
+
+/*------------------------------------------------------------------
+	calculate total size
+  -------------------------------------------------------------------*/
+	/* calculate total size and do sanity check */
+	tmp = spd_read(31);
+	total_size=1<<22;	/* total_size = 4MB */
+	/* now multiply 4M by the smallest device roe density */
+	/* note that we don't support asymetric rows */
+	while (((tmp & 0x0001) == 0) && (tmp != 0)){
+	    total_size= total_size<<1;
+	    tmp = tmp>>1;
+	    }
+	total_size *= spd_read(5);	/* mult by module rows (dimm sides) */
+
+/*------------------------------------------------------------------
+	map  rows * cols * banks to a mode
+ -------------------------------------------------------------------*/
+
+	switch( row )
+	{
+	case 11:
+		switch ( col )
+		{
+		case 8:
+			mode=4; /* mode 5 */
+			break;
+		case 9:
+		case 10:
+			mode=0; /* mode 1 */
+			break;
+		default:
+	     	SPD_ERR("SDRAM - unsupported mode\n");
+		}
+		break;
+	case 12:
+		switch ( col )
+		{
+		case 8:
+			mode=3; /* mode 4 */
+			break;
+		case 9:
+		case 10:
+			mode=1; /* mode 2 */
+			break;
+		default:
+	     	SPD_ERR("SDRAM - unsupported mode\n");
+		}
+		break;
+	case 13:
+		switch ( col )
+		{
+		case 8:
+			mode=5; /* mode 6 */
+			break;
+		case 9:
+		case 10:
+			if (spd_read(17) ==2 )
+				mode=6; /* mode 7 */
+			else
+				mode=2; /* mode 3 */
+			break;
+		case 11:
+			mode=2; /* mode 3 */
+			break;
+		default:
+	     	SPD_ERR("SDRAM - unsupported mode\n");
+		}
+		break;
+	default:
+	     SPD_ERR("SDRAM - unsupported mode\n");
+	}
+
+/*------------------------------------------------------------------
+	using the calculated values, compute the bank
+	config register values.
+ -------------------------------------------------------------------*/
+	sdram0_b1cr = 0;
+	sdram0_b2cr = 0;
+	sdram0_b3cr = 0;
+
+	/* compute the size of each bank */
+	bank_size = total_size / bank_cnt;
+	/* convert bank size to bank size code for ppc4xx
+		by takeing log2(bank_size) - 22 */
+	tmp=bank_size; 		/* start with tmp = bank_size */
+	bank_code=0;			/* and bank_code = 0 */
+	while (tmp>1){ 		/* this takes log2 of tmp */
+		bank_code++;		/* and stores result in bank_code */
+		tmp=tmp>>1;
+		}				/* bank_code is now log2(bank_size) */
+	bank_code-=22;				/* subtract 22 to get the code */
+
+	tmp = SDRAM0_BXCR_SZ(bank_code) | SDRAM0_BXCR_AM(mode) | 1;
+    	sdram0_b0cr = (bank_size) * 0 | tmp;
+    	if(bank_cnt>1) sdram0_b2cr = (bank_size) * 1 | tmp;
+    	if(bank_cnt>2) sdram0_b1cr = (bank_size) * 2 | tmp;
+    	if(bank_cnt>3) sdram0_b3cr = (bank_size) * 3 | tmp;
+
+
+	/*
+	 *   enable sdram controller DCE=1
+	 *  enable burst read prefetch to 32 bytes BRPF=2
+	 *  leave other functions off
+	 */
+
+/*------------------------------------------------------------------
+	now that we've done our calculations, we are ready to
+	program all the registers.
+ -------------------------------------------------------------------*/
+
+
+#define mtsdram0(reg, data)  mtdcr(memcfga,reg);mtdcr(memcfgd,data)
+	/* disable memcontroller so updates work */
+	sdram0_cfg = 0;
+	mtsdram0( mem_mcopt1, sdram0_cfg );
+
+	mtsdram0( mem_besra , sdram0_besr0 );
+	mtsdram0( mem_besrb , sdram0_besr1 );
+	mtsdram0( mem_rtr   , sdram0_rtr );
+	mtsdram0( mem_pmit  , sdram0_pmit );
+	mtsdram0( mem_mb0cf , sdram0_b0cr );
+	mtsdram0( mem_mb1cf , sdram0_b1cr );
+	mtsdram0( mem_mb2cf , sdram0_b2cr );
+	mtsdram0( mem_mb3cf , sdram0_b3cr );
+	mtsdram0( mem_sdtr1 , sdram0_tr );
+	mtsdram0( mem_ecccf , sdram0_ecccfg );
+	mtsdram0( mem_eccerr, sdram0_eccesr );
+
+	/* SDRAM have a power on delay,  500 micro should do */
+	udelay(500);
+	sdram0_cfg = SDRAM0_CFG_DCE | SDRAM0_CFG_BRPF(1) | SDRAM0_CFG_ECCDD | SDRAM0_CFG_EMDULR;
+	if(ecc_on) sdram0_cfg |= SDRAM0_CFG_MEMCHK;
+	mtsdram0( mem_mcopt1, sdram0_cfg );
+
+
+	/* kernel 2.4.2 from mvista has a bug with memory over 128MB */
+#ifdef MVISTA_MEM_BUG
+	if (total_size > 128*1024*1024 )
+		total_size=128*1024*1024;
+#endif
+	return (total_size);
+}
+
+int spd_read(uint addr)
+{
+	char data[2];
+
+	if (i2c_read(SPD_EEPROM_ADDRESS, addr, 1, data, 1) == 0)
+		return (int)data[0];
+	else
+		return 0;
+}
+
+#else                             /* CONFIG_440 */
+
+/*-----------------------------------------------------------------------------
+|  Memory Controller Options 0
++-----------------------------------------------------------------------------*/
+#define SDRAM_CFG0_DCEN           0x80000000  /* SDRAM Controller Enable      */
+#define SDRAM_CFG0_MCHK_MASK      0x30000000  /* Memory data errchecking mask */
+#define SDRAM_CFG0_MCHK_NON       0x00000000  /* No ECC generation            */
+#define SDRAM_CFG0_MCHK_GEN       0x20000000  /* ECC generation               */
+#define SDRAM_CFG0_MCHK_CHK       0x30000000  /* ECC generation and checking  */
+#define SDRAM_CFG0_RDEN           0x08000000  /* Registered DIMM enable       */
+#define SDRAM_CFG0_PMUD           0x04000000  /* Page management unit         */
+#define SDRAM_CFG0_DMWD_MASK      0x02000000  /* DRAM width mask              */
+#define SDRAM_CFG0_DMWD_32        0x00000000  /* 32 bits                      */
+#define SDRAM_CFG0_DMWD_64        0x02000000  /* 64 bits                      */
+#define SDRAM_CFG0_UIOS_MASK      0x00C00000  /* Unused IO State              */
+#define SDRAM_CFG0_PDP            0x00200000  /* Page deallocation policy     */
+
+/*-----------------------------------------------------------------------------
+|  Memory Controller Options 1
++-----------------------------------------------------------------------------*/
+#define SDRAM_CFG1_SRE            0x80000000  /* Self-Refresh Entry           */
+#define SDRAM_CFG1_PMEN           0x40000000  /* Power Management Enable      */
+
+/*-----------------------------------------------------------------------------+
+|  SDRAM DEVPOT Options
++-----------------------------------------------------------------------------*/
+#define SDRAM_DEVOPT_DLL          0x80000000
+#define SDRAM_DEVOPT_DS           0x40000000
+
+/*-----------------------------------------------------------------------------+
+|  SDRAM MCSTS Options
++-----------------------------------------------------------------------------*/
+#define SDRAM_MCSTS_MRSC          0x80000000
+#define SDRAM_MCSTS_SRMS          0x40000000
+#define SDRAM_MCSTS_CIS           0x20000000
+
+/*-----------------------------------------------------------------------------
+|  SDRAM Refresh Timer Register
++-----------------------------------------------------------------------------*/
+#define SDRAM_RTR_RINT_MASK       0xFFFF0000
+#define SDRAM_RTR_RINT_ENCODE(n)  (((n) << 16) & SDRAM_RTR_RINT_MASK)
+#define sdram_HZ_to_ns(hertz)     (1000000000/(hertz))
+
+/*-----------------------------------------------------------------------------+
+|  SDRAM UABus Base Address Reg
++-----------------------------------------------------------------------------*/
+#define SDRAM_UABBA_UBBA_MASK     0x0000000F
+
+/*-----------------------------------------------------------------------------+
+|  Memory Bank 0-7 configuration
++-----------------------------------------------------------------------------*/
+#define SDRAM_BXCR_SDBA_MASK      0xff800000      /* Base address             */
+#define SDRAM_BXCR_SDSZ_MASK      0x000e0000      /* Size                     */
+#define SDRAM_BXCR_SDSZ_8         0x00020000      /*   8M                     */
+#define SDRAM_BXCR_SDSZ_16        0x00040000      /*  16M                     */
+#define SDRAM_BXCR_SDSZ_32        0x00060000      /*  32M                     */
+#define SDRAM_BXCR_SDSZ_64        0x00080000      /*  64M                     */
+#define SDRAM_BXCR_SDSZ_128       0x000a0000      /* 128M                     */
+#define SDRAM_BXCR_SDSZ_256       0x000c0000      /* 256M                     */
+#define SDRAM_BXCR_SDSZ_512       0x000e0000      /* 512M                     */
+#define SDRAM_BXCR_SDAM_MASK      0x0000e000      /* Addressing mode          */
+#define SDRAM_BXCR_SDAM_1         0x00000000      /*   Mode 1                 */
+#define SDRAM_BXCR_SDAM_2         0x00002000      /*   Mode 2                 */
+#define SDRAM_BXCR_SDAM_3         0x00004000      /*   Mode 3                 */
+#define SDRAM_BXCR_SDAM_4         0x00006000      /*   Mode 4                 */
+#define SDRAM_BXCR_SDBE           0x00000001      /* Memory Bank Enable       */
+
+/*-----------------------------------------------------------------------------+
+|  SDRAM TR0 Options
++-----------------------------------------------------------------------------*/
+#define SDRAM_TR0_SDWR_MASK       0x80000000
+#define   SDRAM_TR0_SDWR_2_CLK    0x00000000
+#define   SDRAM_TR0_SDWR_3_CLK    0x80000000
+#define SDRAM_TR0_SDWD_MASK       0x40000000
+#define   SDRAM_TR0_SDWD_0_CLK    0x00000000
+#define   SDRAM_TR0_SDWD_1_CLK    0x40000000
+#define SDRAM_TR0_SDCL_MASK       0x01800000
+#define   SDRAM_TR0_SDCL_2_0_CLK  0x00800000
+#define   SDRAM_TR0_SDCL_2_5_CLK  0x01000000
+#define   SDRAM_TR0_SDCL_3_0_CLK  0x01800000
+#define SDRAM_TR0_SDPA_MASK       0x000C0000
+#define   SDRAM_TR0_SDPA_2_CLK    0x00040000
+#define   SDRAM_TR0_SDPA_3_CLK    0x00080000
+#define   SDRAM_TR0_SDPA_4_CLK    0x000C0000
+#define SDRAM_TR0_SDCP_MASK       0x00030000
+#define   SDRAM_TR0_SDCP_2_CLK    0x00000000
+#define   SDRAM_TR0_SDCP_3_CLK    0x00010000
+#define   SDRAM_TR0_SDCP_4_CLK    0x00020000
+#define   SDRAM_TR0_SDCP_5_CLK    0x00030000
+#define SDRAM_TR0_SDLD_MASK       0x0000C000
+#define   SDRAM_TR0_SDLD_1_CLK    0x00000000
+#define   SDRAM_TR0_SDLD_2_CLK    0x00004000
+#define SDRAM_TR0_SDRA_MASK       0x0000001C
+#define   SDRAM_TR0_SDRA_6_CLK    0x00000000
+#define   SDRAM_TR0_SDRA_7_CLK    0x00000004
+#define   SDRAM_TR0_SDRA_8_CLK    0x00000008
+#define   SDRAM_TR0_SDRA_9_CLK    0x0000000C
+#define   SDRAM_TR0_SDRA_10_CLK   0x00000010
+#define   SDRAM_TR0_SDRA_11_CLK   0x00000014
+#define   SDRAM_TR0_SDRA_12_CLK   0x00000018
+#define   SDRAM_TR0_SDRA_13_CLK   0x0000001C
+#define SDRAM_TR0_SDRD_MASK       0x00000003
+#define   SDRAM_TR0_SDRD_2_CLK    0x00000001
+#define   SDRAM_TR0_SDRD_3_CLK    0x00000002
+#define   SDRAM_TR0_SDRD_4_CLK    0x00000003
+
+/*-----------------------------------------------------------------------------+
+|  SDRAM TR1 Options
++-----------------------------------------------------------------------------*/
+#define SDRAM_TR1_RDSS_MASK         0xC0000000
+#define   SDRAM_TR1_RDSS_TR0        0x00000000
+#define   SDRAM_TR1_RDSS_TR1        0x40000000
+#define   SDRAM_TR1_RDSS_TR2        0x80000000
+#define   SDRAM_TR1_RDSS_TR3        0xC0000000
+#define SDRAM_TR1_RDSL_MASK         0x00C00000
+#define   SDRAM_TR1_RDSL_STAGE1     0x00000000
+#define   SDRAM_TR1_RDSL_STAGE2     0x00400000
+#define   SDRAM_TR1_RDSL_STAGE3     0x00800000
+#define SDRAM_TR1_RDCD_MASK         0x00000800
+#define   SDRAM_TR1_RDCD_RCD_0_0    0x00000000
+#define   SDRAM_TR1_RDCD_RCD_1_2    0x00000800
+#define SDRAM_TR1_RDCT_MASK         0x000001FF
+#define   SDRAM_TR1_RDCT_ENCODE(x)  (((x) << 0) & SDRAM_TR1_RDCT_MASK)
+#define   SDRAM_TR1_RDCT_DECODE(x)  (((x) & SDRAM_TR1_RDCT_MASK) >> 0)
+#define   SDRAM_TR1_RDCT_MIN        0x00000000
+#define   SDRAM_TR1_RDCT_MAX        0x000001FF
+
+/*-----------------------------------------------------------------------------+
+|  SDRAM WDDCTR Options
++-----------------------------------------------------------------------------*/
+#define SDRAM_WDDCTR_WRCP_MASK       0xC0000000
+#define   SDRAM_WDDCTR_WRCP_0DEG     0x00000000
+#define   SDRAM_WDDCTR_WRCP_90DEG    0x40000000
+#define   SDRAM_WDDCTR_WRCP_180DEG   0x80000000
+#define SDRAM_WDDCTR_DCD_MASK        0x000001FF
+
+/*-----------------------------------------------------------------------------+
+|  SDRAM CLKTR Options
++-----------------------------------------------------------------------------*/
+#define SDRAM_CLKTR_CLKP_MASK       0xC0000000
+#define   SDRAM_CLKTR_CLKP_0DEG     0x00000000
+#define   SDRAM_CLKTR_CLKP_90DEG    0x40000000
+#define   SDRAM_CLKTR_CLKP_180DEG   0x80000000
+#define SDRAM_CLKTR_DCDT_MASK       0x000001FF
+
+/*-----------------------------------------------------------------------------+
+|  SDRAM DLYCAL Options
++-----------------------------------------------------------------------------*/
+#define SDRAM_DLYCAL_DLCV_MASK      0x000003FC
+#define   SDRAM_DLYCAL_DLCV_ENCODE(x) (((x)<<2) & SDRAM_DLYCAL_DLCV_MASK)
+#define   SDRAM_DLYCAL_DLCV_DECODE(x) (((x) & SDRAM_DLYCAL_DLCV_MASK)>>2)
+
+/*-----------------------------------------------------------------------------+
+|  General Definition
++-----------------------------------------------------------------------------*/
+#define DEFAULT_SPD_ADDR1   0x53
+#define DEFAULT_SPD_ADDR2   0x52
+#define ONE_BILLION         1000000000
+#define MAXBANKS            4               /* at most 4 dimm banks */
+#define MAX_SPD_BYTES       256
+#define NUMHALFCYCLES       4
+#define NUMMEMTESTS         8
+#define NUMMEMWORDS         8
+#define MAXBXCR             4
+#define TRUE                1
+#define FALSE               0
+
+const unsigned long test[NUMMEMTESTS][NUMMEMWORDS] = {
+    {0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+     0xFFFFFFFF, 0xFFFFFFFF},
+    {0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+     0x00000000, 0x00000000},
+    {0xAAAAAAAA, 0xAAAAAAAA, 0x55555555, 0x55555555, 0xAAAAAAAA, 0xAAAAAAAA,
+     0x55555555, 0x55555555},
+    {0x55555555, 0x55555555, 0xAAAAAAAA, 0xAAAAAAAA, 0x55555555, 0x55555555,
+     0xAAAAAAAA, 0xAAAAAAAA},
+    {0xA5A5A5A5, 0xA5A5A5A5, 0x5A5A5A5A, 0x5A5A5A5A, 0xA5A5A5A5, 0xA5A5A5A5,
+     0x5A5A5A5A, 0x5A5A5A5A},
+    {0x5A5A5A5A, 0x5A5A5A5A, 0xA5A5A5A5, 0xA5A5A5A5, 0x5A5A5A5A, 0x5A5A5A5A,
+     0xA5A5A5A5, 0xA5A5A5A5},
+    {0xAA55AA55, 0xAA55AA55, 0x55AA55AA, 0x55AA55AA, 0xAA55AA55, 0xAA55AA55,
+     0x55AA55AA, 0x55AA55AA},
+    {0x55AA55AA, 0x55AA55AA, 0xAA55AA55, 0xAA55AA55, 0x55AA55AA, 0x55AA55AA,
+     0xAA55AA55, 0xAA55AA55}
+};
+
+
+unsigned char spd_read(uchar chip, uint addr);
+
+void get_spd_info(unsigned long* dimm_populated,
+                  unsigned char* iic0_dimm_addr,
+                  unsigned long  num_dimm_banks);
+
+void check_mem_type
+                 (unsigned long* dimm_populated,
+                  unsigned char* iic0_dimm_addr,
+                  unsigned long  num_dimm_banks);
+
+void check_volt_type
+                 (unsigned long* dimm_populated,
+                  unsigned char* iic0_dimm_addr,
+                  unsigned long  num_dimm_banks);
+
+void program_cfg0(unsigned long* dimm_populated,
+                  unsigned char* iic0_dimm_addr,
+                  unsigned long  num_dimm_banks);
+
+void program_cfg1(unsigned long* dimm_populated,
+                  unsigned char* iic0_dimm_addr,
+                  unsigned long  num_dimm_banks);
+
+void program_rtr (unsigned long* dimm_populated,
+                  unsigned char* iic0_dimm_addr,
+                  unsigned long  num_dimm_banks);
+
+void program_tr0 (unsigned long* dimm_populated,
+                  unsigned char* iic0_dimm_addr,
+                  unsigned long  num_dimm_banks);
+
+void program_tr1 (void);
+
+void program_ecc (unsigned long  num_bytes);
+
+unsigned
+long  program_bxcr(unsigned long* dimm_populated,
+                   unsigned char* iic0_dimm_addr,
+                   unsigned long  num_dimm_banks);
+
+/*
+ * This function is reading data from the DIMM module EEPROM over the SPD bus
+ * and uses that to program the sdram controller.
+ *
+ * This works on boards that has the same schematics that the IBM walnut has.
+ *
+ * BUG: Don't handle ECC memory
+ * BUG: A few values in the TR register is currently hardcoded
+ */
+
+long int spd_sdram(void) {
+    unsigned char iic0_dimm_addr[] = SPD_EEPROM_ADDRESS;
+    unsigned long dimm_populated[sizeof(iic0_dimm_addr)];
+    unsigned long total_size;
+    unsigned long cfg0;
+    unsigned long mcsts;
+    unsigned long num_dimm_banks;               /* on board dimm banks */
+
+    num_dimm_banks = sizeof(iic0_dimm_addr);
+
+	/*
+	 * Make sure I2C controller is initialized
+	 * before continuing.
+	 */
+	i2c_init(CFG_I2C_SPEED, CFG_I2C_SLAVE);
+
+    /*
+     * Read the SPD information using I2C interface. Check to see if the
+     * DIMM slots are populated.
+     */
+    get_spd_info(dimm_populated, iic0_dimm_addr, num_dimm_banks);
+
+    /*
+     * Check the memory type for the dimms plugged.
+     */
+    check_mem_type(dimm_populated, iic0_dimm_addr, num_dimm_banks);
+
+    /*
+     * Check the voltage type for the dimms plugged.
+     */
+    check_volt_type(dimm_populated, iic0_dimm_addr, num_dimm_banks);
+
+    /*
+     * program 440GP SDRAM controller options (SDRAM0_CFG0)
+     */
+    program_cfg0(dimm_populated, iic0_dimm_addr, num_dimm_banks);
+
+    /*
+     * program 440GP SDRAM controller options (SDRAM0_CFG1)
+     */
+    program_cfg1(dimm_populated, iic0_dimm_addr, num_dimm_banks);
+
+    /*
+     * program SDRAM refresh register (SDRAM0_RTR)
+     */
+    program_rtr(dimm_populated, iic0_dimm_addr, num_dimm_banks);
+
+    /*
+     * program SDRAM Timing Register 0 (SDRAM0_TR0)
+     */
+    program_tr0(dimm_populated, iic0_dimm_addr, num_dimm_banks);
+
+    /*
+     * program the BxCR registers to find out total sdram installed
+     */
+    total_size = program_bxcr(dimm_populated, iic0_dimm_addr,
+        num_dimm_banks);
+
+    /*
+     * program SDRAM Clock Timing Register (SDRAM0_CLKTR)
+     */
+    mtsdram(mem_clktr, 0x40000000);
+
+    /*
+     * delay to ensure 200 usec has elapsed
+     */
+    udelay(400);
+
+    /*
+     * enable the memory controller
+     */
+    mfsdram(mem_cfg0, cfg0);
+    mtsdram(mem_cfg0, cfg0 | SDRAM_CFG0_DCEN);
+
+    /*
+     * wait for SDRAM_CFG0_DC_EN to complete
+     */
+    while(1) {
+        mfsdram(mem_mcsts, mcsts);
+        if ((mcsts & SDRAM_MCSTS_MRSC) != 0) {
+            break;
+        }
+    }
+
+    /*
+     * program SDRAM Timing Register 1, adding some delays
+     */
+    program_tr1();
+
+    /*
+     * if ECC is enabled, initialize parity bits
+     */
+
+	return total_size;
+}
+
+unsigned char spd_read(uchar chip, uint addr) {
+	unsigned char data[2];
+
+	if (i2c_read(chip, addr, 1, data, 1) == 0)
+		return data[0];
+	else
+		return 0;
+}
+
+void get_spd_info(unsigned long*   dimm_populated,
+                  unsigned char*   iic0_dimm_addr,
+                  unsigned long    num_dimm_banks)
+{
+    unsigned long dimm_num;
+    unsigned long dimm_found;
+    unsigned char num_of_bytes;
+    unsigned char total_size;
+
+    dimm_found = FALSE;
+    for (dimm_num = 0; dimm_num < num_dimm_banks; dimm_num++) {
+        num_of_bytes = 0;
+        total_size = 0;
+
+        num_of_bytes = spd_read(iic0_dimm_addr[dimm_num], 0);
+        total_size = spd_read(iic0_dimm_addr[dimm_num], 1);
+
+        if ((num_of_bytes != 0) && (total_size != 0)) {
+            dimm_populated[dimm_num] = TRUE;
+            dimm_found = TRUE;
+#if 0
+            printf("DIMM slot %lu: populated\n", dimm_num);
+#endif
+        }
+        else {
+            dimm_populated[dimm_num] = FALSE;
+#if 0
+            printf("DIMM slot %lu: Not populated\n", dimm_num);
+#endif
+        }
+    }
+
+    if (dimm_found == FALSE) {
+        printf("ERROR - No memory installed. Install a DDR-SDRAM DIMM.\n\n");
+        hang();
+    }
+}
+
+void check_mem_type(unsigned long*   dimm_populated,
+                    unsigned char*   iic0_dimm_addr,
+                    unsigned long    num_dimm_banks)
+{
+    unsigned long dimm_num;
+    unsigned char dimm_type;
+
+    for (dimm_num = 0; dimm_num < num_dimm_banks; dimm_num++) {
+        if (dimm_populated[dimm_num] == TRUE) {
+            dimm_type = spd_read(iic0_dimm_addr[dimm_num], 2);
+            switch (dimm_type) {
+            case 7:
+#if 0
+                printf("DIMM slot %lu: DDR SDRAM detected\n", dimm_num);
+#endif
+                break;
+            default:
+                printf("ERROR: Unsupported DIMM detected in slot %lu.\n",
+                    dimm_num);
+                printf("Only DDR SDRAM DIMMs are supported.\n");
+                printf("Replace the DIMM module with a supported DIMM.\n\n");
+                hang();
+                break;
+            }
+        }
+    }
+}
+
+
+void check_volt_type(unsigned long*   dimm_populated,
+                     unsigned char*   iic0_dimm_addr,
+                     unsigned long    num_dimm_banks)
+{
+    unsigned long dimm_num;
+    unsigned long voltage_type;
+
+    for (dimm_num = 0; dimm_num < num_dimm_banks; dimm_num++) {
+        if (dimm_populated[dimm_num] == TRUE) {
+            voltage_type = spd_read(iic0_dimm_addr[dimm_num], 8);
+            if (voltage_type != 0x04) {
+                printf("ERROR: DIMM %lu with unsupported voltage level.\n",
+                    dimm_num);
+                hang();
+            }
+            else {
+#if 0
+                printf("DIMM %lu voltage level supported.\n", dimm_num);
+#endif
+            }
+            break;
+        }
+    }
+}
+
+void program_cfg0(unsigned long* dimm_populated,
+                  unsigned char* iic0_dimm_addr,
+                  unsigned long  num_dimm_banks)
+{
+    unsigned long dimm_num;
+    unsigned long cfg0;
+    unsigned long ecc_enabled;
+    unsigned char ecc;
+    unsigned char attributes;
+    unsigned long data_width;
+    unsigned long dimm_32bit;
+    unsigned long dimm_64bit;
+
+    /*
+     * get Memory Controller Options 0 data
+     */
+    mfsdram(mem_cfg0, cfg0);
+
+    /*
+     * clear bits
+     */
+    cfg0 &= ~(SDRAM_CFG0_DCEN | SDRAM_CFG0_MCHK_MASK |
+              SDRAM_CFG0_RDEN | SDRAM_CFG0_PMUD |
+              SDRAM_CFG0_DMWD_MASK |
+              SDRAM_CFG0_UIOS_MASK | SDRAM_CFG0_PDP);
+
+
+    /*
+     * FIXME: assume the DDR SDRAMs in both banks are the same
+     */
+    ecc_enabled = TRUE;
+    for (dimm_num = 0; dimm_num < num_dimm_banks; dimm_num++) {
+        if (dimm_populated[dimm_num] == TRUE) {
+            ecc = spd_read(iic0_dimm_addr[dimm_num], 11);
+            if (ecc != 0x02) {
+                ecc_enabled = FALSE;
+            }
+
+            /*
+             * program Registered DIMM Enable
+             */
+            attributes = spd_read(iic0_dimm_addr[dimm_num], 21);
+            if ((attributes & 0x02) != 0x00) {
+                cfg0 |= SDRAM_CFG0_RDEN;
+            }
+
+            /*
+             * program DDR SDRAM Data Width
+             */
+            data_width =
+                (unsigned long)spd_read(iic0_dimm_addr[dimm_num],6) +
+                (((unsigned long)spd_read(iic0_dimm_addr[dimm_num],7)) << 8);
+            if (data_width == 64 || data_width == 72) {
+                dimm_64bit = TRUE;
+                cfg0 |= SDRAM_CFG0_DMWD_64;
+            }
+            else if (data_width == 32 || data_width == 40) {
+                dimm_32bit = TRUE;
+                cfg0 |= SDRAM_CFG0_DMWD_32;
+            }
+            else {
+                printf("WARNING: DIMM with datawidth of %lu bits.\n",
+                    data_width);
+                printf("Only DIMMs with 32 or 64 bit datawidths supported.\n");
+                hang();
+            }
+            break;
+        }
+    }
+
+    /*
+     * program Memory Data Error Checking
+     */
+    if (ecc_enabled == TRUE) {
+        cfg0 |= SDRAM_CFG0_MCHK_GEN;
+    }
+    else {
+        cfg0 |= SDRAM_CFG0_MCHK_NON;
+    }
+
+    /*
+     * program Page Management Unit
+     */
+    cfg0 |= SDRAM_CFG0_PMUD;
+
+    /*
+     * program Memory Controller Options 0
+     * Note: DCEN must be enabled after all DDR SDRAM controller
+     * configuration registers get initialized.
+     */
+    mtsdram(mem_cfg0, cfg0);
+}
+
+void program_cfg1(unsigned long* dimm_populated,
+                  unsigned char* iic0_dimm_addr,
+                  unsigned long  num_dimm_banks)
+{
+    unsigned long cfg1;
+    mfsdram(mem_cfg1, cfg1);
+
+    /*
+     * Self-refresh exit, disable PM
+     */
+    cfg1 &= ~(SDRAM_CFG1_SRE | SDRAM_CFG1_PMEN);
+
+    /*
+     * program Memory Controller Options 1
+     */
+    mtsdram(mem_cfg1, cfg1);
+}
+
+void program_rtr (unsigned long* dimm_populated,
+                  unsigned char* iic0_dimm_addr,
+                  unsigned long  num_dimm_banks)
+{
+    unsigned long dimm_num;
+    unsigned long bus_period_x_10;
+    unsigned long refresh_rate = 0;
+    unsigned char refresh_rate_type;
+    unsigned long refresh_interval;
+    unsigned long sdram_rtr;
+    PPC440_SYS_INFO sys_info;
+
+    /*
+     * get the board info
+     */
+    get_sys_info(&sys_info);
+    bus_period_x_10 = ONE_BILLION / (sys_info.freqPLB / 10);
+
+
+    for (dimm_num = 0;  dimm_num < num_dimm_banks; dimm_num++) {
+        if (dimm_populated[dimm_num] == TRUE) {
+            refresh_rate_type = 0x7F & spd_read(iic0_dimm_addr[dimm_num], 12);
+            switch (refresh_rate_type) {
+            case 0x00:
+                refresh_rate = 15625;
+                break;
+            case 0x011:
+                refresh_rate = 15625/4;
+                break;
+            case 0x02:
+                refresh_rate = 15625/2;
+                break;
+            case 0x03:
+                refresh_rate = 15626*2;
+                break;
+            case 0x04:
+                refresh_rate = 15625*4;
+                break;
+            case 0x05:
+                refresh_rate = 15625*8;
+                break;
+            default:
+                printf("ERROR: DIMM %lu, unsupported refresh rate/type.\n",
+                    dimm_num);
+                printf("Replace the DIMM module with a supported DIMM.\n");
+                break;
+            }
+
+            break;
+        }
+    }
+
+    refresh_interval = refresh_rate * 10 / bus_period_x_10;
+    sdram_rtr = (refresh_interval & 0x3ff8) <<  16;
+
+    /*
+     * program Refresh Timer Register (SDRAM0_RTR)
+     */
+    mtsdram(mem_rtr, sdram_rtr);
+}
+
+void program_tr0 (unsigned long* dimm_populated,
+                  unsigned char* iic0_dimm_addr,
+                  unsigned long  num_dimm_banks)
+{
+    unsigned long dimm_num;
+    unsigned long tr0;
+    unsigned char wcsbc;
+    unsigned char t_rp_ns;
+    unsigned char t_rcd_ns;
+    unsigned char t_ras_ns;
+    unsigned long t_rp_clk;
+    unsigned long t_ras_rcd_clk;
+    unsigned long t_rcd_clk;
+    unsigned long t_rfc_clk;
+    unsigned long plb_check;
+    unsigned char cas_bit;
+    unsigned long cas_index;
+    unsigned char cas_2_0_available;
+    unsigned char cas_2_5_available;
+    unsigned char cas_3_0_available;
+    unsigned long cycle_time_ns_x_10[3];
+    unsigned long tcyc_3_0_ns_x_10;
+    unsigned long tcyc_2_5_ns_x_10;
+    unsigned long tcyc_2_0_ns_x_10;
+    unsigned long tcyc_reg;
+    unsigned long bus_period_x_10;
+    PPC440_SYS_INFO sys_info;
+    unsigned long residue;
+
+    /*
+     * get the board info
+     */
+    get_sys_info(&sys_info);
+    bus_period_x_10 = ONE_BILLION / (sys_info.freqPLB / 10);
+
+    /*
+     * get SDRAM Timing Register 0 (SDRAM_TR0) and clear bits
+     */
+    mfsdram(mem_tr0, tr0);
+    tr0 &= ~(SDRAM_TR0_SDWR_MASK | SDRAM_TR0_SDWD_MASK |
+             SDRAM_TR0_SDCL_MASK | SDRAM_TR0_SDPA_MASK |
+             SDRAM_TR0_SDCP_MASK | SDRAM_TR0_SDLD_MASK |
+             SDRAM_TR0_SDRA_MASK | SDRAM_TR0_SDRD_MASK);
+
+    /*
+     * initialization
+     */
+    wcsbc = 0;
+    t_rp_ns = 0;
+    t_rcd_ns = 0;
+    t_ras_ns = 0;
+    cas_2_0_available = TRUE;
+    cas_2_5_available = TRUE;
+    cas_3_0_available = TRUE;
+    tcyc_2_0_ns_x_10 = 0;
+    tcyc_2_5_ns_x_10 = 0;
+    tcyc_3_0_ns_x_10 = 0;
+
+    for (dimm_num = 0; dimm_num < num_dimm_banks; dimm_num++) {
+        if (dimm_populated[dimm_num] == TRUE) {
+            wcsbc = spd_read(iic0_dimm_addr[dimm_num], 15);
+            t_rp_ns  = spd_read(iic0_dimm_addr[dimm_num], 27) >> 2;
+            t_rcd_ns = spd_read(iic0_dimm_addr[dimm_num], 29) >> 2;
+            t_ras_ns = spd_read(iic0_dimm_addr[dimm_num], 30);
+            cas_bit = spd_read(iic0_dimm_addr[dimm_num], 18);
+
+            for (cas_index = 0; cas_index < 3; cas_index++) {
+                switch (cas_index) {
+                case 0:
+                    tcyc_reg = spd_read(iic0_dimm_addr[dimm_num], 9);
+                    break;
+                case 1:
+                    tcyc_reg = spd_read(iic0_dimm_addr[dimm_num], 23);
+                    break;
+                default:
+                    tcyc_reg = spd_read(iic0_dimm_addr[dimm_num], 25);
+                    break;
+                }
+
+                if ((tcyc_reg & 0x0F) >= 10) {
+                    printf("ERROR: Tcyc incorrect for DIMM in slot %lu\n",
+                        dimm_num);
+                    hang();
+                }
+
+                cycle_time_ns_x_10[cas_index] =
+                    (((tcyc_reg & 0xF0) >> 4) * 10) + (tcyc_reg & 0x0F);
+            }
+
+            cas_index = 0;
+
+            if ((cas_bit & 0x80) != 0) {
+                cas_index += 3;
+            }
+            else if ((cas_bit & 0x40) != 0) {
+                cas_index += 2;
+            }
+            else if ((cas_bit & 0x20) != 0) {
+                cas_index += 1;
+            }
+
+            if (((cas_bit & 0x10) != 0) && (cas_index < 3)) {
+                tcyc_3_0_ns_x_10 = cycle_time_ns_x_10[cas_index];
+                cas_index++;
+            }
+            else {
+                if (cas_index != 0) {
+                    cas_index++;
+                }
+                cas_3_0_available = FALSE;
+            }
+
+            if (((cas_bit & 0x08) != 0) || (cas_index < 3)) {
+                tcyc_2_5_ns_x_10 = cycle_time_ns_x_10[cas_index];
+                cas_index++;
+            }
+            else {
+                if (cas_index != 0) {
+                    cas_index++;
+                }
+                cas_2_5_available = FALSE;
+            }
+
+            if (((cas_bit & 0x04) != 0) || (cas_index < 3)) {
+                tcyc_2_0_ns_x_10 = cycle_time_ns_x_10[cas_index];
+                cas_index++;
+            }
+            else {
+                if (cas_index != 0) {
+                    cas_index++;
+                }
+                cas_2_0_available = FALSE;
+            }
+
+            break;
+        }
+    }
+
+    /*
+     * Program SD_WR and SD_WCSBC fields
+     */
+    tr0 |= SDRAM_TR0_SDWR_2_CLK;                /* Write Recovery: 2 CLK */
+    switch (wcsbc) {
+    case 0:
+        tr0 |= SDRAM_TR0_SDWD_0_CLK;
+        break;
+    default:
+        tr0 |= SDRAM_TR0_SDWD_1_CLK;
+        break;
+    }
+
+    /*
+     * Program SD_CASL field
+     */
+    if ((cas_2_0_available == TRUE) &&
+        (bus_period_x_10 >= tcyc_2_0_ns_x_10)) {
+        tr0 |= SDRAM_TR0_SDCL_2_0_CLK;
+    }
+    else if((cas_2_5_available == TRUE) &&
+        (bus_period_x_10 >= tcyc_2_5_ns_x_10)) {
+        tr0 |= SDRAM_TR0_SDCL_2_5_CLK;
+    }
+    else if((cas_3_0_available == TRUE) &&
+        (bus_period_x_10 >= tcyc_3_0_ns_x_10)) {
+        tr0 |= SDRAM_TR0_SDCL_3_0_CLK;
+    }
+    else {
+        printf("ERROR: No supported CAS latency with the installed DIMMs.\n");
+        printf("Only CAS latencies of 2.0, 2.5, and 3.0 are supported.\n");
+        printf("Make sure the PLB speed is within the supported range.\n");
+        hang();
+    }
+
+    /*
+     * Calculate Trp in clock cycles and round up if necessary
+     * Program SD_PTA field
+     */
+    t_rp_clk = sys_info.freqPLB * t_rp_ns / ONE_BILLION;
+    plb_check = ONE_BILLION * t_rp_clk / t_rp_ns;
+    if (sys_info.freqPLB != plb_check) {
+        t_rp_clk++;
+    }
+    switch ((unsigned long)t_rp_clk) {
+    case 0:
+    case 1:
+    case 2:
+        tr0 |= SDRAM_TR0_SDPA_2_CLK;
+        break;
+    case 3:
+        tr0 |= SDRAM_TR0_SDPA_3_CLK;
+        break;
+    default:
+        tr0 |= SDRAM_TR0_SDPA_4_CLK;
+        break;
+    }
+
+    /*
+     * Program SD_CTP field
+     */
+    t_ras_rcd_clk = sys_info.freqPLB * (t_ras_ns - t_rcd_ns) / ONE_BILLION;
+    plb_check = ONE_BILLION * t_ras_rcd_clk / (t_ras_ns - t_rcd_ns);
+    if (sys_info.freqPLB != plb_check) {
+        t_ras_rcd_clk++;
+    }
+    switch (t_ras_rcd_clk) {
+    case 0:
+    case 1:
+    case 2:
+      tr0 |= SDRAM_TR0_SDCP_2_CLK;
+      break;
+    case 3:
+      tr0 |= SDRAM_TR0_SDCP_3_CLK;
+      break;
+    case 4:
+      tr0 |= SDRAM_TR0_SDCP_4_CLK;
+      break;
+    default:
+      tr0 |= SDRAM_TR0_SDCP_5_CLK;
+      break;
+    }
+
+    /*
+     * Program SD_LDF field
+     */
+    tr0 |= SDRAM_TR0_SDLD_2_CLK;
+
+    /*
+     * Program SD_RFTA field
+     * FIXME tRFC hardcoded as 75 nanoseconds
+     */
+    t_rfc_clk = sys_info.freqPLB / (ONE_BILLION / 75);
+    residue = sys_info.freqPLB % (ONE_BILLION / 75);
+    if (residue >= (ONE_BILLION / 150)) {
+        t_rfc_clk++;
+    }
+    switch (t_rfc_clk) {
+    case 0:
+    case 1:
+    case 2:
+    case 3:
+    case 4:
+    case 5:
+    case 6:
+        tr0 |= SDRAM_TR0_SDRA_6_CLK;
+        break;
+    case 7:
+        tr0 |= SDRAM_TR0_SDRA_7_CLK;
+        break;
+    case 8:
+        tr0 |= SDRAM_TR0_SDRA_8_CLK;
+        break;
+    case 9:
+        tr0 |= SDRAM_TR0_SDRA_9_CLK;
+        break;
+    case 10:
+        tr0 |= SDRAM_TR0_SDRA_10_CLK;
+        break;
+    case 11:
+        tr0 |= SDRAM_TR0_SDRA_11_CLK;
+        break;
+    case 12:
+        tr0 |= SDRAM_TR0_SDRA_12_CLK;
+        break;
+    default:
+        tr0 |= SDRAM_TR0_SDRA_13_CLK;
+        break;
+    }
+
+    /*
+     * Program SD_RCD field
+     */
+    t_rcd_clk = sys_info.freqPLB * t_rcd_ns / ONE_BILLION;
+    plb_check = ONE_BILLION * t_rcd_clk / t_rcd_ns;
+    if (sys_info.freqPLB != plb_check) {
+        t_rcd_clk++;
+    }
+    switch (t_rcd_clk) {
+    case 0:
+    case 1:
+    case 2:
+        tr0 |= SDRAM_TR0_SDRD_2_CLK;
+        break;
+    case 3:
+        tr0 |= SDRAM_TR0_SDRD_3_CLK;
+        break;
+    default:
+        tr0 |= SDRAM_TR0_SDRD_4_CLK;
+        break;
+    }
+
+#if 0
+    printf("tr0: %x\n", tr0);
+#endif
+    mtsdram(mem_tr0, tr0);
+}
+
+void program_tr1 (void)
+{
+    unsigned long tr0;
+    unsigned long tr1;
+    unsigned long cfg0;
+    unsigned long ecc_temp;
+    unsigned long dlycal;
+    unsigned long dly_val;
+    unsigned long i, j, k;
+    unsigned long bxcr_num;
+    unsigned long max_pass_length;
+    unsigned long current_pass_length;
+    unsigned long current_fail_length;
+    unsigned long current_start;
+    unsigned long rdclt;
+    unsigned long rdclt_offset;
+    long max_start;
+    long max_end;
+    long rdclt_average;
+    unsigned char window_found;
+    unsigned char fail_found;
+    unsigned char pass_found;
+    unsigned long * membase;
+    PPC440_SYS_INFO sys_info;
+
+    /*
+     * get the board info
+     */
+    get_sys_info(&sys_info);
+
+    /*
+     * get SDRAM Timing Register 0 (SDRAM_TR0) and clear bits
+     */
+    mfsdram(mem_tr1, tr1);
+    tr1 &= ~(SDRAM_TR1_RDSS_MASK | SDRAM_TR1_RDSL_MASK |
+             SDRAM_TR1_RDCD_MASK | SDRAM_TR1_RDCT_MASK);
+
+    mfsdram(mem_tr0, tr0);
+    if (((tr0 & SDRAM_TR0_SDCL_MASK) == SDRAM_TR0_SDCL_2_5_CLK) &&
+       (sys_info.freqPLB > 100000000)) {
+        tr1 |= SDRAM_TR1_RDSS_TR2;
+        tr1 |= SDRAM_TR1_RDSL_STAGE3;
+        tr1 |= SDRAM_TR1_RDCD_RCD_1_2;
+    }
+    else {
+        tr1 |= SDRAM_TR1_RDSS_TR1;
+        tr1 |= SDRAM_TR1_RDSL_STAGE2;
+        tr1 |= SDRAM_TR1_RDCD_RCD_0_0;
+    }
+
+    /*
+     * save CFG0 ECC setting to a temporary variable and turn ECC off
+     */
+    mfsdram(mem_cfg0, cfg0);
+    ecc_temp = cfg0 & SDRAM_CFG0_MCHK_MASK;
+    mtsdram(mem_cfg0, (cfg0 & ~SDRAM_CFG0_MCHK_MASK) | SDRAM_CFG0_MCHK_NON);
+
+    /*
+     * get the delay line calibration register value
+     */
+    mfsdram(mem_dlycal, dlycal);
+    dly_val = SDRAM_DLYCAL_DLCV_DECODE(dlycal) << 2;
+
+    max_pass_length = 0;
+    max_start = 0;
+    max_end = 0;
+    current_pass_length = 0;
+    current_fail_length = 0;
+    current_start = 0;
+    rdclt_offset = 0;
+    window_found = FALSE;
+    fail_found = FALSE;
+    pass_found = FALSE;
+#ifdef DEBUG
+    printf("Starting memory test ");
+#endif
+    for (k = 0; k < NUMHALFCYCLES; k++) {
+        for (rdclt = 0; rdclt < dly_val; rdclt++)  {
+            /*
+             * Set the timing reg for the test.
+             */
+            mtsdram(mem_tr1, (tr1 | SDRAM_TR1_RDCT_ENCODE(rdclt)));
+
+            for (bxcr_num = 0; bxcr_num < MAXBXCR; bxcr_num++) {
+                mtdcr(memcfga, mem_b0cr + (bxcr_num<<2));
+                if ((mfdcr(memcfgd) & SDRAM_BXCR_SDBE) == SDRAM_BXCR_SDBE) {
+                    /* Bank is enabled */
+                    membase = (unsigned long*)
+                        (mfdcr(memcfgd) & SDRAM_BXCR_SDBA_MASK);
+
+                    /*
+                     * Run the short memory test
+                     */
+                    for (i = 0; i < NUMMEMTESTS; i++) {
+                        for (j = 0; j < NUMMEMWORDS; j++) {
+                            membase[j] = test[i][j];
+                            ppcDcbf((unsigned long)&(membase[j]));
+                        }
+
+                        for (j = 0; j < NUMMEMWORDS; j++) {
+                            if (membase[j] != test[i][j]) {
+                                ppcDcbf((unsigned long)&(membase[j]));
+                                break;
+                            }
+                            ppcDcbf((unsigned long)&(membase[j]));
+                        }
+
+                        if (j < NUMMEMWORDS) {
+                            break;
+                        }
+                    }
+
+                    /*
+                     * see if the rdclt value passed
+                     */
+                    if (i < NUMMEMTESTS) {
+                        break;
+                    }
+                }
+            }
+
+            if (bxcr_num == MAXBXCR) {
+                if (fail_found == TRUE) {
+                    pass_found = TRUE;
+                    if (current_pass_length == 0) {
+                        current_start = rdclt_offset + rdclt;
+                    }
+
+                    current_fail_length = 0;
+                    current_pass_length++;
+
+                    if (current_pass_length > max_pass_length) {
+                        max_pass_length = current_pass_length;
+                        max_start = current_start;
+                        max_end = rdclt_offset + rdclt;
+                    }
+                }
+            }
+            else {
+                current_pass_length = 0;
+                current_fail_length++;
+
+                if (current_fail_length >= (dly_val>>2)) {
+                    if (fail_found == FALSE) {
+                        fail_found = TRUE;
+                    }
+                    else if (pass_found == TRUE) {
+                        window_found = TRUE;
+                        break;
+                    }
+                }
+            }
+        }
+#ifdef DEBUG
+        printf(".");
+#endif
+        if (window_found == TRUE) {
+            break;
+        }
+
+        tr1 = tr1 ^ SDRAM_TR1_RDCD_MASK;
+        rdclt_offset += dly_val;
+    }
+#ifdef DEBUG
+    printf("\n");
+#endif
+
+    /*
+     * make sure we find the window
+     */
+    if (window_found == FALSE) {
+       printf("ERROR: Cannot determine a common read delay.\n");
+       hang();
+    }
+
+    /*
+     * restore the orignal ECC setting
+     */
+    mtsdram(mem_cfg0, (cfg0 & ~SDRAM_CFG0_MCHK_MASK) | ecc_temp);
+
+    /*
+     * set the SDRAM TR1 RDCD value
+     */
+    tr1 &= ~SDRAM_TR1_RDCD_MASK;
+    if ((tr0 & SDRAM_TR0_SDCL_MASK) == SDRAM_TR0_SDCL_2_5_CLK) {
+        tr1 |= SDRAM_TR1_RDCD_RCD_1_2;
+    }
+    else {
+        tr1 |= SDRAM_TR1_RDCD_RCD_0_0;
+    }
+
+    /*
+     * set the SDRAM TR1 RDCLT value
+     */
+    tr1 &= ~SDRAM_TR1_RDCT_MASK;
+    while (max_end >= (dly_val<<1)) {
+        max_end -= (dly_val<<1);
+        max_start -= (dly_val<<1);
+    }
+
+    rdclt_average = ((max_start + max_end) >> 1);
+    if (rdclt_average >= 0x60)
+        while(1);
+
+    if (rdclt_average < 0) {
+        rdclt_average = 0;
+    }
+
+    if (rdclt_average >= dly_val) {
+        rdclt_average -= dly_val;
+        tr1 = tr1 ^ SDRAM_TR1_RDCD_MASK;
+    }
+    tr1 |= SDRAM_TR1_RDCT_ENCODE(rdclt_average);
+
+#if 0
+    printf("tr1: %x\n", tr1);
+#endif
+    /*
+     * program SDRAM Timing Register 1 TR1
+     */
+    mtsdram(mem_tr1, tr1);
+}
+
+unsigned long program_bxcr(unsigned long* dimm_populated,
+                           unsigned char* iic0_dimm_addr,
+                           unsigned long  num_dimm_banks)
+{
+    unsigned long dimm_num;
+    unsigned long bxcr_num;
+    unsigned long bank_base_addr;
+    unsigned long bank_size_bytes;
+    unsigned long cr;
+    unsigned long i;
+    unsigned long temp;
+    unsigned char num_row_addr;
+    unsigned char num_col_addr;
+    unsigned char num_banks;
+    unsigned char bank_size_id;
+
+
+    /*
+     * Set the BxCR regs.  First, wipe out the bank config registers.
+     */
+    for (bxcr_num = 0; bxcr_num < MAXBXCR; bxcr_num++) {
+        mtdcr(memcfga, mem_b0cr + (bxcr_num << 2));
+        mtdcr(memcfgd, 0x00000000);
+    }
+
+    /*
+     * reset the bank_base address
+     */
+    bank_base_addr = CFG_SDRAM_BASE;
+
+    for (dimm_num = 0; dimm_num < num_dimm_banks; dimm_num++) {
+        if (dimm_populated[dimm_num] == TRUE) {
+            num_row_addr = spd_read(iic0_dimm_addr[dimm_num], 3);
+            num_col_addr = spd_read(iic0_dimm_addr[dimm_num], 4);
+            num_banks    = spd_read(iic0_dimm_addr[dimm_num], 5);
+            bank_size_id = spd_read(iic0_dimm_addr[dimm_num], 31);
+
+            /*
+             * Set the SDRAM0_BxCR regs
+             */
+            cr = 0;
+            bank_size_bytes = 4 * 1024 * 1024 * bank_size_id;
+            switch (bank_size_id) {
+            case 0x02:
+                cr |= SDRAM_BXCR_SDSZ_8;
+                break;
+            case 0x04:
+                cr |= SDRAM_BXCR_SDSZ_16;
+                break;
+            case 0x08:
+                cr |= SDRAM_BXCR_SDSZ_32;
+                break;
+            case 0x10:
+                cr |= SDRAM_BXCR_SDSZ_64;
+                break;
+            case 0x20:
+                cr |= SDRAM_BXCR_SDSZ_128;
+                break;
+            case 0x40:
+                cr |= SDRAM_BXCR_SDSZ_256;
+                break;
+            case 0x80:
+                cr |= SDRAM_BXCR_SDSZ_512;
+                break;
+            default:
+                printf("DDR-SDRAM: DIMM %lu BxCR configuration.\n",
+                    dimm_num);
+                printf("ERROR: Unsupported value for the banksize: %d.\n",
+                   bank_size_id);
+                printf("Replace the DIMM module with a supported DIMM.\n\n");
+                hang();
+            }
+
+            switch (num_col_addr) {
+            case 0x08:
+                cr |= SDRAM_BXCR_SDAM_1;
+                break;
+            case 0x09:
+                cr |= SDRAM_BXCR_SDAM_2;
+                break;
+            case 0x0A:
+                cr |= SDRAM_BXCR_SDAM_3;
+                break;
+            case 0x0B:
+                cr |= SDRAM_BXCR_SDAM_4;
+                break;
+            default:
+                printf("DDR-SDRAM: DIMM %lu BxCR configuration.\n",
+                   dimm_num);
+                printf("ERROR: Unsupported value for number of "
+                   "column addresses: %d.\n", num_col_addr);
+                printf("Replace the DIMM module with a supported DIMM.\n\n");
+                hang();
+            }
+
+            /*
+             * enable the bank
+             */
+            cr |= SDRAM_BXCR_SDBE;
+
+            /*------------------------------------------------------------------
+            | This next section is hardware dependent and must be programmed
+            | to match the hardware.
+            +-----------------------------------------------------------------*/
+            if (dimm_num == 0) {
+                for (i = 0; i < num_banks; i++) {
+                    mtdcr(memcfga, mem_b0cr + (i << 2));
+                    temp = mfdcr(memcfgd) & ~(SDRAM_BXCR_SDBA_MASK |
+                                              SDRAM_BXCR_SDSZ_MASK |
+                                              SDRAM_BXCR_SDAM_MASK |
+                                              SDRAM_BXCR_SDBE);
+                    cr |= temp;
+                    cr |= bank_base_addr & SDRAM_BXCR_SDBA_MASK;
+                    mtdcr(memcfgd, cr);
+                    bank_base_addr += bank_size_bytes;
+                }
+            }
+            else {
+                for (i = 0; i < num_banks; i++) {
+                    mtdcr(memcfga, mem_b2cr + (i << 2));
+                    temp = mfdcr(memcfgd) & ~(SDRAM_BXCR_SDBA_MASK |
+                                              SDRAM_BXCR_SDSZ_MASK |
+                                              SDRAM_BXCR_SDAM_MASK |
+                                              SDRAM_BXCR_SDBE);
+                    cr |= temp;
+                    cr |= bank_base_addr & SDRAM_BXCR_SDBA_MASK;
+                    mtdcr(memcfgd, cr);
+                    bank_base_addr += bank_size_bytes;
+                }
+            }
+        }
+    }
+
+    return(bank_base_addr);
+}
+
+void program_ecc (unsigned long  num_bytes)
+{
+    unsigned long bank_base_addr;
+    unsigned long current_address;
+    unsigned long end_address;
+    unsigned long address_increment;
+    unsigned long cfg0;
+
+    /*
+     * get Memory Controller Options 0 data
+     */
+    mfsdram(mem_cfg0, cfg0);
+
+    /*
+     * reset the bank_base address
+     */
+    bank_base_addr = CFG_SDRAM_BASE;
+
+    if ((cfg0 & SDRAM_CFG0_MCHK_MASK) != SDRAM_CFG0_MCHK_NON) {
+        mtsdram(mem_cfg0, (cfg0 & ~SDRAM_CFG0_MCHK_MASK) |
+            SDRAM_CFG0_MCHK_GEN);
+
+        if ((cfg0 & SDRAM_CFG0_DMWD_MASK) == SDRAM_CFG0_DMWD_32) {
+            address_increment = 4;
+        }
+        else {
+            address_increment = 8;
+        }
+
+        current_address = (unsigned long)(bank_base_addr);
+        end_address = (unsigned long)(bank_base_addr) + num_bytes;
+
+        while (current_address < end_address) {
+            *((unsigned long*)current_address) = 0x00000000;
+            current_address += address_increment;
+        }
+
+        mtsdram(mem_cfg0, (cfg0 & ~SDRAM_CFG0_MCHK_MASK) |
+            SDRAM_CFG0_MCHK_CHK);
+    }
+}
+
+#endif /* CONFIG_440 */
+
+#endif /* CONFIG_SPD_EEPROM */
diff --git a/cpu/sa1100/serial.c b/cpu/sa1100/serial.c
new file mode 100644
index 0000000..68bcd1f
--- /dev/null
+++ b/cpu/sa1100/serial.c
@@ -0,0 +1,158 @@
+/*
+ * (C) Copyright 2002
+ * Wolfgang Denk, DENX Software Engineering, <wd@denx.de>
+ *
+ * (C) Copyright 2002
+ * Sysgo Real-Time Solutions, GmbH <www.elinos.com>
+ * Marius Groeger <mgroeger@sysgo.de>
+ *
+ * (C) Copyright 2002
+ * Sysgo Real-Time Solutions, GmbH <www.elinos.com>
+ * Alex Zuepke <azu@sysgo.de>
+ *
+ * Copyright (C) 1999 2000 2001 Erik Mouw (J.A.K.Mouw@its.tudelft.nl)
+ *
+ * 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.
+ *
+ * This program 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
+ *
+ */
+
+#include <common.h>
+#include <SA-1100.h>
+
+void serial_setbrg (void)
+{
+	DECLARE_GLOBAL_DATA_PTR;
+
+	unsigned int reg = 0;
+
+	if (gd->baudrate == 1200)
+		reg = 191;
+	else if (gd->baudrate == 9600)
+		reg = 23;
+	else if (gd->baudrate == 19200)
+		reg = 11;
+	else if (gd->baudrate == 38400)
+		reg = 5;
+	else if (gd->baudrate == 57600)
+		reg = 3;
+	else if (gd->baudrate == 115200)
+		reg = 1;
+	else
+		hang ();
+
+#ifdef CONFIG_SERIAL1
+	/* Wait until port is ready ... */
+	while (Ser1UTSR1 & UTSR1_TBY) {
+	}
+
+	/* init serial serial 1 */
+	Ser1UTCR3 = 0x00;
+	Ser1UTSR0 = 0xff;
+	Ser1UTCR0 = (UTCR0_1StpBit | UTCR0_8BitData);
+	Ser1UTCR1 = 0;
+	Ser1UTCR2 = (u32) reg;
+	Ser1UTCR3 = (UTCR3_RXE | UTCR3_TXE);
+#elif CONFIG_SERIAL3
+	/* Wait until port is ready ... */
+	while (Ser3UTSR1 & UTSR1_TBY) {
+	}
+
+	/* init serial serial 3 */
+	Ser3UTCR3 = 0x00;
+	Ser3UTSR0 = 0xff;
+	Ser3UTCR0 = (UTCR0_1StpBit | UTCR0_8BitData);
+	Ser3UTCR1 = 0;
+	Ser3UTCR2 = (u32) reg;
+	Ser3UTCR3 = (UTCR3_RXE | UTCR3_TXE);
+#else
+#error "Bad: you didn't configured serial ..."
+#endif
+}
+
+
+/*
+ * Initialise the serial port with the given baudrate. The settings
+ * are always 8 data bits, no parity, 1 stop bit, no start bits.
+ *
+ */
+int serial_init (void)
+{
+	serial_setbrg ();
+
+	return (0);
+}
+
+
+/*
+ * Output a single byte to the serial port.
+ */
+void serial_putc (const char c)
+{
+#ifdef CONFIG_SERIAL1
+	/* wait for room in the tx FIFO on SERIAL1 */
+	while ((Ser1UTSR0 & UTSR0_TFS) == 0);
+
+	Ser1UTDR = c;
+#elif CONFIG_SERIAL3
+	/* wait for room in the tx FIFO on SERIAL3 */
+	while ((Ser3UTSR0 & UTSR0_TFS) == 0);
+
+	Ser3UTDR = c;
+#endif
+
+	/* If \n, also do \r */
+	if (c == '\n')
+		serial_putc ('\r');
+}
+
+/*
+ * Read a single byte from the serial port. Returns 1 on success, 0
+ * otherwise. When the function is succesfull, the character read is
+ * written into its argument c.
+ */
+int serial_tstc (void)
+{
+#ifdef CONFIG_SERIAL1
+	return Ser1UTSR1 & UTSR1_RNE;
+#elif CONFIG_SERIAL3
+	return Ser3UTSR1 & UTSR1_RNE;
+#endif
+}
+
+/*
+ * Read a single byte from the serial port. Returns 1 on success, 0
+ * otherwise. When the function is succesfull, the character read is
+ * written into its argument c.
+ */
+int serial_getc (void)
+{
+#ifdef CONFIG_SERIAL1
+	while (!(Ser1UTSR1 & UTSR1_RNE));
+
+	return (char) Ser1UTDR & 0xff;
+#elif CONFIG_SERIAL3
+	while (!(Ser3UTSR1 & UTSR1_RNE));
+
+	return (char) Ser3UTDR & 0xff;
+#endif
+}
+
+void
+serial_puts (const char *s)
+{
+	while (*s) {
+		serial_putc (*s++);
+	}
+}
diff --git a/cpu/sa1100/start.S b/cpu/sa1100/start.S
new file mode 100644
index 0000000..c0f30f5
--- /dev/null
+++ b/cpu/sa1100/start.S
@@ -0,0 +1,422 @@
+/*
+ *  armboot - Startup Code for SA1100 CPU
+ *
+ *  Copyright (C) 1998	Dan Malek <dmalek@jlc.net>
+ *  Copyright (C) 1999	Magnus Damm <kieraypc01.p.y.kie.era.ericsson.se>
+ *  Copyright (C) 2000	Wolfgang Denk <wd@denx.de>
+ *  Copyright (c) 2001	Alex Züpke <azu@sysgo.de>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * 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.
+ *
+ * This program 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
+ */
+
+
+
+#include <config.h>
+#include <version.h>
+
+
+/*
+ *************************************************************************
+ *
+ * Jump vector table as in table 3.1 in [1]
+ *
+ *************************************************************************
+ */
+
+
+.globl _start
+_start:	b       reset
+	ldr	pc, _undefined_instruction
+	ldr	pc, _software_interrupt
+	ldr	pc, _prefetch_abort
+	ldr	pc, _data_abort
+	ldr	pc, _not_used
+	ldr	pc, _irq
+	ldr	pc, _fiq
+
+_undefined_instruction:	.word undefined_instruction
+_software_interrupt:	.word software_interrupt
+_prefetch_abort:	.word prefetch_abort
+_data_abort:		.word data_abort
+_not_used:		.word not_used
+_irq:			.word irq
+_fiq:			.word fiq
+
+	.balignl 16,0xdeadbeef
+
+
+/*
+ *************************************************************************
+ *
+ * Startup Code (reset vector)
+ *
+ * do important init only if we don't start from memory!
+ * relocate armboot to ram
+ * setup stack
+ * jump to second stage
+ *
+ *************************************************************************
+ */
+
+/*
+ * CFG_MEM_END is in the board dependent config-file (configs/config_BOARD.h)
+ */
+_TEXT_BASE:
+	.word	TEXT_BASE
+
+.globl _armboot_start
+_armboot_start:
+	.word _start
+
+/*
+ * Note: _armboot_end_data and _armboot_end are defined
+ * by the (board-dependent) linker script.
+ * _armboot_end_data is the first usable FLASH address after armboot
+ */
+.globl _armboot_end_data
+_armboot_end_data:
+	.word armboot_end_data
+.globl _armboot_end
+_armboot_end:
+	.word armboot_end
+
+/*
+ * _armboot_real_end is the first usable RAM address behind armboot
+ * and the various stacks
+ */
+.globl _armboot_real_end
+_armboot_real_end:
+	.word 0x0badc0de
+
+#ifdef CONFIG_USE_IRQ
+/* IRQ stack memory (calculated at run-time) */
+.globl IRQ_STACK_START
+IRQ_STACK_START:
+	.word	0x0badc0de
+
+/* IRQ stack memory (calculated at run-time) */
+.globl FIQ_STACK_START
+FIQ_STACK_START:
+	.word 0x0badc0de
+#endif
+
+
+/*
+ * the actual reset code
+ */
+
+reset:
+	/*
+	 * set the cpu to SVC32 mode
+	 */
+	mrs	r0,cpsr
+	bic	r0,r0,#0x1f
+	orr	r0,r0,#0x13
+	msr	cpsr,r0
+
+	/*
+	 * we do sys-critical inits only at reboot,
+	 * not when booting from ram!
+	 */
+#ifdef CONFIG_INIT_CRITICAL
+	bl	cpu_init_crit
+#endif
+
+relocate:
+	/*
+	 * relocate armboot to RAM
+	 */
+	adr	r0, _start		/* r0 <- current position of code */
+	ldr	r2, _armboot_start
+	ldr	r3, _armboot_end
+	sub	r2, r3, r2		/* r2 <- size of armboot */
+	ldr	r1, _TEXT_BASE		/* r1 <- destination address */
+	add	r2, r0, r2		/* r2 <- source end address */
+
+	/*
+	 * r0 = source address
+	 * r1 = target address
+	 * r2 = source end address
+	 */
+copy_loop:
+	ldmia	r0!, {r3-r10}
+	stmia	r1!, {r3-r10}
+	cmp	r0, r2
+	ble	copy_loop
+
+	/* set up the stack */
+	ldr	r0, _armboot_end
+	add	r0, r0, #CONFIG_STACKSIZE
+	sub	sp, r0, #12		/* leave 3 words for abort-stack */
+
+	ldr	pc, _start_armboot
+
+_start_armboot:	.word start_armboot
+
+
+/*
+ *************************************************************************
+ *
+ * CPU_init_critical registers
+ *
+ * setup important registers
+ * setup memory timing
+ *
+ *************************************************************************
+ */
+
+
+/* Interupt-Controller base address */
+IC_BASE:	.word	0x90050000
+#define ICMR	0x04
+
+
+/* Reset-Controller */
+RST_BASE:		.word   0x90030000
+#define RSRR	0x00
+#define RCSR	0x04
+
+
+/* PWR */
+PWR_BASE:		.word   0x90020000
+#define PSPR    0x08
+#define PPCR    0x14
+cpuspeed:		.word   CFG_CPUSPEED
+
+
+cpu_init_crit:
+	/*
+	 * mask all IRQs
+	 */
+	ldr	r0, IC_BASE
+	mov	r1, #0x00
+	str	r1, [r0, #ICMR]
+
+	/* set clock speed */
+	ldr	r0, PWR_BASE
+	ldr	r1, cpuspeed
+	str	r1, [r0, #PPCR]
+
+	/*
+	 * before relocating, we have to setup RAM timing
+	 * because memory timing is board-dependend, you will
+	 * find a memsetup.S in your board directory.
+	 */
+	mov	ip,	lr
+	bl	memsetup
+	mov	lr,	ip
+
+	/*
+	 * disable MMU stuff and enable I-cache
+	 */
+	mrc	p15,0,r0,c1,c0
+	bic	r0, r0, #0x00002000	@ clear bit 13 (X)
+	bic	r0, r0, #0x0000000f	@ clear bits 3-0 (WCAM)
+	orr	r0, r0, #0x00001000	@ set bit 12 (I) Icache
+	orr	r0, r0, #0x00000002	@ set bit 2 (A) Align
+	mcr	p15,0,r0,c1,c0
+
+	/*
+	 * flush v4 I/D caches
+	 */
+	mov	r0, #0
+	mcr	p15, 0, r0, c7, c7, 0	/* flush v3/v4 cache */
+	mcr	p15, 0, r0, c8, c7, 0	/* flush v4 TLB */
+
+	mov	pc, lr
+
+
+
+
+/*
+ *************************************************************************
+ *
+ * Interrupt handling
+ *
+ *************************************************************************
+ */
+
+@
+@ IRQ stack frame.
+@
+#define S_FRAME_SIZE	72
+
+#define S_OLD_R0	68
+#define S_PSR		64
+#define S_PC		60
+#define S_LR		56
+#define S_SP		52
+
+#define S_IP		48
+#define S_FP		44
+#define S_R10		40
+#define S_R9		36
+#define S_R8		32
+#define S_R7		28
+#define S_R6		24
+#define S_R5		20
+#define S_R4		16
+#define S_R3		12
+#define S_R2		8
+#define S_R1		4
+#define S_R0		0
+
+#define MODE_SVC 0x13
+#define I_BIT	 0x80
+
+/*
+ * use bad_save_user_regs for abort/prefetch/undef/swi ...
+ * use irq_save_user_regs / irq_restore_user_regs for IRQ/FIQ handling
+ */
+
+	.macro	bad_save_user_regs
+	sub	sp, sp, #S_FRAME_SIZE
+	stmia	sp, {r0 - r12}			@ Calling r0-r12
+	add     r8, sp, #S_PC
+
+	ldr	r2, _armboot_end
+	add	r2, r2, #CONFIG_STACKSIZE
+	sub	r2, r2, #8
+	ldmia	r2, {r2 - r4}                   @ get pc, cpsr, old_r0
+	add	r0, sp, #S_FRAME_SIZE		@ restore sp_SVC
+
+	add	r5, sp, #S_SP
+	mov	r1, lr
+	stmia	r5, {r0 - r4}                   @ save sp_SVC, lr_SVC, pc, cpsr, old_r
+	mov	r0, sp
+	.endm
+
+	.macro	irq_save_user_regs
+	sub	sp, sp, #S_FRAME_SIZE
+	stmia	sp, {r0 - r12}			@ Calling r0-r12
+	add     r8, sp, #S_PC
+	stmdb   r8, {sp, lr}^                   @ Calling SP, LR
+	str     lr, [r8, #0]                    @ Save calling PC
+	mrs     r6, spsr
+	str     r6, [r8, #4]                    @ Save CPSR
+	str     r0, [r8, #8]                    @ Save OLD_R0
+	mov	r0, sp
+	.endm
+
+	.macro	irq_restore_user_regs
+	ldmia	sp, {r0 - lr}^			@ Calling r0 - lr
+	mov	r0, r0
+	ldr	lr, [sp, #S_PC]			@ Get PC
+	add	sp, sp, #S_FRAME_SIZE
+	subs	pc, lr, #4			@ return & move spsr_svc into cpsr
+	.endm
+
+	.macro get_bad_stack
+	ldr	r13, _armboot_end		@ setup our mode stack
+	add	r13, r13, #CONFIG_STACKSIZE	@ resides at top of normal stack
+	sub	r13, r13, #8
+
+	str	lr, [r13]			@ save caller lr / spsr
+	mrs	lr, spsr
+	str     lr, [r13, #4]
+
+	mov	r13, #MODE_SVC			@ prepare SVC-Mode
+	msr	spsr_c, r13
+	mov	lr, pc
+	movs	pc, lr
+	.endm
+
+	.macro get_irq_stack			@ setup IRQ stack
+	ldr	sp, IRQ_STACK_START
+	.endm
+
+	.macro get_fiq_stack			@ setup FIQ stack
+	ldr	sp, FIQ_STACK_START
+	.endm
+
+/*
+ * exception handlers
+ */
+	.align  5
+undefined_instruction:
+	get_bad_stack
+	bad_save_user_regs
+	bl 	do_undefined_instruction
+
+	.align	5
+software_interrupt:
+	get_bad_stack
+	bad_save_user_regs
+	bl 	do_software_interrupt
+
+	.align	5
+prefetch_abort:
+	get_bad_stack
+	bad_save_user_regs
+	bl 	do_prefetch_abort
+
+	.align	5
+data_abort:
+	get_bad_stack
+	bad_save_user_regs
+	bl 	do_data_abort
+
+	.align	5
+not_used:
+	get_bad_stack
+	bad_save_user_regs
+	bl 	do_not_used
+
+#ifdef CONFIG_USE_IRQ
+
+	.align	5
+irq:
+	get_irq_stack
+	irq_save_user_regs
+	bl 	do_irq
+	irq_restore_user_regs
+
+	.align	5
+fiq:
+	get_fiq_stack
+	/* someone ought to write a more effiction fiq_save_user_regs */
+	irq_save_user_regs
+	bl 	do_fiq
+	irq_restore_user_regs
+
+#else
+
+	.align	5
+irq:
+	get_bad_stack
+	bad_save_user_regs
+	bl 	do_irq
+
+	.align	5
+fiq:
+	get_bad_stack
+	bad_save_user_regs
+	bl 	do_fiq
+
+#endif
+
+	.align	5
+.globl reset_cpu
+reset_cpu:
+	ldr	r0, RST_BASE
+	mov	r1, #0x0			@ set bit 3-0 ...
+	str	r1, [r0, #RCSR]			@ ... to clear in RCSR
+	mov	r1, #0x1
+	str	r1, [r0, #RSRR]			@ and perform reset
+	b	reset_cpu			@ silly, but repeat endlessly