/*
 * Copyright (c) 2006-2022, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date         Author      Notes
 * 2010-12-21   onelife     Initial creation for EFM32
 * 2011-05-06   onelife     Add EFM32 development kit and SPI Flash support
 * 2011-07-12   onelife     Add SWO output enable function
 * 2011-12-08   onelife     Add giant gecko development kit support
 * 2011-12-09   onelife     Add giant gecko support
 * 2011-12-09   onelife     Add LEUART module support
 * 2011-12-14   onelife     Add LFXO enabling routine in driver initialization function
 * 2011-12-15   onelife     Add MicroSD initialization routine in driver
 *                          initialization function
 * 2011-12-29   onelife     Add keys and joystick initialization routine in
 *                          driver initialization function
 * 2012-02-15   onelife     Modify SWO setup function to support giant gecko
 * 2012-xx-xx   onelife     Modify system clock and ticket related code
 */

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

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

/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
#define IS_NVIC_VECTTAB(VECTTAB)        (((VECTTAB) == RAM_MEM_BASE) || \
                                        ((VECTTAB) == FLASH_MEM_BASE))
#define IS_NVIC_OFFSET(OFFSET)          ((OFFSET) < 0x000FFFFF)

/***************************************************************************//**
 * @addtogroup SysTick_clock_source
 * @{
 ******************************************************************************/
#define SysTick_CLKSource_MASK          ((rt_uint32_t)0x00000004)
#define SysTick_CLKSource_RTC           ((rt_uint32_t)0x00000000)
#define SysTick_CLKSource_HFCORECLK     ((rt_uint32_t)0x00000004)
#define IS_SYSTICK_CLK_SOURCE(SOURCE)   (((SOURCE) == SysTick_CLKSource_RTC) || \
                                        ((SOURCE) == SysTick_CLKSource_HFCORECLK))
/***************************************************************************//**
 * @}
 ******************************************************************************/

/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/
/***************************************************************************//**
 * @brief
 *   Set the allocation and offset of the vector table
 *
 * @details
 *
 * @note
 *
 * @param[in] NVIC_VectTab
 *   Indicate the vector table is allocated in RAM or ROM
 *
 * @param[in] Offset
 *   The vector table offset
 ******************************************************************************/
static void NVIC_SetVectorTable(
    rt_uint32_t NVIC_VectTab,
    rt_uint32_t Offset)
{
    /* Check the parameters */
    RT_ASSERT(IS_NVIC_VECTTAB(NVIC_VectTab));
    RT_ASSERT(IS_NVIC_OFFSET(Offset));

    SCB->VTOR = NVIC_VectTab | (Offset & (rt_uint32_t)0x1FFFFF80);
}

/***************************************************************************//**
 * @brief
 *   Configure the address of vector table
 *
 * @details
 *
 * @note
 *
 ******************************************************************************/
static void NVIC_Configuration(void)
{
#ifdef  VECT_TAB_RAM
    /* Set the vector table allocated at 0x20000000 */
    NVIC_SetVectorTable(RAM_MEM_BASE, 0x0);
#else  /* VECT_TAB_FLASH  */
    /* Set the vector table allocated at 0x00000000 */
    NVIC_SetVectorTable(FLASH_MEM_BASE, 0x0);
#endif

    /* Set NVIC Preemption Priority Bits: 0 bit for pre-emption, 4 bits for
       subpriority */
    NVIC_SetPriorityGrouping(0x7UL);

    /* Set Base Priority Mask Register */
    __set_BASEPRI(EFM32_BASE_PRI_DEFAULT);
}

/***************************************************************************//**
 * @brief
 *   Configure the SysTick clock source
 *
 * @details
 *
 * @note
 *
 * @param[in] SysTick_CLKSource
 *   Specifies the SysTick clock source.
 *
 * @arg SysTick_CLKSource_HCLK_Div8
 *   AHB clock divided by 8 selected as SysTick clock source.
 *
 * @arg SysTick_CLKSource_HCLK
 *   AHB clock selected as SysTick clock source.
 ******************************************************************************/
static void SysTick_CLKSourceConfig(rt_uint32_t SysTick_CLKSource)
{
    /* Check the parameters */
    RT_ASSERT(IS_SYSTICK_CLK_SOURCE(SysTick_CLKSource));

    rt_uint32_t ctrl = SysTick->CTRL;

    ctrl &= ~SysTick_CLKSource_MASK;
    ctrl |= SysTick_CLKSource;

    SysTick->CTRL = ctrl;
}

/***************************************************************************//**
 * @brief
 *   Configure the SysTick for OS tick.
 *
 * @details
 *
 * @note
 *
 ******************************************************************************/
static void  SysTick_Configuration(void)
{
#if defined(EFM32_USING_LFXO)
    /* LETIMER0 configurations */
    const LETIMER_Init_TypeDef letimerInit =
    {
        .enable         = true,                 /* Start counting when init completed. */
        .debugRun       = false,                /* Counter shall not keep running during debug halt. */
        .rtcComp0Enable = false,                /* Don't start counting on RTC COMP0 match. */
        .rtcComp1Enable = false,                /* Don't start counting on RTC COMP1 match. */
        .comp0Top       = true,                 /* Load COMP0 register into CNT when counter underflows. COMP is used as TOP */
        .bufTop         = false,                /* Don't load COMP1 into COMP0 when REP0 reaches 0. */
        .out0Pol        = 0,                    /* Idle value for output 0. */
        .out1Pol        = 0,                    /* Idle value for output 1. */
        .ufoa0          = letimerUFOANone,      /* No output on output 0. */
        .ufoa1          = letimerUFOANone,      /* No output on output 1. */
        .repMode        = letimerRepeatFree     /* Count until stopped by SW. */
    };

    CMU_ClockDivSet(cmuClock_LETIMER0, cmuClkDiv_8);
    CMU_ClockEnable(cmuClock_LETIMER0, true);
    LETIMER_CompareSet(LETIMER0, 0,
        EFM32_LETIMER_TOP_100HZ * RT_TICK_PER_SECOND / 100);

    /* Enable underflow interrupt */
    LETIMER_IntClear(LETIMER0, LETIMER_IF_UF);
    LETIMER_IntEnable(LETIMER0, LETIMER_IF_UF);
    /* Enable LETIMER0 interrupt vector in NVIC */
    NVIC_ClearPendingIRQ(LETIMER0_IRQn);
    NVIC_SetPriority(LETIMER0_IRQn, EFM32_IRQ_PRI_DEFAULT);
    NVIC_EnableIRQ(LETIMER0_IRQn);

    /* Start LETIMER0 */
    LETIMER_Init(LETIMER0, &letimerInit);
#else
    rt_uint32_t     coreClk;
    rt_uint32_t     cnts;

    coreClk = SystemCoreClockGet();
    cnts = coreClk / RT_TICK_PER_SECOND;

    SysTick_Config(cnts);
    SysTick_CLKSourceConfig(SysTick_CLKSource_HFCORECLK);
#endif
}

/***************************************************************************//**
 * @brief
 *   Enable SWO.
 *
 * @details
 *
 * @note
 *
 ******************************************************************************/
void Swo_Configuration(void)
{
    rt_uint32_t *dwt_ctrl = (rt_uint32_t *) 0xE0001000;
    rt_uint32_t *tpiu_prescaler = (rt_uint32_t *) 0xE0040010;
    rt_uint32_t *tpiu_protocol = (rt_uint32_t *) 0xE00400F0;

    CMU->HFPERCLKEN0 |= CMU_HFPERCLKEN0_GPIO;
    /* Enable Serial wire output pin */
    GPIO->ROUTE |= GPIO_ROUTE_SWOPEN;
#if defined(_EFM32_GIANT_FAMILY)
    /* Set location 0 */
    GPIO->ROUTE = (GPIO->ROUTE & ~(_GPIO_ROUTE_SWLOCATION_MASK)) | GPIO_ROUTE_SWLOCATION_LOC0;

    /* Enable output on pin - GPIO Port F, Pin 2 */
    GPIO->P[5].MODEL &= ~(_GPIO_P_MODEL_MODE2_MASK);
    GPIO->P[5].MODEL |= GPIO_P_MODEL_MODE2_PUSHPULL;
    #else
    /* Set location 1 */
    GPIO->ROUTE = (GPIO->ROUTE & ~(_GPIO_ROUTE_SWLOCATION_MASK)) | GPIO_ROUTE_SWLOCATION_LOC1;
    /* Enable output on pin */
    GPIO->P[2].MODEH &= ~(_GPIO_P_MODEH_MODE15_MASK);
    GPIO->P[2].MODEH |= GPIO_P_MODEH_MODE15_PUSHPULL;
#endif
    /* Enable debug clock AUXHFRCO */
    CMU->OSCENCMD = CMU_OSCENCMD_AUXHFRCOEN;

    while(!(CMU->STATUS & CMU_STATUS_AUXHFRCORDY));

    /* Enable trace in core debug */
    CoreDebug->DHCSR |= 1;
    CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;

    /* Enable PC and IRQ sampling output */
    *dwt_ctrl = 0x400113FF;
    /* Set TPIU prescaler to 16. */
    *tpiu_prescaler = 0xf;
    /* Set protocol to NRZ */
    *tpiu_protocol = 2;
    /* Unlock ITM and output data */
    ITM->LAR = 0xC5ACCE55;
    ITM->TCR = 0x10009;
}

/***************************************************************************//**
 * @brief
 *   Initialize the board.
 *
 * @details
 *
 * @note
 *
 ******************************************************************************/
void rt_hw_board_init(void)
{
    /* Chip errata */
    CHIP_Init();

    /* Initialize DVK board register access */
#if defined(EFM32_GXXX_DK)
    DVK_init();
#elif defined(EFM32GG_DK3750)
    DVK_init(DVK_Init_EBI);

    /* Disable all DVK interrupts */
    DVK_disableInterrupt(BC_INTEN_MASK);
    DVK_clearInterruptFlags(BC_INTFLAG_MASK);
#endif

    /* config NVIC Configuration */
    NVIC_Configuration();

#if defined(EFM32_USING_HFXO)
    /* Configure external oscillator */
    SystemHFXOClockSet(EFM32_HFXO_FREQUENCY);

    /* Switching the CPU clock source to HFXO */
    CMU_ClockSelectSet(cmuClock_HF, cmuSelect_HFXO);

    /* Turning off the high frequency RC Oscillator (HFRCO) */
    CMU_OscillatorEnable(cmuOsc_HFRCO, false, false);
#endif

#if defined(EFM32_USING_LFXO)
    CMU_ClockSelectSet(cmuClock_LFA,cmuSelect_LFXO);
    CMU_ClockSelectSet(cmuClock_LFB, cmuSelect_LFXO);
#endif

#if defined(EFM32_SWO_ENABLE)
    /* Enable SWO */
    Swo_Configuration();
#endif

    /* Enable high frequency peripheral clock */
    CMU_ClockEnable(cmuClock_HFPER, true);
    /* Enabling clock to the interface of the low energy modules */
    CMU_ClockEnable(cmuClock_CORELE, true);
    /* Enable GPIO clock */
    CMU_ClockEnable(cmuClock_GPIO, true);

    /* Configure the SysTick */
    SysTick_Configuration();
}

/***************************************************************************//**
 * @brief
 *   Initialize the hardware drivers.
 *
 * @details
 *
 * @note
 *
 ******************************************************************************/
void rt_hw_driver_init(void)
{
    /* Initialize DMA */
    rt_hw_dma_init();

    /* Select LFXO for specified module (and wait for it to stabilize) */
#if (!defined(EFM32_USING_LFXO) && defined(RT_USING_RTC))
#error "Low frequency clock source is needed for using RTC"
#endif

#if (!defined(EFM32_USING_LFXO )&& \
    (defined(RT_USING_LEUART0) || defined(RT_USING_LEUART1)))
#error "Low frequency clock source is needed for using LEUART"
#endif

    /* Initialize USART */
#if (defined(RT_USING_USART0) || defined(RT_USING_USART1) || \
    defined(RT_USING_USART2) || defined(RT_USING_UART0) || \
    defined(RT_USING_UART1))
    rt_hw_usart_init();
#endif

    /* Initialize LEUART */
#if (defined(RT_USING_LEUART0) || defined(RT_USING_LEUART1))
    rt_hw_leuart_init();
#endif

    /* Setup Console */
#if defined(EFM32_GXXX_DK)
    DVK_enablePeripheral(DVK_RS232A);
    DVK_enablePeripheral(DVK_SPI);
#elif defined(EFM32GG_DK3750)
 #if (RT_CONSOLE_DEVICE == EFM_UART1)
    DVK_enablePeripheral(DVK_RS232_UART);
 #elif (RT_CONSOLE_DEVICE == EFM_LEUART1)
    DVK_enablePeripheral(DVK_RS232_LEUART);
 #endif
#endif
    rt_console_set_device(CONSOLE_DEVICE);

    /* Initialize Timer */
#if (defined(RT_USING_TIMER0) || defined(RT_USING_TIMER1) || defined(RT_USING_TIMER2))
    rt_hw_timer_init();
#endif

    /* Initialize ADC */
#if defined(RT_USING_ADC0)
    rt_hw_adc_init();
#endif

    /* Initialize ACMP */
#if (defined(RT_USING_ACMP0) || defined(RT_USING_ACMP1))
    rt_hw_acmp_init();
#endif

    /* Initialize IIC */
#if (defined(RT_USING_IIC0) || defined(RT_USING_IIC1))
    rt_hw_iic_init();
#endif

    /* Initialize RTC */
#if defined(RT_USING_RTC)
    rt_hw_rtc_init();
#endif

    /* Enable SPI access to MicroSD card */
#if defined(EFM32_USING_SPISD)
 #if defined(EFM32_GXXX_DK)
    DVK_writeRegister(BC_SPI_CFG, 1);
 #elif defined(EFM32GG_DK3750)
    DVK_enablePeripheral(DVK_MICROSD);
 #endif
#endif

    /* Enable SPI access to Ethernet */
#if defined(EFM32_USING_ETHERNET)
 #if defined(EFM32GG_DK3750)
    DVK_enablePeripheral(DVK_ETH);
 #endif
#endif

    /* Initialize LCD */
#if defined(EFM32_USING_LCD)
    efm32_spiLcd_init();
#endif

    /* Initialize Keys */
#if defined(EFM32_USING_KEYS)
 #if defined(EFM32GG_DK3750)
    efm32_hw_keys_init();
 #endif
#endif

}

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