/*
 * File      : start.S
 * This file is part of RT-Thread RTOS
 * COPYRIGHT (C) 2006, RT-Thread Development Team
 *
 * The license and distribution terms for this file may be
 * found in the file LICENSE in this distribution or at
 * http://openlab.rt-thread.com/license/LICENSE
 *
 * Change Logs:
 * Date           Author       Notes
 * 2006-03-13     Bernard      first version
 * 2006-10-05     Alsor.Z      for s3c2410 initialize
 * 2008-01-29     Yi.Qiu       for QEMU emulator
 */

/**
 * @addtogroup S3C2410
 */
/*@{*/

#define CONFIG_STACKSIZE 512
#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

	.equ USERMODE, 	0x10
	.equ FIQMODE,	0x11
	.equ IRQMODE,	0x12
	.equ SVCMODE,	0x13
	.equ ABORTMODE,	0x17
	.equ UNDEFMODE,	0x1b
	.equ MODEMASK,	0x1f
	.equ NOINT,		0xc0

.section .init, "ax"
.code 32
.globl _start
_start:
	b	reset
	ldr	pc, _vector_undef
	ldr	pc, _vector_swi
	ldr	pc, _vector_pabt
	ldr	pc, _vector_dabt
	ldr	pc, _vector_resv
	ldr	pc, _vector_irq
	ldr	pc, _vector_fiq

_vector_undef:	.word vector_undef
_vector_swi:	.word vector_swi
_vector_pabt:	.word vector_pabt
_vector_dabt:	.word vector_dabt
_vector_resv:	.word vector_resv
_vector_irq:	.word vector_irq
_vector_fiq:	.word vector_fiq

	.balignl 16,0xdeadbeef

_TEXT_BASE:
	.word	TEXT_BASE

/*
 * rtthread kernel start and end
 * which are defined in linker script
 */
.globl _rtthread_start
_rtthread_start:.word _start
.globl _rtthread_end
_rtthread_end:	.word  _end

/*
 * rtthread bss start and end
 * which are defined in linker script
 */
.globl _bss_start
_bss_start:	.word __bss_start
.globl _bss_end
_bss_end:	.word __bss_end

/* IRQ stack memory (calculated at run-time) */
.globl IRQ_STACK_START
IRQ_STACK_START:.word _irq_stack_start + 1024
.globl FIQ_STACK_START
FIQ_STACK_START:.word _fiq_stack_start + 1024
.globl UNDEFINED_STACK_START
UNDEFINED_STACK_START:.word _undefined_stack_start + CONFIG_STACKSIZE
.globl ABORT_STACK_START
ABORT_STACK_START:.word _abort_stack_start + CONFIG_STACKSIZE
.globl _STACK_START
_STACK_START:.word _svc_stack_start + 4096

	.equ RAM_BASE,	0x00000000	//Start address of RAM
	.equ ROM_BASE,	0x30000000	//Start address of Flash

	.equ INTMSK,	0x4a000008
	.equ WTCON, 	0x53000000
	.equ INTSUBMSK, 0x4a00001c
	.equ LOCKTIME,	0x4c000000

	.equ MPLLCON,	0x4c000004
	.equ M_MDIV,	0x20
	.equ M_PDIV,	0x4
	.equ M_SDIV,	0x2
	.equ CLKDIVN,	0x4c000014	//Clock divider control
	.equ GPHCON,	0x56000070	//Port H control
	.equ GPHUP,		0x56000078	//Pull-up control H
	.equ BWSCON,	0x48000000	//Bus width & wait status
	.equ BANKCON0,	0x48000004	//Boot ROM control
	.equ BANKCON1,	0x48000008	//BANK1 control
	.equ BANKCON2,	0x4800000c	//BANK2 cControl
	.equ BANKCON3,	0x48000010	//BANK3 control
	.equ BANKCON4,	0x48000014	//BANK4 control
	.equ BANKCON5,	0x48000018	//BANK5 control
	.equ BANKCON6,	0x4800001c	//BANK6 control
	.equ BANKCON7,	0x48000020	//BANK7 control
	.equ REFRESH,	0x48000024	//DRAM/SDRAM efresh
	.equ BANKSIZE,	0x48000028	//Flexible Bank Size
	.equ MRSRB6,	0x4800002c	//Mode egister set for SDRAM
	.equ MRSRB7,	0x48000030	//Mode egister set for SDRAM

/* -----------------entry--------------- */
reset:
	/* watch dog disable */
	ldr r0,=WTCON
	ldr r1,=0x0
	str r1,[r0]

	/* set the cpu to SVC32 mode */
	mrs	r0,cpsr
	bic	r0,r0,#MODEMASK
	orr	r0,r0,#SVCMODE
	msr	cpsr,r0

	/* mask all IRQs by clearing all bits in the INTMRs */
	ldr	r1, =INTMSK
	ldr	r0, =0xffffffff
	str	r0, [r1]

	/* set interrupt vector 	*/
	ldr r0, _load_address
	mov	r1, #0x0				/* target address    		*/
	add	r2, r0, #0x20			/* size, 32bytes         	*/

copy_loop:
	ldmia	r0!, {r3-r10}		/* copy from source address [r0]    */
	stmia	r1!, {r3-r10}		/* copy to   target address [r1]    */
	cmp	r0, r2					/* until source end addreee [r2]    */
	ble	copy_loop

	/* setup stack */
	bl	stack_setup

	/* clear .bss */
	mov   r0,#0                     /* get a zero 				*/
	ldr   r1,=__bss_start           /* bss start 				*/
	ldr   r2,=__bss_end           	/* bss end 					*/
bss_loop:
	cmp   r1,r2                     /* check if data to clear 	*/
	strlo r0,[r1],#4                /* clear 4 bytes 			*/
	blo   bss_loop                  /* loop until done 			*/

	/* call C++ constructors of global objects */
	ldr 	r0, =__ctors_start__
	ldr 	r1, =__ctors_end__
ctor_loop:
	cmp 	r0, r1
	beq 	ctor_end
	ldr 	r2, [r0], #4
	stmfd 	sp!, {r0-r1}
	mov 	lr, pc
	bx 		r2
	ldmfd 	sp!, {r0-r1}
	b	ctor_loop
ctor_end:

	/* start RT-Thread Kernel */
	ldr	pc, _rtthread_startup

_rtthread_startup: .word rtthread_startup
#if defined (__FLASH_BUILD__)
_load_address: .word ROM_BASE + _TEXT_BASE
#else
_load_address: .word RAM_BASE + _TEXT_BASE
#endif

/*
 *************************************************************************
 *
 * Interrupt handling
 *
 *************************************************************************
 */

/* exception handlers */
	.align  5
vector_undef:
	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

	bl	rt_hw_trap_udef

	.align	5
vector_swi:
	bl 	rt_hw_trap_swi

	.align	5
vector_pabt:
	bl 	rt_hw_trap_pabt

	.align	5
vector_dabt:
	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

	bl 	rt_hw_trap_dabt

	.align	5
vector_resv:
	bl 	rt_hw_trap_resv

.globl rt_interrupt_enter
.globl rt_interrupt_leave
.globl rt_thread_switch_interrput_flag
.globl rt_interrupt_from_thread
.globl rt_interrupt_to_thread
vector_irq:
	stmfd	sp!, {r0-r12,lr}
	bl	rt_interrupt_enter
	bl	rt_hw_trap_irq
	bl	rt_interrupt_leave

	/* if rt_thread_switch_interrput_flag set, jump to _interrupt_thread_switch and don't return */
	ldr	r0, =rt_thread_switch_interrput_flag
	ldr	r1, [r0]
	cmp	r1, #1
	beq	_interrupt_thread_switch

	ldmfd	sp!, {r0-r12,lr}
	subs	pc, lr, #4

	.align	5
vector_fiq:
	stmfd	sp!,{r0-r7,lr}
	bl 	rt_hw_trap_fiq
	ldmfd	sp!,{r0-r7,lr}
	subs	pc,lr,#4

_interrupt_thread_switch:
	mov	r1,  #0				@ clear rt_thread_switch_interrput_flag
	str	r1,  [r0]

	ldmfd	sp!, {r0-r12,lr}@ reload saved registers
	stmfd	sp!, {r0-r3}	@ save r0-r3
	mov	r1,  sp
	add	sp,  sp, #16		@ restore sp
	sub	r2,  lr, #4			@ save old task's pc to r2

	mrs	r3,  spsr			@ disable interrupt
	orr	r0,  r3, #NOINT
	msr	spsr_c, r0

	ldr	r0,  =.+8			@ switch to interrupted task's stack
	movs	pc,  r0

	stmfd	sp!, {r2}		@ push old task's pc
	stmfd	sp!, {r4-r12,lr}@ push old task's lr,r12-r4
	mov	r4,  r1				@ Special optimised code below
	mov	r5,  r3
	ldmfd	r4!, {r0-r3}
	stmfd	sp!, {r0-r3}	@ push old task's r3-r0
	stmfd	sp!, {r5}		@ push old task's psr
	mrs	r4,  spsr
	stmfd	sp!, {r4}		@ push old task's spsr

	ldr	r4,  =rt_interrupt_from_thread
	ldr	r5,  [r4]
	str	sp,  [r5]			@ store sp in preempted tasks's TCB

	ldr	r6,  =rt_interrupt_to_thread
	ldr	r6,  [r6]
	ldr	sp,  [r6]			@ get new task's stack pointer

	ldmfd	sp!, {r4}		@ pop new task's spsr
	msr	SPSR_cxsf, r4
	ldmfd	sp!, {r4}		@ pop new task's psr
	msr	CPSR_cxsf, r4

	ldmfd	sp!, {r0-r12,lr,pc}	@ pop new task's r0-r12,lr & pc

stack_setup:
	mrs	r0, cpsr
	bic	r0, r0, #MODEMASK
	orr	r1, r0, #UNDEFMODE|NOINT
	msr	cpsr_cxsf, r1		@ undef mode
	ldr	sp, UNDEFINED_STACK_START

	orr	r1,r0,#ABORTMODE|NOINT
	msr	cpsr_cxsf,r1		@ abort mode
	ldr	sp, ABORT_STACK_START

	orr	r1,r0,#IRQMODE|NOINT
	msr	cpsr_cxsf,r1		@ IRQ mode
	ldr	sp, IRQ_STACK_START

	orr	r1,r0,#FIQMODE|NOINT
	msr	cpsr_cxsf,r1		@ FIQ mode
	ldr	sp, FIQ_STACK_START

	bic	r0,r0,#MODEMASK
	orr	r1,r0,#SVCMODE|NOINT
	msr	cpsr_cxsf,r1		@ SVC mode

	ldr	sp, _STACK_START

	/* USER mode is not initialized. */

	mov	pc,lr				@ The LR register may be not valid for the mode changes.

/*@}*/
