/******************************************************************************
 * $Revision: 425 $
 * $Date:: 2017-05-19 10:28:09 +0900#$
 *****************************************************************************/
/* __DISCLAIMER_START__                                                      */
/******************************************************************************
* (c)2017, Cypress Semiconductor Corporation
* or a subsidiary of Cypress Semiconductor Corporation. All rights
* reserved.
*
* This software, including source code, documentation and related
* materials ( "Software" ), is owned by Cypress Semiconductor
* Corporation or one of its subsidiaries ( "Cypress" ) and is protected by
* and subject to worldwide patent protection (United States and foreign),
* United States copyright laws and international treaty provisions.
* Therefore, you may use this Software only as provided in the license
* agreement accompanying the software package from which you
* obtained this Software ( "EULA" ).
*
* If no EULA applies, Cypress hereby grants you a personal, nonexclusive,
* non-transferable license to copy, modify, and compile the
* Software source code solely for use in connection with Cypress' s
* integrated circuit products. Any reproduction, modification, translation,
* compilation, or representation of this Software except as specified
* above is prohibited without the express written permission of Cypress.
*
* Disclaimer: THIS SOFTWARE IS PROVIDED AS-IS, WITH NO
* WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING,
* BUT NOT LIMITED TO, NONINFRINGEMENT, IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE. Cypress reserves the right to make
* changes to the Software without notice. Cypress does not assume any
* liability arising out of the application or use of the Software or any
* product or circuit described in the Software. Cypress does not
* authorize its products for use in any products where a malfunction or
* failure of the Cypress product may reasonably be expected to result in
* significant property damage, injury or death ( "High Risk Product" ). By
* including Cypress' s product in a High Risk Product, the manufacturer
* of such system or application assumes all risk of such use and in doing
* so agrees to indemnify Cypress against all liability.
******************************************************************************/
/* __DISCLAIMER_END__                                                        */
/*****************************************************************************/
/** \file interrupts.c
 **
 ** Definition of Interrupt Controller initialization and service functions.
 ** The assignment of interrupt (IRQ) and non-maskable interrupt (NMI)
 ** vectors and priority levels must be done in interrupts.h.
 **
 ** History:
 **   - 2015-09-01  0.01  HS  Initial version for Traveo
 *****************************************************************************/

/* File version 0xYYXX = vYY.XX */
#define INTERRUPTS_C_VERSION          0x0001U

#ifndef __FILE_VERSION_CHECK__

/*****************************************************************************/
/* Include files                                                             */
/*****************************************************************************/
#include "interrupts.h"
#include "Clock.h"
/*****************************************************************************/
/* Local pre-processor symbols/macros ('#define')                            */
/*****************************************************************************/

/** IRQ: IRQ channel count */
#define INT_IRQ_CHANNEL_COUNT               256U
/** IRQ: lowest priority level */
#define INT_IRQ_PRIORITY_LEVEL_LOWEST       31U
/** IRQ: highest priority level */
#define INT_IRQ_PRIORITY_LEVEL_HIGHEST      0U

/** NMI: NMI channel count */
#define INT_NMI_CHANNEL_COUNT               32U
/** NMI: lowest priority level */
#define INT_NMI_PRIORITY_LEVEL_LOWEST       15U
/** NMI: highest priority level */
#define INT_NMI_PRIORITY_LEVEL_HIGHEST      0U

/** Key to unlock the Interrupt Controller by writing to IRQn_UNLOCK register */
#define INT_CTRL_KEY_UNLOCK                 0x17ACC911UL
/** Key to lock the Interrupt Controller by writing to IRQn_UNLOCK register */
#define INT_CTRL_KEY_LOCK                   0x17B10C11UL

/*****************************************************************************/
/* Global variable definitions (declared in header file with 'extern')       */
/*****************************************************************************/

/*****************************************************************************/
/* Local type definitions ('typedef')                                        */
/*****************************************************************************/

/*****************************************************************************/
/* Local function prototypes ('static')                                      */
/*****************************************************************************/

static __fiq __arm void Interrupt_NMI_Default_Handler(void);
static __irq __arm void Interrupt_IRQ_Default_Handler(void);

/*****************************************************************************/
/* Local variable definitions ('static')                                     */
/*****************************************************************************/

/*****************************************************************************/
/* Function implementation - global ('extern') and local ('static')          */
/*****************************************************************************/

/**
 ******************************************************************************
 ** \brief Unlock and reset the Interrupt Controller 
 **
 ** \pre Must be in system mode
 **
 ** \return None
 ******************************************************************************/
void Interrupt_Controller_Unlock(void)
{
  uint_fast16_t fu16Channel;
  volatile un_irc0_nmivan_t*  aunNmiVectorAddress = &IRC0.unNMIVA0;
  volatile un_irc0_irqvan_t*  aunIrqVectorAddress = &IRC0.unIRQVA0;
  
  /* Write unlock key */
  IRC0_UNLOCK = INT_CTRL_KEY_UNLOCK;
  
  /* Disable IRQs in Interrupt Controller */
  while (IRC0_IRQST_nIRQ == 0U){}
  IRC0_CSR_IRQEN = 0U;
  
  /* Set default NMI handler for each available NMI vector */
  for (fu16Channel = 0U; fu16Channel < INT_NMI_CHANNEL_COUNT; fu16Channel++)
  {
    /* Set default handler */
    aunNmiVectorAddress[fu16Channel].u32Register = (uint32_t)Interrupt_NMI_Default_Handler;
  }

  /* Set default IRQ handler for whole vector address RAM */
  for (fu16Channel = 0U; fu16Channel < INT_IRQ_CHANNEL_COUNT; fu16Channel++)
  {
    /* Set default handler */
    aunIrqVectorAddress[fu16Channel].u32Register = (uint32_t)Interrupt_IRQ_Default_Handler;
  }
}

/**
 ******************************************************************************
 ** \brief Lock Interrupt Controller
 **
 ** \pre Must be in system mode
 **
 ** \return None
 ******************************************************************************/
void Interrupt_Controller_Lock(void)
{
  /* Enable IRQs in Interrupt Controller */
  IRC0_CSR_IRQEN = 1U;
    
  /* Write lock key */
  IRC0_UNLOCK = INT_CTRL_KEY_LOCK;
}




void Interrupt_NMI_ISR_Register(uint16_t u16NMINum, uint32_t u32ISRAddr, uint8_t u8priority)
{
  uint16_t u16Index;
  volatile un_irc0_nmivan_t*  aunNmiVectorAddress = &IRC0.unNMIVA0;
  volatile un_irc0_nmipl0_t*  aunNmiPriorityLevel = &IRC0.unNMIPL0;
    
  if ((IRC0_CSR_LST == 0U) && \
      (u16NMINum < INT_NMI_CHANNEL_COUNT) && \
      (u8priority <= INT_NMI_PRIORITY_LEVEL_LOWEST))
  {
    /* Set user specific ISR */
    aunNmiVectorAddress[u16NMINum].u32Register = u32ISRAddr;
    
    /* Set user specific priority level */
    u16Index = u16NMINum / 4U;
    switch (u16NMINum % 4U)
    {
      case  0 : aunNmiPriorityLevel[u16Index].stcField.u4NMIPL0 = u8priority;
                break ;
                
      case  1 : aunNmiPriorityLevel[u16Index].stcField.u4NMIPL1 = u8priority;
                break ;
                
      case  2 : aunNmiPriorityLevel[u16Index].stcField.u4NMIPL2 = u8priority;
                break ;
                
      case  3 : aunNmiPriorityLevel[u16Index].stcField.u4NMIPL3 = u8priority;
                break ;
                
      default : break ;
    }
  }
}

void Interrupt_NMI_Channel_Disable(uint16_t u16NMINum)
{
  uint16_t u16Index;
  volatile un_irc0_nmivan_t*  aunNmiVectorAddress = &IRC0.unNMIVA0;
  volatile un_irc0_nmipl0_t*  aunNmiPriorityLevel = &IRC0.unNMIPL0;
    
  if ((IRC0_CSR_LST == 0U) && \
      (u16NMINum < INT_NMI_CHANNEL_COUNT))
  {
    /* Set default NMI handler */
    aunNmiVectorAddress[u16NMINum].u32Register = (uint32_t)Interrupt_NMI_Default_Handler;
    
    /* Set user specific priority level */
    u16Index = u16NMINum / 4U;
    switch (u16NMINum % 4U)
    {
      case  0 : aunNmiPriorityLevel[u16Index].stcField.u4NMIPL0 = INT_NMI_PRIORITY_LEVEL_LOWEST;
                break ;
                
      case  1 : aunNmiPriorityLevel[u16Index].stcField.u4NMIPL1 = INT_NMI_PRIORITY_LEVEL_LOWEST;
                break ;
                
      case  2 : aunNmiPriorityLevel[u16Index].stcField.u4NMIPL2 = INT_NMI_PRIORITY_LEVEL_LOWEST;
                break ;
                
      case  3 : aunNmiPriorityLevel[u16Index].stcField.u4NMIPL3 = INT_NMI_PRIORITY_LEVEL_LOWEST;
                break ;
                
      default : break ;
    }
  }
}

void Interrupt_IRQ_ISR_Register(uint16_t u16IRQNum, uint32_t u32ISRAddr, uint8_t u8priority)
{
  uint16_t u16Index;
  volatile un_irc0_irqvan_t*  aunIrqVectorAddress    = &IRC0.unIRQVA0;
  volatile un_irc0_irqpl0_t*  aunIrqPriorityLevel    = (volatile un_irc0_irqpl0_t*)&IRC0.unIRQPL0;  /* take un_irqn_irqpl8_t as this structure provides all IRQPL0...3 */
  volatile un_irc0_irqces0_t* aunIrqChannelEnableSet = &IRC0.unIRQCES0;
    
  if ((IRC0_CSR_LST == 0U) && \
      (u16IRQNum < INT_IRQ_CHANNEL_COUNT) && \
      (u8priority <= INT_IRQ_PRIORITY_LEVEL_LOWEST))
  {
    /* Set user specific ISR */
    aunIrqVectorAddress[u16IRQNum].u32Register = u32ISRAddr;
    
    /* Set user specific priority level (byte array in type is used to avoid RMW accesses) */
    u16Index = u16IRQNum / 4U;
    switch (u16Index % 4U)
    {
      case  0 : aunIrqPriorityLevel[u16Index].au8Byte[0] = u8priority;
                break;

      case  1 : aunIrqPriorityLevel[u16Index].au8Byte[1] = u8priority;
                break;
                
      case  2 : aunIrqPriorityLevel[u16Index].au8Byte[2] = u8priority;
                break;

      case  3 : aunIrqPriorityLevel[u16Index].au8Byte[3] = u8priority;
                break;

      default : break;
    }
    
    /* Enable IRQ channel */
    u16Index = u16IRQNum / 32U;
    aunIrqChannelEnableSet[u16Index].u32Register = 1U << (u16IRQNum % 32U);
  }
}

void Interrupt_IRQ_Channel_Disable(uint16_t u16IRQNum)
{
  uint16_t u16Index;
  volatile un_irc0_irqvan_t*  aunIrqVectorAddress    = &IRC0.unIRQVA0;
  volatile un_irc0_irqpl0_t*  aunIrqPriorityLevel    = (volatile un_irc0_irqpl0_t*)&IRC0.unIRQPL0;  /* take un_irqn_irqpl8_t as this structure provides all IRQPL0...3 */
  volatile un_irc0_irqcec0_t* aunIrqChannelEnableClr = &IRC0.unIRQCEC0;
    
  if ((IRC0_CSR_LST == 0U) && \
      (u16IRQNum < INT_IRQ_CHANNEL_COUNT))
  {
    /* Set user specific ISR */
    aunIrqVectorAddress[u16IRQNum].u32Register = (uint32_t)Interrupt_IRQ_Default_Handler;
    
    /* Set user specific priority level (byte array in type is used to avoid RMW accesses) */
    u16Index = u16IRQNum / 4U;
    switch (u16Index % 4U)
    {
      case  0 : aunIrqPriorityLevel[u16Index].au8Byte[0] = INT_IRQ_PRIORITY_LEVEL_LOWEST;
                break;

      case  1 : aunIrqPriorityLevel[u16Index].au8Byte[1] = INT_IRQ_PRIORITY_LEVEL_LOWEST;
                break;
                
      case  2 : aunIrqPriorityLevel[u16Index].au8Byte[2] = INT_IRQ_PRIORITY_LEVEL_LOWEST;
                break;

      case  3 : aunIrqPriorityLevel[u16Index].au8Byte[3] = INT_IRQ_PRIORITY_LEVEL_LOWEST;
                break;

      default : break;
    }
    
    /* Enable IRQ channel */
    u16Index = u16IRQNum / 32U;
    aunIrqChannelEnableClr[u16Index].u32Register = 1U << (u16IRQNum % 32U);
  }
}

/**
 ******************************************************************************
 ** \brief Default NMI handler 
 **
 ** Default non-maskable interrupt handler used for NMI vectors which are not
 ** assigned to user specific interrupt service routines.
 **
 ******************************************************************************/
static __fiq __arm void Interrupt_NMI_Default_Handler(void)
{
    /* Current mode: FIQ mode using FIQ stack */
    /* (not-nested interrupt!)                */
    
    while (1)
    {
        /* wait for HWWD reset       */
        /* or implement user handler */
      Set_LED_Flag(0X7AA7u);
    }

    /* If the ISR should be left, add the IRQ specific exit sequence */
}

/**
 ******************************************************************************
 ** \brief Default IRQ handler 
 **
 ** Default interrupt handler used for IRQ vectors which are not
 ** assigned to user specific interrupt service routines.
 **
 ******************************************************************************/
static __irq __arm void Interrupt_IRQ_Default_Handler(void)
{
    /* Current mode: Interrupt mode using interrupt stack */
    /* (not-nested interrupt!)                            */

    while (1)
    {
        /* wait for HWWD reset       */
        /* or implement user handler */
        Set_LED_Flag(0X5AA5u);
    }

    /* If the ISR should be left, add the IRQ specific exit sequence */
}

#endif /* __FILE_VERSION_CHECK__ */
