/*
 * Copyright (C) 2014-2018 MIPS Tech, LLC
 *
 * 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 of the copyright holder 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 HOLDER 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.
*/

#define _RESETCODE

#include <mips/regdef.h>
#include <mips/cpu.h>
#include <mips/asm.h>

MIPS_NOMIPS16

	.set push
	MIPS_NOMICROMIPS

LEAF(__reset_vector)
	lui	k1, %hi(__cpu_init)
	addiu	k1, %lo(__cpu_init)
	mtc0	zero, C0_COUNT	  /* Clear CP0 Count (Used to measure boot time.) */
	jr	k1
	.space 32		  /* Just to cope with a quirk of MIPS malta boards */
				  /* this can be deleted for anything else */
END(__reset_vector)
	.set pop

LEAF(__cpu_init)

	/*
	 * Verify the code is here due to a reset and not NMI. If this is an NMI then trigger
	 * a debugger breakpoint using a sdbbp instruction.
	 */

	mfc0	k1, C0_STATUS
	ext	k1, k1, SR_NMI_SHIFT, 1
	beqz	k1, $Lnot_nmi
	move	k0, t9			/* Preserve t9 */
	move	k1, a0			/* Preserve a0 */
	li	t9, 15			/* UHI exception operation */
	li	a0, 0			/* Use hard register context */
	sdbbp	1			/* Invoke UHI operation */

$Lnot_nmi:

	/* Init CP0 Status, Count, Compare, Watch*, and Cause */
	jal	  __init_cp0

	/*
	 * Initialise L2/L3 cache
	 * This could be done from cached code if there is a cca override or similar
	 */
	jal	__init_l23cache
	/* Save the L2 config register */
	move	s0, a0

	/* Initialize the L1 instruction cache */
	jal	__init_icache

	/*
	 * The changing of Kernel mode cacheability must be done from KSEG1
	 * Since the code is executing from KSEG0 it needs to do a jump to KSEG1, change K0
	 * and jump back to KSEG0.
	 */

	lui	a2, %hi(__change_k0_cca)
	addiu	a2, a2, %lo(__change_k0_cca)
	li	a1, 0xf
	ins	a2, a1, 29, 1		/* changed to KSEG1 address by setting bit 29 */
	jalr	a2

	.weak __init_l23cache_cached
	lui	a2, %hi(__init_l23cache_cached)
	addiu	a2, a2, %lo(__init_l23cache_cached)
	beqz	a2, 1f
	/* Pass in the L2 config register */
	move	a0, s0
	jalr	a2
1:
	/* Initialize the L1 data cache */
	jal	__init_dcache

	/* Initialize the TLB */
	jal	__init_tlb

	/* Allow everything else to be initialized via a hook */
	.weak __boot_init_hook
	lui	a2, %hi(__boot_init_hook)
	addiu	a2, a2, %lo(__boot_init_hook)
	beqz	a2, 1f
	jalr	a2
1:
	/* Skip copy to ram when executing in place */
	.weak __xip
	lui	a1, %hi(__xip)
	addiu	a1, a1, %lo(__xip)
	bnez	a1, $Lcopy_to_ram_done
	/* Copy code and data to RAM */
	li	s1, 0xffffffff

	/* Copy code and read-only/initialized data from FLASH to (uncached) RAM */
	lui	a1, %hi(__flash_app_start)
	addiu	a1, a1, %lo(__flash_app_start)
	ins	a1, s1, 29, 1		/* Make it uncached (kseg1) */
	lui	a2, %hi(__app_start)
	addiu	a2, a2, %lo(__app_start)
	ins	a2, s1, 29, 1		/* Make it uncached (kseg1) */
	lui	a3, %hi(_edata)
	addiu	a3, a3, %lo(_edata)
	ins	a3, s1, 29, 1		/* Make it uncached (kseg1) */
	beq	a2, a3, $Lcopy_to_ram_done
$Lnext_ram_word:
	lw	a0, 0(a1)
	addiu	a2, a2, 4
	addiu	a1, a1, 4
	sw	a0, -4(a2)
	bne	a3, a2, $Lnext_ram_word
$Lcopy_to_ram_done:

	# Prepare for eret to _start
	lui	ra, %hi($Lall_done)	/* If main returns then go to all_done */
	addiu	ra, ra, %lo($Lall_done)
	lui	t0, %hi(_start)
	addiu	t0, t0, %lo(_start)
	mtc0	t0, C0_ERRPC		/* Set ErrorEPC to _start */
	ehb
	li	a0, 0			/* UHI compliant null argument setup */

	/* Return from exception will now execute the application startup code */
	eret

$Lall_done:
	/* Allow an exit hook to intercept the end of execution */
	.weak __boot_exit_hook
	lui	a2, %hi(__boot_exit_hook)
	addiu	a2, a2, %lo(__boot_exit_hook)
	beqz	a2, 1f
	jalr	a2
1:
	/*
	 * If _start returns it will return to this point.
	 * Just spin here reporting the exit.
	 */
	li	t9, 1			/* UHI exit operation */
	move	a0, va0			/* Collect exit code for UHI exit */
	sdbbp	1			/* Invoke UHI operation */
	b	$Lall_done
END(__cpu_init)

/**************************************************************************************
    B O O T   E X C E P T I O N   H A N D L E R S (CP0 Status[BEV] = 1)
**************************************************************************************/
/* NOTE: the linker script must insure that this code starts at start + 0x200 so the exception */
/* vectors will be addressed properly. */

/* TLB refill, 32 bit task. */
.org 0x200
LEAF(__boot_tlb_refill)
	move	k0, t9			/* Preserve t9 */
	move	k1, a0			/* Preserve a0 */
	li	t9, 15			/* UHI exception operation */
	li	a0, 0			/* Use hard register context */
	sdbbp	1			/* Invoke UHI operation */
END(__boot_tlb_refill)

/* XTLB refill, 64 bit task. */
.org 0x280
LEAF(__boot_xtlb_refill)
	move	k0, t9			/* Preserve t9 */
	move	k1, a0			/* Preserve a0 */
	li	t9, 15			/* UHI exception operation */
	li	a0, 0			/* Use hard register context */
	sdbbp	1			/* Invoke UHI operation */
END(__boot_xtlb_refill)

/* Cache error exception. */
.org 0x300
LEAF(__boot_cache_error)
	move	k0, t9			/* Preserve t9 */
	move	k1, a0			/* Preserve a0 */
	li	t9, 15			/* UHI exception operation */
	li	a0, 0			/* Use hard register context */
	sdbbp	1			/* Invoke UHI operation */
END(__boot_cache_error)

/* General exception. */
.org 0x380
LEAF(__boot_general_exception)
	move	k0, t9			/* Preserve t9 */
	move	k1, a0			/* Preserve a0 */
	li	t9, 15			/* UHI exception operation */
	li	a0, 0			/* Use hard register context */
	sdbbp	1			/* Invoke UHI operation */
END(__boot_general_exception)

# If you want the above code to fit into 1k flash you will need to leave out the
# code below. This is the code that covers the debug exception which you normally will not get.

/* EJTAG Debug */
.org 0x480 
LEAF(__boot_debug_exception)
	PTR_MFC0  k1, C0_DEPC
	PTR_MTC0  k1, C0_DESAVE
	lui       k1, %hi(1f)
	addiu     k1, %lo(1f)
	PTR_MTC0  k1, C0_DEPC
	ehb
	deret
1:	wait
	b	  1b  /* Stay here */
END(__boot_debug_exception)
