/* _NVRM_COPYRIGHT_BEGIN_
 *
 * Copyright 2021 by NVIDIA Corporation.  All rights reserved.  All
 * information contained herein is proprietary and confidential to NVIDIA
 * Corporation.  Any use, reproduction, or disclosure without the written
 * permission of NVIDIA Corporation is prohibited.
 *
 * _NVRM_COPYRIGHT_END_
 */

#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>

#include <error/common-errors.h>

#include <fbdma/memutils.h>
#include <cpu/io_local.h>

//
// MISRA 4.9 RFD: function-like macros
// MISRA 15.1 RFD: Forward gotos
// MISRA 8.7 RFD: Functions with external linkage only referenced in one translation unit
//
START_RFD_BLOCK(MISRA, DEVIATE, Directive_4_9, "Approval: Bug 200531995, DR: SWE-FSP-012-SWSADR.docx",
                MISRA, DEVIATE, Rule_15_1, "Approval: Bug 3176459, DR: SWE-FSP-028-SWSADR.docx",
                MISRA, DEVIATE, Rule_8_7, "Approval: Bug 200531999, DR: SWE-FSP-015-SWSADR.docx")

#if NVRISCV_HAS_FBIF
#include <fbdma/fbif.h>
#endif // NVRISCV_HAS_FBIF


/*!
 * @brief Checks whether an address is contained within a memory region.
 *
 * @param[in]    checkAddr      The address to check.
 * @param[in]    regionStart    The start address of the memory region.
 * @param[in]    regionEnd      The end address of the memory region.
 *
 * @return true if the check address lies within the memory region.
 *         false otherwise.
 *
 * @note The byte at regionEnd is not included in the memory region.
 */
static inline bool
s_addrInRange
(
    uintptr_t checkAddr,
    uintptr_t regionStart,
    uintptr_t regionEnd
)
{
    return ((checkAddr >= regionStart) && (checkAddr < regionEnd));
}

#if NVRISCV_HAS_FBIF
error_t
riscvPaToFbifAperture
(
    uintptr_t pa,
    FBIF_TRANSCFG_TARGET *pTarget,
    uint64_t *pOffset
)
{
    RISCV_MEM_TARGET memTarget;
    error_t status;

    // Obtain the memory target and offset for the provided physical address.
    status = riscvPaToTargetOffset(pa, &memTarget, pOffset);
    if(status != E_SUCCESS)
    {
        goto out;
    }

    // Translate the memory target to its corresponding FBIF target.
    switch (memTarget)
    {
        case RISCV_MEM_TARGET_FBGPA:
            *pTarget = FBIF_TRANSCFG_TARGET_LOCAL_FB;
            break;

        case RISCV_MEM_TARGET_SYSGPA:
            //
            // Just assume coherent here as this value is meant to be
            // passed to dmaPa() anyway.
            //
            *pTarget = FBIF_TRANSCFG_TARGET_COHERENT_SYSTEM;
            break;

        default:
            status = E_INVALID_PARAM;
            break;
    }

out:
    return status;
}
#endif // NVRISCV_HAS_FBIF

error_t
riscvPaToTargetOffset
(
    uintptr_t pa,
    RISCV_MEM_TARGET *pTarget,
    uint64_t *pOffset
)
{
    error_t status = E_SUCCESS;
    RISCV_MEM_TARGET target;
    uint64_t offset;

    // Determine which memory region pa resides in.
    if (s_addrInRange(pa, NV_RISCV_AMAP_IMEM_START, NV_RISCV_AMAP_IMEM_END))
    {
        target = RISCV_MEM_TARGET_IMEM;
        offset = pa - NV_RISCV_AMAP_IMEM_START;
    }
    else if (s_addrInRange(pa, NV_RISCV_AMAP_DMEM_START, NV_RISCV_AMAP_DMEM_END))
    {
        target = RISCV_MEM_TARGET_DMEM;
        offset = pa - NV_RISCV_AMAP_DMEM_START;
    }
    else if (s_addrInRange(pa, NV_RISCV_AMAP_FBGPA_START, NV_RISCV_AMAP_FBGPA_END))
    {
        target = RISCV_MEM_TARGET_FBGPA;
        offset = pa - NV_RISCV_AMAP_FBGPA_START;
    }
#ifdef NV_RISCV_AMAP_SYSGPA_START
    else if (s_addrInRange(pa, NV_RISCV_AMAP_SYSGPA_START, NV_RISCV_AMAP_SYSGPA_END))
    {
        target = RISCV_MEM_TARGET_SYSGPA;
        offset = pa - NV_RISCV_AMAP_SYSGPA_START;
    }
#endif // NV_RISCV_AMAP_SYSGPA_START
    else
    {
        // Not a supported memory region.
        status = E_INVALID_PARAM;
        goto out;
    }

    // Return target/offset information as requested.
    if (pTarget != NULL)
    {
        *pTarget = target;
    }

    if (pOffset != NULL)
    {
        *pOffset = offset;
    }

out:
    return status;
}

END_RFD_BLOCK(MISRA, DEVIATE, Directive_4_9, "Approval: Bug 200531995, DR: SWE-FSP-012-SWSADR.docx",
                MISRA, DEVIATE, Rule_15_1, "Approval: Bug 3176459, DR: SWE-FSP-028-SWSADR.docx",
                MISRA, DEVIATE, Rule_8_7, "Approval: Bug 200531999, DR: SWE-FSP-015-SWSADR.docx")
