/*
 * Copyright (c) 2022, NVIDIA CORPORATION.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *  * Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  * 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.
 *  * Neither the name of NVIDIA CORPORATION 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 ``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 OWNER 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.
 */

#ifndef CPU__ARMV7_PMU_REGS_H
#define CPU__ARMV7_PMU_REGS_H
#define FSP__CPU__ARMV7_PMU_REGS_H               1

/* Compiler headers */
#include <stdint.h>

/* Early FSP headers */
#include <misc/ct-assert.h>

/* Hardware headers */

/* Late FSP headers */
#include <misc/nvrm_drf.h>
#include <misc/macros.h>          // for START_RFD_BLOCK, END_RFD_BLOCK, INLINE_RFD
#include <misc/bitops.h>

/* Module-specific FSP headers */
#include <cpu/armv7-regs.h>
#include <cpu/built-ins.h>

/*
 * Compile-time check for FSP header files
 *   Each FSP header file contains a signature unique to that file, and the
 *   FSP project.  The CT_ASSERT macro (contained in misc/ct-assert.h) can
 *   check for this signature.  If it does not exist, then the build will
 *   abort.
 *
 *   This is a trap for projects which have their own include files of the
 *   same names, but different contents.  This trap ensures that only the
 *   files from the FSP project, are built into the FSP source code.
 */
START_RFD_BLOCK(MISRA, DEVIATE, Rule_1_2, "Approval: Bug 200531996, DR: SWE-FSP-009-SWSADR.docx",
                MISRA, DEVIATE, Rule_2_3, "Approval:  On-file, DR: SWE-FSP-010-SWSADR.docx",
                MISRA, DEVIATE, Directive_4_9, "Approval: Bug 200531995, DR: SWE-FSP-012-SWSADR.docx")
CT_ASSERT(FSP__MISC__MACROS_H, "Header file missing or invalid.")
CT_ASSERT(FSP__MISC__NVRM_DRF_H, "Header file missing or invalid.")
CT_ASSERT(FSP__CPU__BUILT_INS_H, "Header file missing or invalid.")
END_RFD_BLOCK(MISRA, DEVIATE, Rule_2_3, "Approval:  On-file, DR: SWE-FSP-010-SWSADR.docx")

/**
 * @file armv7-pmu-regs.h
 * @brief Functions that provide access to PMU specific registers.
 */

/*
 * R5 PMU events
 */
#define ARMV7_PMU_SW_INCR               0x00
#define ARMV7_PMU_ICACHE_MISS           0x01
#define ARMV7_PMU_DCACHE_MISS           0x03
#define ARMV7_PMU_DCACHE_ACCESS         0x04
#define ARMV7_PMU_DATA_READ_EXEC        0x06
#define ARMV7_PMU_DATA_WRITE_EXEC       0x07
#define ARMV7_PMU_INSTR_EXEC            0x08
#define ARMV7_PMU_DUAL_ISSUE_EXEC       0x5E
#define ARMV7_PMU_EXCEPTION_TAKEN       0x09
#define ARMV7_PMU_EXCEPTION_RETURN      0x0A
#define ARMV7_PMU_CONTEXT_ID_CHANGED    0x0B
#define ARMV7_PMU_PC_CHANGED            0x0C
#define ARMV7_PMU_BRANCH_IMMEDIATE      0x0D
#define ARMV7_PMU_PROC_RETURN           0x0E
#define ARMV7_PMU_UNALIGNED_ACCESS      0x0F
#define ARMV7_PMU_BRANCH_NOT_PREDICTED  0x10
#define ARMV7_PMU_CYCLE_COUNT           0x11
#define ARMV7_PMU_BRANCH_PREDICTION     0x12
#define ARMV7_PMU_STALL_INST_BUFFER     0x40
#define ARMV7_PMU_STALL_DATA_DEPEND     0x41
#define ARMV7_PMU_DCACHE_WRITE_BACK     0x42
#define ARMV7_PMU_EXT_MEM_REQ           0x43
#define ARMV7_PMU_STALL_LSU_BUSY        0x44
#define ARMV7_PMU_STORE_BUFF_DRAIN      0x45
#define ARMV7_PMU_FIQ_DISABLED_CYCLES   0x46
#define ARMV7_PMU_IRQ_DISABLED_CYCLES   0x47
#define ARMV7_PMU_ETMEXTOUT0            0x48
#define ARMV7_PMU_ETMEXTOUT1            0x49
#define ARMV7_PMU_ICACHE_TAG_RAM_ERR    0x4A
#define ARMV7_PMU_ICACHE_DATA_RAM_ERR   0x4B
#define ARMV7_PMU_DCACHE_DIRTY_ERR      0x4C
#define ARMV7_PMU_DCACHE_DATA_RAM_ERR   0x4D
#define ARMV7_PMU_TCM_PREFETCH_ECC      0x4E
#define ARMV7_PMU_TCU_LDSTORE_ECC       0x4F
#define ARMV7_PMU_ST_BUFF_MERGE         0x50
#define ARMV7_PMU_LSU_STALL_FULL_ST_BUFF        0x51
#define ARMV7_PMU_LSU_STALL_FULL_ST_Q   0x52
#define ARMV7_PMU_IDIV_EXEC             0x53
#define ARMV7_PMU_IDIV_STALL            0x54
#define ARMV7_PMU_PLD_LINEFILL          0x55
#define ARMV7_PMU_PLD_LINEFILL_SHORTAGE 0x56
#define ARMV7_PMU_NON_CACHE_AXI         0x57
#define ARMV7_PMU_ICACHE_ACCESS         0x58
#define ARMV7_PMU_ST_BUFF_TWO_SLOTS     0x59
#define ARMV7_PMU_DUAL_ISSUE_A          0x5A
#define ARMV7_PMU_DUAL_ISSUE_B1_B2_F2_F2D       0x5B
#define ARMV7_PMU_DUAL_ISSUE_OTHER      0x5C
#define ARMV7_PMU_DOUBLE_FLOAT_INST     0x5D
#define ARMV7_PMU_DCACHE_DATA_ECC       0x60
#define ARMV7_PMU_DACHE_TAG_ECC         0x61
#define ARMV7_PMU_LIVELOCK              0x62
#define ARMV7_PMU_ATCM_MULTI_BIT_ECC    0x64
#define ARMV7_PMU_B0TCM_MULTI_BIT_ECC   0x65
#define ARMV7_PMU_B1TCM_MULTI_BIT_ECC   0x66
#define ARMV7_PMU_ATCM_SINGLE_BIT_ECC   0x67
#define ARMV7_PMU_B0TCM_SINGLE_BIT_ECC  0x68
#define ARMV7_PMU_B1TCM_SINGLE_BIT_ECC  0x69
#define ARMV7_PMU_TCM_CORR_ECC_LD_ST    0x6A
#define ARMV7_PMU_TCM_CORR_ECC_PREFETCH 0x6B
#define ARMV7_PMU_TCM_FATAL_ECC_AXI     0x6C
#define ARMV7_PMU_TCM_CORR_ECC_AXI      0x6D
#define ARMV7_PMU_CORRECTABLE_EVENTS    0x6E
#define ARMV7_PMU_FATAL_EVENTS          0x6F
#define ARMV7_PMU_CORR_BUS_FAULTS       0x70
#define ARMV7_PMU_FATAL_BUS_FAULTS      0x71
#define ARMV7_PMU_DCACHE_ACCESSES       0x72
#define ARMV7_PMU_DCACHE_INVALIDATE     0x73
#define ARMV7_PMU_CYCLE_COUNT2          0xFF

/*****************************************************
 * Macros for accessing fields in the PMCR register
 *****************************************************/
/*
 * Get the Implementer code from the PMCR
 */
#define ARMV7_PMCR_GET_IMP(pmcr)        (EXTRACT(pmcr, 31, 24))

/*
 * Get the ID code from the PMCR
 */
#define ARMV7_PMCR_GET_IDCODE(pmcr)     (EXTRACT(pmcr, 23, 16))

/*
 * Get the number of PMU counters implemented
 */
#define ARMV7_PMCR_GET_N(pmcr)          (EXTRACT(pmcr, 15, 11))

#define ARMV7_PMCR_DP   BIT(5)          // disable PMCCNTR
#define ARMV7_PMCR_X    BIT(4)          // enable export of events to event bus
#define ARMV7_PMCR_D    BIT(3)          // count ever 64th processor cycle
#define ARMV7_PMCR_C    BIT(2)          // cycle counter reset
#define ARMV7_PMCR_P    BIT(1)          // event counter reset
#define ARMV7_PMCR_E    BIT(0)          // enable all counters


/*****************************************************
 * Macros for accessing fields in the following registers
 *      PMCNTENSET
 *      PMCNTENCLR
 *      PMOVSR
 *      PMSWINC
 *      PMINTENSET
 *      PMINTENCLR
 *****************************************************/
#define ARMV7_PMCNT_C   BIT(31)         // cycle counter
#define ARMV7_PMCNT_P2  BIT(2)          // counter 2 enable
#define ARMV7_PMCNT_P1  BIT(1)          // counter 1 enable
#define ARMV7_PMCNT_P0  BIT(0)          // counter 0 enable


/*****************************************************
 * Macros for accessing fields in the PMSELR register
 *****************************************************/
#define ARMV7_PMSEL_GET(selr)   (EXTRACT(selr, 4, 0))
#define ARMV7_PMSEL_MASK        MASK(4, 0)


/*****************************************************
 * Macros for accessing fields in the PMXEVTYPER registers
 *****************************************************/
#define ARMV7_EVENT_SEL_GET(xevselr)    (EXTRACT(xevselr, 7, 0))
#define ARMV7_EVENT_SEL_SET(xevselr, sel)       (INSERT(xevselr, 7, 0, sel)


/*****************************************************
 * Macros for accessing fields in the PMUSERENR register
 *****************************************************/
#define ARMV7_USER_EN   BIT(0)          // enable PMU access from USER mode

/**
 * @brief Read Performance Monitor Control Register
 *
 * This function returns the current contents of the PMCR.
 *
 * Refer to section 6.3.1 of the ARM Cortex R5 TRM (version 1.2) for details
 * of this register.
 *
 * @return uint32_t     value of the PMCR
 */
static inline uint32_t
rd_PMCR(void)
{
    uint32_t    reg;

    __asm__ __volatile__("mrc p15, 0, %0, c9, c12, 0\n\t" : "=r" (reg));

    return reg;
}

/**
 * @brief Write Performance Monitor Control Register
 *
 * This function writes new contents to the PMCR.
 *
 * Refer to section 6.3.1 of the ARM Cortex R5 TRM (version 1.2) for details
 * of this register.
 *
 * @param [in] reg      value to be written to the PMCR.
 *
 * @return none
 */
static inline void
wr_PMCR(uint32_t reg)
{
    __asm__ __volatile__("mcr p15, 0, %0, c9, c12, 0\n\t" : : "r" (reg));
}

/**
 * @brief Read Count Enable Set Register
 *
 * This function returns the current contents of the PMCNTENSET register.
 *
 * Refer to section 6.3.2 of the ARM Cortex R5 TRM (version 1.2) for details
 * of this register.
 *
 * @return uint32_t     value of the PMCNTENSET register
 */
static inline uint32_t
rd_PMCNTENSET(void)
{
    uint32_t    reg;

    __asm__ __volatile__("mrc p15, 0, %0, c9, c12, 1\n\t" : "=r" (reg));

    return reg;
}

/**
 * @brief Write Count Enable Set Register
 *
 * This function writes a value into the PMCNTENSET register.
 *
 * Refer to section 6.3.2 of the ARM Cortex R5 TRM (version 1.2) for details
 * of this register.
 *
 * @param [in] reg      value to be written into the PMCNTENSET register
 *
 * @return none
 */
static inline void
wr_PMCNTENSET(uint32_t reg)
{
    __asm__ __volatile__("mcr p15, 0, %0, c9, c12, 1\n\t" : : "r" (reg));
}

/**
 * @brief Read Count Enable Clear Register
 *
 * This function reads the current value of the PMCNTENCLR register.
 *
 * Refer to section 6.3.3 of the ARM Cortex R5 TRM (version 1.2) for details
 * of this register.
 *
 * @return uint32_t     value of the PMCNTENCLR register
 */
static inline uint32_t
rd_PMCNTENCLR(void)
{
    uint32_t    reg;

    __asm__ __volatile__("mrc p15, 0, %0, c9, c12, 2\n\t" : "=r" (reg));

    return reg;
}

/**
 * @brief Write Count Enable Clear Register
 *
 * This function writes a value into the PMCNTENCLR register.
 *
 * Refer to section 6.3.3 of the ARM Cortex R5 TRM (version 1.2) for details
 * of this register.
 *
 * @param [in] reg      value to be written into the PMCNTENSET register
 *
 * @return none
 */
static inline void
wr_PMCNTENCLR(uint32_t reg)
{
    __asm__ __volatile__("mcr p15, 0, %0, c9, c12, 2\n\t" : : "r" (reg));
}

/**
 * @brief Read Overflow Flag Status Register
 *
 * This function reads the current value of the PMOVSR register.
 *
 * Refer to section 6.3.4 of the ARM Cortex R5 TRM (version 1.2) for details
 * of this register.
 *
 * @return uint32_t     value of the PMOVSR register
 */
static inline uint32_t
rd_PMOVSR(void)
{
    uint32_t    reg;

    __asm__ __volatile__("mrc p15, 0, %0, c9, c12, 3\n\t" : "=r" (reg));

    return reg;
}

/**
 * @brief Write Overflow Flag Status Register
 *
 * This function writes a value into the PMOVSR register.
 *
 * Refer to section 6.3.4 of the ARM Cortex R5 TRM (version 1.2) for details
 * of this register.
 *
 * @param [in] reg      value to be written into the PMOVSR register
 *
 * @return none
 */
static inline void
wr_PMOVSR(uint32_t reg)
{
    __asm__ __volatile__("mcr p15, 0, %0, c9, c12, 3\n\t" : : "r" (reg));
}

/**
 * @brief Write Software Increment Register
 *
 * This function writes a value into the PMSWINC register.
 *
 * Refer to section 6.3.5 of the ARM Cortex R5 TRM (version 1.2) for details
 * of this register.
 *
 * @param [in] reg      value to be written into the PMSWINC register
 *
 * @return none
 */
static inline void
wr_PMSWINC(uint32_t reg)
{
    __asm__ __volatile__("mcr p15, 0, %0, c9, c12, 4\n\t" : : "r" (reg));
}

/**
 * @brief Read Performance Counter Selection Register
 *
 * This function reads the current value of the PMSELR register.
 *
 * Refer to section 6.3.6 of the ARM Cortex R5 TRM (version 1.2) for details
 * of this register.
 *
 * @return uint32_t     value of the PMSELR register
 */
static inline uint32_t
rd_PMSELR(void)
{
    uint32_t    reg;

    __asm__ __volatile__("mrc p15, 0, %0, c9, c12, 5\n\t" : "=r" (reg));

    return reg;
}

/**
 * @brief Write Performance Counter Selection Register
 *
 * This function writes a value into the PMSELR register.
 *
 * Refer to section 6.3.6 of the ARM Cortex R5 TRM (version 1.2) for details
 * of this register.
 *
 * @param [in] reg      value to be written into the PMSELR register
 *
 * @return none
 */
static inline void
wr_PMSELR(uint32_t reg)
{
    __asm__ __volatile__("mcr p15, 0, %0, c9, c12, 5\n\t" : : "r" (reg));
}

/**
 * @brief Read Cycle Count Register
 *
 * This function reads the current value of the PMCCNTR register.
 *
 * Refer to section 6.3.7 of the ARM Cortex R5 TRM (version 1.2) for details
 * of this register.
 *
 * @return uint32_t     value of the PMCCNTR register
 */
static inline uint32_t
rd_PMCCNTR(void)
{
    uint32_t    reg;

    __asm__ __volatile__("mrc p15, 0, %0, c9, c13, 0\n\t" : "=r" (reg));

    return reg;
}

/**
 * @brief Write Cycle Count Register
 *
 * This function writes a value into the PMCCNTR register.
 *
 * Refer to section 6.3.7 of the ARM Cortex R5 TRM (version 1.2) for details
 * of this register.
 *
 * @param [in] reg      value to be written into the PMCCNTR register
 *
 * @return none
 */
static inline void
wr_PMCCNTR(uint32_t reg)
{
    __asm__ __volatile__("mcr p15, 0, %0, c9, c13, 0\n\t" : : "r" (reg));
}

/**
 * @brief Read Event Type Selection Register
 *
 * This function reads the current value of the PMXEVTYPER register.
 *
 * Refer to section 6.3.8 of the ARM Cortex R5 TRM (version 1.2) for details
 * of this register.
 *
 * @return uint32_t     value of the PMXEVTYPER register
 */
static inline uint32_t
rd_PMXEVTYPER(void)
{
    uint32_t    reg;

    __asm__ __volatile__("mrc p15, 0, %0, c9, c13, 1\n\t" : "=r" (reg));

    return reg;
}

/**
 * @brief Write Event Type Selection Register
 *
 * This function writes a value into the PMXEVTYPER register.
 *
 * Refer to section 6.3.8 of the ARM Cortex R5 TRM (version 1.2) for details
 * of this register.
 *
 * @param [in] reg      value to be written into the PMXEVTYPER register
 *
 * @return none
 */
static inline void
wr_PMXEVTYPER(uint32_t reg)
{
    __asm__ __volatile__("mcr p15, 0, %0, c9, c13, 1\n\t" : : "r" (reg));
}

/**
 * @brief Read Event Count Register
 *
 * This function reads the current value of the PMXEVCNTR register.
 *
 * Refer to section 6.3.9 of the ARM Cortex R5 TRM (version 1.2) for details
 * of this register.
 *
 * @return uint32_t     value of the PMXEVCNTR register
 */
static inline uint32_t
rd_PMXEVCNTR(void)
{
    uint32_t    reg;

    __asm__ __volatile__("mrc p15, 0, %0, c9, c13, 2\n\t" : "=r" (reg));

    return reg;
}

/**
 * @brief Write Event Count Register
 *
 * This function writes a value into the PMXEVCNTR register.
 *
 * Refer to section 6.3.9 of the ARM Cortex R5 TRM (version 1.2) for details
 * of this register.
 *
 * @param [in] reg      value to be written into the PMXEVCNTR register
 *
 * @return none
 */
static inline void
wr_PMXEVCNTR(uint32_t reg)
{
    __asm__ __volatile__("mcr p15, 0, %0, c9, c13, 2\n\t" : : "r" (reg));
}

/**
 * @brief Read User Enable Register
 *
 * This function reads the current value of the PMUSERENR register.
 *
 * Refer to section 6.3.10 of the ARM Cortex R5 TRM (version 1.2) for details
 * of this register.
 *
 * @return uint32_t     value of the PMUSERENR register
 */
static inline uint32_t
rd_PMUSERENR(void)
{
    uint32_t    reg;

    __asm__ __volatile__("mrc p15, 0, %0, c9, c14, 0\n\t" : "=r" (reg));

    return reg;
}

/**
 * @brief Write User Enable Register
 *
 * This function writes a value into the PMUSERENR register.
 *
 * Refer to section 6.3.10 of the ARM Cortex R5 TRM (version 1.2) for details
 * of this register.
 *
 * @param [in] reg      value to be written into the PMUSERENR register
 *
 * @return none
 */
static inline void
wr_PMUSERENR(uint32_t reg)
{
    __asm__ __volatile__("mcr p15, 0, %0, c9, c14, 0\n\t" : : "r" (reg));
}

/**
 * @brief Read Interrupt Enable Set Register
 *
 * This function reads the current value of the PMINTENSET register.
 *
 * Refer to section 6.3.11 of the ARM Cortex R5 TRM (version 1.2) for details
 * of this register.
 *
 * @return uint32_t     value of the PMINTENSET register
 */
static inline uint32_t
rd_PMINTENSET(void)
{
    uint32_t    reg;

    __asm__ __volatile__("mrc p15, 0, %0, c9, c14, 1\n\t" : "=r" (reg));

    return reg;
}

/**
 * @brief Write Interrupt Enable Set Register
 *
 * This function writes a value into the PMINTENSET register.
 *
 * Refer to section 6.3.11 of the ARM Cortex R5 TRM (version 1.2) for details
 * of this register.
 *
 * @param [in] reg      value to be written into the PMINTENSET register
 *
 * @return none
 */
static inline void
wr_PMINTENSET(uint32_t reg)
{
    __asm__ __volatile__("mcr p15, 0, %0, c9, c14, 1\n\t" : : "r" (reg));
}

/**
 * @brief Read Interrupt Enable Clear Register
 *
 * This function reads the current value of the PMINTENCLR register.
 *
 * Refer to section 6.3.12 of the ARM Cortex R5 TRM (version 1.2) for details
 * of this register.
 *
 * @return uint32_t     value of the PMINTENCLR register
 */
static inline uint32_t
rd_PMINTENCLR(void)
{
    uint32_t    reg;

    __asm__ __volatile__("mrc p15, 0, %0, c9, c14, 2\n\t" : "=r" (reg));

    return reg;
}

/**
 * @brief Write Interrupt Enable Clear Register
 *
 * This function writes a value into the PMINTENCLR register.
 *
 * Refer to section 6.3.12 of the ARM Cortex R5 TRM (version 1.2) for details
 * of this register.
 *
 * @param [in] reg      value to be written into the PMINTENCLR register
 *
 * @return none
 */
static inline void
wr_PMINTENCLR(uint32_t reg)
{
    __asm__ __volatile__("mcr p15, 0, %0, c9, c14, 2\n\t" : : "r" (reg));
}

/**
 * @brief Set up an event counter
 *
 * This function will set up an event counter so that it can count
 * the specified event.
 *
 * @param [in] cntr_num         number of the event counter to be set up
 * @param [in] event            ID of the event that is to be counted
 *
 * @return none
 */
static inline void
pmu_set_event_counter(uint32_t cntr_num, uint32_t event)
{
    wr_PMSELR(cntr_num);
    wr_PMXEVTYPER(event);
}

/**
 * @brief Read an event counter
 *
 * This function will read the contents of the specified event counter.
 *
 * @param [in] cntr_num         number of the event counter that is to be read
 *
 * @return uint32_t             value contained in the indicated counter
 */
static inline uint32_t
pmu_read_counter(uint32_t cntr_num)
{
    wr_PMSELR(cntr_num);

    return rd_PMXEVCNTR();
}

/**
 * @brief Clear an event counter
 *
 * This function will clear (set to 0) the specified event counter.
 *
 * @param [in] cntr_num         number of the event counter that is to be
 *                              cleared
 *
 * @return none
 */
static inline void
pmu_clear_counter(uint32_t cntr_num)
{
    wr_PMSELR(cntr_num);
    wr_PMXEVCNTR(0);
}

#endif
