ARM: implement relocation for ARM926

Change the implementation for arm926 to relocate the code to
an arbitrary address in RAM.

Adapt the TX25 (i.MX25), magnesium board to test the changes.

On the tx25 board TEXT_BASE is set to the final relocation
address to prevent one more copying of u-boot code
when relocating. More info see:
doc/README.arm-relocation

da850 board:
Tested-by: Ben Gardiner <bengardiner@nanometrics.ca>

Portions of this work were supported by funding from
the CE Linux Forum.

Signed-off-by: Heiko Schocher <hs@denx.de>
Cc: Ben Gardiner <bengardiner@nanometrics.ca>
diff --git a/arch/arm/cpu/arm926ejs/start.S b/arch/arm/cpu/arm926ejs/start.S
index cf40ce1..16ee972 100644
--- a/arch/arm/cpu/arm926ejs/start.S
+++ b/arch/arm/cpu/arm926ejs/start.S
@@ -114,12 +114,15 @@
  *************************************************************************
  */
 
+.globl _TEXT_BASE
 _TEXT_BASE:
 	.word	TEXT_BASE
 
+#if defined(CONFIG_SYS_ARM_WITHOUT_RELOC)
 .globl _armboot_start
 _armboot_start:
 	.word _start
+#endif
 
 /*
  * These are defined in the board-specific linker script.
@@ -143,8 +146,166 @@
 FIQ_STACK_START:
 	.word 0x0badc0de
 #endif
+
+#if !defined(CONFIG_SYS_ARM_WITHOUT_RELOC)
+/* IRQ stack memory (calculated at run-time) + 8 bytes */
+.globl IRQ_STACK_START_IN
+IRQ_STACK_START_IN:
+	.word	0x0badc0de
+
+.globl _datarel_start
+_datarel_start:
+	.word __datarel_start
+
+.globl _datarelrolocal_start
+_datarelrolocal_start:
+	.word __datarelrolocal_start
+
+.globl _datarellocal_start
+_datarellocal_start:
+	.word __datarellocal_start
+
+.globl _datarelro_start
+_datarelro_start:
+	.word __datarelro_start
+
+.globl _got_start
+_got_start:
+	.word __got_start
+
+.globl _got_end
+_got_end:
+	.word __got_end
+
+/*
+ * 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
+
+	/*
+	 * we do sys-critical inits only at reboot,
+	 * not when booting from ram!
+	 */
+#ifndef CONFIG_SKIP_LOWLEVEL_INIT
+	bl	cpu_init_crit
+#endif
+
+/* Set stackpointer in internal RAM to call board_init_f */
+call_board_init_f:
+	ldr	sp, =(CONFIG_SYS_INIT_SP_ADDR)
+	ldr	r0,=0x00000000
+	bl	board_init_f
+
+/*------------------------------------------------------------------------------*/
+
+/*
+ * void relocate_code (addr_sp, gd, addr_moni)
+ *
+ * This "function" does not return, instead it continues in RAM
+ * after relocating the monitor code.
+ *
+ */
+	.globl	relocate_code
+relocate_code:
+	mov	r4, r0	/* save addr_sp */
+	mov	r5, r1	/* save addr of gd */
+	mov	r6, r2	/* save addr of destination */
+	mov	r7, r2	/* save addr of destination */
+
+	/* Set up the stack						    */
+stack_setup:
+	mov	sp, r4
+
+	adr	r0, _start
+	ldr	r2, _TEXT_BASE
+	ldr	r3, _bss_start
+	sub	r2, r3, r2		/* r2 <- size of armboot	    */
+	add	r2, r0, r2		/* r2 <- source end address	    */
+	cmp	r0, r6
+	beq	clear_bss
+
+#ifndef CONFIG_SKIP_RELOCATE_UBOOT
+copy_loop:
+	ldmia	r0!, {r9-r10}		/* copy from source address [r0]    */
+	stmia	r6!, {r9-r10}		/* copy to   target address [r1]    */
+	cmp	r0, r2			/* until source end addreee [r2]    */
+	ble	copy_loop
+
+#ifndef CONFIG_PRELOADER
+	/* fix got entries */
+	ldr	r1, _TEXT_BASE		/* Text base */
+	mov	r0, r7			/* reloc addr */
+	ldr	r2, _got_start		/* addr in Flash */
+	ldr	r3, _got_end		/* addr in Flash */
+	sub	r3, r3, r1
+	add	r3, r3, r0
+	sub	r2, r2, r1
+	add	r2, r2, r0
+
+fixloop:
+	ldr	r4, [r2]
+	sub	r4, r4, r1
+	add	r4, r4, r0
+	str	r4, [r2]
+	add	r2, r2, #4
+	cmp	r2, r3
+	bne	fixloop
+#endif
+#endif	/* #ifndef CONFIG_SKIP_RELOCATE_UBOOT */
 
+clear_bss:
+#ifndef CONFIG_PRELOADER
+	ldr	r0, _bss_start
+	ldr	r1, _bss_end
+	ldr	r3, _TEXT_BASE		/* Text base */
+	mov	r4, r7			/* reloc addr */
+	sub	r0, r0, r3
+	add	r0, r0, r4
+	sub	r1, r1, r3
+	add	r1, r1, r4
+	mov	r2, #0x00000000		/* clear			    */
+
+clbss_l:str	r2, [r0]		/* clear loop...		    */
+	add	r0, r0, #4
+	cmp	r0, r1
+	bne	clbss_l
 
+	bl coloured_LED_init
+	bl red_LED_on
+#endif
+
+/*
+ * We are done. Do not return, instead branch to second part of board
+ * initialization, now running from RAM.
+ */
+#ifdef CONFIG_NAND_SPL
+	ldr     pc, _nand_boot
+
+_nand_boot: .word nand_boot
+#else
+	ldr	r0, _TEXT_BASE
+	ldr	r2, _board_init_r
+	sub	r2, r2, r0
+	add	r2, r2, r7	/* position from board_init_r in RAM */
+	/* setup parameters for board_init_r */
+	mov	r0, r5		/* gd_t */
+	mov	r1, r7		/* dest_addr */
+	/* jump to it ... */
+	mov	lr, r2
+	mov	pc, lr
+
+_board_init_r: .word board_init_r
+#endif
+
+#else /* #if !defined(CONFIG_SYS_ARM_WITHOUT_RELOC) */
 /*
  * the actual reset code
  */
@@ -221,7 +382,7 @@
 #else
 	.word start_armboot
 #endif /* CONFIG_NAND_SPL */
-
+#endif /* #if !defined(CONFIG_SYS_ARM_WITHOUT_RELOC) */
 
 /*
  *************************************************************************
@@ -307,10 +468,13 @@
 	@ carve out a frame on current user stack
 	sub	sp, sp, #S_FRAME_SIZE
 	stmia	sp, {r0 - r12}	@ Save user registers (now in svc mode) r0-r12
-
+#if defined(CONFIG_SYS_ARM_WITHOUT_RELOC)
 	ldr	r2, _armboot_start
 	sub	r2, r2, #(CONFIG_STACKSIZE+CONFIG_SYS_MALLOC_LEN)
 	sub	r2, r2, #(CONFIG_SYS_GBL_DATA_SIZE+8)  @ set base 2 words into abort stack
+#else
+	ldr	r2, IRQ_STACK_START_IN
+#endif
 	@ get values for "aborted" pc and cpsr (into parm regs)
 	ldmia	r2, {r2 - r3}
 	add	r0, sp, #S_FRAME_SIZE		@ grab pointer to old stack
@@ -342,9 +506,13 @@
 	.endm
 
 	.macro get_bad_stack
+#if defined(CONFIG_SYS_ARM_WITHOUT_RELOC)
 	ldr	r13, _armboot_start		@ setup our mode stack
 	sub	r13, r13, #(CONFIG_STACKSIZE+CONFIG_SYS_MALLOC_LEN)
 	sub	r13, r13, #(CONFIG_SYS_GBL_DATA_SIZE+8) @ reserved a couple spots in abort stack
+#else
+	ldr	r13, IRQ_STACK_START_IN		@ setup our mode stack
+#endif
 
 	str	lr, [r13]	@ save caller lr in position 0 of saved stack
 	mrs	lr, spsr	@ get the spsr