@  This is the init for an AT91SAM7S64
@ This code is entered with interrupts disabled
@ This code should work as start code from flash or RAM.
@ It does not have to be placed at address 0 since it will copy the vectors
@ there if needed.
@

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@ Macros to do memory initialisation

	.macro  mem_copy,  source, source_end, dest
	ldr   r0,=\source
	ldr   r1,=\source_end
	ldr   r2,=\dest
	bl    mem_copy_func
	.endm

	.macro mem_initialise, dest, dest_end, val
	ldr   r0,=\dest
	ldr   r1,=\dest_end
	ldr   r2,=\val
	bl    mem_init_func
	.endm

	.macro mac_string str
	.string \str
	.endm


@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@


	.text
	.code 32
	.align 	0
	
	.global start	
	.extern main

@ Flash is always a 1MB
@ RAM is always at 2MB
@ 0MB will be flash or RAM depending on the remap state.
@
start:
@ Disable interrupts (IRQ and FIQ). Should not have to do this, but lets not assume anything
	mrs  r0,cpsr
	orr  r0,r0,#0xc0
	msr  cpsr_all,r0

@
@ This patch resets the chip
@
@ patch begin
	ldr r0, =0xfffffd00
0:
	ldr r1, [r0, #4] @ RSTC_SR
	ands r1, r1, #0x20000
	bne 0b
	ldr r1, =0xa5000004
	ldr r1, [r0] @ RSTC_CR
1:
	ldr r1, [r0, #4] @ RSTC_SR
	ands r1, r1, #0x20000
	bne 1b
@ patch end

@
@ Do the remapping, if required, so that RAM is at address 0.
@
@  We read what is at address 0 and 2MB
@ We then invert what is at 2MB and read at 0 again
@ If address 0 has changed, then RAM is already at 0, so there is
@ no need to remap

	ldr  r0,=0x000000
	ldr  r2,=0x200000
	ldr  r3,[r0]
	ldr  r4,[r2]
	ldr  r5,=0xFFFFFFFF
	eor  r5,r4,r5
	str  r5,[r2]
	ldr  r5,[r0]
	str  r4,[r2]

	cmp  r3,r5
	bne  skip_remap_toggle

	ldr  r0,=0xFFFFFF00
	ldr  r1,=1
	str  r1,[r0]

skip_remap_toggle:

		b    continue_boot


continue_boot:

@   MC_FMR:
@   Set up flash memory accessing and memory for 48 MHz FMCN = 50)
@   2 wait states (FWS = 1)
@   No special flags
@

	ldr r0,=0xFFFFFF60
	ldr r1,=((72 << 16) | (1 << 8))
	str r1,[r0]

@
@    Disable watchdog
@    Set MDT_MR disable bit. 
@    Note: probably want to set this differently at some stage...
@
	ldr r0,=0xFFFFFD44
	ldr r1,=0x00008000
	str r1,[r0]

@
@    Set up PLL & clock source
@    The main crystal is 18.432 MHz
@    We want as close as we can get to 48MHz
@    We use the PLL to generate 18432000 / 14 * 73 = 96109714
@    We then divide this by 2

@ Start main osc

	ldr r0,=0xFFFFFC20  @ PMC_MOR
	ldr r1,=0x00000601
	str r1,[r0]

	ldr r0,=0xFFFFFC68  @ PMC_SR
wait_MOSCS:
	ldr r1,[r0]
	ands r1,r1,#1
	beq wait_MOSCS

	@ set up PLL
	ldr r0,=0xFFFFFC2C  @ PMC_PLLR
	ldr r1,=0x00481C0E
	str r1,[r0]

	ldr r0,=0xFFFFFC68  @ PMC_SR
wait_pll_lock:
	ldr r1,[r0]
	ands r1,r1,#4
	beq wait_pll_lock

	ldr r0,=0xFFFFFC68  @ PMC_SR
wait_MCLK_Ready0:
	ldr r1,[r0]
	ands r1,r1,#8
	beq  wait_MCLK_Ready0

@ Set up master clock to use clock div 2
	ldr r0,=0xFFFFFC30 @ PMC_MCKR
	ldr r1,=0x4
	str r1,[r0]

	ldr r0,=0xFFFFFC68  @ PMC_SR
wait_MCLK_Ready1:
	ldr r1,[r0]
	ands r1,r1,#8
	beq  wait_MCLK_Ready1

@ Set up master clock to use PLL clock div 2
	ldr r0,=0xFFFFFC30 @ PMC_MCKR
	ldr r1,=0x7
	str r1,[r0]

	ldr r0,=0xFFFFFC68  @ PMC_SR
wait_MCLK_Ready2:
	ldr r1,[r0]
	ands r1,r1,#8
	beq  wait_MCLK_Ready2


@
@
@ Copy the vector table
@
vector_copy:
	mem_copy       __vectors_load_start__, __vectors_load_end__, __vectors_ram_start__

@
@ Initialise memory regions
@
data_copy:
	mem_copy       __data_load_start__, __data_load_end__, __data_ram_start__
ram_text_copy:
	mem_copy       __ramtext_load_start__, __ramtext_load_end__, __ramtext_ram_start__
bss_init:
	mem_initialise __bss_start__, __bss_end__, 0
stack_init_0:
	mem_initialise __stack_start__, __stack_end__, 0x6b617453 @ 'Stak'

@
@   Set up stacks etc.
@   We set up the irq and system stacks
@   We switch to system mode for further execution
@   NB Stacks should be 8-byte aligned for APCS
@   They should already be aligned, but we mask the values to make sure.
@
stack_init_1:
@ Set up Interrupt stack
                msr   CPSR_c,#0xD2 @ IRQ mode, IRQ, FIQ off
                ldr   r0, =__irq_stack__
                mov   sp,r0
		bic   sp,sp,#7    @ make sure it is 8-byte aligned
@
@ Set up System stack
		msr   CPSR_c,#0xDF		@ System mode , I and F bits set (interrupts disabled)
		ldr   r3, =__system_stack__
		mov   sp, r3
		bic   sp,sp,#7    @ make sure it is 8-byte aligned

@ Set up Supervisor stack
		msr   CPSR_c,#0xD3		@ Supervisor Mode, I and F bits set (interrupts disabled)
		ldr   r3, =__irq_stack__
		mov   sp, r3
		bic   sp,sp,#7    @ make sure it is 8-byte aligned


@ Set up initial frame pointers etc
		mov     a2, #0							@ Second arg: fill value
		mov	fp, a2							@ Null frame pointer
		mov	r7, a2							@ Null frame pointer for Thumb

@ Kick into main code using interworking

	ldr r5,=main
	mov lr,pc
	bx  r5

@ If we get here then main returned -- bad!
main_returned:
	b main_returned




@
@ Dummy handlers
@
	.global undef_handler
	.global swi_handler
	.global prefetch_abort_handler
	.global data_abort_handler
	.global reserved_handler


undef_handler:
	b undef_handler
swi_handler:
        stmfd		sp!, {r0}
	ldr		r0,[lr,#-4]		// Calculate address of SWI instruction and load it into r0.
	bic		r0,r0,#0xff000000	
        cmp		r0, #1			//Check the argument with SWI instruction, compare against 1
	beq		return_handler		
        ldmfd		sp!, {r0}
        stmfd		sp!, {r2}
        stmfd		sp!, {r3}	
	stmfd		sp!, {lr}		
	ldmfd		sp!, {pc}^

return_handler:
        ldmfd		sp!, {r0}
	ldmfd		sp!, {r14}		@restore spsr
	msr		spsr, r14
	ldmfd		sp!, {pc}^		@restore pc (lr value) and cpsr (spsr value)
					

prefetch_abort_handler:
	b prefetch_abort_handler

	.extern data_abort_pc
	.extern data_abort_C

data_abort_handler:
	ldr r0,=data_abort_pc
	str lr,[r0]
	msr   CPSR_c,#0xDF		@ System mode , I and F bits set (interrupts disabled)
	ldr r0,=data_abort_C
	mov lr,pc
	bx  r0
data_abort_C_returned:
	b data_abort_C_returned
reserved_handler:
	b reserved_handler



@ Little helper funcs


@ mem_copy_func r0 = source start, r1 = end of source, r2 = destination
@ Will not copy if source == destination
@ Will try to use ldm/stm if 16-byte aligned.
mem_copy_func:
	@ bail if source and dest addresses are the same
	cmp   r0,r2
	bxeq  lr
	
	@ test if all addressed are 16-byte aligned, if so use ldm/stm copy
	mov   r3,r0
	orr   r3,r3,r1
	orr   r3,r3,r2
	ands  r3,r3,#15
	beq     mcf_16_aligned
	
	@ else use 4-byte aligned loop
mcf_loop:
	cmp   r0,r1
	ldrlo r3,[r2],#4 
	strlo r3,[r0],#4 
	blo   mcf_loop
	bx    lr

mcf_16_aligned:
	cmp   r0,r1
	ldmloia r0!,{r3-r6}
	stmloia r2!,{r3-r6}
	blo   mcf_16_aligned
	bx    lr
	

@ mem_init_func: r0 = start address, r1 = end address, r2 is value to write
@ If start and end addresses are multiples of 16 then we use stms to do the
@ storing.
mem_init_func:
	orr   r3,r0,r1
	ands  r3,r2,#15
	beq   mif_16_aligned
mif_loop:
	cmp   r0,r1
	strlo r2,[r0],#4 
	blo   mif_loop
	bx    lr

mif_16_aligned:
	mov   r3,r2
	mov   r4,r2
	mov   r5,r2
mif_16_loop:
	cmp   r0,r1
	stmloia r0!,{r2-r5}
	blo   mif_16_loop
	bx    lr
