/*****************************************************************************
 * arch/arm/src/str71x/str71x_head.S
 *
 *   Copyright (C) 2008-2009, 2012 Gregory Nutt. All rights reserved.
 *   Author: Gregory Nutt <gnutt@nuttx.org>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 3. Neither the name NuttX nor the names of its contributors may be
 *    used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 *****************************************************************************/

/*****************************************************************************
 * Included Files
 *****************************************************************************/

#include <nuttx/config.h>		/* NuttX configuration settings */
#include <arch/board/board.h>	/* Board-specific settings */

#include "arm.h"				/* ARM-specific settings */
#include "chip.h"				/* Chip-specific settings */
#include "up_internal.h"
#include "up_arch.h"

/*****************************************************************************
 * Pre-processor Definitions
 *****************************************************************************/

/* This file holds the NuttX start logic that runs when the STR711
 * is reset.  This logic must be located at address 0x4000:0000 in
 * flash.  It will also be mapped to address zero when the STR711 is
 * reset.
 */

/*****************************************************************************
 * External references
 *****************************************************************************/

	.globl	str71x_prccuinit			/* Clock initialization */
	.globl	up_lowsetup					/* Early initialization of UART */
#ifdef USE_EARLYSERIALINIT
	.globl	up_earlyserialinit			/* Early initialization of serial driver */
#endif
#ifdef CONFIG_ARCH_LEDS
	.globl	board_autoled_initialize	/* Boot LED setup */
#endif
#ifdef CONFIG_DEBUG
	.globl	up_lowputc					/* Low-level debug output */
#endif
	.globl	os_start					/* NuttX entry point */

/*****************************************************************************
 * Macros
 *****************************************************************************/

/*****************************************************************************
 * Name: showprogress
 *
 * Description:
 *   Print a character on the UART to show boot status. This macro will
 *   modify r0, r1, r2 and r14
 *
 *****************************************************************************/

	.macro	showprogress, code
#ifdef CONFIG_DEBUG
	mov		r0, #\code
	bl		up_lowputc
#endif
	.endm

/*****************************************************************************
 * Name: emiinit
 *
 * Description:
 *   Initialize external memory banks 0-3 as configured
 *
 *****************************************************************************/

	.macro	emiinit, base, value
#if defined(CONFIG_STR71X_BANK0) || defined(CONFIG_STR71X_BANK1) || \
    defined(CONFIG_STR71X_BANK2) || defined(CONFIG_STR71X_BANK3)

	/* In order to use the external memory, certain GPIO pins must be
	 * configured in the alternate function:
	 *
	 * GPIO    ALT     Description
	 * P2.0-3  CS.0-3  External memory chip select for banks 0,1,3,4
	 * P2.4-7  A.20-23 External memory extended address bus (needed for
	 *                 address space > 1Mb)
	 */

#ifdef CONFIG_STR71X_BIGEXTMEM
#  define EXTMEM_GPIO_BITSET 0x000000ff		/* P2.0-7 */
#else
#  define EXTMEM_GPIO_BITSET 0x0000000f		/* P2.0-3 */
#endif

	ldr		\base, =STR71X_GPIO_BASE		; Configure P2.0 to P2.3/7 in AF_PP mode
	ldr		\value, [\base, #STR71X_GPIO_PC0_OFFSET]
	orr		\value, \value, #EXTMEM_GPIO_BITSET
	str		\value, [\base, #STR71X_GPIO_PC0_OFFSET]
	ldr		\value, [\base, #STR71X_GPIO_PC1_OFFSET]
	orr		\value, \value, #EXTMEM_GPIO_BITSET
	str		\value, [\base, #STR71X_GPIO_PC1_OFFSET]
	ldr		\value, [\base, #STR71X_GPIO_PC2_OFFSET]
	orr		\value, \value, #EXTMEM_GPIO_BITSET
	str		\value, [\base, #STR71X_GPIO_PC2_OFFSET]

	/* Enable bank 0 */

	ldr		\base, =STR71X_EMI_BASE

#ifdef CONFIG_STR71X_BANK0

	/* Get the bank 0 size */

#  if CONFIG_STR71X_BANK0_SIZE == 8
#    define EXTMEM_BANK0_SIZE STR71X_EMIBCON_BSIZE8
#  elif CONFIG_STR71X_BANK0_SIZE == 16
#    define EXTMEM_BANK0_SIZE STR71X_EMIBCON_BSIZE16
#  else
#    error "CONFIG_STR71X_BANK0_SIZE invalid"
#  endif

	/* Get the bank 0 waitstates */

#  if !defined(CONFIG_STR71X_BANK0_WAITSTATES) || \
      CONFIG_STR71X_BANK0_WAITSTATES < 0 || CONFIG_STR71X_BANK0_WAITSTATES > 15
#    error "CONFIG_STR71X_BANK0_WAITSTATES invalid"
#  else
     /* Bits 2-5: wait states */
#    define EXTMEM_BANK0_WAITSTATES (CONFIG_STR71X_BANK0_WAITSTATES << 2)
#  endif

	ldr		\value, =(STR71X_EMIBCON_ENABLE|EXTMEM_BANK0_WAITSTATES|EXTMEM_BANK0_SIZE)
#else
	mov		\value, #0
#endif
	str		\value, [\base, #STR71X_EMI_BCON0_OFFSET]

	/* Enable bank 1 */

#ifdef CONFIG_STR71X_BANK1

	/* Get the bank 1 size */

#  if CONFIG_STR71X_BANK1_SIZE == 8
#    define EXTMEM_BANK1_SIZE STR71X_EMIBCON_BSIZE8
#  elif CONFIG_STR71X_BANK1_SIZE == 16
#    define EXTMEM_BANK1_SIZE STR71X_EMIBCON_BSIZE16
#  else
#    error "CONFIG_STR71X_BANK1_SIZE invalid"
#  endif

	/* Get the bank 1 waitstates */

#  if !defined(CONFIG_STR71X_BANK1_WAITSTATES) || \
      CONFIG_STR71X_BANK1_WAITSTATES < 0 || CONFIG_STR71X_BANK1_WAITSTATES > 15
#    error "CONFIG_STR71X_BANK1_WAITSTATES invalid"
#  else
     /* Bits 2-5: wait states */
#    define EXTMEM_BANK1_WAITSTATES (CONFIG_STR71X_BANK1_WAITSTATES << 2)
#  endif

	ldr		\value, =(STR71X_EMIBCON_ENABLE|EXTMEM_BANK1_WAITSTATES|EXTMEM_BANK1_SIZE)
#else
	mov		\value, #0
#endif
	str		\value, [\base, #STR71X_EMI_BCON1_OFFSET]

	/* Enable bank 2 */

#ifdef CONFIG_STR71X_BANK2

	/* Get the bank 2 size */

#  if CONFIG_STR71X_BANK2_SIZE == 8
#    define EXTMEM_BANK2_SIZE STR71X_EMIBCON_BSIZE8
#  elif CONFIG_STR71X_BANK2_SIZE == 16
#    define EXTMEM_BANK2_SIZE STR71X_EMIBCON_BSIZE16
#  else
#    error "CONFIG_STR71X_BANK2_SIZE invalid"
#  endif

	/* Get the bank 2 waitstates */

#  if !defined(CONFIG_STR71X_BANK2_WAITSTATES) || \
      CONFIG_STR71X_BANK2_WAITSTATES < 2 || CONFIG_STR71X_BANK2_WAITSTATES > 15
#    error "CONFIG_STR71X_BANK2_WAITSTATES invalid"
#  else
     /* Bits 2-5: wait states */
#    define EXTMEM_BANK2_WAITSTATES (CONFIG_STR71X_BANK2_WAITSTATES << 2)
#  endif

	ldr		\value, =(STR71X_EMIBCON_ENABLE|EXTMEM_BANK2_WAITSTATES|EXTMEM_BANK2_SIZE)
#else
	mov		\value, #0
#endif
	str		\value, [\base, #STR71X_EMI_BCON2_OFFSET]

	/* Enable bank 3 */

#ifdef CONFIG_STR71X_BANK3

	/* Get the bank 3 size */

#  if CONFIG_STR71X_BANK3_SIZE == 8
#    define EXTMEM_BANK3_SIZE STR71X_EMIBCON_BSIZE8
#  elif CONFIG_STR71X_BANK3_SIZE == 16
#    define EXTMEM_BANK3_SIZE STR71X_EMIBCON_BSIZE16
#  else
#    error "CONFIG_STR71X_BANK3_SIZE invalid"
#  endif

	/* Get the bank 3 waitstates */

#  if !defined(CONFIG_STR71X_BANK3_WAITSTATES) || \
      CONFIG_STR71X_BANK3_WAITSTATES < 3 || CONFIG_STR71X_BANK3_WAITSTATES > 15
#    error "CONFIG_STR71X_BANK3_WAITSTATES invalid"
#  else
     /* Bits 2-5: wait states */
#    define EXTMEM_BANK3_WAITSTATES (CONFIG_STR71X_BANK3_WAITSTATES << 2)
#  endif

	ldr		\value, =(STR71X_EMIBCON_ENABLE|EXTMEM_BANK3_WAITSTATES|EXTMEM_BANK3_SIZE)
#else
	mov		\value, #0
#endif
	str		\value, [\base, #STR71X_EMI_BCON3_OFFSET]
#endif
	.endm

/*****************************************************************************
 * Name: eicinit
 *
 * Description:
 *   The EIC is initialized for use with NuttX.  This initialization does not
 *   take advantage of the high performance capabilities of the EIC.  Instead,
 *   The EIC is only used to to provide NuttX IRQ numbers.  Here is what is
 *   done:
 *
 *     IRQs and FIQs are disabled
 *     IVR set to zero
 *     All channels are disabled
 *     Channels set to priority 0
 *     All SIR[n] registers contain the NuttX IRQ number in the MS 16-bits
 *
 *   At the time of IRQ processing, the IVR will contain the decoded IRQ
 *   number needed by NuttX.
 *
 *****************************************************************************/

	.macro	eicinit, eicbase, value, irqno, offset
	/* Disable and clear all interrupts */

	ldr		\eicbase, =STR71X_EIC_BASE

	/* Disable FIQ and IRQ */

	mov		\value, #0
	str		\value, [\eicbase, #STR71X_EIC_ICR_OFFSET]

	/* Disable all channel interrupts */

	str		\value, [\eicbase, #STR71X_EIC_IER_OFFSET]

	/* Clear all pending IRQs */

	ldr		\value, =0xffffffff
	str		\value, [\eicbase, #STR71X_EIC_IPR_OFFSET]

	/* Disable FIQ channels/clear pending FIQs */

	mov		\value, #0x0c
	str		\value, [\eicbase, #STR71X_EIC_FIR_OFFSET]

	/* Reset the current priority register */

	mov		\value, #0
	str		\value, [\eicbase, #STR71X_EIC_CIPR_OFFSET]

	/* Zero IVR 31:16 */

	str		\value, [\eicbase, #STR71X_EIC_IVR_OFFSET]

	/* Set up the loop to initialize each SIR register.  Start
	 * with IRQ number 0 and SIR0
	 */

	mov		\irqno, #0
	ldr		\offset, =STR71X_EIC_SIR_OFFSET

	/* Then loop for each EIC channel */
eicloop:
	/* Shift the IRQ number to bits 16-31 and save the shifted IRQ
	 * number as SIR[irqno].  This will appear as bits 0:15 in the
	 * IVR during IRQ processing.
	 *
	 * NOTE that the initial priority is set to zero -- the current
	 * interrupt priority (CIP) is always zero, so these interrupts
	 * are all disabled.
	 */

	mov		\value, \irqno, lsl #16
	str		\value, [\eicbase, \offset]

	/* Increment the offset to the next SIR register and inrement
	 * the IRQ number.
	 */

	add		\offset, \offset, #4
	add		\irqno, \irqno, #1

	/* Continue to loop until all of the SIR registers have been
	 * initializeed.
	 */

	cmp		\irqno, #STR71X_EIC_NCHANNELS
	blt		eicloop	
	.endm

/*****************************************************************************
 * Name: periphinit
 *
 * Description"
 *   Disable all perfipherals (except EIC)
 *
 *****************************************************************************/

	.macro	periphinit, value, base1, base2
#ifndef CONFIG_STR71X_DISABLE_PERIPHINIT
	/* Set up APB1 and APB2 addresses */

	ldr		\base1, =STR71X_APB1_BASE
	ldr		\base2, =STR71X_APB2_BASE

	/* Disable all APB1 peripherals */

	ldr		\value, =STR71X_APB1_APB1ALL
	strh	\value, [\base1, #STR71X_APB_CKDIS_OFFSET]

	/* Disable all(or most) APB2 peripherals */

	ldr		\value, =(STR71X_APB2_APB2ALL & ~STR71X_APB2_EIC)
	strh	\value, [\base2, #STR71X_APB_CKDIS_OFFSET]

	/* Allow EMI and USB */

	ldr		\base1, =STR71X_RCCU_BASE
#ifdef CONFIG_STR71X_USB
	ldr		\value, =(STR71X_RCCUPER_EMI|STR71X_RCCUPER_USBKERNEL)
#else
	ldr		\value, =STR71X_RCCUPER_EMI
#endif
	strh	\value, [\base1, #STR71X_RCCU_PER_OFFSET]
#endif
	.endm

/*****************************************************************************
 * Name: remap
 *
 * Description:
 *   Remap memory at address 0x0000000 to either FLASH.  The system always
 *   boots at Bank0, sector 0 of FLASH.  Part of the initial setup will be to
 *   map the memory appropriately for the execution configuration.  Various
 *   options are possible, but only boot from FLASH is currently supported.
 *
 *****************************************************************************/

	.macro	remap, base, value

	/* Read the PCU BOOTCR register */

	ldr		\base, =STR71X_PCU_BASE
	ldrh	\value, [\base, #STR71X_PCU_BOOTCR_OFFSET]

	/* Mask out the old boot mode bits and set the boot mode to FLASH */

	bic		\value, \value, #STR71X_PCUBOOTCR_BOOTMASK
	orr		\value, \value, #STR71X_PCUBOOTCR_BMFLASH

	/* Save the modified BOOTCR register */

	strh	\value, [\base, #STR71X_PCU_BOOTCR_OFFSET]
	.endm

/*****************************************************************************
 * Text
 *****************************************************************************/

	.text

/*****************************************************************************
 * Name: _vector_table
 *
 * Description:
 *   Interrrupt vector table.  This must be located at the beginning
 *   of the memory space (at the beginning FLASH which will be mapped to
 *   address 0x00000000).  The first entry in the vector table is the reset
 *   vector and this is the code that will execute whn the processor is reset.
 *
 *****************************************************************************/

	.globl	_vector_table
	.type	_vector_table, %function
_vector_table:
	ldr		pc, .Lresethandler			/* 0x00: Reset */
	ldr		pc, .Lundefinedhandler		/* 0x04: Undefined instruction */
	ldr		pc, .Lswihandler			/* 0x08: Software interrupt */
	ldr		pc, .Lprefetchaborthandler	/* 0x0c: Prefetch abort */
	ldr		pc, .Ldataaborthandler		/* 0x10: Data abort */
	.long	0							/* 0x14: Reserved vector */
	ldr		pc, .Lirqhandler			/* 0x18: IRQ */
	ldr		pc, .Lfiqhandler			/* 0x1c: FIQ */

	.globl	__start
	.globl	up_vectorundefinsn
	.globl	up_vectorswi
	.globl	up_vectorprefetch
	.globl	up_vectordata
	.globl	up_vectorirq
	.globl	up_vectorfiq

.Lresethandler:
	.long	__start
.Lundefinedhandler:
	.long	up_vectorundefinsn
.Lswihandler:
	.long	up_vectorswi
.Lprefetchaborthandler:
	.long	up_vectorprefetch
.Ldataaborthandler:
	.long	up_vectordata
.Lirqhandler:
	.long	up_vectorirq
.Lfiqhandler:
	.long	up_vectorfiq
	.size	_vector_table, . - _vector_table

/*****************************************************************************
 * Name: __start
 *
 * Description:
 *   Reset entry point.  This is the first function to execute when
 *   the processor is reset.  It initializes hardware and then gives
 *   control to NuttX.
 *
 *****************************************************************************/

	.global __start
	.type	__start, #function

__start:
	/* On reset, an aliased copy of FLASH is mapped to address 0x00000000.
	 *  Continue execution in the 'real' FLASH address space rather than
	 *  the aliased copy
	 */

	ldr		pc, =__flashstart
__flashstart:
	.rept 9
	nop				/* Wait for OSC stabilization */
	.endr

	/* Setup the initial processor mode */

	mov		r0, #(SVC_MODE | PSR_I_BIT | PSR_F_BIT )
	msr		cpsr, r0

	/* Initialize the external memory interface (EMI) */

	emiinit	r0, r1

	/* Initialize the enhanced interrupt controller (EIC) */

	eicinit	r0, r1, r2, r3

	/* Disable all peripherals except EIC */

	periphinit	r0, r1, r2

	/* Map memory appropriately for configuration */

	remap	r0, r1

	/* Setup system stack (and get the BSS range) */

	adr		r0, LC0
	ldmia	r0, {r4, r5, sp}

	/* Clear system BSS section */

	mov		r0, #0
1:	cmp		r4, r5
	strcc	r0, [r4], #4
	bcc		1b

	/* Copy system .data sections from FLASH to new home in RAM. */

	adr		r3, LC2
	ldmia	r3, {r0, r1, r2}

2:	ldmia	r0!, {r3 - r10}
	stmia	r1!, {r3 - r10}
	cmp		r1, r2
	blt		2b

	/* Initialize clocking */

	bl		str71x_prccuinit

	/* Configure the uart so that we can get debug output as soon
	 * as possible.
	 */

	bl		up_lowsetup
	showprogress 'A'

	/* Perform early serial initialization */

	mov		fp, #0
#ifdef USE_EARLYSERIALINIT
	bl		up_earlyserialinit
#endif

	showprogress 'B'

	/* Call C++ constructors */

#ifdef CONFIG_CPLUSPLUS
	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
	mov		pc, r2
	ldmfd	sp!, {r0-r1}
	b		ctor_loop
ctor_end:

	showprogress 'C'
#endif
	showprogress '\n'

	/* Initialize onboard LEDs */

#ifdef CONFIG_ARCH_LEDS
	bl		board_autoled_initialize
#endif

	/* Then jump to OS entry */

	b		os_start

	/* Call destructors  -- never get here */

#if 0 /* CONFIG_CPLUSPLUS */
	ldr		r0, =__dtors_start__
	ldr		r1, =__dtors_end__
dtor_loop:
	cmp		r0, r1
	beq		dtor_end
	ldr		r2, [r0], #4
	stmfd	sp!, {r0-r1}
	mov		lr, pc
	mov		pc, r2
	ldmfd	sp!, {r0-r1}
	b		dtor_loop
dtor_end:
#endif

	/* Variables:
	 * _sbss is the start of the BSS region (see ld.script)
	 * _ebss is the end of the BSS regsion (see ld.script)
	 * The idle task stack starts at the end of BSS and is
	 * of size CONFIG_IDLETHREAD_STACKSIZE.  The heap continues
	 * from there until the end of memory.  See g_idle_topstack
	 * below.
	 */

LC0:	.long	_sbss
	.long	_ebss
	.long	_ebss+CONFIG_IDLETHREAD_STACKSIZE-4

LC2:	.long	_eronly	/* Where .data defaults are stored in FLASH */
	.long	_sdata	/* Where .data needs to reside in SDRAM */
	.long	_edata
	.size	__start, .-__start

	/* This global variable is unsigned long g_idle_topstack and is
	 * exported from here only because of its coupling to LCO
	 * above.
	 */

	.data
	.align	4
	.globl	g_idle_topstack
	.type	g_idle_topstack, object
g_idle_topstack:
	.long	_ebss+CONFIG_IDLETHREAD_STACKSIZE
	.size	g_idle_topstack, .-g_idle_topstack

	.end
