/*
 * Copyright (c) 2013-2019, Huawei Technologies Co., Ltd. All rights reserved.
 * Copyright (c) 2020, Huawei Device Co., Ltd. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this list of
 *    conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice, this list
 *    of conditions and the following disclaimer in the documentation and/or other materials
 *    provided with the distribution.
 *
 * 3. Neither the name of the copyright holder nor the names of its contributors may be used
 *    to endorse or promote products derived from this software without specific prior written
 *    permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 * CONTRIBUTORS 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.
 */

#include "los_hw_pri.h"
#include "los_tick_pri.h"
#include "los_sys_pri.h"
#include "gic_common.h"

#define STRING_COMB(x, y, z)        x ## y ## z

#ifdef LOSCFG_ARCH_SECURE_MONITOR_MODE
#define TIMER_REG(reg)              STRING_COMB(TIMER_REG_, CNTPS, reg)
#else
#define TIMER_REG(reg)              STRING_COMB(TIMER_REG_, CNTP, reg)
#endif
#define TIMER_REG_CTL               TIMER_REG(_CTL)     /* 32 bits */
#define TIMER_REG_TVAL              TIMER_REG(_TVAL)    /* 32 bits */
#define TIMER_REG_CVAL              TIMER_REG(_CVAL)    /* 64 bits */
#define TIMER_REG_CT                TIMER_REG(CT)       /* 64 bits */

#ifdef __LP64__

#define TIMER_REG_CNTFRQ            cntfrq_el0

/* CNTP AArch64 registers */
#define TIMER_REG_CNTP_CTL          cntp_ctl_el0
#define TIMER_REG_CNTP_TVAL         cntp_tval_el0
#define TIMER_REG_CNTP_CVAL         cntp_cval_el0
#define TIMER_REG_CNTPCT            cntpct_el0

/* CNTPS AArch64 registers */
#define TIMER_REG_CNTPS_CTL         cntps_ctl_el1
#define TIMER_REG_CNTPS_TVAL        cntps_tval_el1
#define TIMER_REG_CNTPS_CVAL        cntps_cval_el1
#define TIMER_REG_CNTPSCT           cntpct_el0

#define READ_TIMER_REG32(reg)       AARCH64_SYSREG_READ(reg)
#define READ_TIMER_REG64(reg)       AARCH64_SYSREG_READ(reg)
#define WRITE_TIMER_REG32(reg, val) AARCH64_SYSREG_WRITE(reg, (UINT64)(val))
#define WRITE_TIMER_REG64(reg, val) AARCH64_SYSREG_WRITE(reg, val)

#else /* Aarch32 */

#define TIMER_REG_CNTFRQ            CP15_REG(c14, 0, c0, 0)

/* CNTP AArch32 registers */
#define TIMER_REG_CNTP_CTL          CP15_REG(c14, 0, c2, 1)
#define TIMER_REG_CNTP_TVAL         CP15_REG(c14, 0, c2, 0)
#define TIMER_REG_CNTP_CVAL         CP15_REG64(c14, 2)
#define TIMER_REG_CNTPCT            CP15_REG64(c14, 0)

/* CNTPS AArch32 registers are banked and accessed though CNTP */
#define CNTPS CNTP

#define READ_TIMER_REG32(reg)       ARM_SYSREG_READ(reg)
#define READ_TIMER_REG64(reg)       ARM_SYSREG64_READ(reg)
#define WRITE_TIMER_REG32(reg, val) ARM_SYSREG_WRITE(reg, val)
#define WRITE_TIMER_REG64(reg, val) ARM_SYSREG64_WRITE(reg, val)

#endif

#define OS_CYCLE_PER_TICK (g_sysClock / LOSCFG_BASE_CORE_TICK_PER_SECOND)

UINT32 HalClockFreqRead(VOID)
{
    return READ_TIMER_REG32(TIMER_REG_CNTFRQ);
}

VOID HalClockFreqWrite(UINT32 freq)
{
    WRITE_TIMER_REG32(TIMER_REG_CNTFRQ, freq);
}

STATIC_INLINE VOID TimerCtlWrite(UINT32 cntpCtl)
{
    WRITE_TIMER_REG32(TIMER_REG_CTL, cntpCtl);
}

STATIC_INLINE UINT64 TimerCvalRead(VOID)
{
    return READ_TIMER_REG64(TIMER_REG_CVAL);
}

STATIC_INLINE VOID TimerCvalWrite(UINT64 cval)
{
    WRITE_TIMER_REG64(TIMER_REG_CVAL, cval);
}

STATIC_INLINE VOID TimerTvalWrite(UINT32 tval)
{
    WRITE_TIMER_REG32(TIMER_REG_TVAL, tval);
}

UINT64 HalClockGetCycles(VOID)
{
    UINT64 cntpct;

    cntpct = READ_TIMER_REG64(TIMER_REG_CT);
    return cntpct;
}

LITE_OS_SEC_TEXT VOID OsTickEntry(VOID)
{
    TimerCtlWrite(0);

    OsTickHandler();

    /*
     * use last cval to generate the next tick's timing is
     * absolute and accurate. DO NOT use tval to drive the
     * generic time in which case tick will be slower.
     */
    TimerCvalWrite(TimerCvalRead() + OS_CYCLE_PER_TICK);
    TimerCtlWrite(1);
}

LITE_OS_SEC_TEXT_INIT VOID HalClockInit(VOID)
{
    UINT32 ret;

    g_sysClock = HalClockFreqRead();
    ret = LOS_HwiCreate(OS_TICK_INT_NUM, MIN_INTERRUPT_PRIORITY, 0, OsTickEntry, 0);
    if (ret != LOS_OK) {
        PRINT_ERR("%s, %d create tick irq failed, ret:0x%x\n", __FUNCTION__, __LINE__, ret);
    }
}

LITE_OS_SEC_TEXT_INIT VOID HalClockStart(VOID)
{
    HalIrqUnmask(OS_TICK_INT_NUM);

    /* triggle the first tick */
    TimerCtlWrite(0);
    TimerTvalWrite(OS_CYCLE_PER_TICK);
    TimerCtlWrite(1);
}

VOID HalDelayUs(UINT32 usecs)
{
    UINT64 cycles = (UINT64)usecs * HalClockFreqRead() / OS_SYS_US_PER_SECOND;
    UINT64 deadline = HalClockGetCycles() + cycles;

    while (HalClockGetCycles() < deadline) {
        __asm__ volatile ("nop");
    }
}

UINT64 hi_sched_clock(VOID)
{
    return LOS_CurrNanosec();
}

UINT32 HalClockGetTickTimerCycles(VOID)
{
    UINT64 cval = TimerCvalRead();
    UINT64 cycles = HalClockGetCycles();

    return (UINT32)((cval > cycles) ? (cval - cycles) : 0);
}

VOID HalClockTickTimerReload(UINT32 cycles)
{
    HalIrqMask(OS_TICK_INT_NUM);
    HalIrqClear(OS_TICK_INT_NUM);

    TimerCtlWrite(0);
    TimerCvalWrite(HalClockGetCycles() + cycles);
    TimerCtlWrite(1);

    HalIrqUnmask(OS_TICK_INT_NUM);
}
