/*
 * Copyright (c) 2006-2022, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date         Author      Notes
 * 2010-12-29   onelife     Initial creation for EFM32
 * 2011-07-12   onelife     Disable interrupts in GPIO handler
 * 2011-12-09   onelife     Add giant gecko support
 * 2011-12-09   onelife     Add UART module support
 * 2011-12-09   onelife     Add LEUART module support
 * 2011-12-27   onelife     Utilize "XXX_PRESENT" and "XXX_COUNT"
 */

/* Includes ------------------------------------------------------------------*/
#include "board.h"
#include "hdl_interrupt.h"

/***************************************************************************//**
 * @addtogroup efm32
 * @{
 ******************************************************************************/

/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
#ifdef RT_IRQHDL_DEBUG
#define hdl_debug(format,args...)           rt_kprintf(format, ##args)
#else
#define hdl_debug(format,args...)
#endif

/* Private variables ---------------------------------------------------------*/
efm32_irq_hook_t dmaCbTable[DMA_CHAN_COUNT * 2] = {RT_NULL};
efm32_irq_hook_t timerCbTable[TIMER_COUNT]      = {RT_NULL};
#if defined(LETIMER_PRESENT)
efm32_irq_hook_t letimerCbTable[LETIMER_COUNT]  = {RT_NULL};
#endif
efm32_irq_hook_t rtcCbTable[RTC_COUNT]          = {RT_NULL};
efm32_irq_hook_t gpioCbTable[16]                = {RT_NULL};
efm32_irq_hook_t acmpCbTable[ACMP_COUNT]        = {RT_NULL};
#if defined(USART_PRESENT)
 #if defined(UART_PRESENT)
efm32_irq_hook_t usartCbTable[USART_COUNT * 2 + UART_COUNT * 2] = {RT_NULL};
 #else
efm32_irq_hook_t usartCbTable[USART_COUNT * 2]  = {RT_NULL};
 #endif
#endif
#if defined(LEUART_PRESENT)
efm32_irq_hook_t leuartCbTable[LEUART_COUNT]    = {RT_NULL};
#endif
#if defined(I2C_PRESENT)
efm32_irq_hook_t iicCbTable[I2C_COUNT]          = {RT_NULL};
#endif

/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/

/***************************************************************************//**
 * @brief
 *  NMI exception handler
 *
 * @details
 *
 * @note
 ******************************************************************************/
void NMI_Handler(void)
{
    hdl_debug("[NMI_Handler: NOP]\n");
}

/***************************************************************************//**
 * @brief
 *  Memory manage exception handler
 *
 * @details
 *
 * @note
 ******************************************************************************/
void MemManage_Handler(void)
{
    hdl_debug("[MemManage_Handler: infinite loop]\n");
    while (1);
}

/***************************************************************************//**
 * @brief
 *  Bus fault exception handler
 *
 * @details
 *
 * @note
 ******************************************************************************/
void BusFault_Handler(void)
{
    hdl_debug("[BusFault_Handler: infinite loop]\n");
    while (1);
}

/***************************************************************************//**
 * @brief
 *  Usage fault exception handler
 *
 * @details
 *
 * @note
 ******************************************************************************/
void UsageFault_Handler(void)
{
    hdl_debug("[UsageFault_Handler: infinite loop]\n");
    while (1);
}

/***************************************************************************//**
 * @brief
 *  Supervisor call exception handler
 *
 * @details
 *
 * @note
 ******************************************************************************/
void SVC_Handler(void)
{
    hdl_debug("[SVC_Handler: NOP]\n");
}

/***************************************************************************//**
 * @brief
 *   Debug monitor exception handler
 *
 * @details
 *
 * @note
 ******************************************************************************/
void DebugMon_Handler(void)
{
    hdl_debug("[DebugMon_Handler: NOP]\n");
}

/***************************************************************************//**
 * @brief
 *  System tick timer interrupt handler
 *
 * @details
 *
 * @note
 *
 ******************************************************************************/
void SysTick_Handler(void)
{
    /* enter interrupt */
    rt_interrupt_enter();

    rt_tick_increase();

    /* leave interrupt */
    rt_interrupt_leave();
}

/*******************************************************************************
 *                 STM32F10x Peripherals Interrupt Handlers
 *  Add here the Interrupt Handler for the used peripheral(s) (PPP), for the
 *  available peripheral interrupt handler's name please refer to the startup
 *  file (startup_stm32f10x_xx.s).
/******************************************************************************/

/***************************************************************************//**
 * @brief
 *  Common DMA interrupt handler
 *
 * @details
 *
 * @note
 *
 ******************************************************************************/
void DMA_IRQHandler_All(rt_uint32_t channel, rt_bool_t primary, void *user)
{
    /* enter interrupt */
    rt_interrupt_enter();

    /* invoke callback function */
    if (dmaCbTable[channel].cbFunc != RT_NULL)
    {
        (dmaCbTable[channel].cbFunc)(dmaCbTable[channel].userPtr);
    }

    /* leave interrupt */
    rt_interrupt_leave();
}

/***************************************************************************//**
 * @brief
 *  Common Timer0 interrupt handler
 *
 * @details
 *  This function handles Timer0 counter overflow interrupt request
 *
 * @note
 *
 ******************************************************************************/
void TIMER0_IRQHandler(void)
{
    if (TIMER0->IF & TIMER_IF_OF)
    {
        /* invoke callback function */
        if (timerCbTable[0].cbFunc != RT_NULL)
        {
            (timerCbTable[0].cbFunc)(timerCbTable[0].userPtr);
        }

        /* clear interrupt */
        BITBAND_Peripheral(&(TIMER0->IFC), _TIMER_IF_OF_SHIFT, 0x1UL);
    }
}


/***************************************************************************//**
 * @brief
 *  Common Timer1 interrupt handler
 *
 * @details
 *  This function handles Timer1 counter overflow interrupt request
 *
 * @note
 *
 ******************************************************************************/
void TIMER1_IRQHandler(void)
{
    if (TIMER1->IF & TIMER_IF_OF)
    {
        /* invoke callback function */
        if (timerCbTable[1].cbFunc != RT_NULL)
        {
            (timerCbTable[1].cbFunc)(timerCbTable[1].userPtr);
        }

        /* clear interrupt */
        BITBAND_Peripheral(&(TIMER1->IFC), _TIMER_IF_OF_SHIFT, 0x1UL);
    }
}

/***************************************************************************//**
 * @brief
 *  Common Timer2 interrupt handler
 *
 * @details
 *  This function handles Timer2 counter overflow interrupt request
 *
 * @note
 *
 ******************************************************************************/
void TIMER2_IRQHandler(void)
{
    if (TIMER2->IF & TIMER_IF_OF)
    {
        /* invoke callback function */
        if (timerCbTable[2].cbFunc != RT_NULL)
        {
            (timerCbTable[2].cbFunc)(timerCbTable[2].userPtr);
        }

        /* clear interrupt */
        BITBAND_Peripheral(&(TIMER2->IFC), _TIMER_IF_OF_SHIFT, 0x1UL);
    }
}

#if defined(LETIMER_PRESENT)
/***************************************************************************//**
 * @brief
 *  Common Low Energy Timer0 interrupt handler
 *
 * @details
 *  This function handles Timer0 counter overflow interrupt request
 *
 * @note
 *
 ******************************************************************************/
void LETIMER0_IRQHandler(void)
{
    if (LETIMER0->IF & LETIMER_IF_UF)
    {
        /* enter interrupt */
        rt_interrupt_enter();

        rt_tick_increase();

        /* leave interrupt */
        rt_interrupt_leave();

        /* invoke callback function */
/*      if (letimerCbTable[0].cbFunc != RT_NULL)
        {
            (letimerCbTable[0].cbFunc)(letimerCbTable[0].userPtr);
        }
*/
        /* clear interrupt */
        BITBAND_Peripheral(&(LETIMER0->IFC), _LETIMER_IF_UF_SHIFT, 0x1UL);
    }
}
#endif

/***************************************************************************//**
 * @brief
 *  Common RTC interrupt handler
 *
 * @details
 *  This function handles RTC counter overflow interrupt request
 *
 * @note
 *
 ******************************************************************************/
void RTC_IRQHandler(void)
{
    /* enter interrupt */
    rt_interrupt_enter();

    if (RTC->IF & RTC_IF_OF)
    {
        /* invoke callback function */
        if (rtcCbTable[0].cbFunc != RT_NULL)
        {
            (rtcCbTable[0].cbFunc)(rtcCbTable[0].userPtr);
        }
    }

    /* leave interrupt */
    rt_interrupt_leave();
}

/***************************************************************************//**
 * @brief
 *  Common even number GPIO interrupt handler
 *
 * @details
 *
 * @note
 *
 ******************************************************************************/
void GPIO_EVEN_IRQHandler(void)
{
    rt_uint16_t flag, n;
    rt_base_t level;

    /* Disable interrupt */
    level = rt_hw_interrupt_disable();
    /* Enter ISR */
    rt_interrupt_enter();

     /* invoke callback function */
    flag = (rt_uint16_t)(GPIO->IF & 0xFFFF);
    for ( n = 0; flag > 0; flag = flag >> 2, n = n + 2)
    {
        if ((flag & 0x0001) && (gpioCbTable[n].cbFunc != RT_NULL))
        {
            (gpioCbTable[n].cbFunc)(gpioCbTable[n].userPtr);
        }
    }

    /* clear interrupt */
    GPIO->IFC = 0x5555UL;

    /* Leave ISR */
    rt_interrupt_leave();
    /* Enable interrupt */
    rt_hw_interrupt_enable(level);
}

/***************************************************************************//**
 * @brief
 *  Common odd number GPIO interrupt handler
 *
 * @details
 *
 * @note
 *
 ******************************************************************************/
void GPIO_ODD_IRQHandler(void)
{
    rt_uint16_t flag, n;
    rt_base_t level;

    /* Disable interrupt */
    level = rt_hw_interrupt_disable();
    /* Enter ISR */
    rt_interrupt_enter();

     /* invoke callback function */
    flag = (rt_uint16_t)(GPIO->IF & 0xFFFF) >> 1;
    for ( n = 1; flag > 0; flag = flag >> 2, n = n + 2)
    {
        if ((flag & 0x0001) && (gpioCbTable[n].cbFunc != RT_NULL))
        {
            (gpioCbTable[n].cbFunc)(gpioCbTable[n].userPtr);
        }
    }

    /* clear interrupt */
    GPIO->IFC = 0xAAAAUL;

    /* Leave ISR */
    rt_interrupt_leave();
    /* Enable interrupt */
    rt_hw_interrupt_enable(level);
}

/***************************************************************************//**
 * @brief
 *  Common ACMP interrupt handler
 *
 * @details
 *  This function handles ACMP edge trigger interrupt request
 *
 * @note
 *
 ******************************************************************************/
void ACMP0_IRQHandler(void)
{
    /* enter interrupt */
    rt_interrupt_enter();

    if (ACMP0->IF & ACMP_IF_EDGE)
    {
        /* invoke callback function */
        if (acmpCbTable[0].cbFunc != RT_NULL)
        {
            (acmpCbTable[0].cbFunc)(acmpCbTable[0].userPtr);
        }

        /* clear interrupt */
        BITBAND_Peripheral(&(ACMP0->IFC), _ACMP_IF_EDGE_SHIFT, 0x1UL);
    }

    if (ACMP1->IF & ACMP_IF_EDGE)
    {
        /* invoke callback function */
        if (acmpCbTable[1].cbFunc != RT_NULL)
        {
            (acmpCbTable[1].cbFunc)(acmpCbTable[1].userPtr);
        }

        /* clear interrupt */
        BITBAND_Peripheral(&(ACMP1->IFC), _ACMP_IF_EDGE_SHIFT, 0x1UL);
    }

    /* leave interrupt */
    rt_interrupt_leave();
}

#if defined(USART_PRESENT)
/***************************************************************************//**
 * @brief
 *  Common USART0 TX interrupt handler
 *
 * @details
 *  This function handles USART0 TX complete interrupt request
 *
 * @note
 *
 ******************************************************************************/
void USART0_TX_IRQHandler(void)
{
    /* enter interrupt */
    rt_interrupt_enter();

    if (USART0->IF & USART_IF_TXC)
    {
        /* invoke callback function */
        if (usartCbTable[0].cbFunc != RT_NULL)
        {
            (usartCbTable[0].cbFunc)(usartCbTable[0].userPtr);
        }

        /* clear interrupt */
        BITBAND_Peripheral(&(USART0->IFC), _USART_IF_TXC_SHIFT, 0x1UL);
    }

    /* leave interrupt */
    rt_interrupt_leave();
}

/***************************************************************************//**
 * @brief
 *  Common USART0 RX interrupt handler
 *
 * @details
 *  This function handles USART0 RX data valid interrupt request
 *
 * @note
 *
 ******************************************************************************/
void USART0_RX_IRQHandler(void)
{
    if (USART0->IF & USART_IF_RXDATAV)
    {
        /* invoke callback function */
        if (usartCbTable[1].cbFunc != RT_NULL)
        {
            (usartCbTable[1].cbFunc)(usartCbTable[1].userPtr);
        }
    }
}
#endif

#if (defined(USART_PRESENT) && (USART_COUNT > 1))
/***************************************************************************//**
 * @brief
 *  Common USART1 TX interrupt handler
 *
 * @details
 *  This function handles USART1 TX complete interrupt request
 *
 * @note
 *
 ******************************************************************************/
void USART1_TX_IRQHandler(void)
{
    /* enter interrupt */
    rt_interrupt_enter();

    if (USART1->IF & USART_IF_TXC)
    {
        /* invoke callback function */
        if (usartCbTable[2].cbFunc != RT_NULL)
        {
            (usartCbTable[2].cbFunc)(usartCbTable[2].userPtr);
        }

        /* clear interrupt */
        BITBAND_Peripheral(&(USART1->IFC), _USART_IF_TXC_SHIFT, 0x1UL);
    }

    /* leave interrupt */
    rt_interrupt_leave();
}

/***************************************************************************//**
 * @brief
 *  Common USART1 RX interrupt handler
 *
 * @details
 *  This function handles USART1 RX data valid interrupt request
 *
 * @note
 *
 ******************************************************************************/
void USART1_RX_IRQHandler(void)
{
    if (USART1->IF & USART_IF_RXDATAV)
    {
        /* invoke callback function */
        if (usartCbTable[3].cbFunc != RT_NULL)
        {
            (usartCbTable[3].cbFunc)(usartCbTable[3].userPtr);
        }
    }
}
#endif

#if (defined(USART_PRESENT) && (USART_COUNT > 2))
/***************************************************************************//**
 * @brief
 *  Common USART2 TX interrupt handler
 *
 * @details
 *  This function handles USART2 TX complete interrupt request
 *
 * @note
 *
 ******************************************************************************/
void USART2_TX_IRQHandler(void)
{
    /* enter interrupt */
    rt_interrupt_enter();

    if (USART2->IF & USART_IF_TXC)
    {
        /* invoke callback function */
        if (usartCbTable[4].cbFunc != RT_NULL)
        {
            (usartCbTable[4].cbFunc)(usartCbTable[4].userPtr);
        }

        /* clear interrupt */
        BITBAND_Peripheral(&(USART2->IFC), _USART_IF_TXC_SHIFT, 0x1UL);
    }

    /* leave interrupt */
    rt_interrupt_leave();
}

/***************************************************************************//**
 * @brief
 *  Common USART2 RX interrupt handler
 *
 * @details
 *  This function handles USART2 RX data valid interrupt request
 *
 * @note
 *
 ******************************************************************************/
void USART2_RX_IRQHandler(void)
{
    if (USART2->IF & USART_IF_RXDATAV)
    {
        /* invoke callback function */
        if (usartCbTable[5].cbFunc != RT_NULL)
        {
            (usartCbTable[5].cbFunc)(usartCbTable[5].userPtr);
        }
    }
}
#endif

#if defined(UART_PRESENT)
/***************************************************************************//**
 * @brief
 *  Common UART0 TX interrupt handler
 *
 * @details
 *  This function handles UART0 TX complete interrupt request
 *
 * @note
 *
 ******************************************************************************/
void UART0_TX_IRQHandler(void)
{
    /* enter interrupt */
    rt_interrupt_enter();

    if (UART0->IF & UART_IF_TXC)
    {
        /* invoke callback function */
        if (usartCbTable[USART_COUNT * 2].cbFunc != RT_NULL)
        {
            (usartCbTable[USART_COUNT * 2].cbFunc)(usartCbTable[USART_COUNT * 2].userPtr);
        }

        /* clear interrupt */
        BITBAND_Peripheral(&(UART0->IFC), _UART_IF_TXC_SHIFT, 0x1UL);
    }

    /* leave interrupt */
    rt_interrupt_leave();
}

/***************************************************************************//**
 * @brief
 *  Common UART0 RX interrupt handler
 *
 * @details
 *  This function handles UART0 RX data valid interrupt request
 *
 * @note
 *
 ******************************************************************************/
void UART0_RX_IRQHandler(void)
{
    if (UART0->IF & UART_IF_RXDATAV)
    {
        /* invoke callback function */
        if (usartCbTable[USART_COUNT * 2 + 1].cbFunc != RT_NULL)
        {
            (usartCbTable[USART_COUNT * 2 + 1].cbFunc)(usartCbTable[USART_COUNT * 2 + 1].userPtr);
        }
    }
}
#endif

#if (defined(UART_PRESENT) && (UART_COUNT > 1))
/***************************************************************************//**
 * @brief
 *  Common UART1 TX interrupt handler
 *
 * @details
 *  This function handles UART1 TX complete interrupt request
 *
 * @note
 *
 ******************************************************************************/
void UART1_TX_IRQHandler(void)
{
    /* enter interrupt */
    rt_interrupt_enter();

    if (UART1->IF & UART_IF_TXC)
    {
        /* invoke callback function */
        if (usartCbTable[USART_COUNT * 2 + 2].cbFunc != RT_NULL)
        {
            (usartCbTable[USART_COUNT * 2 + 2].cbFunc)(usartCbTable[USART_COUNT * 2 + 2].userPtr);
        }

        /* clear interrupt */
        BITBAND_Peripheral(&(UART1->IFC), _UART_IF_TXC_SHIFT, 0x1UL);
    }

    /* leave interrupt */
    rt_interrupt_leave();
}

/***************************************************************************//**
 * @brief
 *  Common UART1 RX interrupt handler
 *
 * @details
 *  This function handles UART1 RX data valid interrupt request
 *
 * @note
 *
 ******************************************************************************/
void UART1_RX_IRQHandler(void)
{
    if (UART1->IF & UART_IF_RXDATAV)
    {
        /* invoke callback function */
        if (usartCbTable[USART_COUNT * 2 + 3].cbFunc != RT_NULL)
        {
            (usartCbTable[USART_COUNT * 2 + 3].cbFunc)(usartCbTable[USART_COUNT * 2 + 3].userPtr);
        }
    }
}
#endif

#if defined(LEUART_PRESENT)
/***************************************************************************//**
 * @brief
 *  Common LEUART0 interrupt handler
 *
 * @details
 *  This function handles LEUART0 interrupt request
 *
 * @note
 *
 ******************************************************************************/
void LEUART0_IRQHandler(void)
{
    if (LEUART0->IF & LEUART_IF_RXDATAV)
    {
        /* invoke callback function */
        if (leuartCbTable[0].cbFunc != RT_NULL)
        {
            (leuartCbTable[0].cbFunc)(leuartCbTable[0].userPtr);
        }
    }
}
#endif

#if (defined(LEUART_PRESENT) && (LEUART_COUNT > 1))
/***************************************************************************//**
 * @brief
 *  Common LEUART1 interrupt handler
 *
 * @details
 *  This function handles LEUART1 interrupt request
 *
 * @note
 *
 ******************************************************************************/
void LEUART1_IRQHandler(void)
{
    if (LEUART1->IF & LEUART_IF_RXDATAV)
    {
        /* invoke callback function */
        if (leuartCbTable[1].cbFunc != RT_NULL)
        {
            (leuartCbTable[1].cbFunc)(leuartCbTable[1].userPtr);
        }
    }
}
#endif

#if defined(I2C_PRESENT)
/***************************************************************************//**
 * @brief
 *  Common IIC0 interrupt handler
 *
 * @details
 *  This function handles IIC0 slave mode interrupt requests
 *
 * @note
 *
 ******************************************************************************/
void I2C0_IRQHandler(void)
{
    if ((I2C0->IF & I2C_IF_ADDR) || \
        (I2C0->IF & I2C_IF_RXDATAV) || \
        (I2C0->IF & I2C_IF_SSTOP))
    {
        /* invoke callback function */
        if (iicCbTable[0].cbFunc != RT_NULL)
        {
            (iicCbTable[0].cbFunc)(iicCbTable[0].userPtr);
        }
    }

    I2C_IntClear(I2C0, I2C_IFC_ADDR | I2C_IFC_SSTOP);
}
#endif

/***************************************************************************//**
 * @brief
 *  EFM32 common interrupt handlers register function
 *
 * @details
 *
 * @note
 *
 ******************************************************************************/
void efm32_irq_hook_register(efm32_irq_hook_init_t *hook)
{
    switch (hook->type)
    {
    case efm32_irq_type_dma:
        dmaCbTable[hook->unit].cbFunc = hook->cbFunc;
        dmaCbTable[hook->unit].userPtr = hook->userPtr;
        break;

    case efm32_irq_type_rtc:
        rtcCbTable[hook->unit].cbFunc = hook->cbFunc;
        rtcCbTable[hook->unit].userPtr = hook->userPtr;
        break;

    case efm32_irq_type_timer:
        timerCbTable[hook->unit].cbFunc = hook->cbFunc;
        timerCbTable[hook->unit].userPtr = hook->userPtr;
        break;

    case efm32_irq_type_letimer:
        letimerCbTable[hook->unit].cbFunc = hook->cbFunc;
        letimerCbTable[hook->unit].userPtr = hook->userPtr;
        break;

    case efm32_irq_type_gpio:
        gpioCbTable[hook->unit].cbFunc = hook->cbFunc;
        gpioCbTable[hook->unit].userPtr = hook->userPtr;
        break;

    case efm32_irq_type_acmp:
        acmpCbTable[hook->unit].cbFunc = hook->cbFunc;
        acmpCbTable[hook->unit].userPtr = hook->userPtr;
        break;
#if defined(USART_PRESENT)
    case efm32_irq_type_usart:
        usartCbTable[hook->unit].cbFunc = hook->cbFunc;
        usartCbTable[hook->unit].userPtr = hook->userPtr;
        break;
#endif
#if defined(LEUART_PRESENT)
    case efm32_irq_type_leuart:
        leuartCbTable[hook->unit].cbFunc = hook->cbFunc;
        leuartCbTable[hook->unit].userPtr = hook->userPtr;
        break;
#endif
#if defined(I2C_PRESENT)
    case efm32_irq_type_iic:
        iicCbTable[hook->unit].cbFunc = hook->cbFunc;
        iicCbTable[hook->unit].userPtr = hook->userPtr;
        break;
#endif
    default:
        break;
    }

    hdl_debug("Hook Registered: type: %s, unit: %x, cbFunc: %x, userPtr: %x\n", \
        hook->type, hook->unit, hook->cbFunc, hook->userPtr);
}

/***************************************************************************//**
 * @}
 ******************************************************************************/