/**
 * \file
 *
 * Copyright (C) 2009 Atmel Corporation. All rights reserved.
 *
 * \page License
 *
 * 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. The name of Atmel may not be used to endorse or promote products derived
 * from this software without specific prior written permission.
 *
 * 4. This software may only be redistributed and used in connection with an
 * Atmel AVR product.
 *
 * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
 * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL 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.
 */
/*!
 * \file
 *
 * \brief Low-level startup code for standalone deployment on AT32AP7200.
 *
 * - Compiler:           GNU GCC for AVR32
 * - Supported devices:  AT32AP7200
 * - AppNote:
 *
 * \author               Atmel Corporation: http://www.atmel.com \n
 *                       Support and FAQ: http://support.atmel.no/
 *
 * \page License
 *
 * Copyright (C) 2008, Atmel Corporation All rights reserved.
 *
 * 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. The name of ATMEL may not be used to endorse or promote products derived
 * from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY ATMEL ``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 EXPRESSLY AND
 * SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL 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.
 */
#include <chip/memory-map.h>
#include <arch/sysreg.h>
#include <pm/pm_v3_regs.h>
#include <board/clk.h>

/*
 * OSC0_STARTUP is the value written to the STARTUP bitfield in
 * PM_OSCCTRL0.
 *
 * OSC0_STARTUP_TIMEOUT is the corresponding number of RC oscillator
 * cycles + 25%
 */
#if BOARD_OSC0_STARTUP_US == 0
# define OSC0_STARTUP		PM_OSC_STARTUP_0
# define OSC0_STARTUP_TIMEOUT	8
#elif BOARD_OSC0_STARTUP_US <= 727
# define OSC0_STARTUP		PM_OSC_STARTUP_32
# define OSC0_STARTUP_TIMEOUT	40
#elif BOARD_OSC0_STARTUP_US <= 1500
# define OSC0_STARTUP		PM_OSC_STARTUP_64
# define OSC0_STARTUP		80
#elif BOARD_OSC0_STARTUP_US <= 12000
# define OSC0_STARTUP		PM_OSC_STARTUP_512
# define OSC0_STARTUP_TIMEOUT	640
#elif BOARD_OSC0_STARTUP_US <= 23000
# define OSC0_STARTUP		PM_OSC_STARTUP_1024
# define OSC0_STARTUP_TIMEOUT	1280
#elif BOARD_OSC0_STARTUP_US <= 93000
# define OSC0_STARTUP		PM_OSC_STARTUP_4096
# define OSC0_STARTUP_TIMEOUT	5120
#elif BOARD_OSC0_STARTUP_US <= 186000
# define OSC0_STARTUP		PM_OSC_STARTUP_8192
# define OSC0_STARTUP_TIMEOUT	10240
#elif BOARD_OSC0_STARTUP_US <= 372000
# define OSC0_STARTUP		PM_OSC_STARTUP_16384
# define OSC0_STARTUP_TIMEOUT	20480
#else
# error BOARD_OSC0_STARTUP_US is too high
#endif

#if BOARD_OSC0_MODE & ~15
# error BOARD_OSC0_MODE is invalid
#endif

#define OSCCTRL0_VALUE	(OSC0_STARTUP | BOARD_OSC0_MODE)
/*
 * After reset, we have to assume that the internal RC oscillator is
 * the only one running. It has a nominal frequency of 32 kHz, but it
 * may be running at anything between 20 kHz and 44 kHz. So in order
 * to make our timeouts long enough to handle all cases, we assume
 * it's running at 44 kHz.
 *
 * The 32 kHz crystal oscillator may already be running when we enter
 * this code. We must make sure we don't disable it, or we'll mess up
 * the RTC.
 */
#define RCOSC_MAX_RATE	44000

/* These registers have a relatively long lifetime */
#define zero	r0
#define pm	r1
#define mcctrl	r2

	/*
	 * To save some space, we use the same entry point for
	 * exceptions and reset. This avoids lots of alignment padding
	 * since the reset vector is always suitably aligned.
	 */
	.section .exception.text, "ax", @progbits
	.global	_start
	.global	_evba
	.type	_start, @function
	.type	_evba, @function
_start:
	.size	_start, 0
_evba:
	.org	0x00
	rjmp	unknown_exception	/* Unrecoverable exception */
	.org	0x04
	rjmp	unknown_exception	/* TLB multiple hit */
	.org	0x08
	rjmp	unknown_exception	/* Bus error data fetch */
	.org	0x0c
	rjmp	unknown_exception	/* Bus error instruction fetch */
	.org	0x10
	rjmp	unknown_exception	/* NMI */
	.org	0x14
	rjmp	unknown_exception	/* Instruction address */
	.org	0x18
	rjmp	unknown_exception	/* ITLB protection */
	.org	0x1c
	rjmp	unknown_exception	/* Breakpoint */
	.org	0x20
	rjmp	unknown_exception	/* Illegal opcode */
	.org	0x24
	rjmp	unknown_exception	/* Unimplemented instruction */
	.org	0x28
	rjmp	unknown_exception	/* Privilege violation */
	.org	0x2c
	rjmp	unknown_exception	/* Floating-point */
	.org	0x30
	rjmp	unknown_exception	/* Coprocessor absent */
	.org	0x34
	rjmp	unknown_exception	/* Data Address (read) */
	.org	0x38
	rjmp	unknown_exception	/* Data Address (write) */
	.org	0x3c
	rjmp	unknown_exception	/* DTLB Protection (read) */
	.org	0x40
	rjmp	unknown_exception	/* DTLB Protection (write) */
	.org	0x44
	rjmp	unknown_exception	/* DTLB Modified */

	.org	0x50
	rjmp	unknown_exception	/* ITLB Miss */
	.org	0x60
	rjmp	unknown_exception	/* DTLB Miss (read) */
	.org	0x70
	rjmp	unknown_exception	/* DTLB Miss (write) */

	.size	_evba, . - _evba

	.align	2
	.type	unknown_exception, @function
unknown_exception:
	/* Figure out whether we're handling an exception (Exception
	 * mode) or just booting (Supervisor mode). */
	csrfcz	SYSREG_SR_MODE_BIT(1)
	brcc	cpu_bootstrap

	/* This is an exception. Complain. */
	pushm	r0-r12
	sub	r8, sp, 13 * 4
	mov	r9, lr
	mfsr	r10, SYSREG_RAR_EX
	mfsr	r11, SYSREG_RSR_EX
	pushm	r8-r11
	mfsr	r12, SYSREG_ECR
	mov	r11, sp
	rcall	do_unknown_exception
1:	rjmp	1b
	.size	unknown_exception, . - unknown_exception

	.type	cpu_bootstrap, @function
cpu_bootstrap:
	mov	pm, lo(PM_BASE)
	orh	pm, hi(PM_BASE)

	/* Initialize and enable OSC0 */
	ld.w	mcctrl, pm[PM_MCCTRL]
	mov	r9, OSCCTRL0_VALUE
	st.w	pm[PM_OSCCTRL(0)], r9
	sbr	mcctrl, PM_MCCTRL_OSCEN_BIT(0)
	st.w	pm[PM_MCCTRL], mcctrl

	/* OSC0 startup timeout begins here */
	mov	zero, 0
	mtsr	SYSREG_COMPARE, zero
	mtsr	SYSREG_COUNT, zero

	/* Jump to cacheable area (P1 segment) */
	sub	r0, pc, . - 1f
	cbr	r0, 29
	mov	pc, r0

	/* Wait for OSC0 to become ready */
1:	ld.w	r8, pm[PM_POSCSR]
	bld	r8, PM_OSCRDY_BIT(0)
	brcs	.Losc0_ready
	mfsr	r9, SYSREG_COUNT
	cp.w	r9, OSC0_STARTUP_TIMEOUT
	brlt	1b

.Losc0_ready:
	/* Switch to OSC0 as the main clock source */
	andl	mcctrl, lo(~PM_MCCTRL_MCSEL_MASK)
	orl	mcctrl, PM_MCCTRL_MCSEL_OSC0
	st.w	pm[PM_MCCTRL], mcctrl

	/* Set up the exception handlers */
	lda.w	r1, _evba
	mtsr	SYSREG_EVBA, r1

	/*
	 * Copy the .data section and zero out the .bss section. We're
	 * clobbering all our "fixed" registers here.
	 */
	lda.w	r8, _etext
	lda.w	r9, _data
	lda.w	r10, _edata
	cp.w	r9, r10
	breq	2f

1:	ldm	r8++, r0-r7
	stm	r9, r0-r7
	sub	r9, -32
	cp.w	r9, r10
	brlt	1b

2:	lda.w	r8, _ebss
	mov	r0, 0
	mov	r1, 0
	mov	r2, 0
	mov	r3, 0
	cp.w	r9, r8
	breq	2f

1:	stm	r9, r0-r3
	sub	r9, -16
	cp.w	r9, r8
	brlt	1b

2:	/* Initialize the stack pointer */
	lda.w	sp, _end

	/* Run the application */
	call	main
1:	rjmp	1b

	.cpool
	.size	cpu_bootstrap, . - cpu_bootstrap

#ifdef CONFIG_NANOTRACE_SIZE
	.section .nanotrace, "aw", @nobits
	.global	NANOTRACE
	.type	NANOTRACE, @object
NANOTRACE:
	.fill	CONFIG_NANOTRACE_SIZE
	.size	NANOTRACE, . - NANOTRACE
#endif
