/*
 * Copyright 2017-2020 NXP
 * All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include "fsl_rdc.h"

/******************************************************************************
 * Definitions
 *****************************************************************************/

/* Component ID definition, used by tools. */
#ifndef FSL_COMPONENT_ID
#define FSL_COMPONENT_ID "platform.drivers.rdc"
#endif

typedef union
{
    rdc_domain_assignment_t _mda;
    uint32_t _u32;
} rdc_mda_reg_t;

typedef union
{
    rdc_hardware_config_t _vir;
    uint32_t _u32;
} rdc_vir_reg_t;

/*******************************************************************************
 * Prototypes
 ******************************************************************************/

/*!
 * @brief Get instance number for RDC module.
 *
 * @param base RDC peripheral base address.
 */
uint32_t RDC_GetInstance(RDC_Type *base);

/*******************************************************************************
 * Variables
 ******************************************************************************/

/*! @brief Pointers to rdc bases for each instance. */
static RDC_Type *const s_rdcBases[] = RDC_BASE_PTRS;

#if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
/*! @brief Pointers to rdc clocks for each instance. */
static const clock_ip_name_t s_rdcClocks[] = RDC_CLOCKS;
#endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */

/******************************************************************************
 * CODE
 *****************************************************************************/

uint32_t RDC_GetInstance(RDC_Type *base)
{
    uint32_t instance;

    /* Find the instance index from base address mappings. */
    for (instance = 0; instance < ARRAY_SIZE(s_rdcBases); instance++)
    {
        if (s_rdcBases[instance] == base)
        {
            break;
        }
    }

    assert(instance < ARRAY_SIZE(s_rdcBases));

    return instance;
}

/*!
 * brief Initializes the RDC module.
 *
 * This function enables the RDC clock.
 *
 * param base RDC peripheral base address.
 */
void RDC_Init(RDC_Type *base)
{
#if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
    CLOCK_EnableClock(s_rdcClocks[RDC_GetInstance(base)]);
#endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
}

/*!
 * brief De-initializes the RDC module.
 *
 * This function disables the RDC clock.
 *
 * param base RDC peripheral base address.
 */
void RDC_Deinit(RDC_Type *base)
{
#if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
    CLOCK_DisableClock(s_rdcClocks[RDC_GetInstance(base)]);
#endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
}

/*!
 * brief Gets the RDC hardware configuration.
 *
 * This function gets the RDC hardware configurations, including number of bus
 * masters, number of domains, number of memory regions and number of peripherals.
 *
 * param base RDC peripheral base address.
 * param config Pointer to the structure to get the configuration.
 */
void RDC_GetHardwareConfig(RDC_Type *base, rdc_hardware_config_t *config)
{
    assert(NULL != config);

    rdc_vir_reg_t vir;
    vir._u32 = base->VIR;

    *config = vir._vir;
}

/*!
 * brief Set master domain assignment
 *
 * param base RDC peripheral base address.
 * param master Which master to set.
 * param domainAssignment Pointer to the assignment.
 */
void RDC_SetMasterDomainAssignment(RDC_Type *base, rdc_master_t master, const rdc_domain_assignment_t *domainAssignment)
{
    assert((uint32_t)master < RDC_MDA_COUNT);

    rdc_mda_reg_t mda;

    mda._mda = *domainAssignment;

    base->MDA[master] = mda._u32;
}

/*!
 * brief Get default master domain assignment
 *
 * The default configuration is:
 * code
   assignment->domainId = 0U;
   assignment->lock = 0U;
   endcode
 *
 * param domainAssignment Pointer to the assignment.
 */
void RDC_GetDefaultMasterDomainAssignment(rdc_domain_assignment_t *domainAssignment)
{
    assert(NULL != domainAssignment);

    rdc_mda_reg_t mda;
    mda._u32 = 0U;

    *domainAssignment = mda._mda;
}

/*!
 * brief Set peripheral access policy.
 *
 * param base RDC peripheral base address.
 * param config Pointer to the policy configuration.
 */
void RDC_SetPeriphAccessConfig(RDC_Type *base, const rdc_periph_access_config_t *config)
{
    assert((uint32_t)config->periph < RDC_PDAP_COUNT);

    uint32_t periph  = (uint32_t)config->periph;
    uint32_t regPDAP = config->policy;

    if (config->lock)
    {
        regPDAP |= RDC_PDAP_LCK_MASK;
    }

    if (config->enableSema)
    {
        regPDAP |= RDC_PDAP_SREQ_MASK;
    }

    base->PDAP[periph] = regPDAP;

    __DSB();
}

/*!
 * brief Get default peripheral access policy.
 *
 * The default configuration is:
 * code
    config->lock = false;
    config->enableSema = false;
    config->policy = RDC_ACCESS_POLICY(0, kRDC_ReadWrite) |
                     RDC_ACCESS_POLICY(1, kRDC_ReadWrite) |
                     RDC_ACCESS_POLICY(2, kRDC_ReadWrite) |
                     RDC_ACCESS_POLICY(3, kRDC_ReadWrite);
   endcode
 *
 * param config Pointer to the policy configuration.
 */
void RDC_GetDefaultPeriphAccessConfig(rdc_periph_access_config_t *config)
{
    assert(NULL != config);

    /* Initializes the configure structure to zero. */
    (void)memset(config, 0, sizeof(*config));

    config->lock       = false;
    config->enableSema = false;
    config->policy     = RDC_ACCESS_POLICY(0U, kRDC_ReadWrite) | RDC_ACCESS_POLICY(1U, kRDC_ReadWrite) |
                     RDC_ACCESS_POLICY(2U, kRDC_ReadWrite) | RDC_ACCESS_POLICY(3U, kRDC_ReadWrite);
}

/*!
 * brief Set memory region access policy.
 *
 * Note that when setting the baseAddress and endAddress in p config,
 * should be aligned to the region resolution, see rdc_mem_t
 * definitions.
 *
 * param base RDC peripheral base address.
 * param config Pointer to the policy configuration.
 */
void RDC_SetMemAccessConfig(RDC_Type *base, const rdc_mem_access_config_t *config)
{
    assert((uint32_t)config->mem < RDC_MRC_COUNT);

    uint32_t mem = (uint32_t)config->mem;
    /* The configuration is enabled by default. */
    uint32_t regMRC = config->policy | RDC_MRC_ENA_MASK;

    if (config->lock)
    {
        regMRC |= RDC_MRC_LCK_MASK;
    }

#if (defined(FSL_FEATURE_RDC_MEM_REGION_ADDR_SHIFT) && FSL_FEATURE_RDC_MEM_REGION_ADDR_SHIFT)
    base->MR[mem].MRSA = (uint32_t)(config->baseAddress >> (uint32_t)FSL_FEATURE_RDC_MEM_REGION_ADDR_SHIFT);
    base->MR[mem].MREA = (uint32_t)(config->endAddress >> (uint32_t)FSL_FEATURE_RDC_MEM_REGION_ADDR_SHIFT);
#else
    base->MR[mem].MRSA = (uint32_t)config->baseAddress;
    base->MR[mem].MREA = (uint32_t)config->endAddress;
#endif
    base->MR[mem].MRC = regMRC;

    __DSB();
}

/*!
 * brief Get default memory region access policy.
 *
 * The default configuration is:
 * code
    config->lock = false;
    config->baseAddress = 0;
    config->endAddress = 0;
    config->policy = RDC_ACCESS_POLICY(0, kRDC_ReadWrite) |
                     RDC_ACCESS_POLICY(1, kRDC_ReadWrite) |
                     RDC_ACCESS_POLICY(2, kRDC_ReadWrite) |
                     RDC_ACCESS_POLICY(3, kRDC_ReadWrite);
   endcode
 *
 * param config Pointer to the policy configuration.
 */
void RDC_GetDefaultMemAccessConfig(rdc_mem_access_config_t *config)
{
    assert(NULL != config);

    /* Initializes the configure structure to zero. */
    (void)memset(config, 0, sizeof(*config));

    config->lock        = false;
    config->baseAddress = 0;
    config->endAddress  = 0;
    config->policy      = RDC_ACCESS_POLICY(0U, kRDC_ReadWrite) | RDC_ACCESS_POLICY(1U, kRDC_ReadWrite) |
                     RDC_ACCESS_POLICY(2U, kRDC_ReadWrite) | RDC_ACCESS_POLICY(3U, kRDC_ReadWrite);
}

/*!
 * brief Get the memory region violation status.
 *
 * The first access violation is captured. Subsequent violations are ignored
 * until the status register is cleared. Contents are cleared upon reading the
 * register. Clearing of contents occurs only when the status is read by the
 * memory region's associated domain ID(s).
 *
 * param base RDC peripheral base address.
 * param mem Which memory region to get.
 * param status The returned status.
 */
void RDC_GetMemViolationStatus(RDC_Type *base, rdc_mem_t mem, rdc_mem_status_t *status)
{
    assert((uint32_t)mem < RDC_MRC_COUNT);

    uint32_t regMRVS = base->MR[mem].MRVS;

    status->hasViolation = ((regMRVS & RDC_MRVS_AD_MASK) != 0U);
    status->domainID     = (uint8_t)((regMRVS & RDC_MRVS_VDID_MASK) >> RDC_MRVS_VDID_SHIFT);
#if (defined(FSL_FEATURE_RDC_MEM_REGION_ADDR_SHIFT) && FSL_FEATURE_RDC_MEM_REGION_ADDR_SHIFT)
    regMRVS &= RDC_MRVS_VADR_MASK;
    status->address = ((uint64_t)regMRVS) << (uint32_t)FSL_FEATURE_RDC_MEM_REGION_ADDR_SHIFT;
#else
    regMRVS &= RDC_MRVS_VADR_MASK;
    status->address = (uint64_t)regMRVS;
#endif
}
