/*******************************************************************************
* DISCLAIMER
* This software is supplied by Renesas Electronics Corporation and is only
* intended for use with Renesas products. No other uses are authorized. This
* software is owned by Renesas Electronics Corporation and is protected under
* all applicable laws, including copyright laws.
* THIS SOFTWARE IS PROVIDED "AS IS" AND RENESAS MAKES NO WARRANTIES REGARDING
* THIS SOFTWARE, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING BUT NOT
* LIMITED TO WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
* AND NON-INFRINGEMENT. ALL SUCH WARRANTIES ARE EXPRESSLY DISCLAIMED.
* TO THE MAXIMUM EXTENT PERMITTED NOT PROHIBITED BY LAW, NEITHER RENESAS
* ELECTRONICS CORPORATION NOR ANY OF ITS AFFILIATED COMPANIES SHALL BE LIABLE
* FOR ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES FOR
* ANY REASON RELATED TO THIS SOFTWARE, EVEN IF RENESAS OR ITS AFFILIATES HAVE
* BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
* Renesas reserves the right, without notice, to make changes to this software
* and to discontinue the availability of this software. By using this software,
* you agree to the additional terms and conditions found by accessing the
* following link:
* http://www.renesas.com/disclaimer
* Copyright 2018-2023 Renesas Electronics Corporation All rights reserved.
*******************************************************************************/


/*******************************************************************************
 * DESCRIPTION   : generic timer
 ******************************************************************************/
/******************************************************************************
 * @file          generic_timer.c
 * - Version      : 0.08
 * @brief
 * .
 *****************************************************************************/
/******************************************************************************
 * History : DD.MM.YYYY Version  Description
 *         : 19.01.2022 0.01     First Release
 *         : 17.02.2022 0.02     Support AArch32
 *         : 09.05.2022 0.03     Supports argument check of micro_wait()
 *                               Moved the definition of the define value
 *                               Removed __ARM_ARCH_8R__ and __ARM_ARCH_8A__
 *                               Change macro to inline function
 *                               Added initial settings
 *                               Remove unnecessary casts
 *                               Change the value of RCAR_CNTC_EXTAL
 *         : 16.06.2022 0.04     Change the value of RCAR_CNTC_EXTAL
 *         : 16.06.2022 0.05     Change log output
 *         : 31.10.2022 0.06     License notation change.
 *         : 04.04.2023 0.07     Removed stdio.h.
 *         : 21.08.2023 0.08     Add support for V4M.
 *****************************************************************************/

#include <stdint.h>
#include <mem_io.h>
#include <timer.h>
#include <log.h>

#if (RCAR_LSI == RCAR_S4)
#define RCAR_CNTC_EXTAL (16666666U) /* 16.666666MHz */
#elif ((RCAR_LSI == RCAR_V4H) || (RCAR_LSI == RCAR_V4M))
#define RCAR_CNTC_EXTAL (16666600U) /* 16.666600MHz */
#endif /* RCAR_LSI == RCAR_S4 */
#define CNTFID_OFF          (0x0020U)
#define CNTCR_OFF           (0x0000U)
#define CNTCR_EN            ((1U) << 0U)
#define RCAR_CNTC_BASE      (0xE6080000U)
#define RCAR_CONV_MICROSEC  (1000000U)
#define RCAR_MAX_WAITTIME   (10000000U)
#define RCAR_MIN_WAITTIME   (0U)

#ifdef __aarch64__
static inline uint64_t get_cntfrq(void)
{
    uint64_t    freq;
    __asm__ volatile ("mrs %0, cntfrq_el0" : "=r" (freq));

    return(freq);
}

static inline void set_cntfrq(uint64_t reg_cntfid)
{
    __asm__ volatile ("msr cntfrq_el0, %0" :: "r" (reg_cntfid));
}

static inline uint64_t get_cntpct(void)
{
    uint64_t    base_count;
    __asm__ volatile ("mrs %0, cntpct_el0" : "=r" (base_count));

    return(base_count);
}

#elif __arm__
static inline uint32_t get_cntfrq(void)
{
    uint32_t    freq;
    __asm__ volatile("mrc p15, 0, %0, c14, c0, 0" : "=r" (freq));

    return(freq);
}

static inline void set_cntfrq(uint32_t reg_cntfid)
{
    __asm__ volatile("mcr p15, 0, %0, c14, c0, 0" : : "r" (reg_cntfid));
}

static inline uint64_t get_cntpct(void)
{
    uint64_t    base_count;
    __asm__ volatile ("mrrc p15, 0, %Q0, %R0, c14" : "=r" (base_count));

    return(base_count);
}
#endif /* __aarch64__ */

void generic_timer_init(void)
{
    /* Update memory mapped and register based freqency */
    /* AArch64:cntfrq_el0 */
    /* AArch32:cntfrq */
    set_cntfrq(RCAR_CNTC_EXTAL);

    mem_write32(RCAR_CNTC_BASE + CNTFID_OFF, RCAR_CNTC_EXTAL);

    /* Enable counter */
    mem_bitset32(RCAR_CNTC_BASE + CNTCR_OFF, CNTCR_EN);
}
/* End of function generic_timer_init(void) */

void micro_wait(uint64_t micro_sec)
{
    uint64_t base_count = 0U;
    uint64_t get_count  = 0U;
    uint64_t wait_time  = 0U;
#ifdef __aarch64__
    uint64_t freq       = 0U;
#elif __arm__
    uint32_t freq       = 0U;
#endif /* __aarch64__ */


    if((micro_sec > RCAR_MIN_WAITTIME) && (micro_sec <= RCAR_MAX_WAITTIME))
    {
        /* AArch64:cntfrq_el0 */
        /* AArch32:cntfrq */
        freq = get_cntfrq();
        /* AArch64:cntpct_el0 */
        /* AArch32:cntpct */
        base_count = get_cntpct();

        micro_sec *= freq;
        while (micro_sec > wait_time)
        {
            /* cntpct */
            get_count = get_cntpct();

            /* INT30-C Pre confirmation */
            if (get_count < base_count)
            {
                ERROR("micro_wait(Timer value error!!).\n");
                panic;
            }
            else
            {
                wait_time = ((get_count - base_count) * RCAR_CONV_MICROSEC);
            }
        }
    }
    else
    {
        ERROR("micro_wait(wait time)\n");
        ERROR("wait time = 0x%x\n", (unsigned int)micro_sec);
        panic;
    }

}
/* End of function micro_wait(uint64_t micro_sec) */

