/****************************************************************************
 * arch/mips/src/pic32mx/pic32mx-head.S
 *
 *   Copyright (C) 2011 Gregory Nutt. All rights reserved.
 *   Author: Gregory Nutt <spudmonkey@racsa.co.cr>
 *
 * 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>

#include <arch/mips32/registers.h>
#include <arch/pic32mx/cp0.h>

#include "pic32mx-config.h"
#include "excptmacros.h"

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

#ifdef CONFIG_PIC32MX_MVEC0
#  error "Multi-vectors not supported"
#  ifndef CONFIG_PIC32MX_EBASE
#    error "EBASE address provided"  /* Should come from the linker script */
#  endif
#  ifndef CONFIG_PIC32MX_VECTORSPACING
#    error "No vector spacing provided"
#  endif
#endif

/* Linker memory organization ***********************************************/
/* Data memory is organized as follows:
 *
 * 1) Possible space reserved for debug data
 * 2) Ram functions: (.data):
 *    Start:   _sramfunc
 *    End(+1): _eramfunc
 * 3) Initialized data (.data):
 *    Start:   _sdata
 *    End(+1): _edata
 * 4) Uninitialized data (.bss):
 *    Start:   _sbss
 *    End(+1): _ebss
 *
 * The following are placed outside of the "normal" memory segments -- mostly
 * so that they do not have to be cleared on power up.
 *
 * 5) Idle thread stack:
 *    Start:   _ebss
 *    End(+1): _ebss+CONFIG_IDLETHREAD_STACKSIZE
 * 6) Optional interrupt stack
 *    Start:   _ebss+CONFIG_IDLETHREAD_STACKSIZE
 *    End(+1): _ebss+CONFIG_IDLETHREAD_STACKSIZE+(CONFIG_ARCH_INTERRUPTSTACK & ~3)
 * 6a) Heap (without interupt stack)
 *    Start:   _ebss+CONFIG_IDLETHREAD_STACKSIZE
 *    End(+1): to the end of memory
 * 6b) Heap (with interupt stack)
 *    Start:   _ebss+CONFIG_IDLETHREAD_STACKSIZE+(CONFIG_ARCH_INTERRUPTSTACK & ~3)
 *    End(+1): to the end of memory
 */

#define PIC32MX_STACK_BASE      _ebss
#define PIC32MX_STACK_TOP       _ebss+CONFIG_IDLETHREAD_STACKSIZE-4

#if CONFIG_ARCH_INTERRUPTSTACK > 3
#  define PIC32MX_INTSTACK_BASE PIC32MX_STACK_TOP
#  define PIC32MX_INTSTACK_SIZE (CONFIG_ARCH_INTERRUPTSTACK & ~3)
#  define PIC32MX_INTSTACK_TOP  PIC32MX_STACK_TOP+PIC32MX_INTSTACK_SIZE
#  define PIC32MX_HEAP_BASE     PIC32MX_INTSTACK_TOP
#else
#  define PIC32MX_HEAP_BASE     PIC32MX_STACK_TOP
#endif

/****************************************************************************
 * Global Symbols
 ****************************************************************************/

	.file	"pic32mx-head.S"

	/* Exported symbols */

	.globl	__reset
	.global __start
	.global halt
	.global	devconfig
#if CONFIG_ARCH_INTERRUPTSTACK > 3
	.global g_intstackbase
	.global	g_nestlevel
#endif
	.global	g_heapbase

	/* Imported symbols */

	.global os_start
	.global	pic32mx_dobev
	.global pic32mx_decodeirq
#ifdef CONFIG_PIC32MX_NMIHANDLER
	.global	pic32mx_donmi
#endif

/****************************************************************************
 * Name: __reset
 *
 * Description:
 *   Reset entry point.  This function is positioned at the beginning of
 *   the boot FLASH by the linker in KSEG1.  Simply jumps to the __start
 *   logic in KSEG0 (also in the boot FLASH).
 *
 * Input Parameters:
 *   None
 *
 * Returned Value:
 *   Does not return
 *
 ****************************************************************************/

	.section .reset, "ax", @progbits
	.set	noreorder
	.ent	__reset
__reset:
	la		k0, __start	/* Just jump to the startup initialization code */
	jr		k0
	nop
	.end	__reset

/****************************************************************************
 * Name: _bev_exception
 *
 * Description:
 *   Boot Exception Vector Handler.  Jumps to _bev_handler
 *
 * Input Parameters:
 *   None
 *
 * Returned Value:
 *   Does not return
 *
 ****************************************************************************/

	.section .bev_excpt,"ax",@progbits
	.set	noreorder
	.ent	_bev_exception
_bev_exception:
	la		k0, _bev_handler
	jr		k0
	nop
	.end _bev_exception

/****************************************************************************
 * Name: _int_exception
 *
 * Description:
 *   Interrupt Exception Vector Handler.  Jumps to _int_handler
 *
 * Input Parameters:
 *   None
 *
 * Returned Value:
 *   Does not return
 *
 ****************************************************************************/

	.section .int_excpt,"ax",@progbits
	.set	noreorder
	.ent	_int_exception
_int_exception:
	la		k0, _int_handler
	jr		k0
	nop
	.end _int_exception

/****************************************************************************
 * Name: __start
 *
 * Description:
 *   This is the KSEG0 startup code.  It receives control from the reset
 *   entry point.  This lgic This prepares the processor to execute
 *   C code, performs some very low-level initialization, then starts NuttX
 *   (via __start_nuttx
 *
 * Input Parameters:
 *   None
 *
 * Returned Value:
 *   Does not return
 *
 ****************************************************************************/

	.section .start, "ax", @progbits
	.set	noreorder
	.ent	__start
__start:

	/* If this function was entered because of an NMI, then turn processing
	 * over to the NMI handler.
	 */

#ifdef CONFIG_PIC32MX_NMIHANDLER
	mfc0	k0, $12				/* Load CP0 status register */
	ext		k0, k0, 19, 1		/* Extract NMI bit */
	beqz	k0, .Lnotnmi
	nop
	la		k0, _nmi_handler
	jr		k0
	nop

	/* This is not an NMI */

.Lnotnmi:
#endif

	/* Initialize the stack pointer */

	la		sp, PIC32MX_STACK_TOP

	/* Initialize the globl pointer (gp).  _gp is initialized by the linker
	 * script to point to the "middle" of the small variables region.
	 */

	la		gp, _gp

	/* Initialize Global Pointer in Shadow Set.  The SRSCtl PSS field must
	 * be set to the shadow set in which to initialize the global pointer.
	 * Since we only have a single shadow set, we will initialize
	 * SRSCtl PSS to SRSCtl HSS.  We then write the global pointer to the
	 * previous shadow set to ensure that on interrupt, the global pointer
	 * has been initialized.
	 */

	mfc0	t1, PIC32MX_CP0_SRSCTL	/* Read SRSCtl register */
	add		t3, t1, zero			/* Save off current SRSCtl */
	ext		t2, t1, 26, 4			/* to obtain HSS field */
	ins		t1, t2, 6, 4			/* Put HSS field */
	mtc0	t1, PIC32MX_CP0_SRSCTL	/* into SRSCtl PSS  */
	wrpgpr	gp, gp					/* Set global pointer in PSS */
	mtc0	t3, PIC32MX_CP0_SRSCTL	/* Restore SRSCtl */

	/* Clear uninitialized data sections */

	la		t0, _sbss
	la		t1, _ebss
	b		.Lbsscheck
	nop

.Lbssloop:	
	sw		zero, 0x0(t0)
	sw		zero, 0x4(t0)
	sw		zero, 0x8(t0)
	sw		zero, 0xc(t0)
	addu	t0, 16

.Lbsscheck:
	bltu	t0, t1, .Lbssloop
	nop

	/* Copy initialized data from program flash to data memory */

	la		t0, _data_loaddr
	la		t1, _sdata
	la		t2, _edata
	b		.Ldatacheck
	nop

.Ldataloop:	
	lw		t3, (t0)
	sw		t3, (t1)
	addu	t0, 4
	addu	t1, 4

.Ldatacheck:
	bltu	t1, t2, .Ldataloop
	nop

	/* If there are no RAM functions, skip the next two sections --
	 * copying RAM functions from program flash to data memory and
	 * initializing bus matrix registers.
	 */

#ifdef CONFIG_PIC32MX_RAMFUNCS
	la		t1, _ramfunc_sizeof
	beqz	t1, .Lnoramfuncs
	nop

	/* Copy RAM functions from program flash to data memory */

	la		t0, _ramfunc_loadaddr
	la		t1, _sramfunc
	la		t2, _eramfunc

.Lramfuncloop:  
	lw		t3,(t0)
	sw		t3,(t1)
	addu	t0,4
	addu	t1,4

	bltu	t1, t2, .Lramfuncloop
	nop

	/* Initialize bus matrix registers if RAM functions exist in the
	 * application
	 */

	la		t1, _bmxdkpba_address
	la		t2, BMXDKPBA
	sw		t1, 0(t2)
	la		t1, _bmxdudba_address
	la		t2, BMXDUDBA
	sw		t1, 0(t2)
	la		t1, _bmxdupba_address
	la		t2, BMXDUPBA
	sw		t1, 0(t2)
.Lnoramfuncs:
#endif

	/* Initialize CP0 Count register */

	mtc0	zero, PIC32MX_CP0_COUNT

	/* Initialize Compare register */

	li		t2, -1
	mtc0	t2, PIC32MX_CP0_COMPARE

	/* Initialize EBase register */

#ifdef CONFIG_PIC32MX_MVEC
	la		t1, CONFIG_PIC32MX_EBASE
	mtc0	t1, PIC32MX_CP0_EBASE

	/* Initialize IntCtl register */

	li		t1, CONFIG_PIC32MX_VECTORSPACING
	li		t2, 0
	ins		t2, t1, CP0_INTCTL_VS_SHIFT, 5
	mtc0	t2, PIC32MX_CP0_INTCTL
#endif

	/* Initialize CAUSE registers
	 * - Enable counting of Count register (DC = 0)
	 * - Use special exception vector (IV = 1)
	 * - Clear pending software interrupts (IP1:IP0 = 0)
	 */

	li		t1, CP0_CAUSE_IV
	mtc0	t1, PIC32MX_CP0_CAUSE

	/* Initialize STATUS register
	 * - Access to Coprocessor 0 not allowed in user mode (CU0 = 0)
	 * - User mode uses configured endianness (RE = 0)
	 * - Preserve Bootstrap Exception vectors (BEV)
	 * - Preserve soft reset (SR) and non-maskable interrupt (NMI)
	 * - CorExtend enabled based on whether CorExtend User Defined
	 *   Instructions have been implemented (CEE = Config(UDI))
	 * - Disable any pending interrups (IM7..IM2 = 0, IM1..IM0 = 0)
	 * - Disable hardware interrupts (IPL7:IPL2 = 0)
	 * - Base mode is Kernel mode (UM = 0)
	 * - Error level is normal (ERL = 0)
	 * - Exception level is normal (EXL = 0)
	 * - Interrupts are disabled (IE = 0)
	 */

	mfc0	t0, PIC32MX_CP0_CONFIG
	ext		t1, t0, 22,1			/* Extract UDI from Config register */
	sll		t1, t1, 17				/* Move UDI to Status.CEE location */
	mfc0	t0, PIC32MX_CP0_STATUS
	and		t0, t0, 0x00580000		/* Preserve SR, NMI, and BEV */
	or		t0, t1, t0				/* Include Status.CEE (from UDI) */
	mtc0	t0, PIC32MX_CP0_STATUS

	/* Initialize Status BEV for normal exception vectors */

	mfc0	t0, PIC32MX_CP0_STATUS
	and		t0, t0, ~CP0_STATUS_BEV	/* Clear BEV */
	mtc0	t0, PIC32MX_CP0_STATUS

	/* Start NuttX. We do this via a thunk in the text section so that
	 * a normal jump and link can be used, enabling the startup code
	 * to work properly whether main is written in MIPS16 or MIPS32
	 * code. I.e., the linker will correctly adjust the JAL to JALX if
	 * necessary
	 */

	la		t0, __start_nuttx
	jr		t0
	nop
	.end __start

/****************************************************************************
 * Name: _bev_handler
 *
 * Description:
 *   BEV exception handler.  Calls pic32mx_dobev()
 *
 ****************************************************************************/

	.section .bev_handler, "ax", @progbits
	.set	noreorder
	.ent	_bev_handler
_bev_handler:
	EXCPT_PROLOGUE t0				/* Save registers on stack, enable nested interrupts */
	move	a0, sp					/* Pass register save structure as the parameter 1 */
	USE_INTSTACK t0, t1, t2			/* Switch to the interrupt stack */
	la		t0, pic32mx_dobev		/* Call up_dobev(regs) */
	jalr	t0, ra
	di								/* Disable interrupts */
	RESTORE_STACK t0, t1			/* Undo the operations of USE_STACK */
	EXCPT_EPILOGUE v0				/* Return to the context returned by up_dobev() */
	.end	_bev_handler

/****************************************************************************
 * Name: _int_handler
 *
 * Description:
 *   Interrupt exception handler.  Calls up_decodeirq()
 *
 ****************************************************************************/

	.section .int_handler, "ax", @progbits
	.set	noreorder
	.ent	_int_handler
_int_handler:
	EXCPT_PROLOGUE t0				/* Save registers on stack, enable nested interrupts */
	move	a0, sp					/* Pass register save structure as the parameter 1 */
	USE_INTSTACK t0, t1, t2			/* Switch to the interrupt stack */
	la		t0, pic32mx_decodeirq	/* Call pic32mx_decodeirq(regs) */
	jalr	t0, ra
	di								/* Disable interrupts */
	RESTORE_STACK t0, t1			/* Undo the operations of USE_STACK */
	EXCPT_EPILOGUE v0				/* Return to the context returned by pic32mx_decodeirq() */
	.end	_int_handler

/****************************************************************************
 * Name: _nmi_handler
 *
 * Description:
 *   NMI exception handler.  Calls pic32mx_donmi
 *
 ****************************************************************************/

#ifdef CONFIG_PIC32MX_NMIHANDLER
	.section .nmi_handler, "ax", @progbits
	.set	noreorder
	.ent	_nmi_handler
_nmi_handler:
	EXCPT_PROLOGUE t0				/* Save registers on stack, enable nested interrupts */
	move	a0, sp					/* Pass register save structure as the parameter 1 */
	USE_INTSTACK t0, t1, t2			/* Switch to the interrupt stack */
	la		t0, pic32mx_donmi		/* Call up_donmi(regs) */
	jalr	t0, ra
	di								/* Disable interrupts */
	RESTORE_STACK t0, t1			/* Undo the operations of USE_STACK */
	EXCPT_EPILOGUE v0				/* Return to the context returned by pic32mx_donmi() */
	.end	_nmi_handler
#endif

/****************************************************************************
 * Name: __start_nuttx
 *
 * Description:
 *
 * Input Parameters:
 *   None
 *
 * Returned Value:
 *   Does not return
 *
 ****************************************************************************/

	.text
	.ent __start_nuttx
__start_nuttx:
	/* Perform low level initialization */

	la		t0, pic32mx_lowinit
	jalr	t0, ra
	nop

	/* Call os_start */

	la		t0, os_start
	jalr	t0, ra
	nop

	/* Just in case main returns, go into an infinite loop */

halt:
1:
	b	 1b
	nop
	.end __start_nuttx

/****************************************************************************
 * Device Configuration
 ****************************************************************************/

	.section .devcfg, "a"
	.type	devconfig, object
devconfig:
devconfig3:
	.long	CONFIG_PIC32MX_USERID  << DEVCFG3_USERID_SHIFT | \
			CONFIG_PIC32MX_SRSSEL  << DEVCFG3_FSRSSEL_SHIFT | \
			CONFIG_PIC32MX_FMIIEN  << 24 | CONFIG_PIC32MX_FETHIO  << 25 | \
			CONFIG_PIC32MX_FCANIO  << 26 | CONFIG_PIC32MX_FSCM1IO << 29 | \
			CONFIG_PIC32MX_USBIDO  << 30 | CONFIG_PIC32MX_VBUSIO  << 31 | \
			DEVCFG3_UNUSED

devconfig2:
	.long	CONFIG_PIC32MX_PLLIDIV  | CONFIG_PIC32MX_PLLMULT | \
			CONFIG_PIC32MX_UPLLIDIV | DEVCFG2_FPLLODIV_DIV1  | \
			DEVCFG2_UNUSED

devconfig1:
	.long	DEVCFG1_FNOSC_POSCPLL   | DEVCFG1_POSCMOD_HS     | \
			CONFIG_PIC32MX_PBDIV    | DEVCFG1_FCKSM_NONE     | \
			CONFIG_PIC32MX_WDENABLE | DEVCFG1_UNUSED

devconfig0:
	.long	CONFIG_PIC32MX_DEBUGGER    << DEVCFG0_DEBUG_SHIFT | \
			CONFIG_PIC32MX_ICESEL      << 3 | \
			CONFIG_PIC32MX_PROGFLASHWP << DEVCFG0_PWP_SHIFT | \
			CONFIG_PIC32MX_BOOTFLASHWP << 24 | \
			CONFIG_PIC32MX_CODEWP      << 28 | \
			DEVCFG0_UNUSED
	.size	devconfig, .-devconfig

/****************************************************************************
 * Global Data
 ****************************************************************************/

/* Interrupt stack variables */

#if CONFIG_ARCH_INTERRUPTSTACK > 3

/* g_instackbase is a pointer to the final, aligned word of the interrupt
 * stack.
 */
 
	.sdata
	.type	g_intstackbase, object
g_intstackbase:
	.long	PIC32MX_INTSTACK_TOP-4
	.size	g_intstackbase, .-g_intstackbase

/* g_nextlevel is the exception nesting level... the interrupt stack is not
 * available to nested exceptions.
 */

	.sbss
	.type	g_nestlevel, object
g_nestlevel:
	.skip		4
#endif

/* This global variable is unsigned int g_heapbase and is exported here only
 * because of its coupling to idle thread stack.
 */

	.sdata
	.type	g_heapbase, object
g_heapbase:
	.long	PIC32MX_HEAP_BASE
	.size	g_heapbase, .-g_heapbase

