/*
 * Copyright (c) 2021, Shenzhen Academy of Aerospace Technology
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2021-11-16     Dystopia     the first version
 */

#include "common.h"

CSL_BootcfgRegs * gp_bootcfg_regs = (CSL_BootcfgRegs *)CSL_BOOT_CFG_REGS;
CSL_CgemRegs * gp_cgem_regs = (CSL_CgemRegs *)CSL_CGEM0_5_REG_BASE_ADDRESS_REGS;
CSL_TmrPlusRegs * gp_timer_regs[9] = {
    (CSL_TmrPlusRegs *)CSL_TIMER_0_REGS,
    (CSL_TmrPlusRegs *)CSL_TIMER_1_REGS,
    (CSL_TmrPlusRegs *)CSL_TIMER_2_REGS,
    (CSL_TmrPlusRegs *)CSL_TIMER_3_REGS,
    (CSL_TmrPlusRegs *)CSL_TIMER_4_REGS,
    (CSL_TmrPlusRegs *)CSL_TIMER_5_REGS,
    (CSL_TmrPlusRegs *)CSL_TIMER_6_REGS,
    (CSL_TmrPlusRegs *)CSL_TIMER_7_REGS,
    (CSL_TmrPlusRegs *)(CSL_TIMER_7_REGS+(CSL_TIMER_7_REGS-CSL_TIMER_6_REGS))
};

void cpu_interrupt_init(void)
{
    //clear interrupt and excpetion events
    ICR = IFR;
    ECR = EFR;
    IER = 3;     //disable all interrupts

    /* disable event combine */
    gp_cgem_regs->EVTMASK[0] = 0xffffffff;
    gp_cgem_regs->EVTMASK[1] = 0xffffffff;
    gp_cgem_regs->EVTMASK[2] = 0xffffffff;
    gp_cgem_regs->EVTMASK[3] = 0xffffffff;

     /*Clear all CPU events*/
    gp_cgem_regs->EVTCLR[0] = 0xFFFFFFFF;
    gp_cgem_regs->EVTCLR[1] = 0xFFFFFFFF;
    gp_cgem_regs->EVTCLR[2] = 0xFFFFFFFF;
    gp_cgem_regs->EVTCLR[3] = 0xFFFFFFFF;

    /*Interrupt Service Table Pointer to begining of LL2 memory*/
    ISTP = 0x800000;
}

void keystone_cpu_init(void)
{
    /* clear all interrupt flag/status, setup ISTP to begining of LL2 */
    cpu_interrupt_init();
}

/*===============================Timer=================================*/
void reset_timer(int timer_num)
{
    if(gp_timer_regs[timer_num]->TGCR)
    {
        gp_timer_regs[timer_num]->TGCR = 0;
        gp_timer_regs[timer_num]->TCR= 0;
    }
}

void timer64_init(Timer64_Config * tmrCfg)
{
    reset_timer(tmrCfg->timer_num);

    gp_timer_regs[tmrCfg->timer_num]->CNTLO = 0;
    gp_timer_regs[tmrCfg->timer_num]->CNTHI = 0;

    /*please note, in clock mode, two timer periods generate a clock,
    one timer period output high voltage level, the other timer period
    output low voltage level, so, the timer period should be half to the
    desired output clock period*/
    if(TIMER_PERIODIC_CLOCK == tmrCfg->timerMode)
    {
        tmrCfg->period = tmrCfg->period/2;
    }

    /*the value written into period register is the expected value minus one*/
    gp_timer_regs[tmrCfg->timer_num]->PRDLO = _loll(tmrCfg->period-1);
    gp_timer_regs[tmrCfg->timer_num]->PRDHI = _hill(tmrCfg->period-1);
    if(tmrCfg->reload_period>1)
    {
        gp_timer_regs[tmrCfg->timer_num]->RELLO = _loll(tmrCfg->reload_period-1);
        gp_timer_regs[tmrCfg->timer_num]->RELHI = _hill(tmrCfg->reload_period-1);
    }

    if(TIMER_WATCH_DOG == tmrCfg->timerMode)
    {
        gp_timer_regs[tmrCfg->timer_num]->TGCR =
            /*Select watch-dog mode*/
            (CSL_TMR_TIMMODE_WDT << CSL_TMR_TGCR_TIMMODE_SHIFT)
            /*Remove the timer from reset*/
            | (CSL_TMR_TGCR_TIMLORS_MASK)
            | (CSL_TMR_TGCR_TIMHIRS_MASK);
    }
    else if(TIMER_PERIODIC_WAVE == tmrCfg->timerMode)
    {
        gp_timer_regs[tmrCfg->timer_num]->TGCR = TMR_TGCR_PLUSEN_MASK
            /*for plus featuers, dual 32-bit unchained timer mode should be used*/
            | (CSL_TMR_TIMMODE_DUAL_UNCHAINED << CSL_TMR_TGCR_TIMMODE_SHIFT)
            /*Remove the timer from reset*/
            | (CSL_TMR_TGCR_TIMLORS_MASK);

        //in plus mode, interrupt/event must be enabled manually
        gp_timer_regs[tmrCfg->timer_num]->INTCTL_STAT= TMR_INTCTLSTAT_EN_ALL_CLR_ALL;
    }
    else
    {
        gp_timer_regs[tmrCfg->timer_num]->TGCR =
            /*Select 64-bit general timer mode*/
            (CSL_TMR_TIMMODE_GPT << CSL_TMR_TGCR_TIMMODE_SHIFT)
            /*Remove the timer from reset*/
            | (CSL_TMR_TGCR_TIMLORS_MASK)
            | (CSL_TMR_TGCR_TIMHIRS_MASK);
    }

    /*make timer stop with emulation*/
    gp_timer_regs[tmrCfg->timer_num]->EMUMGT_CLKSPD = (gp_timer_regs[tmrCfg->timer_num]->EMUMGT_CLKSPD&
        ~(CSL_TMR_EMUMGT_CLKSPD_FREE_MASK|CSL_TMR_EMUMGT_CLKSPD_SOFT_MASK));

    if(TIMER_WATCH_DOG == tmrCfg->timerMode)
    {
        /*enable watchdog timer*/
        gp_timer_regs[tmrCfg->timer_num]->WDTCR = CSL_TMR_WDTCR_WDEN_MASK
            | (CSL_TMR_WDTCR_WDKEY_CMD1 << CSL_TMR_WDTCR_WDKEY_SHIFT);

        gp_timer_regs[tmrCfg->timer_num]->TCR =
            (CSL_TMR_CLOCK_INP_NOGATE << CSL_TMR_TCR_TIEN_LO_SHIFT)
            | (CSL_TMR_CLKSRC_INTERNAL << CSL_TMR_TCR_CLKSRC_LO_SHIFT)
            /*The timer is enabled continuously*/
            | (CSL_TMR_ENAMODE_CONT << CSL_TMR_TCR_ENAMODE_LO_SHIFT)
            | ((tmrCfg->pulseWidth << CSL_TMR_TCR_PWID_LO_SHIFT)&CSL_TMR_TCR_PWID_LO_MASK)
            /*select pulse mode*/
            | (CSL_TMR_CP_PULSE << CSL_TMR_TCR_CP_LO_SHIFT)
            | (CSL_TMR_INVINP_UNINVERTED << CSL_TMR_TCR_INVINP_LO_SHIFT)
            | (CSL_TMR_INVOUTP_UNINVERTED << CSL_TMR_TCR_INVOUTP_LO_SHIFT)
            | (0 << CSL_TMR_TCR_TSTAT_LO_SHIFT);

        /*active watchdog timer*/
        gp_timer_regs[tmrCfg->timer_num]->WDTCR = CSL_TMR_WDTCR_WDEN_MASK
            | (CSL_TMR_WDTCR_WDKEY_CMD2 << CSL_TMR_WDTCR_WDKEY_SHIFT);
    }
    else if(TIMER_ONE_SHOT_PULSE == tmrCfg->timerMode)
    {
        gp_timer_regs[tmrCfg->timer_num]->TCR =
            (CSL_TMR_CLOCK_INP_NOGATE << CSL_TMR_TCR_TIEN_LO_SHIFT)
            | (CSL_TMR_CLKSRC_INTERNAL << CSL_TMR_TCR_CLKSRC_LO_SHIFT)
            /*The timer is enabled one-shot*/
            | (CSL_TMR_ENAMODE_ENABLE << CSL_TMR_TCR_ENAMODE_LO_SHIFT)
            | ((tmrCfg->pulseWidth << CSL_TMR_TCR_PWID_LO_SHIFT)&CSL_TMR_TCR_PWID_LO_MASK)
            /*select pulse mode*/
            | (CSL_TMR_CP_PULSE << CSL_TMR_TCR_CP_LO_SHIFT)
            | (CSL_TMR_INVINP_UNINVERTED << CSL_TMR_TCR_INVINP_LO_SHIFT)
            | (CSL_TMR_INVOUTP_UNINVERTED << CSL_TMR_TCR_INVOUTP_LO_SHIFT)
            | (0 << CSL_TMR_TCR_TSTAT_LO_SHIFT);
    }
    else if(TIMER_PERIODIC_CLOCK == tmrCfg->timerMode)
    {
        gp_timer_regs[tmrCfg->timer_num]->TCR =
            (CSL_TMR_CLOCK_INP_NOGATE << CSL_TMR_TCR_TIEN_LO_SHIFT)
            | (CSL_TMR_CLKSRC_INTERNAL << CSL_TMR_TCR_CLKSRC_LO_SHIFT)
            /*The timer is enabled continuously*/
            | (CSL_TMR_ENAMODE_CONT << CSL_TMR_TCR_ENAMODE_LO_SHIFT)
            | ((tmrCfg->pulseWidth << CSL_TMR_TCR_PWID_LO_SHIFT)&CSL_TMR_TCR_PWID_LO_MASK)
            /*select clock mode*/
            | (CSL_TMR_CP_CLOCK << CSL_TMR_TCR_CP_LO_SHIFT)
            | (CSL_TMR_INVINP_UNINVERTED << CSL_TMR_TCR_INVINP_LO_SHIFT)
            | (CSL_TMR_INVOUTP_UNINVERTED << CSL_TMR_TCR_INVOUTP_LO_SHIFT)
            | (0 << CSL_TMR_TCR_TSTAT_LO_SHIFT);
    }
    else if(TIMER_PERIODIC_WAVE == tmrCfg->timerMode)
    {
        gp_timer_regs[tmrCfg->timer_num]->TCR =
            (CSL_TMR_CLOCK_INP_NOGATE << CSL_TMR_TCR_TIEN_LO_SHIFT)
            | (CSL_TMR_CLKSRC_INTERNAL << CSL_TMR_TCR_CLKSRC_LO_SHIFT)
            /*The timer is enabled continuously with period reload*/
            | (CSL_TMR_ENAMODE_CONT_RELOAD << CSL_TMR_TCR_ENAMODE_LO_SHIFT)
            | ((tmrCfg->pulseWidth << CSL_TMR_TCR_PWID_LO_SHIFT)&CSL_TMR_TCR_PWID_LO_MASK)
            /*select clock mode*/
            | (CSL_TMR_CP_CLOCK << CSL_TMR_TCR_CP_LO_SHIFT)
            | (CSL_TMR_INVINP_UNINVERTED << CSL_TMR_TCR_INVINP_LO_SHIFT)
            | (CSL_TMR_INVOUTP_UNINVERTED << CSL_TMR_TCR_INVOUTP_LO_SHIFT)
            | (0 << CSL_TMR_TCR_TSTAT_LO_SHIFT);
    }
    else     /*TIMER_PERIODIC_PULSE*/
    {
        gp_timer_regs[tmrCfg->timer_num]->TCR =
            (CSL_TMR_CLOCK_INP_NOGATE << CSL_TMR_TCR_TIEN_LO_SHIFT)
            | (CSL_TMR_CLKSRC_INTERNAL << CSL_TMR_TCR_CLKSRC_LO_SHIFT)
            /*The timer is enabled continuously*/
            | (CSL_TMR_ENAMODE_CONT << CSL_TMR_TCR_ENAMODE_LO_SHIFT)
            | ((tmrCfg->pulseWidth << CSL_TMR_TCR_PWID_LO_SHIFT)&CSL_TMR_TCR_PWID_LO_MASK)
            /*select clock mode*/
            | (CSL_TMR_CP_PULSE << CSL_TMR_TCR_CP_LO_SHIFT)
            | (CSL_TMR_INVINP_UNINVERTED << CSL_TMR_TCR_INVINP_LO_SHIFT)
            | (CSL_TMR_INVOUTP_UNINVERTED << CSL_TMR_TCR_INVOUTP_LO_SHIFT)
            | (0 << CSL_TMR_TCR_TSTAT_LO_SHIFT);
    }
}