/* ---------------------------------------------------------------------------- */
/*                  Atmel Microcontroller Software Support                      */
/*                       SAM Software Package License                           */
/* ---------------------------------------------------------------------------- */
/* Copyright (c) 2015, Atmel Corporation                                        */
/*                                                                              */
/* All rights reserved.                                                         */
/*                                                                              */
/* Redistribution and use in source and binary forms, with or without           */
/* modification, are permitted provided that the following condition is met:    */
/*                                                                              */
/* - Redistributions of source code must retain the above copyright notice,     */
/* this list of conditions and the disclaimer below.                            */
/*                                                                              */
/* Atmel's name may not be used to endorse or promote products derived from     */
/* this software without specific prior written permission.                     */
/*                                                                              */
/* DISCLAIMER:  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   */
/* 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
 * This file contains the default exception handlers.
 *
 * \note
 * The exception handler has weak aliases.
 * As they are weak aliases, any function with the same name will override
 * this definition.
 */

/*----------------------------------------------------------------------------
 *        Headers
 *----------------------------------------------------------------------------*/

#include "chip.h"

/*----------------------------------------------------------------------------
 *        Exported functions
 *----------------------------------------------------------------------------*/

/**
 * \brief Default NMI interrupt handler.
 */
void NMI_Handler(void)
{
	printf("\r\n-E- Enter NMI_Handler!");

	while (1);
}

/**
 * \brief This function back trace the stack to give exact address where fault
 happened
**/
__STATIC_INLINE uint32_t StackUnwind(void)
{
	uint32_t Fault_Add;

#if defined (__CC_ARM)
	uint32_t temp;
	__ASM("mrs temp, msp ");
	__ASM{ ldr Fault_Add, [temp, #28]}
#else
	__ASM("mrs r0, msp ");
	__ASM("ldr %0, [r0,#28]" : "=r" (Fault_Add));
#endif
	return Fault_Add;
}

/**
 * \brief If Other Faults are enabled then HardFault error will look for those
 *  errors to give more detail about fault
**/
static void HardFault_reason(void)
{
	uint32_t CFSRValue;
	TRACE_DEBUG("In Hard Fault Handler\n\r");
	TRACE_DEBUG("SCB->HFSR = 0x%08x\n\r", SCB->HFSR);

	if ((SCB->HFSR & SCB_HFSR_DEBUGEVT_Msk)) {
		TRACE_DEBUG("Debug Event Hard Fault\n\r");
		TRACE_DEBUG("SCB->DFSR = 0x%08x\n", SCB->DFSR);
	}

	if ((SCB->HFSR & SCB_HFSR_VECTTBL_Msk)) {
		TRACE_DEBUG("Fault was due to vector table read on \
			exception processing\n\r");
	}

	// Forced HardFault
	if ((SCB->HFSR & SCB_HFSR_FORCED_Msk)) {
		TRACE_DEBUG("Forced Hard Fault\n\r");
		TRACE_DEBUG("SCB->CFSR = 0x%08x\n\r", SCB->CFSR);

		// Usage Fault
		if ((SCB->CFSR & SCB_CFSR_USGFAULTSR_Msk)) {
			CFSRValue = SCB->CFSR;
			TRACE_DEBUG("Usage fault: ");
			CFSRValue >>= SCB_CFSR_USGFAULTSR_Pos;

			if ((CFSRValue & (1 << 9)))
				TRACE_DEBUG("Divide by zero\n\r");

			if ((CFSRValue & (1 << 8)))
				TRACE_DEBUG("Unaligned access error\n\r");

			if ((CFSRValue & (1 << 3)))
				TRACE_DEBUG("Coprocessor access error\n\r");

			if ((CFSRValue & (1 << 2)))
				TRACE_DEBUG("Integrity check error on EXC_RETURN\n\r");
		}

		// Bus Fault
		if ((SCB->CFSR & SCB_CFSR_BUSFAULTSR_Msk)) {
			CFSRValue = SCB->CFSR;
			TRACE_DEBUG("Bus fault: ");
			CFSRValue >>= SCB_CFSR_BUSFAULTSR_Pos;

			if ((CFSRValue & (1 << 7)) && (CFSRValue & (1 << 1))) {
				TRACE_DEBUG("Precise data access error. Bus Fault Address \
					Register is: %x \n\r", SCB->BFAR);
			}

			if ((CFSRValue & (1 << 4)))
				TRACE_DEBUG("Bus fault has occurred on exception entry\n\r");

			if ((CFSRValue & (1 << 3)))
				TRACE_DEBUG("bus fault has occurred on exception return\n\r");

			if ((CFSRValue & (1 << 2)))
				TRACE_DEBUG("Imprecise data access error\n\r");

			if ((CFSRValue & (1 << 0))) {
				TRACE_DEBUG("This bit indicates a bus fault on an instruction \
					pre-fetch. \n\r");
			}
		}
	}

	// MemoryFault
	if ((SCB->CFSR & SCB_CFSR_MEMFAULTSR_Msk)) {
		CFSRValue = SCB->CFSR;
		TRACE_DEBUG("Memory fault: ");
		CFSRValue >>= SCB_CFSR_MEMFAULTSR_Pos;

		if ((CFSRValue & (1 << 9)) != 0)
			TRACE_DEBUG("Divide by zero\n\r");
	}

	__ISB();
	__DMB();
	__ASM volatile("BKPT #01");
}
/**
 * \brief Default HardFault interrupt handler.
 */

void HardFault_Handler(void)
{
	printf("\n\rHardFault at address 0X%x\n\r", (int)StackUnwind());
	__ISB();
	__DMB();
	HardFault_reason();
}

#ifndef MPU_EXAMPLE_FEATURE
/**
 * \brief Default MemManage interrupt handler.
 */
void MemManage_Handler(void)
{
	printf("\n\rMemoryMemFault (MPU fault) at address 0X%x\n\r",
		   (int)StackUnwind());
	__ISB();
	__DMB();
	__ASM volatile("BKPT #01");
}
#endif

/**
 * \brief Default BusFault interrupt handler.
 */
void BusFault_Handler(void)
{
	__ASM("nop");
	__ASM("nop");
	printf("\n\rBus Fault at address 0X%x\n\r", (int)StackUnwind());

	__ISB();
	__DMB();
	__ASM volatile("BKPT #01");
}

/**
 * \brief Default UsageFault interrupt handler.
 */
void UsageFault_Handler(void)
{
	printf("\r\nUsage fault at address 0X%x", (int)StackUnwind());

	__ISB();
	__DMB();
	__ASM volatile("BKPT #01");
}
