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

#include "fsl_xspi.h"

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

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

#define FREQ_1MHz        (1000000UL)
#define XSPI_LUT_KEY_VAL (0x5AF05AF0UL)


/* XSPI FSM status. */
enum
{
    kXSPI_TransactionIsGrantedXSPIsBusy = 0x0U, /*!< Transaction is granted, but XSPI is busy
                                                  with any previous DMA transaction is ongoing*/
    kXSPI_TbdrLockIsOpen           = 0x1U,      /*!< TBDR lock is open. IPS master can write in TBDR.*/
    kXSPI_WriteTransferIsTriggered = 0x2U,      /*!< Write transfer is triggered. SEQID is written to XSPI.*/
    kXSPI_ReadTransferIsTriggered  = 0x3U,      /*!< Read transfer is triggered. SEQID is written to XSPI.*/
};

/* XSPI transfer state, _xspi_transfer_state. */
typedef enum _xspi_transfer_state
{
    kXSPI_StateIdle      = 0x0U, /*!< Transfer is done. */
    kXSPI_StateBusyWrite = 0x1U, /*!< XSPI is busy write transfer. */
    kXSPI_StateBusyRead  = 0x2U, /*!< XSPI is busy write transfer. */
    kXSPI_StateStartIpAccess,
    kXSPI_StateIpAccessGranted,
} xspi_transfer_state_t;

/*! brief Typedef for interrupt handler. */
typedef void (*xspi_isr_t)(XSPI_Type *base, xspi_handle_t *handle);

#define XSPI_TG_MDAD_REG_OFFSET_ARRAY                                                   \
    {                                                                                   \
        (uint32_t) offsetof(XSPI_Type, TG0MDAD), (uint32_t)offsetof(XSPI_Type, TG1MDAD) \
    }

#define XSPI_FRAD_WORD0_REG_OFFSET_ARRAY                                                            \
    {                                                                                               \
        (uint32_t) offsetof(XSPI_Type, FRAD0_WORD0), (uint32_t)offsetof(XSPI_Type, FRAD1_WORD0),    \
            (uint32_t)offsetof(XSPI_Type, FRAD2_WORD0), (uint32_t)offsetof(XSPI_Type, FRAD3_WORD0), \
            (uint32_t)offsetof(XSPI_Type, FRAD4_WORD0), (uint32_t)offsetof(XSPI_Type, FRAD5_WORD0), \
            (uint32_t)offsetof(XSPI_Type, FRAD6_WORD0), (uint32_t)offsetof(XSPI_Type, FRAD7_WORD0), \
    }

#define XSPI_FRAD_WORD1_REG_OFFSET_ARRAY                                                            \
    {                                                                                               \
        (uint32_t) offsetof(XSPI_Type, FRAD0_WORD1), (uint32_t)offsetof(XSPI_Type, FRAD1_WORD1),    \
            (uint32_t)offsetof(XSPI_Type, FRAD2_WORD1), (uint32_t)offsetof(XSPI_Type, FRAD3_WORD1), \
            (uint32_t)offsetof(XSPI_Type, FRAD4_WORD1), (uint32_t)offsetof(XSPI_Type, FRAD5_WORD1), \
            (uint32_t)offsetof(XSPI_Type, FRAD6_WORD1), (uint32_t)offsetof(XSPI_Type, FRAD7_WORD1), \
    }

#define XSPI_FRAD_WORD2_REG_OFFSET_ARRAY                                                            \
    {                                                                                               \
        (uint32_t) offsetof(XSPI_Type, FRAD0_WORD2), (uint32_t)offsetof(XSPI_Type, FRAD1_WORD2),    \
            (uint32_t)offsetof(XSPI_Type, FRAD2_WORD2), (uint32_t)offsetof(XSPI_Type, FRAD3_WORD2), \
            (uint32_t)offsetof(XSPI_Type, FRAD4_WORD2), (uint32_t)offsetof(XSPI_Type, FRAD5_WORD2), \
            (uint32_t)offsetof(XSPI_Type, FRAD6_WORD2), (uint32_t)offsetof(XSPI_Type, FRAD7_WORD2), \
    }

#define XSPI_FRAD_WORD3_REG_OFFSET_ARRAY                                                            \
    {                                                                                               \
        (uint32_t) offsetof(XSPI_Type, FRAD0_WORD3), (uint32_t)offsetof(XSPI_Type, FRAD1_WORD3),    \
            (uint32_t)offsetof(XSPI_Type, FRAD2_WORD3), (uint32_t)offsetof(XSPI_Type, FRAD3_WORD3), \
            (uint32_t)offsetof(XSPI_Type, FRAD4_WORD3), (uint32_t)offsetof(XSPI_Type, FRAD5_WORD3), \
            (uint32_t)offsetof(XSPI_Type, FRAD6_WORD3), (uint32_t)offsetof(XSPI_Type, FRAD7_WORD3), \
    }

#define XSPI_FRAD_WORD4_REG_OFFSET_ARRAY                                                            \
    {                                                                                               \
        (uint32_t) offsetof(XSPI_Type, FRAD0_WORD4), (uint32_t)offsetof(XSPI_Type, FRAD1_WORD4),    \
            (uint32_t)offsetof(XSPI_Type, FRAD2_WORD4), (uint32_t)offsetof(XSPI_Type, FRAD3_WORD4), \
            (uint32_t)offsetof(XSPI_Type, FRAD4_WORD4), (uint32_t)offsetof(XSPI_Type, FRAD5_WORD4), \
            (uint32_t)offsetof(XSPI_Type, FRAD6_WORD4), (uint32_t)offsetof(XSPI_Type, FRAD7_WORD4), \
    }

#define XSPI_FRAD_WORD5_REG_OFFSET_ARRAY                                                            \
    {                                                                                               \
        (uint32_t) offsetof(XSPI_Type, FRAD0_WORD5), (uint32_t)offsetof(XSPI_Type, FRAD1_WORD5),    \
            (uint32_t)offsetof(XSPI_Type, FRAD2_WORD5), (uint32_t)offsetof(XSPI_Type, FRAD3_WORD5), \
            (uint32_t)offsetof(XSPI_Type, FRAD4_WORD5), (uint32_t)offsetof(XSPI_Type, FRAD5_WORD5), \
            (uint32_t)offsetof(XSPI_Type, FRAD6_WORD5), (uint32_t)offsetof(XSPI_Type, FRAD7_WORD5), \
    }

#define XSPI_TGSFAR_REG_OFFSET                                                              \
    {                                                                                       \
        offsetof(XSPI_Type, TGSFAR), offsetof(XSPI_Type, SUB_REG_MDAM_ARRAY[0].TGSFAR_SUB), \
    }

#define XSPI_TGSFARS_REG_OFFSET                                                               \
    {                                                                                         \
        offsetof(XSPI_Type, TGSFARS), offsetof(XSPI_Type, SUB_REG_MDAM_ARRAY[0].TGSFARS_SUB), \
    }

#define XSPI_TGIPCRS_REG_OFFSET                                                               \
    {                                                                                         \
        offsetof(XSPI_Type, TGIPCRS), offsetof(XSPI_Type, SUB_REG_MDAM_ARRAY[0].TGIPCRS_SUB), \
    }

#define XSPI_SFP_TG_IPCR_REG_OFFSET                                                                   \
    {                                                                                                 \
        offsetof(XSPI_Type, SFP_TG_IPCR), offsetof(XSPI_Type, SUB_REG_MDAM_ARRAY[0].SFP_TG_SUB_IPCR), \
    }

#define XSPI_SFP_TG_SFAR_REG_OFFSET                                                                   \
    {                                                                                                 \
        offsetof(XSPI_Type, SFP_TG_SFAR), offsetof(XSPI_Type, SUB_REG_MDAM_ARRAY[0].SFP_TG_SUB_SFAR), \
    }

#define XSPI_MCR_X16_MODE_MASK  (0x300000UL)
#define XSPI_MCR_X16_MODE_SHIFT (20UL)
#define XSPI_MCR_X16_MODE(x)    (((uint32_t)(x) << XSPI_MCR_X16_MODE_SHIFT) & XSPI_MCR_X16_MODE_MASK)
/*******************************************************************************
 * Prototypes
 ******************************************************************************/
static uint8_t XSPI_GetPPWBFromPageSize(uint32_t pageSize);

/*******************************************************************************
 * Variables
 ******************************************************************************/
/*! brief Pointers to xspi bases for each instance. */
static XSPI_Type *const s_xspiBases[] = XSPI_BASE_PTRS;

/*! brief Pointers to xspi IRQ number for each instance. */
static const IRQn_Type s_xspiIrqs[] = XSPI_IRQS;

/*! brief Pointers to xspi amba base for each instance. */
static uint32_t s_xspiAmbaBase[] = XSPI_AMBA_BASES;

static xspi_transfer_t *s_xspiTransfer[ARRAY_SIZE(s_xspiBases)];

#if defined(FSL_DRIVER_TRANSFER_DOUBLE_WEAK_IRQ) && FSL_DRIVER_TRANSFER_DOUBLE_WEAK_IRQ
static xspi_handle_t *s_xspiHandle[ARRAY_SIZE(s_xspiBases)];
#endif

#if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
/* Clock name array */
static const clock_ip_name_t s_xspiClock[] = XSPI_CLOCKS;

#ifdef CACHE64_CLOCKS
/* Array of CACHE64_CTRL clock name. */
static const clock_ip_name_t s_xspi_cache64Clocks[] = CACHE64_CLOCKS;
#endif
#endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */

/*! brief Pointers to XSPI resets for each instance. */
static const reset_ip_name_t s_xspiResets[] = XSPI_RSTS;

static uint32_t s_tgSfarsRegOffset[]     = XSPI_TGSFARS_REG_OFFSET;
static uint32_t s_tgIpcrsRegOffset[]     = XSPI_TGIPCRS_REG_OFFSET;
static uint32_t s_sfpTgIpcrRegOffset[]   = XSPI_SFP_TG_IPCR_REG_OFFSET;
static uint32_t s_sfpTgIpSfarRegOffset[] = XSPI_SFP_TG_SFAR_REG_OFFSET;
static uint32_t s_tgMdadRegOffset[]      = XSPI_TG_MDAD_REG_OFFSET_ARRAY;

volatile bool g_IpAccessIpcrFail = false;
volatile bool g_IpAccessSfarFail = false;

volatile uint32_t g_programFlashSize = 0UL;

/*******************************************************************************
 * Code
 ******************************************************************************/

static uint8_t XSPI_GetPPWBFromPageSize(uint32_t pageSize)
{
    uint8_t ppwbValue = 0U;

    g_programFlashSize = pageSize;
    switch (pageSize)
    {
        case 64UL:
        {
            ppwbValue = 6U;
            break;
        }
        case 128UL:
        {
            ppwbValue = 7U;
            break;
        }
        case 256UL:
        {
            ppwbValue = 8U;
            break;
        }
        case 512UL:
        {
            ppwbValue = 9U;
            break;
        }
        case 1024UL:
        {
            ppwbValue = 10U;
            break;
        }
        case 2048UL:
        {
            ppwbValue = 11U;
        }
        case 4096UL:
        {
            ppwbValue = 12U;
        }
        default:
        {
            /* Input pageSize not included. */
            assert(false);
            break;
        }
    }

    return ppwbValue;
}

static status_t XSPI_StartIpReadNonBlocking(XSPI_Type *base, xspi_handle_t *handle, uint32_t dataSize)
{
    uint32_t rxBufferWaterMark = 0UL;
    uint32_t transferSize = 0UL;
    status_t status = kStatus_Success;
    uint32_t instance = XSPI_GetInstance(base);
    
    XSPI_ClearRxBuffer(base);
    XSPI_ClearCmdExecutionArbitrationStatusFlags(base, (uint32_t)(kXSPI_FlagIpCmdFinished | kXSPI_FlagRxBufferWatermarkExceeded |
                                                  kXSPI_FlagRxBufferOverflow));
    if (dataSize >= XSPI_IP_RX_BUFFER_SIZE)
    {
        /* In case of data size is larger than RX buffer size, the big size request should be break into small
        size sub-request, and the transfer size of sub-request is equal to buffer size. */
        rxBufferWaterMark = XSPI_IP_RX_BUFFER_SIZE >> 1UL;
        transferSize = XSPI_IP_RX_BUFFER_SIZE;
    }
    else if (dataSize > (XSPI_IP_RX_BUFFER_SIZE >> 1UL))
    {
        /* In case of data size is larger than maximum watermark value, watermark set as maximum value, and
        transfer size is data size. */
        rxBufferWaterMark = XSPI_IP_RX_BUFFER_SIZE >> 1UL;
        transferSize = dataSize;
    }
    else if (dataSize % 4UL == 0UL)
    {
        /* In case of data size less than maximum watermark value and is aligned with 4 bytes, watermark set as 
        data size, transfer size also set as data size. */
        rxBufferWaterMark = dataSize;
        transferSize = dataSize;
    }
    else
    {
        rxBufferWaterMark = (dataSize / 4UL) << 2UL;
        if (rxBufferWaterMark == 0UL)
        {
            rxBufferWaterMark = 4UL;
        }
        transferSize = dataSize;
    }
    (void)XSPI_UpdateRxBufferWaterMark(base, rxBufferWaterMark);

    handle->state = kXSPI_StateStartIpAccess;
    status = XSPI_StartIpAccessNonBlocking(base, s_xspiTransfer[instance]->deviceAddress,
                                        s_xspiTransfer[instance]->seqIndex, transferSize, 
                                        s_xspiTransfer[instance]->targetGroup,
                                        s_xspiTransfer[instance]->lockArbitration);

    if (status != kStatus_Success)
    {
        return status;
    }
    if (handle->state != kXSPI_StateIpAccessGranted)
    {
        return kStatus_XSPI_IpAccessNotGranted;
    }
    handle->state = kXSPI_StateBusyRead;

    XSPI_EnableInterrupts(base, (uint64_t)(kXSPI_RxBufferDrainIntEnable | kXSPI_RxBufferOverflowIntEnable |
                            kXSPI_TimeoutErrIntEnable | kXSPI_IpCmdTrigFailErrorIntEnable));
    return status;
}

static status_t XSPI_StartIpWriteNonBlocking(XSPI_Type *base, xspi_handle_t *handle, uint32_t dataSize)
{
    status_t status = kStatus_Success;
    uint32_t transferSize = 0UL;
    uint32_t instance = XSPI_GetInstance(base);

    XSPI_ClearTxBuffer(base);

    if (dataSize >= XSPI_IP_TX_BUFFER_SIZE)
    {
        transferSize = XSPI_IP_TX_BUFFER_SIZE;
    }
    else
    {
        transferSize = dataSize;
    }

    handle->curTransferSize = transferSize;
    handle->state = kXSPI_StateStartIpAccess;
    status = XSPI_StartIpAccessNonBlocking(base, s_xspiTransfer[instance]->deviceAddress,
                                        s_xspiTransfer[instance]->seqIndex, transferSize,
                                        s_xspiTransfer[instance]->targetGroup,
                                        s_xspiTransfer[instance]->lockArbitration);
    if (status != kStatus_Success)
    {
        return status;
    }
    if (handle->state != kXSPI_StateIpAccessGranted)
    {
        return kStatus_XSPI_IpAccessNotGranted;
    }

    handle->state = kXSPI_StateBusyWrite;
    XSPI_EnableInterrupts(base, (uint64_t)(kXSPI_TxBufferFillIntEnable | kXSPI_TxBufferUnderrunIntEnable|
                                        kXSPI_TimeoutErrIntEnable | kXSPI_IpCmdTrigFailErrorIntEnable));
    
    return status;
}

/******************* Initialization And Deinitialization Functional Interfaces Start ***********************/
/*!
 * brief Get the instance number for XSPI.
 *
 * param base XSPI base pointer.
 */
uint32_t XSPI_GetInstance(XSPI_Type *base)
{
    uint32_t instance;

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

    assert(instance < ARRAY_SIZE(s_xspiBases));

    return instance;
}

/*!
 * brief Check and clear IP command execution errors.
 *
 * param base XSPI base pointer.
 * param status interrupt status.
 */
status_t XSPI_CheckAndClearError(XSPI_Type *base, uint32_t status)
{
    status_t result = kStatus_Success;

    /* Check for error. */
    status &= (uint32_t)(kXSPI_ErrorNoFradMatch | kXSPI_ErrorFrad0Access | kXSPI_ErrorFrad1Access | \
                    kXSPI_ErrorFrad2Access | kXSPI_ErrorFrad3Access | kXSPI_ErrorFrad4Access | kXSPI_ErrorFrad5Access |
                    kXSPI_ErrorFrad6Access | kXSPI_ErrorFrad7Access | kXSPI_ErrorIpBusTransfer | kXSPI_ErrorTg0Ipcr |
                    kXSPI_ErrorTg0Sfar | kXSPI_ErrorTg1Ipcr | kXSPI_ErrorTg1Sfar | kXSPI_ErrorTimeout);
    if (0U != status)
    {
        /* Select the correct error code.. */
        if (0U != (status & (uint32_t)kXSPI_SequenceExecutionTimeoutFlag))
        {
            result = kStatus_XSPI_SequenceExecutionTimeout;
            /* Clear the flags. */
            base->ERRSTAT = (uint32_t)kXSPI_SequenceExecutionTimeoutFlag;
        }
        else if (0U != ((status & (uint32_t)kXSPI_FradMatchErrorFlag) | (status & (uint32_t)kXSPI_FradnAccErrorFlag)))
        {
            result = kStatus_XSPI_FradCheckError;
            /* Clear the flags. */
            if (0U != (status & (uint32_t)kXSPI_FradMatchErrorFlag))
            {
                base->ERRSTAT = (uint32_t)kXSPI_FradMatchErrorFlag;
            }
            else
            {
                base->ERRSTAT = (uint32_t)kXSPI_FradnAccErrorFlag;
            }
        }
        else if (0U != (status & (uint32_t)kXSPI_IpsErrorFlag))
        {
            result = kStatus_XSPI_IpsBusTransError;
            /* Clear the flags. */
            base->IPSERROR |= XSPI_IPSERROR_CLR_MASK;
        }
        else if (0U != ((status & (uint32_t)kXSPI_Tg0SfarErrorFlag) | (status & (uint32_t)kXSPI_Tg1SfarErrorFlag) |
                        (status & (uint32_t)kXSPI_TgnIpcrErrorFlag)))
        {
            result = kStatus_XSPI_TgQueueWritingError;
            /* Clear the flags. */
            if (0U != ((status & (uint32_t)kXSPI_Tg0SfarErrorFlag) | (status & (uint32_t)kXSPI_Tg1SfarErrorFlag)))
            {
                base->TGSFARS |= XSPI_TGSFARS_CLR_MASK;
            }
            else
            {
                base->TGIPCRS |= XSPI_TGIPCRS_CLR_MASK;
            }
        }
        else
        {
            assert(false);
        }

        /* Reset fifo. These flags clear automatically. */
        base->MCR |= XSPI_MCR_CLR_RXF_MASK;
        base->MCR |= XSPI_MCR_CLR_TXF_MASK;
    }

    return result;
}

/*!
 * brief Initializes the XSPI module and internal state.
 *
 * This function  configures the XSPI with the
 * input configure parameters. Users should call this function before any XSPI operations.
 *
 * param base XSPI peripheral base address.
 * param config XSPI configure structure.
 */

void XSPI_Init(XSPI_Type *base, const xspi_config_t *ptrConfig)
{
    uint32_t tmp32 = 0UL;

#if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
    /* Enable the xspi clock */
    (void)CLOCK_EnableClock(s_xspiClock[XSPI_GetInstance(base)]);
#endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
    /* Reset the XSPI module */
    RESET_ReleasePeripheralReset(s_xspiResets[XSPI_GetInstance(base)]);

    /* Reset SFM, AHB domain and TG queue. */
    XSPI_SoftwareReset(base);

    /* Disable module before initialization. */
    XSPI_EnableModule(base, false);

    /* Clear AHB buffer, TX buffer, RX buffer. */
    XSPI_ClearAhbBuffer(base);
    XSPI_ClearTxBuffer(base);
    XSPI_ClearRxBuffer(base);

    /* If any flags are asserted, clear firstly. */
    tmp32 = base->FR;
    if (tmp32 != 0UL)
    {
        base->FR = tmp32;
    }

    XSPI_EnableDozeMode(base, ptrConfig->enableDoze);

    base->MCR = ((base->MCR) & (~XSPI_MCR_END_CFG_MASK)) | XSPI_MCR_END_CFG(ptrConfig->byteOrder);

    if (ptrConfig->ptrAhbAccessConfig != NULL)
    {
        (void)XSPI_SetAhbAccessConfig(base, ptrConfig->ptrAhbAccessConfig);
    }

    if (ptrConfig->ptrIpAccessConfig != NULL)
    {
        (void)XSPI_SetIpAccessConfig(base, ptrConfig->ptrIpAccessConfig);
    }

    /* Enable XSPI module. */
    XSPI_EnableModule(base, true);
}

/*!
 * brief Gets default settings for XSPI.
 *
 * param base XSPI peripheral base address.
 * param config XSPI configuration structure.
 * param devconfig Flash configuration parameters.

 */
void XSPI_GetDefaultConfig(xspi_config_t *ptrConfig)
{
    assert(ptrConfig != NULL);

    ptrConfig->byteOrder  = kXSPI_64BitLE;
    ptrConfig->enableDoze = false;

    if (ptrConfig->ptrAhbAccessConfig != NULL)
    {
        /* If ptrAhbAccessConfig is not NULL, it means AHB access feature will be used. */
        ptrConfig->ptrAhbAccessConfig->ahbAlignment                = kXSPI_AhbAlignmentNoLimit;
        ptrConfig->ptrAhbAccessConfig->ahbErrorPayload.highPayload = 0UL;
        ptrConfig->ptrAhbAccessConfig->ahbErrorPayload.lowPayload  = 0UL;
        ptrConfig->ptrAhbAccessConfig->ahbSplitSize                = kXSPI_AhbSplitSizeDisabled;

        for (uint8_t i = 0U; i < XSPI_BUFCR_COUNT; i++)
        {
            ptrConfig->ptrAhbAccessConfig->buffer[i].masterId = i;
            if (i == 3U)
            {
                ptrConfig->ptrAhbAccessConfig->buffer[i].enaPri.enableAllMaster = true;
            }
            else
            {
                ptrConfig->ptrAhbAccessConfig->buffer[i].enaPri.enablePriority = false;
            }
            ptrConfig->ptrAhbAccessConfig->buffer[i].bufferSize = 0x80U;

            ptrConfig->ptrAhbAccessConfig->buffer[i].ptrSubBuffer0Config = NULL;
            ptrConfig->ptrAhbAccessConfig->buffer[i].ptrSubBuffer1Config = NULL;
            ptrConfig->ptrAhbAccessConfig->buffer[i].ptrSubBuffer2Config = NULL;
            ptrConfig->ptrAhbAccessConfig->buffer[i].ptrSubBuffer3Config = NULL;
        }
        ptrConfig->ptrAhbAccessConfig->ptrAhbWriteConfig         = NULL;
        ptrConfig->ptrAhbAccessConfig->enableAHBPrefetch         = true;
        ptrConfig->ptrAhbAccessConfig->enableAHBBufferWriteFlush = false;
        ptrConfig->ptrAhbAccessConfig->ARDSeqIndex               = 0U;
    }

    if (ptrConfig->ptrIpAccessConfig != NULL)
    {
        /* If ptrAhbAccessConfig is not NULL, it means AHB access feature will be used. */
        /* In default settings, FRAD check and MDAD check are both disabled. */
        ptrConfig->ptrIpAccessConfig->ptrSfpMdadConfig               = NULL;
        ptrConfig->ptrIpAccessConfig->ptrSfpFradConfig               = NULL;
        ptrConfig->ptrIpAccessConfig->sfpArbitrationLockTimeoutValue = 0xFFFFFUL;
        ptrConfig->ptrIpAccessConfig->ipAccessTimeoutValue           = 0xFFFFUL;
    }
}

/*!
 * brief Deinitialize the XSPI module.
 *
 * Clears the XSPI state and  XSPI module registers.
 * param base XSPI peripheral base address.
 */
void XSPI_Deinit(XSPI_Type *base)
{
    XSPI_EnableModule(base, false);
    /* Reset peripheral. */
    XSPI_SoftwareReset(base);
}

/*! brief Updates the LUT table.
 *
 * param base XSPI peripheral base address.
 * param index From which index start to update. It could be any index of the LUT table, which
 * also allows user to update command content inside a command. Each command consists of up to
 * 10 instructions and occupy 4*32-bit memory.
 * param cmd Command sequence array.
 * param count Number of instruction-operand pairs.
 */
void XSPI_UpdateLUT(XSPI_Type *base, uint8_t index, const uint32_t *cmd, uint8_t count)
{
    assert(index < 80U);

    uint32_t i = 0;
    volatile uint32_t *lutBase;

    /* Wait for bus to be idle before changing flash configuration. */
    while (!XSPI_GetBusIdleStatus(base))
    {
    }

    /* Unlock LUT for update. */
    base->LUTKEY = XSPI_LUT_KEY_VAL;
    base->LCKCR  = 0x02;

    lutBase = &base->LUT[index];
    for (i = 0; i < count; i++)
    {
        *lutBase++ = *cmd++;
    }

    /* Lock LUT. */
    base->LUTKEY = XSPI_LUT_KEY_VAL;
    base->LCKCR  = 0x01;
}

/******************* Initialization And Deinitialization Functional Interfaces End ***********************/

/***************************** XSPI Controller Low-Level Inferfaces Start ********************************/
/*!
 * brief Reset Serial flash memory domain and AHB domain at the same time.
 *
 * note Resetting only the Serial flash memory domain and AHB domain may cause undesirable side effects.
 *
 * param base XSPI peripheral base address.
 */
void XSPI_ResetSfmAndAhbDomain(XSPI_Type *base)
{
    /* XSPI module must be enabled, before assert reset to domains. */
    XSPI_EnableModule(base, true);

    base->MCR |= XSPI_MCR_SWRSTSD_MASK | XSPI_MCR_SWRSTHD_MASK;
    for (uint8_t i = 0U; i < 6U; i++)
    {
        __NOP();
    }
    /* Before de-assert the software reset, XSPI module should be disabled. */
    XSPI_EnableModule(base, false);
    base->MCR &= ~(XSPI_MCR_SWRSTSD_MASK | XSPI_MCR_SWRSTHD_MASK);
    for (uint8_t i = 0U; i < 6U; i++)
    {
        __NOP();
    }
    /* After de-assert the software resets, XSPI module should be enabled. */
    XSPI_EnableModule(base, true);
}

/***************************** XSPI Controller Low-Level Inferfaces End ********************************/

/***************************** External Device Control Low-Level Interfaces Start ********************************/
/*!
 * brief Set Hyper bus X16 mode.
 *
 * param base XSPI peripheral base address.
 * param x16Mode Specify X16 mode.
 */
void XSPI_SetHyperBusX16Mode(XSPI_Type *base, xspi_hyper_bus_x16_mode_t x16Mode)
{
    bool isEnabled = false;

    if (XSPI_CheckModuleEnabled(base))
    {
        isEnabled = true;
        XSPI_EnableModule(base, false);
    }

    base->MCR = ((base->MCR) & (~XSPI_MCR_X16_MODE_MASK)) | XSPI_MCR_X16_MODE(x16Mode);

    if (isEnabled)
    {
        XSPI_EnableModule(base, true);
    }
}

/*!
 * brief Update DLL configuration.
 * 
 * param[in] base XSPI peripheral base address.
 * param[in] ptrDllConfig Pointer to the DLL configuration.
 * param[in] enableDDR DDR mode is enabled or not.
 * param[in] enableX16Mode X16 mode is enabled or not.
 * param[in] xspiRootClk The frequency of xspi root clock, the unit is Hz.
 */
void XSPI_UpdateDllValue(XSPI_Type *base, xspi_dll_config_t *ptrDllConfig,
                        bool enableDDR, bool enableX16Mode, uint32_t xspiRootClk)
{
    uint32_t offsetDelayElementCount = 0UL;
    uint8_t tapNum              = ptrDllConfig->dllCustomDelayTapNum;
    bool enableCourseDelayLine8 = ptrDllConfig->enableCdl8;

    if (ptrDllConfig->useRefValue == true)
    {
        if (enableDDR)
        {
            tapNum                 = FSL_FEATURE_XSPI_DLL_REF_VALUE_DDR_DELAY_TAP_NUM;
            enableCourseDelayLine8 = true;
        }
        else
        {
            tapNum                 = FSL_FEATURE_XSPI_DLL_REF_VALUE_SDR_DELAY_TAP_NUM;
            enableCourseDelayLine8 = false;
        }
    }

    base->MCR |= XSPI_MCR_MDIS_MASK;
    base->SMPR = (((base->SMPR) & (~XSPI_SMPR_DLLFSMPFA_MASK)) | XSPI_SMPR_DLLFSMPFA(tapNum));
    base->MCR &= ~XSPI_MCR_MDIS_MASK;

    if (ptrDllConfig->dllMode == kXSPI_BypassMode)
    {
        /* In case of DLL mode is selected as Bypass mode. */
        uint32_t delayElementCoarseAdjust = 0UL;
        uint32_t delayElementFineAdjust   = 0UL;

        offsetDelayElementCount  = (uint32_t)((ptrDllConfig->useRefValue) ?
                                       FSL_FEATURE_XSPI_DLL_REF_VALUE_BYPASS_OFFSET_DELAY_ELEMENT_COUNT :
                                       (ptrDllConfig->dllCustomPara.bypassModePara.offsetDelayElementCount));
        delayElementCoarseAdjust = (uint32_t)((ptrDllConfig->useRefValue) ?
                                       FSL_FEATURE_XSPI_DLL_REF_VALUE_BYPASS_DELAY_ELEMENT_COARSE :
                                       (ptrDllConfig->dllCustomPara.bypassModePara.delayElementCoarseValue));
        delayElementFineAdjust   = (uint32_t)((ptrDllConfig->useRefValue) ?
                                       FSL_FEATURE_XSPI_DLL_REF_VALUE_BYPASS_DELAY_ELEMENT_FINE :
                                       (ptrDllConfig->dllCustomPara.bypassModePara.delayElementFineValue));

        base->DLLCR[0] &=
            ~(XSPI_DLLCR_SLAVE_AUTO_UPDT_MASK | XSPI_DLLCR_SLV_DLY_FINE_MASK | XSPI_DLLCR_SLV_DLY_COARSE_MASK |
              XSPI_DLLCR_SLV_FINE_OFFSET_MASK | XSPI_DLLCR_DLL_CDL8_MASK);
        /* Enable subordinate delay chain as bypass mode. */
        base->DLLCR[0] |= XSPI_DLLCR_SLV_EN_MASK | XSPI_DLLCR_SLV_DLL_BYPASS_MASK;
        /* program DLL to desired delay. */
        base->DLLCR[0] |=
            XSPI_DLLCR_SLV_DLY_FINE(delayElementFineAdjust) | XSPI_DLLCR_SLV_DLY_COARSE(delayElementCoarseAdjust) |
            XSPI_DLLCR_SLV_FINE_OFFSET(offsetDelayElementCount) | XSPI_DLLCR_DLL_CDL8(enableCourseDelayLine8);
        /* Load above settings into delay chain. */
        base->DLLCR[0] |= XSPI_DLLCR_SLV_UPD_MASK;

        while ((base->DLLSR & XSPI_DLLSR_SLVA_LOCK_MASK) == 0UL)
        {
        }
        base->DLLCR[0] &= ~XSPI_DLLCR_SLV_UPD_MASK;
    }
    else
    {
        /* In case of DLL mode is selected as auto update mode. */
        bool enableHighFreq                   = false;
        uint32_t refCounterValue               = 0UL;
        uint32_t resolutionValue               = 0UL;
        uint32_t tDiv16OffsetDelayElementCount = 0UL;

        enableHighFreq =
            (ptrDllConfig->useRefValue) ?
                ((xspiRootClk >= FSL_FEATURE_XSPI_DLL_REF_VALUE_AUTOUPDATE_FREQ_THRESHOLD) ? true : false) :
                ((bool)(ptrDllConfig->dllCustomPara.autoUpdateModoPara.enableHighFreq));
        refCounterValue = (uint32_t)((ptrDllConfig->useRefValue) ?
                            FSL_FEATURE_XSPI_DLL_REF_VALUE_AUTOUPDATE_REF_COUNTER :
                            (ptrDllConfig->dllCustomPara.autoUpdateModoPara.referenceCounter));
        resolutionValue = (uint32_t)((ptrDllConfig->useRefValue) ?
                            FSL_FEATURE_XSPI_DLL_REF_VALUE_AUTOUPDATE_RES :
                            (ptrDllConfig->dllCustomPara.autoUpdateModoPara.resolution));
        tDiv16OffsetDelayElementCount =
            (uint32_t)((ptrDllConfig->useRefValue) ?
                FSL_FEATURE_XSPI_DLL_REF_VALUE_AUTOUPDATE_T_DIV16_OFFSET_DELAY_ELEMENT_COUNT :
                (ptrDllConfig->dllCustomPara.autoUpdateModoPara.tDiv16OffsetDelayElementCount));
        offsetDelayElementCount = (uint32_t)((ptrDllConfig->useRefValue) ?
                                      FSL_FEATURE_XSPI_DLL_REF_VALUE_AUTOUPDATE_OFFSET_DELAY_ELEMENT_COUNT :
                                      (ptrDllConfig->dllCustomPara.autoUpdateModoPara.offsetDelayElementCount));

        base->DLLCR[0] &= ~(XSPI_DLLCR_SLV_DLL_BYPASS_MASK | XSPI_DLLCR_DLL_CDL8_MASK | XSPI_DLLCR_SLV_DLY_OFFSET_MASK |
                            XSPI_DLLCR_SLV_FINE_OFFSET_MASK | XSPI_DLLCR_DLLRES_MASK | XSPI_DLLCR_DLL_REFCNTR_MASK |
                            XSPI_DLLCR_FREQEN_MASK);
        /* Enable subordinate as auto update mode. */
        base->DLLCR[0] |= XSPI_DLLCR_SLV_EN_MASK | XSPI_DLLCR_SLAVE_AUTO_UPDT_MASK;
        /* program DLL to desired delay. */
        base->DLLCR[0] |= XSPI_DLLCR_DLLRES(resolutionValue) | XSPI_DLLCR_DLL_REFCNTR(refCounterValue) |
                          XSPI_DLLCR_SLV_FINE_OFFSET(offsetDelayElementCount) |
                          XSPI_DLLCR_SLV_DLY_OFFSET(tDiv16OffsetDelayElementCount) | XSPI_DLLCR_FREQEN(enableHighFreq);
        /* Load above settings into delay chain. */
        base->DLLCR[0] |= XSPI_DLLCR_SLV_UPD_MASK;
        base->DLLCR[0] |= XSPI_DLLCR_DLLEN_MASK;
        base->DLLCR[0] &= ~XSPI_DLLCR_SLV_UPD_MASK;
        while ((base->DLLSR & XSPI_DLLSR_SLVA_LOCK_MASK) == 0UL)
        {
        }
    }
}

/*!
 * brief Set Data learning configurations.
 * 
 * param[in] base XSPI peripheral base address.
 * param[in] ptrDataLearningConfig Pointer to data learning configuration.
 * 
 * retval kStatus_XSPI_AhbReadAccessAsserted Fail to set data learning configuration due to AHB read access asserted.
 * retval kStatus_XSPI_IPAccessAsserted Fail to set data learning configuration due to a IP access already asserted.
 * retval kStatus_XSPI_AhbWriteAccessAsserted Fail to set data learning configuration due to AHB write access asserted.
 * retval kStatus_Success  Successfully to set Data learning configurations.
 */
status_t XSPI_SetDataLearningConfig(XSPI_Type *base, xspi_data_learning_config_t *ptrDataLearningConfig)
{
    assert(ptrDataLearningConfig != NULL);

    if (XSPI_CheckAhbReadAccessAsserted(base))
    {
        return kStatus_XSPI_AhbReadAccessAsserted;
    }

    if (XSPI_CheckIPAccessAsserted(base))
    {
        return kStatus_XSPI_IPAccessAsserted;
    }

    if (XSPI_CheckAhbWriteAccessAsserted(base))
    {
        return kStatus_XSPI_AhbWriteAccessAsserted;
    }

    base->DLCR = ((base->DLCR) & ~(XSPI_DLCR_DLP_SEL_FA_MASK | XSPI_DLCR_DL_NONDLP_FLSH_MASK)) |
                 (XSPI_DLCR_DLP_SEL_FA(ptrDataLearningConfig->padSelected) |
                  XSPI_DLCR_DL_NONDLP_FLSH(ptrDataLearningConfig->deviceSupported));

    base->DLPR = ptrDataLearningConfig->pattern;

    return kStatus_Success;
}

/*!
 * brief Update address mode to access external device.
 * 
 * param base XSPI peripheral base address.
 * param addrMode Specify the address mode to update.
 *
 * retval kStatus_XSPI_AhbReadAccessAsserted Fail to update address mode due to AHB read access asserted.
 * retval kStatus_XSPI_IPAccessAsserted Fail to update address mode due to a IP access already asserted.
 * retval kStatus_XSPI_AhbWriteAccessAsserted Fail to update address mode due to AHB write access asserted.
 * retval kStatus_Success  Successfully to update address mode.
 */
status_t XSPI_UpdateDeviceAddrMode(XSPI_Type *base, xspi_device_addr_mode_t addrMode)
{
    if (XSPI_CheckAhbReadAccessAsserted(base))
    {
        return kStatus_XSPI_AhbReadAccessAsserted;
    }

    if (XSPI_CheckIPAccessAsserted(base))
    {
        return kStatus_XSPI_IPAccessAsserted;
    }

    if (XSPI_CheckAhbWriteAccessAsserted(base))
    {
        return kStatus_XSPI_AhbWriteAccessAsserted;
    }
    
    if (addrMode != kXSPI_Device4ByteAddressable)
    {
        base->SFACR =
            (base->SFACR & ~(XSPI_SFACR_WA_MASK | XSPI_SFACR_WA_4B_EN_MASK)) | XSPI_SFACR_WA(addrMode);
    }
    else
    {
        base->SFACR &= ~XSPI_SFACR_WA_MASK;
        base->SFACR |= XSPI_SFACR_WA_4B_EN_MASK;
    }
    
    return kStatus_Success;
}

/***************************** External Device Control Low-Level Interfaces End ********************************/

/***************************** External Device Control Functional Interfaces Start ********************************/
/*!
 * brief Set device configuration.
 * 
 * param[in] base XSPI peripheral base address.
 * param[in] devConfig Pointer to device configuration.
 *
 * retval kStatus_XSPI_AhbReadAccessAsserted Fail to set device configuration due to AHB read access asserted.
 * retval kStatus_XSPI_IPAccessAsserted Fail to set device configuration due to a IP access already asserted.
 * retval kStatus_XSPI_AhbWriteAccessAsserted Fail to set device configuration due to AHB write access asserted.
 * retval kStatus_Success  Successfully to set device configurations.
 */
status_t XSPI_SetDeviceConfig(XSPI_Type *base, xspi_device_config_t *devConfig)
{
    assert(devConfig != NULL);

    bool enableDDR     = false;
    bool enableX16Mode = false;
    uint32_t instance  = XSPI_GetInstance(base);

    if (XSPI_CheckIPAccessAsserted(base))
    {
        return kStatus_XSPI_IPAccessAsserted;
    }

    if (XSPI_CheckAhbReadAccessAsserted(base))
    {
        return kStatus_XSPI_AhbReadAccessAsserted;
    }

    if (XSPI_CheckAhbWriteAccessAsserted(base))
    {
        return kStatus_XSPI_AhbWriteAccessAsserted;
    }

    /* Set sample clock config. */
    bool isEnabled = false;

    if (XSPI_CheckModuleEnabled(base))
    {
        isEnabled = true;
        XSPI_EnableModule(base, false);
    }

    if (devConfig->enableCknPad)
    {
        base->MCR |= XSPI_MCR_CKN_FA_EN_MASK;
    }
    else
    {
        base->MCR &= ~XSPI_MCR_CKN_FA_EN_MASK;
    }

    uint8_t tmp8 = 0U;
    if (devConfig->deviceInterface == kXSPI_StrandardExtendedSPI)
    {
        tmp8 = XSPI_GetPPWBFromPageSize(devConfig->interfaceSettings.strandardExtendedSPISettings.pageSize);
        base->MCR &= ~XSPI_MCR_DQS_OUT_EN_MASK;
        base->SFACR = (base->SFACR & ~XSPI_SFACR_PPWB_MASK) | XSPI_SFACR_PPWB(tmp8);
    }
    else
    {
        tmp8 = XSPI_GetPPWBFromPageSize(devConfig->interfaceSettings.hyperBusSettings.pageSize);
        base->SFACR = (base->SFACR & ~XSPI_SFACR_PPWB_MASK) | XSPI_SFACR_PPWB(tmp8);
        XSPI_SetHyperBusX16Mode(base, devConfig->interfaceSettings.hyperBusSettings.x16Mode);
        if (devConfig->interfaceSettings.hyperBusSettings.x16Mode != kXSPI_x16ModeDisable)
        {
            enableX16Mode = true;
        }
        XSPI_EnableVariableLatency(base, devConfig->interfaceSettings.hyperBusSettings.enableVariableLatency);
        base->MCR |= XSPI_MCR_DQS_OUT_EN_MASK;
        base->SFACR = (base->SFACR & ~XSPI_SFACR_FORCE_A10_MASK) |
                      XSPI_SFACR_FORCE_A10(devConfig->interfaceSettings.hyperBusSettings.forceBit10To1);
    }

    base->FLSHCR = ((base->FLSHCR) & ~(XSPI_FLSHCR_TCSS_MASK | XSPI_FLSHCR_TCSH_MASK)) |
                   (XSPI_FLSHCR_TCSS(devConfig->CSSetupTime) | XSPI_FLSHCR_TCSH(devConfig->CSHoldTime));

    if (devConfig->addrMode != kXSPI_Device4ByteAddressable)
    {
        base->SFACR =
            (base->SFACR & ~(XSPI_SFACR_WA_MASK | XSPI_SFACR_WA_4B_EN_MASK)) | XSPI_SFACR_WA(devConfig->addrMode);
    }
    else
    {
        base->SFACR |= XSPI_SFACR_WA_4B_EN_MASK;
    }

    base->SFACR =
        (base->SFACR & ~(XSPI_SFACR_CAS_INTRLVD_MASK | XSPI_SFACR_CAS_MASK)) |
        (XSPI_SFACR_CAS_INTRLVD(devConfig->enableCASInterleaving) | XSPI_SFACR_CAS(devConfig->columnAddrWidth));

    if (devConfig->ptrDeviceRegInfo != NULL)
    {
        (void)XSPI_SetSFMStatusRegInfo(base, devConfig->ptrDeviceRegInfo);
    }

    if (devConfig->ptrDeviceDdrConfig != NULL)
    {
        enableDDR = devConfig->ptrDeviceDdrConfig->enableDdr;
    }

    if (enableDDR)
    {
        base->FLSHCR = ((base->FLSHCR) & ~XSPI_FLSHCR_TDH_MASK) |
                       XSPI_FLSHCR_TDH(devConfig->ptrDeviceDdrConfig->ddrDataAlignedClk);
        base->SFACR = ((base->SFACR) & ~XSPI_SFACR_BYTE_SWAP_MASK) |
                      XSPI_SFACR_BYTE_SWAP(devConfig->ptrDeviceDdrConfig->enableByteSwapInOctalMode);
        base->MCR |= XSPI_MCR_DDR_EN_MASK;
    }
    else
    {
        base->MCR &= ~XSPI_MCR_DDR_EN_MASK;
    }

    for (uint8_t i = 0U; i < XSPI_SFAD_COUNT2; i++)
    {
        base->SFAD[0][i] = s_xspiAmbaBase[instance] + XSPI_SFAD_TPAD(devConfig->deviceSize[i]);
    }

    uint32_t tmp32 = (base->MCR) & ~(XSPI_MCR_DQS_FA_SEL_MASK | XSPI_MCR_DQS_EN_MASK | XSPI_MCR_DQS_LAT_EN_MASK);

    if ((devConfig->sampleClkConfig.sampleClkSource == kXSPI_SampleClkFromDqsPadLoopback) ||
        (devConfig->sampleClkConfig.sampleClkSource == kXSPI_SampleClkFromExternalDQS))
    {
        tmp32 |= XSPI_MCR_DQS_EN_MASK;
    }
    tmp32 |= XSPI_MCR_DQS_LAT_EN(devConfig->sampleClkConfig.enableDQSLatency) |
             XSPI_MCR_DQS_FA_SEL((uint32_t)(devConfig->sampleClkConfig.sampleClkSource) & 0x3UL);
    base->MCR = tmp32;

    base->SMPR &= ~(XSPI_SMPR_FSPHS_MASK | XSPI_SMPR_FSDLY_MASK);

    if (devConfig->sampleClkConfig.sampleClkSource == kXSPI_SampleClkFromInvertedFullySpeedDummyPadLoopback)
    {
        base->SMPR |= XSPI_SMPR_FSPHS_MASK;
    }

    if (devConfig->sampleClkConfig.sampleClkSource == kXSPI_SampleClkFromHalfSpeedDummyPadLoopback)
    {
        base->SMPR |= XSPI_SMPR_FSDLY_MASK;
    }

    XSPI_UpdateDllValue(base, &(devConfig->sampleClkConfig.dllConfig), enableDDR, enableX16Mode,
                        devConfig->xspiRootClk);

    if (isEnabled)
    {
        XSPI_EnableModule(base, true);
    }

    return kStatus_Success;
}
/***************************** External Device Control Functional Interfaces End ********************************/

/***************************** IPS Access Control Low-Level Interfaces Start ********************************/
/*!
 * brief Update watermark for RX buffer.
 *
 * code
 * Set watermark as 4 bytes:
 * XSPI_UpdateRxBufferWaterMark(XSPI0, 4UL);
 * Set watermark as 8 bytes:
 * XSPI_UpdateRxBufferWaterMark(XSPI0, 8UL);
 * endcode
 *
 *
 * param[in] base XSPI peripheral base address.
 * param[in] waterMark Specify the number of bytes in the RX buffer which causes XSPI to assert the watermark exceeded
 * flag, should be in multiple of 4 bytes.
 *
 * retval kStatus_XSPI_IPAccessAsserted Fail to update watermark for Rx buffer, due to IP access is asserted.
 * retval kStatus_XSPI_WaterMarkIllegal Fail to update watermark for Tx buffer, due to input watermark is not the
 * multiple of 4 bytes.
 * retval kStatus_Success Successful to update watermark.
 */
status_t XSPI_UpdateRxBufferWaterMark(XSPI_Type *base, uint32_t waterMark)
{
    assert(waterMark != 0UL);
    assert(waterMark <= 256UL);

    if (XSPI_CheckIPAccessAsserted(base))
    {
        return kStatus_XSPI_IPAccessAsserted;
    }

    if ((waterMark % 4UL) != 0UL)
    {
        return kStatus_XSPI_WaterMarkIllegal;
    }

    base->RBCT = XSPI_RBCT_WMRK((waterMark / 4UL) - 1UL);

    return kStatus_Success;
}

/*!
 * brief Update watermark for TX buffer.
 *
 * param[in] base XSPI peripheral base address.
 * param[in] waterMark The watermark to set, the unit is byte, should be the multiple of 4 byte.
 *
 * retval  kStatus_XSPI_IPAccessAsserted Fail to update watermark for Tx buffer, due to IP access is asserted.
 * retval kStatus_XSPI_WaterMarkIllegal Fail to update watermark for Tx buffer, due to input watermark is not the
 * multiple of 4 bytes.
 * retval kStatus_Success Successful to update watermark.
 */
status_t XSPI_UpdateTxBufferWaterMark(XSPI_Type *base, uint32_t waterMark)
{
    assert(waterMark != 0UL);
    if (XSPI_CheckIPAccessAsserted(base))
    {
        return kStatus_XSPI_IPAccessAsserted;
    }

    if ((waterMark % 4UL) != 0UL)
    {
        return kStatus_XSPI_WaterMarkIllegal;
    }

    base->TBCT = XSPI_TBCT_WMRK((waterMark / 4UL) - 1UL);

    return kStatus_Success;
}

/*!
 * brief Set exclusive access lock mode for the specific frad..
 * 
 * param[in] base XSPI peripheral base address.
 * param[in] ealMode Specify the exclusive access lock mode.
 * param[in] fradId Specify the frad.
 */
void XSPI_SetSFPFradEALMode(XSPI_Type *base, xspi_exclusive_access_lock_mode_t ealMode, uint8_t fradId)
{
    uint32_t fradWord3RegOffset[] = XSPI_FRAD_WORD3_REG_OFFSET_ARRAY;

    uint32_t fradWord3RegAddr = (uint32_t)base + fradWord3RegOffset[fradId];

    xspi_exclusive_access_lock_mode_t curEalMode =
        (xspi_exclusive_access_lock_mode_t)(uint32_t)(((*(uint32_t *)fradWord3RegAddr) & XSPI_FRAD0_WORD3_EAL_MASK) >>
                                                      XSPI_FRAD0_WORD3_EAL_SHIFT);

    if (curEalMode != ealMode)
    {
        if (curEalMode == kXSPI_ExclusiveAccessLockEnabled)
        {
            *(uint32_t *)fradWord3RegAddr = (*(uint32_t *)fradWord3RegAddr & ~XSPI_FRAD0_WORD3_EAL_MASK) |
                                            XSPI_FRAD0_WORD3_EAL(kXSPI_ExclusiveAccessLockExceptMasterId);

            if (ealMode == kXSPI_ExclusiveAccessLockDisabled)
            {
                *(uint32_t *)fradWord3RegAddr = (*(uint32_t *)fradWord3RegAddr & ~XSPI_FRAD0_WORD3_EAL_MASK);
            }
        }
        else
        {
            *(uint32_t *)fradWord3RegAddr =
                (*(uint32_t *)fradWord3RegAddr & ~XSPI_FRAD0_WORD3_EAL_MASK) | XSPI_FRAD0_WORD3_EAL(ealMode);
        }
    }
}

/*!
 * brief Update SFP configurations, including MDAD configurations and FRAD configurations.
 *
 * param[in] base XSPI peripheral base address.
 * param[in] ptrMdadConfig Pointer to the SFP MDAD configuration.
 * param[in] ptrFradConfig Pointer to the SFP FRAD configuration.
 */
void XSPI_UpdateSFPConfig(XSPI_Type *base,
                          xspi_sfp_mdad_config_t *ptrSfpMdadConfig,
                          xspi_sfp_frad_config_t *ptrSfpFradConfig)
{
    bool enableSFP  = false;
    bool enableMdad = false;
    bool enableFrad = false;
    uint8_t i       = 0U;

    enableMdad = (ptrSfpMdadConfig != NULL) ? true : false;
    enableFrad = (ptrSfpFradConfig != NULL) ? true : false;
    enableSFP  = (bool)(enableFrad | enableFrad);

    if (enableSFP)
    {
        if (enableMdad)
        {
            base->MGC |= (XSPI_MGC_GVLDMDAD_MASK | XSPI_MGC_GVLD_MASK);
            uint32_t tgMdadRegAddr     = 0UL;

            for (i = 0U; i < XSPI_TARGET_GROUP_COUNT; i++)
            {
                tgMdadRegAddr = (uint32_t)base + s_tgMdadRegOffset[i];

                *(uint32_t *)tgMdadRegAddr = XSPI_TG0MDAD_MIDMATCH(ptrSfpMdadConfig->tgMdad[i].masterIdReference) |
                                             XSPI_TG0MDAD_MASKTYPE(ptrSfpMdadConfig->tgMdad[i].maskType) |
                                             XSPI_TG0MDAD_MASK(ptrSfpMdadConfig->tgMdad[i].mask) |
                                             XSPI_TG0MDAD_VLD(ptrSfpMdadConfig->tgMdad[i].assignIsValid) |
                                             XSPI_TG0MDAD_SA(ptrSfpMdadConfig->tgMdad[i].secureAttribute);

                if (ptrSfpMdadConfig->tgMdad[i].enableDescriptorLock)
                {
                    *(uint32_t *)tgMdadRegAddr |= XSPI_TG0MDAD_LCK_MASK;
                }
            }
        }

        if (enableFrad)
        {
            base->MGC |= (XSPI_MGC_GVLDFRAD_MASK | XSPI_MGC_GVLD_MASK);
            uint32_t fradWord0RegOffset[] = XSPI_FRAD_WORD0_REG_OFFSET_ARRAY;
            uint32_t fradWord1RegOffset[] = XSPI_FRAD_WORD1_REG_OFFSET_ARRAY;
            uint32_t fradWord2RegOffset[] = XSPI_FRAD_WORD2_REG_OFFSET_ARRAY;
            uint32_t fradWord3RegOffset[] = XSPI_FRAD_WORD3_REG_OFFSET_ARRAY;

            uint32_t fradWord0RegAddr = 0UL;
            uint32_t fradWord1RegAddr = 0UL;
            uint32_t fradWord2RegAddr = 0UL;
            uint32_t fradWord3RegAddr = 0UL;

            for (i = 0U; i < XSPI_SFP_FRAD_COUNT; i++)
            {
                fradWord0RegAddr = (uint32_t)base + fradWord0RegOffset[i];
                fradWord1RegAddr = (uint32_t)base + fradWord1RegOffset[i];
                fradWord2RegAddr = (uint32_t)base + fradWord2RegOffset[i];
                fradWord3RegAddr = (uint32_t)base + fradWord3RegOffset[i];

                /* Set the most-significant 16 bits of the starting address(64-KB alignment). */
                *(uint32_t *)fradWord0RegAddr = ptrSfpFradConfig->fradConfig[i].startAddress & 0xFFFF0000UL;
                /* Set the most-significant 16 bits of the ending address(64-KB alignment). */
                *(uint32_t *)fradWord1RegAddr = ptrSfpFradConfig->fradConfig[i].endAddress & 0xFFFF0000UL;

                XSPI_SetSFPFradEALMode(base, ptrSfpFradConfig->fradConfig[i].exclusiveAccessLock, i);
                if (ptrSfpFradConfig->fradConfig[i].exclusiveAccessLock == kXSPI_ExclusiveAccessLockDisabled)
                {
                    *(uint32_t *)fradWord2RegAddr =
                        (*(uint32_t *)fradWord3RegAddr &
                         (~(XSPI_FRAD0_WORD2_MD0ACP_MASK | XSPI_FRAD0_WORD2_MD1ACP_MASK))) |
                        (XSPI_FRAD0_WORD2_MD0ACP(ptrSfpFradConfig->fradConfig[i].tg0MasterAccess) |
                         XSPI_FRAD0_WORD2_MD1ACP(ptrSfpFradConfig->fradConfig[i].tg1MasterAccess));
                }
                *(uint32_t *)fradWord3RegAddr =
                    ((*(uint32_t *)fradWord3RegAddr) & (~(XSPI_FRAD0_WORD3_LOCK_MASK | XSPI_FRAD0_WORD3_VLD_MASK))) |
                    (XSPI_FRAD0_WORD3_VLD(ptrSfpFradConfig->fradConfig[i].assignIsValid) |
                     XSPI_FRAD0_WORD3_LOCK(ptrSfpFradConfig->fradConfig[i].descriptorLock));
            }
        }
    }
    else
    {
        base->MGC &= ~(XSPI_MGC_GVLDFRAD_MASK | XSPI_MGC_GVLDMDAD_MASK | XSPI_MGC_GVLD_MASK);
    }
}

/*!
 * brief Get FARD latest transaction information.
 * 
 * param[in] base XSPI peripheral base address.
 * param[out] ptrInfo Pointer to the variable in type of ref xspi_frad_transaction_info_t to store information.
 * param[in] fradId Specify the frad Id.
 */
void XSPI_GetFradLastTransactionsInfo(XSPI_Type *base, xspi_frad_transaction_info_t *ptrInfo, uint8_t fradId)
{
    uint32_t fradWord4RegOffset[] = XSPI_FRAD_WORD4_REG_OFFSET_ARRAY;
    uint32_t fradWord5RegOffset[] = XSPI_FRAD_WORD5_REG_OFFSET_ARRAY;

    uint32_t fradWord4RegAddr = (uint32_t)base + fradWord4RegOffset[fradId];
    uint32_t fradWord5RegAddr = (uint32_t)base + fradWord5RegOffset[fradId];

    ptrInfo->startAddr = (*(uint32_t *)fradWord4RegAddr);
    ptrInfo->masterId =
        (uint8_t)(((*(uint32_t *)fradWord5RegAddr) & XSPI_FRAD0_WORD5_CMP_MDID_MASK) >> XSPI_FRAD0_WORD5_CMP_MDID_SHIFT);
    ptrInfo->isSecureAccess    = (bool)(((*(uint32_t *)fradWord5RegAddr) & XSPI_FRAD0_WORD5_CMP_SA_MASK) != 0UL);
    ptrInfo->isPrivilegeAccess = (bool)(((*(uint32_t *)fradWord5RegAddr) & XSPI_FRAD0_WORD5_CMP_PA_MASK) != 0UL);
    ptrInfo->isCompError       = (bool)(((*(uint32_t *)fradWord5RegAddr) & XSPI_FRAD0_WORD5_CMP_ERR_MASK) != 0UL);
    ptrInfo->isCompValid       = (bool)(((*(uint32_t *)fradWord5RegAddr) & XSPI_FRAD0_WORD5_CMPVALID_MASK) != 0UL);
}

/*!
 * brief Update SFP arbitration lock timeout counter.
 *
 * note The SFP arbitration lock time out counter starts when Page program wait flag asserted.
 *
 * param[in] base XSPI peripheral base address.
 * param[in] countValue The count value, specify the time in IPS clock cycles.
 *
 * retval kStatus_XSPI_RegWriteLocked Write operation to related register is locked.
 * retval kStatus_Success Success to update timeout counter.
 */
status_t XSPI_UpdateSFPArbitrationLockTimeoutCounter(XSPI_Type *base, uint32_t countValue)
{
    if (XSPI_CheckGlobalConfigLocked(base))
    {
        return kStatus_XSPI_RegWriteLocked;
    }

    base->SFP_ARB_TIMEOUT = countValue;

    return kStatus_Success;
}

/*!
 * brief Update the count of SFP access timeout counter.
 *
 * note The counter starts when any IP access pass SFP check(if enabled), and request is granted by XSPI arbiter and
 * XSPI is IDLE.
 *
 * note The counter does not start in case of IP access was granted by XSPI is not IDLE.
 *
 * param base XSPI peripheral base address.
 * param countValue The count value, specify the time in IPS clock cycles.
 *
 * retval kStatus_XSPI_RegWriteLocked Write operation to related register is locked.
 * retval kStatus_Success Success to update timeout counter.
 */
status_t XSPI_UpdateIPAccessTimeoutCounter(XSPI_Type *base, uint32_t countValue)
{
    if (XSPI_CheckGlobalConfigLocked(base))
    {
        return kStatus_XSPI_RegWriteLocked;
    }

    base->MTO = countValue;

    return kStatus_Success;
}

/*!
 * brief Get MDAD check error reason for specific target group.
 * 
 * param[in] base XSPI peripheral base address.
 * param[in] tgId Specify the target group.
 *
 * return The details of MDAD error reason, in type of ref xspi_mdad_error_reason_t.
 */
xspi_mdad_error_reason_t XSPI_GetMdadErrorReason(XSPI_Type *base, xspi_target_group_t tgId)
{
    uint8_t tmp8 = 0U;

    if (tgId == kXSPI_TargetGroup0)
    {
        tmp8 = (uint8_t)((base->TGIPCRS & XSPI_TGIPCRS_ERR_MASK) >> XSPI_TGIPCRS_ERR_SHIFT);
    }
    else
    {
        tmp8 = (uint8_t)((base->SUB_REG_MDAM_ARRAY[0].TGIPCRS_SUB & XSPI_TGIPCRS_SUB_ERR_MASK) >>
                         XSPI_TGIPCRS_SUB_ERR_SHIFT);
    }

    return (xspi_mdad_error_reason_t)tmp8;
}

/*!
 * brief Clear address write status for specific target group.
 * 
 * param[in] base XSPI peripheral base address.
 * param[in] tgId Specify the target group to clear address write status.
 */
void XSPI_ClearTgAddrWriteStatus(XSPI_Type *base, xspi_target_group_t tgId)
{
    uint32_t tgSfarsRegAddr = (uint32_t)base + (uint32_t)s_tgSfarsRegOffset[(uint8_t)(tgId)];

    *((uint32_t *)tgSfarsRegAddr) |= XSPI_TGSFARS_CLR_MASK;
}

/*!
 * brief Get address write status for specific target group.
 * 
 * param[in] base XSPI peripheral base address.
 * param[in] tgId Specify the target group.
 * param[out] ptrStatus Pointer to the variable in type of ref xspi_tg_add_write_status_t
                        to store address write status.
 */
void XSPI_GetTgAddrWriteStatus(XSPI_Type *base, xspi_target_group_t tgId, xspi_tg_add_write_status_t *ptrStatus)
{
    uint32_t tgSfarsRegAddr = (uint32_t)base + (uint32_t)s_tgSfarsRegOffset[(uint8_t)(tgId)];
    uint32_t tmp32          = (*(uint32_t *)tgSfarsRegAddr);

    ptrStatus->managerId       = (uint8_t)(tmp32 & XSPI_TGSFARS_TG_MID_MASK);
    ptrStatus->secureWrite     = (bool)((tmp32 & XSPI_TGSFARS_SA_MASK) != 0UL);
    ptrStatus->privilegedWrite = (bool)((tmp32 & XSPI_TGSFARS_PA_MASK) != 0UL);
}

/*!
 * brief Unlock Ip access arbitration.
 * 
 * param[in] base XSPI peripheral base address.
 * param[in] tgId Specify the target group.
 */
void XSPI_UnlockIpAccessArbitration(XSPI_Type *base, xspi_target_group_t tgId)
{
    uint32_t tgIpcrRegOffset[] = XSPI_TGIPCRS_REG_OFFSET;
    uint32_t tgIpcrRegAddr     = (uint32_t)base + tgIpcrRegOffset[(uint8_t)tgId];

    *(uint32_t *)tgIpcrRegAddr |= XSPI_SFP_TG_IPCR_ARB_UNLOCK_MASK;
}

/*!
 * brief Start IP access(including read and write).
 * 
 * param[in] base XSPI peripheral base address.
 * param[in] addr Address of external device to read/write.
 * param[in] seqIndex Sequence Id of the pre-programmed LUT.
 * param[in] byteSize Size of data to read/write, the unit of byte.
 * param[in] tgId Specify the target group used to write/read.
 * param[in] lockArbitration Lock arbitration or not.
 *
 * retval kStatus_XSPI_IpAccessAddrSettingInvalid Wrong Address input.
 * retval kStatus_XSPI_IpAccessIPCRInvalid Wrong seqindex or bytesize input.
 * retval kStatus_Success Success to start Ip access.
 */
status_t XSPI_StartIpAccess(
    XSPI_Type *base, uint32_t addr, uint8_t seqIndex, size_t byteSize, xspi_target_group_t tgId, bool lockArbitration)
{
    uint32_t tgSfarsRegAddr     = (uint32_t)base + (uint32_t)s_tgSfarsRegOffset[(uint8_t)tgId];
    uint32_t tgIpcrsRegAddr     = (uint32_t)base + (uint32_t)s_tgIpcrsRegOffset[(uint8_t)tgId];
    uint32_t sfpTgIpcrRegAddr   = (uint32_t)base + (uint32_t)s_sfpTgIpcrRegOffset[(uint8_t)tgId];
    uint32_t sfpTgIpSfarRegAddr = (uint32_t)base + (uint32_t)s_sfpTgIpSfarRegOffset[(uint8_t)tgId];
    uint32_t tgMdadRegAddr      = (uint32_t)base + (uint32_t)s_tgMdadRegOffset[(uint8_t)tgId];

    bool mdadEnabled = false;
    bool mdadValid   = false;
    uint32_t tmp32   = 0UL;

    XSPI_ClearErrorStatusFlags(base, kXSPI_ArbWinEventFlag);
    mdadEnabled = (bool)((base->MGC & XSPI_MGC_GVLDMDAD_MASK) != 0UL);
    mdadValid   = (((*(uint32_t *)tgMdadRegAddr) & XSPI_TG0MDAD_VLD_MASK) != 0UL);
    /* Check the selected target group queue is empty. */
    while (((*(volatile uint32_t *)tgSfarsRegAddr) & XSPI_TGSFARS_VLD_MASK) != 0UL)
    {
    }

    /* Set target address. */
    (*(uint32_t *)sfpTgIpSfarRegAddr) = addr;

    if (mdadEnabled && mdadValid)
    {
        do
        {
            tmp32 = (*(volatile uint32_t *)tgSfarsRegAddr & (XSPI_TGSFARS_VLD_MASK | XSPI_TGSFARS_ERR_MASK));
            if (tmp32 == XSPI_TGSFARS_ERR_MASK)
            {
                /* The manager ID or write attributes to set SFAR register is not valid. */
                return kStatus_XSPI_IpAccessAddrSettingInvalid;
            }
        } while (tmp32 != XSPI_TGSFARS_VLD_MASK);
    }

    /* Set transfer size and sequence ID. */
    (*(uint32_t *)sfpTgIpcrRegAddr) = XSPI_SFP_TG_IPCR_IDATSZ(byteSize) | XSPI_SFP_TG_IPCR_ARB_LOCK(lockArbitration) |
                                      XSPI_SFP_TG_IPCR_SEQID(seqIndex);
    if (mdadEnabled && mdadValid)
    {
        do
        {
            tmp32 = (*(volatile uint32_t *)tgIpcrsRegAddr & (XSPI_TGIPCRS_ERR_MASK | XSPI_TGIPCRS_VLD_MASK));
            if ((tmp32 & XSPI_TGIPCRS_ERR_MASK) != 0UL)
            {
                return kStatus_XSPI_IpAccessIPCRInvalid;
            }
        } while ((tmp32 & XSPI_TGIPCRS_VLD_MASK) == 0UL);
    }

    /* Blocking until the IP request is granted. */
    while (XSPI_CheckIpRequestGranted(base) == false)
    {
    }
    XSPI_ClearErrorStatusFlags(base, kXSPI_ArbWinEventFlag);

    return kStatus_Success;
}

/*!
 * brief Start IP access in non-blocking way.
 * 
 * param[in] base XSPI peripheral base address.
 * param[in] addr Address of external device to read/write.
 * param[in] seqIndex Sequence Id of the pre-programmed LUT.
 * param[in] byteSize Size of data to transfer, the unit of byte.
 * param[in] tgId Specify the target group to use.
 * param[in] lockArbitration Lock arbitration or not.
 * 
 * retval kStatus_Success Success to start Ip access.
 * retval kStatus_XSPI_IpAccessIPCRInvalid Invalid to set IPCR register.
 * retval kStatus_XSPI_IpAccessAddrSettingInvalid Invalid to set SFAR register.
 */
status_t XSPI_StartIpAccessNonBlocking(XSPI_Type *base, uint32_t addr, uint8_t seqIndex,
                                        size_t byteSize, xspi_target_group_t tgId, bool lockArbitration)
{
    uint32_t tgSfarsRegAddr     = (uint32_t)base + (uint32_t)s_tgSfarsRegOffset[(uint8_t)tgId];
    uint32_t sfpTgIpcrRegAddr   = (uint32_t)base + (uint32_t)s_sfpTgIpcrRegOffset[(uint8_t)tgId];
    uint32_t sfpTgIpSfarRegAddr = (uint32_t)base + (uint32_t)s_sfpTgIpSfarRegOffset[(uint8_t)tgId];
    uint32_t tgMdadRegAddr      = (uint32_t)base + (uint32_t)s_tgMdadRegOffset[(uint8_t)tgId];

    bool mdadEnabled = false;
    bool mdadValid   = false;

    XSPI_ClearErrorStatusFlags(base, kXSPI_ArbWinEventFlag);
    mdadEnabled = (bool)((base->MGC & XSPI_MGC_GVLDMDAD_MASK) != 0UL);
    mdadValid   = (((*(uint32_t *)tgMdadRegAddr) & XSPI_TG0MDAD_VLD_MASK) != 0UL);

    /* Check the selected target group queue is empty. */
    while (((*(volatile uint32_t *)tgSfarsRegAddr) & XSPI_TGSFARS_VLD_MASK) != 0UL)
    {
    }

    if (mdadEnabled && mdadValid)
    {
        if (tgId == kXSPI_TargetGroup0)
        {
            XSPI_EnableInterrupts(base, (uint64_t)(kXSPI_Tg0SfarErrIntEnable | kXSPI_Tg0IpcrErrIntEnable));
        }
        else
        {
            XSPI_EnableInterrupts(base, (uint64_t)(kXSPI_Tg1SfarErrIntEnable | kXSPI_Tg1IpcrErrIntEnable));
        }
    }

    /* Set target address. */
    (*(uint32_t *)sfpTgIpSfarRegAddr) = addr;
    /* Set transfer size and sequence ID. */
    (*(uint32_t *)sfpTgIpcrRegAddr) = XSPI_SFP_TG_IPCR_IDATSZ(byteSize) | XSPI_SFP_TG_IPCR_ARB_LOCK(lockArbitration) |
                                      XSPI_SFP_TG_IPCR_SEQID(seqIndex);

    if (mdadEnabled && mdadValid)
    {
        if (tgId == kXSPI_TargetGroup0)
        {
            XSPI_DisableInterrupts(base, (uint64_t)(kXSPI_Tg0SfarErrIntEnable | kXSPI_Tg0SfarErrIntEnable));
        }
        else
        {
            XSPI_DisableInterrupts(base, (uint64_t)(kXSPI_Tg1SfarErrIntEnable | kXSPI_Tg1SfarErrIntEnable));
        }

        if (g_IpAccessIpcrFail)
        {
            return kStatus_XSPI_IpAccessIPCRInvalid;
        }

        if (g_IpAccessSfarFail)
        {
            return kStatus_XSPI_IpAccessAddrSettingInvalid;
        }
    }

    XSPI_EnableInterrupts(base, (uint64_t)(kXSPI_ArbitrationWinIntEnable));

    return kStatus_Success;    
}

/***************************** IPS Access Control Low-Level Interfaces End ********************************/

/***************************** IPS Access Control Functional Interfaces Start ********************************/
/*!
 * brief Set IP access configurations, including SFP configurations, sfp arbitration lock timeout value, Ip access
 * timeout value.
 * 
 * param[in] base XSPI peripheral base address.
 * param[in] ptrIpAccessConfig Pointer to the variable which contains Ip access configurations.
 *
 * retval kStatus_XSPI_RegWriteLocked Write operation to related register is locked.
 * retval kStatus_Success Success to update timeout counter.
 */
status_t XSPI_SetIpAccessConfig(XSPI_Type *base, xspi_ip_access_config_t *ptrIpAccessConfig)
{
    assert(ptrIpAccessConfig != NULL);

    status_t status = kStatus_Success;

    XSPI_ClearIPAccessSeqPointer(base);
    XSPI_UpdateSFPConfig(base, ptrIpAccessConfig->ptrSfpMdadConfig, ptrIpAccessConfig->ptrSfpFradConfig);
    status = XSPI_UpdateSFPArbitrationLockTimeoutCounter(base, ptrIpAccessConfig->sfpArbitrationLockTimeoutValue);

    if (status == kStatus_Success)
    {
        status = XSPI_UpdateIPAccessTimeoutCounter(base, ptrIpAccessConfig->ipAccessTimeoutValue);
    }

    return status;
}

/*!
 * brief Sends a buffer of data bytes using blocking method.
 * note This function blocks via polling until all bytes have been sent.
 * param base XSPI peripheral base address
 * param buffer The data bytes to send
 * param size The number of data bytes to send
 * retval kStatus_Success write success without error
 * retval kStatus_XSPI_SequenceExecutionTimeout sequence execution timeout
 * retval kStatus_XSPI_IpCommandSequenceError IP command sequence error detected
 * retval kStatus_XSPI_IpCommandGrantTimeout IP command grant timeout detected
 */
status_t XSPI_WriteBlocking(XSPI_Type *base, uint8_t *buffer, size_t size)
{
    status_t result = kStatus_Success;
    uint32_t i      = 0;

    /* Blocking until TX buffer is unlocked. */
    while (XSPI_CheckTxBuffLockOpen(base) == false)
    {
    }

    base->TBCT = 256UL - ((uint32_t)size / 4UL - 1UL);
    /* Send data buffer */
    while (0U != size)
    {
        result = XSPI_CheckAndClearError(base, base->ERRSTAT);

        if (kStatus_Success != result)
        {
            return result;
        }

        /* Write watermark level data into tx fifo . */
        /* Write word aligned data into tx fifo. */
        for (i = 0U; i < (size / 4U); i++)
        {
            while (1UL == ((base->SR & XSPI_SR_TXFULL_MASK) >> XSPI_SR_TXFULL_SHIFT))
            {
            }
            base->TBDR = *(uint32_t *)buffer;
            buffer += 4U;
        }

        /* Adjust size by the amount processed. */
        size -= 4U * i;

        /* Write word un-aligned data into tx fifo. */
        if (0x00U != size)
        {
            uint32_t tempVal = 0x00U;

            for (uint32_t j = 0U; j < size; j++)
            {
                tempVal |= ((uint32_t)*buffer++ << (8U * j));
            }

            while (1UL == ((base->SR & XSPI_SR_TXFULL_MASK) >> XSPI_SR_TXFULL_SHIFT))
            {
            }
            base->TBDR = tempVal;
        }

        size = 0U;
    }

    /*clear TX Buffer Fill Flag*/
    base->FR = XSPI_FR_TBFF_MASK;

    while (XSPI_CheckIPAccessAsserted(base))
    {
    }

    return result;
}

/*!
 * brief Receives a buffer of data bytes using a blocking method.
 * note This function blocks via polling until all bytes have been sent.
 * param base XSPI peripheral base address
 * param buffer The data bytes to send
 * param size The number of data bytes to receive
 * retval kStatus_Success read success without error
 * retval kStatus_XSPI_SequenceExecutionTimeout sequence execution timeout
 * retval kStatus_XSPI_IpCommandSequenceError IP command sequence error detected
 * retval kStatus_XSPI_IpCommandGrantTimeout IP command grant timeout detected
 */
status_t XSPI_ReadBlocking(XSPI_Type *base, uint8_t *buffer, size_t size)
{
    assert(size <= XSPI_IP_RX_BUFFER_SIZE);

    uint32_t rxWatermark  = base->RBCT + 1UL;
    status_t status       = kStatus_Success;
    uint32_t i            = 0UL;
    uint32_t removedCount = 0UL;
    uint32_t pollingBitMask     = XSPI_SR_RXWE_MASK;

    if (XSPI_CheckFSMValid(base) == false)
    {
        XSPI_ClearRxBuffer(base);
        return kStatus_NoTransferInProgress;
    }

    if (size == XSPI_IP_RX_BUFFER_SIZE)
    {
        pollingBitMask = XSPI_SR_RXFULL_MASK;
    }

    while ((1UL != (base->SR & XSPI_SR_BUSY_MASK)) && (((base->SR & XSPI_SR_IP_ACC_MASK) >> XSPI_SR_IP_ACC_SHIFT) != 0UL))
    {
    }

      /* Loop until Rx buffer watermark exceeded status asserted. */
      while ((base->SR & pollingBitMask) == 0UL)
      {
          if((base->ERRSTAT & XSPI_ERRSTAT_TO_ERR_MASK) != 0UL)
          {
              XSPI_ClearRxBuffer(base);
              base->ERRSTAT = XSPI_ERRSTAT_TO_ERR_MASK;
              return kStatus_Timeout;
          }
      }

    uint32_t adjuestedSize = size + (4UL - size % 4UL) % 4UL;

    if (XSPI_GetRxBufferAvailableBytesCount(base) != adjuestedSize)
    {
        XSPI_ClearRxBuffer(base);
        return kStatus_XSPI_RxBufferEntriesCountError;
    }
    /* Send data buffer */
    while (0UL != size)
    {
        status = XSPI_CheckAndClearError(base, base->ERRSTAT);

        if (kStatus_Success != status)
        {
            break;
        }

        /* Read word aligned data from rx fifo. */
        if (size >= 4UL * rxWatermark)
        {
            removedCount = XSPI_GetRxBufferRemovedBytesCount(base);
            for (i = 0UL; i < rxWatermark; i++)
            {
                *(uint32_t *)buffer = base->RBDR[i];
                buffer += 4UL;
            }

            /* RX buffer POP, trigger RX pop event. */
            XSPI_TriggerRxBufferPopEvent(base);
            while ((XSPI_GetRxBufferRemovedBytesCount(base) - removedCount) != (rxWatermark * 4UL))
            {
            }
            size = size - 4UL * rxWatermark;
        }
        else
        {
            for (i = 0UL; i < (size / 4UL); i++)
            {
                *(uint32_t *)buffer = base->RBDR[i];
                buffer += 4UL;
            }

            /* Adjust size by the amount processed. */
            size -= 4UL * i;

            /* Read word un-aligned data from rx fifo. */
            if (0UL != size)
            {
                uint32_t tempVal = base->RBDR[i];

                for (i = 0U; i < size; i++)
                {
                    *buffer++ = ((uint8_t)(tempVal >> (8U * i)) & 0xFFU);
                }
            }

            size = 0UL;
        }
    }

    /* Clear Rx buffer after all entries are read out. */
    XSPI_ClearRxBuffer(base);

    return status;
}

/*!
 * brief Execute command to transfer a buffer data bytes using a blocking method.
 * param base XSPI peripheral base address
 * param xfer pointer to the transfer structure.
 * retval kStatus_Success command transfer success without error
 * retval kStatus_XSPI_SequenceExecutionTimeout sequence execution timeout
 * retval kStatus_XSPI_IpCommandSequenceError IP command sequence error detected
 * retval kStatus_XSPI_IpCommandGrantTimeout IP command grant timeout detected
 */
status_t XSPI_TransferBlocking(XSPI_Type *base, xspi_transfer_t *xfer)
{
    status_t status    = kStatus_Success;
    uint32_t dataSize  = xfer->dataSize;
    uint8_t *ptrBuffer = (uint8_t *)xfer->data;
    uint32_t transferSize      = 0UL;
    uint32_t addr = xfer->deviceAddress;

    if ((xfer->cmdType == kXSPI_Write) || (xfer->cmdType == kXSPI_Config))
    {
        while(dataSize > 0UL)
        {
            if (dataSize <= g_programFlashSize)
            {
                transferSize = dataSize;
            }
            else
            {
                transferSize = g_programFlashSize;
            }

            status = XSPI_StartIpAccess(base, addr, xfer->seqIndex, transferSize, xfer->targetGroup,
                                    xfer->lockArbitration);
            if (status != kStatus_Success)
            {
                return status;
            }

            /* Clear TX buffer pointer. */
            XSPI_ClearTxBuffer(base);

            status = XSPI_WriteBlocking(base, ptrBuffer, transferSize);
            if (status != kStatus_Success)
            {
                return status;
            }
            dataSize -= transferSize;
            
            if (dataSize != 0UL)
            {
                ptrBuffer += transferSize;
                addr += transferSize;
            }
        }
    }
    else if (xfer->cmdType == kXSPI_Read)
    {
        uint32_t rxBufferWaterMark = 0UL;
        while (dataSize > 0UL)
        {
            XSPI_ClearRxBuffer(base);

            /* calculate watermark and transfer size based on data size. */
            if (dataSize >= XSPI_IP_RX_BUFFER_SIZE)
            {
                /* In case of data size bigger than RX buffer size, the big size request should be
                break into small size sub-request, and the transfer size of sub-request is equal to buffer size. */
                rxBufferWaterMark = XSPI_IP_RX_BUFFER_SIZE >> 1UL;
                transferSize      = XSPI_IP_RX_BUFFER_SIZE;
            }
            else if (dataSize > (XSPI_IP_RX_BUFFER_SIZE >> 1UL))
            {
                /* In case of data size bigger than maximum watermark value, watermark set as maximum value, and
                transfer size is data size. */
                rxBufferWaterMark = XSPI_IP_RX_BUFFER_SIZE >> 1UL;
                transferSize      = dataSize;
            }
            else if (dataSize % 4UL == 0UL)
            {
                /* In case of data size less than maximum watermark value and is aligned with 4 bytes, watermark set
                as data size, transfer size also set as data size. */
                rxBufferWaterMark = dataSize;
                transferSize      = dataSize;
            }
            else
            {
                /* In case of data size is not 4-byte aligned, watermark set as closest value, and transfer size
                set as data size. */
                rxBufferWaterMark = (dataSize / 4UL) << 2UL;
                if (rxBufferWaterMark == 0UL)
                {
                    rxBufferWaterMark = 4UL;
                }
                transferSize = dataSize;
            }
            (void)XSPI_UpdateRxBufferWaterMark(base, rxBufferWaterMark);

            status =
                XSPI_StartIpAccess(base, addr, xfer->seqIndex, transferSize, xfer->targetGroup, false);
            if (status != kStatus_Success)
            {
                return status;
            }
            status = XSPI_ReadBlocking(base, ptrBuffer, transferSize);
            if (status == kStatus_Timeout)
            {
                XSPI_ResetTgQueue(base);
                XSPI_ResetSfmAndAhbDomain(base);
                break;
            }
            dataSize -= transferSize;
            if (dataSize != 0UL)
            {
                ptrBuffer += transferSize;
                addr += transferSize;
            }
        }
    }
    else
    {
        /* Set command. */
        status = XSPI_StartIpAccess(base, xfer->deviceAddress, xfer->seqIndex, 0UL, xfer->targetGroup,
                                    xfer->lockArbitration);
        if (status != kStatus_Success)
        {
            return status;
        }
    }

    /* Wait for bus to be idle before changing flash configuration. */
    while (!XSPI_GetBusIdleStatus(base))
    {
    }

    if (xfer->cmdType == kXSPI_Command)
    {
        status = XSPI_CheckAndClearError(base, base->ERRSTAT);
        while (XSPI_CheckIPAccessAsserted(base))
        {
        }
    }

    return status;
}

/*!
 * brief Initializes the XSPI handle which is used in transactional functions.
 *
 * param base XSPI peripheral base address.
 * param handle pointer to xspi_handle_t structure to store the transfer state.
 * param callback pointer to user callback function.
 * param userData user parameter passed to the callback function.
 */
void XSPI_TransferCreateHandle(XSPI_Type *base,
                               xspi_handle_t *handle,
                               xspi_transfer_callback_t callback,
                               void *userData)
{
    assert(NULL != handle);

    uint32_t instance = XSPI_GetInstance(base);

    /* Zero handle. */
    (void)memset(handle, 0, sizeof(*handle));

    /* Set callback and userData. */
    handle->completionCallback = callback;
    handle->userData           = userData;

    /* Enable NVIC interrupt. */
    (void)EnableIRQ(s_xspiIrqs[instance]);

#if defined(FSL_DRIVER_TRANSFER_DOUBLE_WEAK_IRQ) && FSL_DRIVER_TRANSFER_DOUBLE_WEAK_IRQ
    s_xspiHandle[instance] = handle;
#endif
}


/*!
 * brief Performs a interrupt non-blocking transfer on the XSPI bus.
 *
 * note Calling the API returns immediately after transfer initiates. The user needs
 * to call XSPI_GetTransferCount to poll the transfer status to check whether
 * the transfer is finished. If the return status is not kStatus_XSPI_Busy, the transfer
 * is finished. For XSPI_Read, the dataSize should be multiple of rx watermark level, or
 * XSPI could not read data properly.
 *
 * param base XSPI peripheral base address.
 * param handle pointer to xspi_handle_t structure which stores the transfer state.
 * param xfer pointer to xspi_transfer_t structure.
 * retval kStatus_Success Successfully start the data transmission.
 * retval kStatus_XSPI_Busy Previous transmission still not finished.
 */
status_t XSPI_TransferNonBlocking(XSPI_Type *base, xspi_handle_t *handle, xspi_transfer_t *xfer)
{
    status_t status = kStatus_Success;
    uint32_t instance = XSPI_GetInstance(base);

    assert(NULL != handle);
    assert(NULL != xfer);

    /* Check if the I2C bus is idle - if not return busy status. */
    if (handle->state != (uint32_t)kXSPI_StateIdle)
    {
        status = kStatus_XSPI_Busy;
    }
    else
    {
        handle->dataSize = xfer->dataSize;
        handle->transferTotalSize = xfer->dataSize;
        handle->data = (uint8_t *)xfer->data;
        s_xspiTransfer[instance] = xfer;

        if (xfer->cmdType == kXSPI_Read)
        {

            status = XSPI_StartIpReadNonBlocking(base, handle, xfer->dataSize);
        }
        else if ((xfer->cmdType == kXSPI_Config) || (xfer->cmdType == kXSPI_Write))
        {
            status = XSPI_StartIpWriteNonBlocking(base, handle, xfer->dataSize);
        }
        else
        {
            handle->state = kXSPI_StateStartIpAccess;
            status = XSPI_StartIpAccessNonBlocking(base, xfer->deviceAddress, xfer->seqIndex, 0UL, xfer->targetGroup,
                                                xfer->lockArbitration);
            if (status != kStatus_Success)
            {
                return status;
            }
            if (handle->state != kXSPI_StateIpAccessGranted)
            {
                status = kStatus_XSPI_IpAccessNotGranted;
            }
            else
            {
                handle->state = kXSPI_StateIdle;
                status = kStatus_Success;
            }
        }
    }

    return status;
}

/*!
 * brief Gets the master transfer status during a interrupt non-blocking transfer.
 *
 * param base XSPI peripheral base address.
 * param handle pointer to xspi_handle_t structure which stores the transfer state.
 * param count Number of bytes transferred so far by the non-blocking transaction.
 * retval kStatus_InvalidArgument count is Invalid.
 * retval kStatus_Success Successfully return the count.
 */
status_t XSPI_TransferGetCount(XSPI_Type *base, xspi_handle_t *handle, size_t *count)
{
    assert(NULL != handle);

    status_t status = kStatus_Success;

    if (handle->state == (uint32_t)kXSPI_StateIdle)
    {
        status = kStatus_NoTransferInProgress;
    }
    else
    {
        *count = handle->transferTotalSize - handle->dataSize;
    }

    return status;
}

/*!
 * brief Aborts an interrupt non-blocking transfer early.
 *
 * note This API can be called at any time when an interrupt non-blocking transfer initiates
 * to abort the transfer early.
 *
 * param base XSPI peripheral base address.
 * param handle pointer to xspi_handle_t structure which stores the transfer state
 */
void XSPI_TransferAbort(XSPI_Type *base, xspi_handle_t *handle)
{
    assert(NULL != handle);

    XSPI_DisableInterrupts(base, (uint64_t)(kXSPI_RxBufferDrainIntEnable | kXSPI_RxBufferOverflowIntEnable |
                            kXSPI_TimeoutErrIntEnable | kXSPI_IpCmdTrigFailErrorIntEnable |
                            kXSPI_TxBufferFillIntEnable | kXSPI_TxBufferUnderrunIntEnable|
                            kXSPI_TimeoutErrIntEnable | kXSPI_IpCmdTrigFailErrorIntEnable | 
                            kXSPI_ArbitrationWinIntEnable));
    handle->state = (uint32_t)kXSPI_StateIdle;
}

/***************************** IPS Access Control Functional Interfaces End ********************************/

/***************************** AHB Access Control Low-Level Interfaces Start ********************************/
/*!
 * brief Set AHB transaction(read/write) boundary.
 *
 * note For AHB write, XSPI keeps track of the start address and then compares it with the address of subsequent
 * transaction receive on the AHB bus, when the input alignment is reached, XSPI negates AHB HREADY to block new
 * accesses. This signal will be kept low until all the previous received write data is written to external memory and
 * chip select is de-asserted. After that it allows next AHB write data to be received by making HREADY high thus
 * ensuring that transaction is split at address boundary on external memory.
 *
 * note For AHB read, XSPI checks the start address and end address to see if it is crossing the address boundary
 * specified by input alignment. If the transaction crosses the address boundary, then it reduces the transaction size
 * such that the data pre-fetch is stopped before the address boundary. Now if the AHB master is reading the data
 * sequentially it will get buffer hits up to the input alignment boundary. When it reaches the next address boundary
 * it will get a buffer miss and a new data pre-fetch will be launched towards the external memory device.
 *
 * param[in] base XSPI peripheral base address.
 * param[in] alignment Specify the AHB alignment, in type of ref xspi_ahb_alignment_t.
 *
 * retval kStatus_XSPI_AhbReadAccessAsserted Fail due to an AHB read access already asserted
 * retval kStatus_XSPI_AhbWriteAccessAsserted Fail due to an AHB write access already asserted
 * retval kStatus_XSPI_RegWriteLocked Fail due to write operation to related registers is locked.
 * retval kStatus_Success Success to set AHB access boundary.
 */
status_t XSPI_SetAhbAccessBoundary(XSPI_Type *base, xspi_ahb_alignment_t alignment)
{
#if defined(XSPI_BFGENCR_ALIGN_MASK)
    if (XSPI_CheckAhbReadAccessAsserted(base))
    {
        return kStatus_XSPI_AhbReadAccessAsserted;
    }

    if (XSPI_CheckAhbWriteAccessAsserted(base))
    {
        return kStatus_XSPI_AhbWriteAccessAsserted;
    }

    if (XSPI_CheckGlobalConfigLocked(base))
    {
        return kStatus_XSPI_RegWriteLocked;
    }

    base->BFGENCR = ((base->BFGENCR & ~(XSPI_BFGENCR_ALIGN_MASK)) | XSPI_BFGENCR_ALIGN(alignment));
#endif /* defined(XSPI_BFGENCR_ALIGN_MASK) */
    
    return kStatus_Success;
}
/*!
 * brief Set AHB read sequence Id.
 * 
 * param[in] base XSPI peripheral base address.
 * param[in] seqId Specify the sequence Id in LUT used for AHB read.
 *
 * retval kStatus_XSPI_AhbReadAccessAsserted Fail due to an AHB read access already asserted
 * retval kStatus_XSPI_AhbWriteAccessAsserted Fail due to an AHB write access already asserted
 * retval kStatus_XSPI_RegWriteLocked Fail due to write operation to related registers is locked.
 * retval kStatus_Success Success to set AHB read sequence Id.
 */
status_t XSPI_SetAhbReadDataSeqId(XSPI_Type *base, uint8_t seqId)
{
    if (XSPI_CheckAhbReadAccessAsserted(base))
    {
        return kStatus_XSPI_AhbReadAccessAsserted;
    }

    if (XSPI_CheckAhbWriteAccessAsserted(base))
    {
        return kStatus_XSPI_AhbWriteAccessAsserted;
    }

    if (XSPI_CheckGlobalConfigLocked(base))
    {
        return kStatus_XSPI_RegWriteLocked;
    }

    base->BFGENCR = ((base->BFGENCR & (~XSPI_BFGENCR_SEQID_MASK)) | XSPI_BFGENCR_SEQID(seqId));

    return kStatus_Success;
}
/*!
 * brief Set AHB write sequence Id.
 * 
 * param[in] base XSPI peripheral base address.
 * param[in] seqId Specify the sequence Id in LUT used for AHB write.
 *
 * retval kStatus_XSPI_AhbReadAccessAsserted Fail due to an AHB read access already asserted
 * retval kStatus_XSPI_AhbWriteAccessAsserted Fail due to an AHB write access already asserted
 * retval kStatus_XSPI_RegWriteLocked Fail due to write operation to related registers is locked.
 * retval kStatus_Success Success to set AHB write sequence Id.
 */
status_t XSPI_SetAhbWriteDataSeqId(XSPI_Type *base, uint8_t seqId)
{
    if (XSPI_CheckAhbReadAccessAsserted(base))
    {
        return kStatus_XSPI_AhbReadAccessAsserted;
    }

    if (XSPI_CheckAhbWriteAccessAsserted(base))
    {
        return kStatus_XSPI_AhbWriteAccessAsserted;
    }

    if (XSPI_CheckGlobalConfigLocked(base))
    {
        return kStatus_XSPI_RegWriteLocked;
    }

    base->BFGENCR = ((base->BFGENCR & (~XSPI_BFGENCR_SEQID_WR_MASK)) | XSPI_BFGENCR_SEQID_WR(seqId));

    return kStatus_Success;
}
/*!
 * brief Set AHB buffer configurations.
 * 
 * param[in] base XSPI peripheral base address.
 * param[in] ptrBuffer0Config Pointer to the variable which contain buffer0 configurations.
 * param[in] ptrBuffer1Config Pointer to the variable which contain buffer1 configurations. 
 * param[in] ptrBuffer2Config Pointer to the variable which contain buffer2 configurations.
 * param[in] ptrBuffer3Config Pointer to the variable which contain buffer3 configurations.
 * 
 * retval kStatus_XSPI_AhbReadAccessAsserted Fail due to an AHB read access already asserted.
 * retval kStatus_XSPI_AhbWriteAccessAsserted Fail due to an AHB write access already asserted.
 * retval kStatus_Success Success to set AHB buffer configurations.
 */
status_t XSPI_SetAhbBufferConfig(XSPI_Type *base,
                                 xspi_ahbBuffer_config_t *ptrBuffer0Config,
                                 xspi_ahbBuffer_config_t *ptrBuffer1Config,
                                 xspi_ahbBuffer_config_t *ptrBuffer2Config,
                                 xspi_ahbBuffer_config_t *ptrBuffer3Config)
{
    assert(ptrBuffer0Config != NULL);
    assert(ptrBuffer1Config != NULL);
    assert(ptrBuffer2Config != NULL);
    assert(ptrBuffer3Config != NULL);

    assert((ptrBuffer0Config->bufferSize + ptrBuffer1Config->bufferSize + ptrBuffer2Config->bufferSize +
            ptrBuffer3Config->bufferSize) == 512U);

    xspi_ahbBuffer_config_t *ahbBufferConfigs[4];
    uint32_t subBufferStartAddr; /* Specify the upper 16-bits of the start address range. */
    uint32_t subBufferEndAddr;   /* Specify the upper 16-bits of the end address range. */
    xspi_ahbBuffer_sub_buffer_config_t *curSubBufferConfig = NULL;

    ahbBufferConfigs[0] = ptrBuffer0Config;
    ahbBufferConfigs[1] = ptrBuffer1Config;
    ahbBufferConfigs[2] = ptrBuffer2Config;
    ahbBufferConfigs[3] = ptrBuffer3Config;
    if (XSPI_CheckAhbReadAccessAsserted(base))
    {
        return kStatus_XSPI_AhbReadAccessAsserted;
    }

    if (XSPI_CheckAhbWriteAccessAsserted(base))
    {
        return kStatus_XSPI_AhbWriteAccessAsserted;
    }

    (void)XSPI_UpdateAhbBufferSize(base, ptrBuffer0Config->bufferSize, ptrBuffer1Config->bufferSize,
                                   ptrBuffer2Config->bufferSize, ptrBuffer3Config->bufferSize);

    for (uint8_t i = 0U; i < XSPI_BUFCR_COUNT; i++)
    {
        /* The buffer size is set as transfer size, because when buffer-miss the
        XSPI will clear AHB buffer and fetch size of data from external device.
        If transfer size is less than buffer size, then left space will never used. */
        base->BUFCR[i] =
            XSPI_BUFCR_MSTRID(ahbBufferConfigs[i]->masterId) | XSPI_BUFCR_ADATSZ(ahbBufferConfigs[i]->bufferSize);

        /* Same bit field represent different feature for different AHB buffer. */
        if ((i == 0U) || (i == 1U))
        {
            base->BUFCR[i] |= XSPI_BUFCR_HP_EN(ahbBufferConfigs[i]->enaPri.enablePriority);
        }

        if (i == 3U)
        {
            base->BUFCR[i] |= XSPI_BUFCR_ALLMST(ahbBufferConfigs[i]->enaPri.enableAllMaster);
        }

        if ((ahbBufferConfigs[i]->ptrSubBuffer0Config == NULL) && (ahbBufferConfigs[i]->ptrSubBuffer1Config == NULL) &&
            (ahbBufferConfigs[i]->ptrSubBuffer2Config == NULL) && (ahbBufferConfigs[i]->ptrSubBuffer3Config == NULL))
        {
            base->BUFCR[i] &= ~XSPI_BUFCR_SUB_DIV_EN_MASK;
        }
        else
        {
            if (((1U << (4U - (uint8_t)(ahbBufferConfigs[i]->ptrSubBuffer0Config->divFactor))) +
                 (1U << (4U - (uint8_t)(ahbBufferConfigs[i]->ptrSubBuffer1Config->divFactor))) +
                 (1U << (4U - (uint8_t)(ahbBufferConfigs[i]->ptrSubBuffer2Config->divFactor)))) > 16U)
            {
                return kStatus_XSPI_AhbSubBufferFactorError;
            }

            base->BUFCR[i] |= XSPI_BUFCR_SUB_DIV_EN_MASK |
                              XSPI_BUFCR_SUBBUF0_DIV(ahbBufferConfigs[i]->ptrSubBuffer0Config->divFactor) |
                              XSPI_BUFCR_SUBBUF1_DIV(ahbBufferConfigs[i]->ptrSubBuffer1Config->divFactor) |
                              XSPI_BUFCR_SUBBUF1_DIV(ahbBufferConfigs[i]->ptrSubBuffer2Config->divFactor);

            /* Set AHB buffer sub buffer start and end address range. */
            for (uint8_t j = 0U; j < XSPI_BUF_ADDR_RANGE_COUNT2; j++)
            {
                curSubBufferConfig =
                    (xspi_ahbBuffer_sub_buffer_config_t *)(ahbBufferConfigs[i]->ptrSubBuffer0Config) + j;
                subBufferStartAddr = ((curSubBufferConfig->startAddr) & 0xFFFF0000UL) >> 16UL;
                subBufferEndAddr   = ((curSubBufferConfig->endAddr) & 0xFFFF0000UL) >> 16UL;

                base->BUF_ADDR_RANGE[i][j] =
                    XSPI_BUF_ADDR_RANGE_STARTADR(subBufferStartAddr) | XSPI_BUF_ADDR_RANGE_ENDADR(subBufferEndAddr);

                if (curSubBufferConfig->enableAhbMonitor)
                {
                    /* Enable AHB monitor to current sub division. */
                    XSPI_EnableAhbBufferPerfMonitor(base, i, j);
                }
            }
        }
    }
    return kStatus_Success;
}

/*!
 * brief Set AHB transaction(read/write) split size.
 *
 * note For AHB write, if enable split feature(ahbSplitSize not set as kXSPI_AhbSplitSizeDisabled), XSPI will split one
 * single AHB write burst into smaller bursts on external device side.
 *
 * note For AHB read, if enable split feature, HW will realign the prefetch size to split size and in that way split
 * a single read burst into smaller bursts.
 *
 * param[in] base XSPI peripheral base address.
 * param[in] ahbSplitSize Specify the AHB split size.
 *
 * retval kStatus_XSPI_AhbReadAccessAsserted Fail due to an AHB read access already asserted
 * retval kStatus_XSPI_AhbWriteAccessAsserted Fail due to an AHB write access already asserted
 * retval kStatus_XSPI_RegWriteLocked Fail due to write operation to related registers is locked.
 * retval kStatus_Success Success to set AHB access split size.
 */
status_t XSPI_SetAhbAccessSplitSize(XSPI_Type *base, xspi_ahb_split_size_t ahbSplitSize)
{
    if (XSPI_CheckAhbReadAccessAsserted(base))
    {
        return kStatus_XSPI_AhbReadAccessAsserted;
    }

    if (XSPI_CheckAhbWriteAccessAsserted(base))
    {
        return kStatus_XSPI_AhbWriteAccessAsserted;
    }

    if (XSPI_CheckGlobalConfigLocked(base))
    {
        return kStatus_XSPI_RegWriteLocked;
    }

    if (ahbSplitSize == kXSPI_AhbSplitSizeDisabled)
    {
        base->BFGENCR &= ~XSPI_BFGENCR_SPLITEN_MASK;
    }
    else
    {
        base->BFGENCR |= XSPI_BFGENCR_SPLITEN_MASK;
        base->BFGENCR = (base->BFGENCR & (~XSPI_BFGENCR_AHBSSIZE_MASK)) | XSPI_BFGENCR_AHBSSIZE(ahbSplitSize);
    }

    return kStatus_Success;
}

/*!
 * brief Specify how long XSPI can hold HEADY low while waiting for response to an AHB transfer.
 *
 * note If the specified time out value expired, an AHB illegal transaction error will be triggerred.
 *
 * param[in] base XSPI peripheral base address.
 * param[in] timeoutValue In multiples of 50000 AHB clock cycles, default value is 3,
 *                      it means waiting for 150000 AHB clock cycles.
 *
 * retval kStatus_XSPI_AhbReadAccessAsserted Fail due to an AHB read access already asserted
 * retval kStatus_XSPI_AhbWriteAccessAsserted Fail due to an AHB write access already asserted
 * retval kStatus_Success Success to set AHB HREADY timeout value.
 */
status_t XSPI_UpdateAhbHreadyTimeOutValue(XSPI_Type *base, uint16_t timeoutValue)
{
    if (XSPI_CheckAhbReadAccessAsserted(base))
    {
        return kStatus_XSPI_AhbReadAccessAsserted;
    }

    if (XSPI_CheckAhbWriteAccessAsserted(base))
    {
        return kStatus_XSPI_AhbWriteAccessAsserted;
    }

    base->AHRDYTO = XSPI_AHRDYTO_HREADY_TO(timeoutValue);

    return kStatus_Success;
}

/*!
 * brief Pre-define error payload which will be provided on the read data bus when the HEADY timeout counter expires.
 *
 * note If the incoming AHB read request master is not mapped to any of the AHB buffer, the XSPI will keep the AHB
 * HREADY signal low and will provided this pre-defined error payload on the AHB read data bus when the HEADY timeout
 * counter expires.
 *
 * param[in] base XSPI peripheral base address.
 * param[in] highPayload High 32bits payload to set.
 * param[in] lowPayload Low 32bits payload to set.

 * retval kStatus_XSPI_AhbReadAccessAsserted Fail due to an AHB read access already asserted
 * retval kStatus_XSPI_AhbWriteAccessAsserted Fail due to an AHB write access already asserted
 * retval kStatus_Success Success to set AHB HREADY timeout value.
 */
status_t XSPI_SetAhbErrorPayload(XSPI_Type *base, uint32_t highPayload, uint32_t lowPayload)
{
    if (XSPI_CheckAhbReadAccessAsserted(base))
    {
        return kStatus_XSPI_AhbReadAccessAsserted;
    }

    if (XSPI_CheckAhbWriteAccessAsserted(base))
    {
        return kStatus_XSPI_AhbWriteAccessAsserted;
    }

    base->AHB_ERR_PAYLOAD_HI = highPayload;
    base->AHB_ERR_PAYLOAD_LO = lowPayload;

    return kStatus_Success;
}

/*!
 * brief Return AHB read error information.
 * 
 * param[in] base XSPI peripheral base address.
 *
 * return Latest AHB read error information, in type of xspi_ahb_read_error_info_t.
 */
xspi_ahb_read_error_info_t XSPI_ReturnAhbReadErrorInfo(XSPI_Type *base)
{
    xspi_ahb_read_error_info_t errorInfo;

    errorInfo.errorAddr   = base->AHB_RD_ERR_ADDR;
    errorInfo.errMasterId = (uint8_t)(base->AHB_RD_ERR_MID & XSPI_AHB_RD_ERR_MID_REMID_MASK);

    return errorInfo;
}

/*!
 * brief Get Ahb request suspend information if priority mechanism is enabled.
 *
 * param[in] base XSPI peripheral base address.
 * param[out] ptrSuspendInfo Contain latest suspend info, the value may be 0xFF if the item is invalid in current
 * suspend state.
 */
void XSPI_GetAhbRequestSuspendInfo(XSPI_Type *base, xspi_ahb_request_suspend_info_t *ptrSuspendInfo)
{
    uint32_t tmp32 = 0UL;

    tmp32 = base->SPNDST;

    ptrSuspendInfo->state       = (xspi_ahb_request_suspend_state_t)(uint8_t)(tmp32 & XSPI_SPNDST_STATE_MASK);
    ptrSuspendInfo->subBufferId = 0xFFU;
    ptrSuspendInfo->ahbBufferId = 0xFFU;
    ptrSuspendInfo->dataLeft    = 0xFFFFU;
    ptrSuspendInfo->address     = 0xFFFFFFFFUL;

    if ((ptrSuspendInfo->state == kXSPI_AhbRequestSuspended) || (ptrSuspendInfo->state == kXSPI_AhbReqestResumed))
    {
        ptrSuspendInfo->subBufferId = (uint8_t)((tmp32 & XSPI_SPNDST_SPDSBUF_MASK) >> XSPI_SPNDST_SPDSBUF_SHIFT);
        ptrSuspendInfo->ahbBufferId = (uint8_t)((tmp32 & XSPI_SPNDST_SPDBUF_MASK) >> XSPI_SPNDST_SPDBUF_SHIFT);
        ptrSuspendInfo->dataLeft    = (uint16_t)((tmp32 & XSPI_SPNDST_DATLFT_MASK) >> XSPI_SPNDST_DATLFT_SHIFT);

        if (ptrSuspendInfo->state == kXSPI_AhbRequestSuspended)
        {
            ptrSuspendInfo->address = base->SPNDST_ADDR;
        }
    }
}

/*!
 * brief Enable/disable the clearing of AHB read prefetch buffers when the same flash address is written by an
 *   AHB or IP command.
 *
 * param[in] base XSPI peripheral base address.
 * param[in] enable Used to enable/disable write flush.
 *
 * retval kStatus_XSPI_AhbReadAccessAsserted Fail due to an AHB read access already asserted.
 * retval kStatus_XSPI_AhbWriteAccessAsserted Fail due to an AHB write access already asserted.
 * retval kStatus_Success Successfully to enable/disable AHB buffer write flush.
 */
status_t XSPI_EnableAhbBufferWriteFlush(XSPI_Type *base, bool enable)
{
    if (XSPI_CheckAhbReadAccessAsserted(base))
    {
        return kStatus_XSPI_AhbReadAccessAsserted;
    }

    if (XSPI_CheckAhbWriteAccessAsserted(base))
    {
        return kStatus_XSPI_AhbWriteAccessAsserted;
    }

    if (XSPI_CheckGlobalConfigLocked(base))
    {
        return kStatus_XSPI_RegWriteLocked;
    }

    if (enable)
    {
        base->BFGENCR |= XSPI_BFGENCR_WR_FLUSH_EN_MASK;
    }
    else
    {
        base->BFGENCR &= ~XSPI_BFGENCR_WR_FLUSH_EN_MASK;
    }

    return kStatus_Success;
}

/*!
 * brief Block access(write and read) after the AHB write operation.
 *
 * note If external flash support RWW, read should not be blocked:
 * code
 *  XSPI_BlockAccessAfterAhbWrite(XSPI0, true, false);
 * endcode
 *
 * note If either of access is blocked, the page program wait flag will be asserted after
 * an AHB write sequence completed. The assertion of flag will lock the arbitration and all
 * access to the external memory are blocked. And the internal "page wait time" counter
 * starts(Invoke XSPI_UpdatePageWaitTimeCounter to update counter value). After this counter
 * reaches the value, a read is triggered by the XSPI module to read external device's
 * status register(The seq id should be pre-defiend by XSPI_SetAhbReadStatusRegSeqId),
 * and the value is stored in the XSPI internal regsiter. And there are two
 * options(Invoke XSPI_SelectPPWFlagClearPolicy to select) to clear the asserted page program wait flag.
 *      1. Automatic cleared by XSPI hardware;
 *      2. Cleared by software.
 *
 * note As soon as the page program wait flag is asserted, another counter(SFP arbitration
 * lock counter) also started, if the flag not cleared in desired counter value the
 * arbitration lock timeout error will be generated.
 *
 * param[in] base XSPI peripheral base address.
 * param[in] blockSequentWrite Block sequence write or not.
 * param[in] blockRead Block read or not.
 *
 * retval kStatus_XSPI_AhbReadAccessAsserted Fail due to an AHB read access already asserted
 * retval kStatus_XSPI_AhbWriteAccessAsserted Fail due to an AHB write access already asserted
 * retval kStatus_Success Success to set related registers..
 */
status_t XSPI_BlockAccessAfterAhbWrite(XSPI_Type *base, bool blockSequentWrite, bool blockRead)
{
    uint32_t tmp32 = 0UL;

    if (XSPI_CheckAhbReadAccessAsserted(base))
    {
        return kStatus_XSPI_AhbReadAccessAsserted;
    }

    if (XSPI_CheckAhbWriteAccessAsserted(base))
    {
        return kStatus_XSPI_AhbWriteAccessAsserted;
    }

    tmp32 = base->AWRCR;
    tmp32 &= ~(XSPI_AWRCR_PPW_RD_DIS_MASK | XSPI_AWRCR_PPW_WR_DIS_MASK);
    tmp32 |= XSPI_AWRCR_PPW_RD_DIS(blockRead) | XSPI_AWRCR_PPW_WR_DIS(blockSequentWrite);

    base->AWRCR = tmp32;

    return kStatus_Success;
}

/*!
 * brief Set Page program wait flag clear policy.
 * 
 * note If set as hardware policy, the device's WIP information should be set by invoking XSPI_SetSFMStatusRegInfo()
 *
 * param[in] base XSPI peripheral base address.
 * param[in] policy Specify the policy to clear page program wait flag.
 *
 * retval kStatus_XSPI_AhbReadAccessAsserted Fail due to an AHB read access already asserted
 * retval kStatus_XSPI_AhbWriteAccessAsserted Fail due to an AHB write access already asserted
 * retval kStatus_XSPI_RegWriteLocked Fail due to write operation to related registers is locked.
 * retval kStatus_Success Success to set PPW flag clear policy.
 */
status_t XSPI_SelectPPWFlagClearPolicy(XSPI_Type *base, xspi_ppw_flag_clear_policy_t policy)
{
    if (XSPI_CheckAhbReadAccessAsserted(base))
    {
        return kStatus_XSPI_AhbReadAccessAsserted;
    }

    if (XSPI_CheckAhbWriteAccessAsserted(base))
    {
        return kStatus_XSPI_AhbWriteAccessAsserted;
    }

    if (XSPI_CheckGlobalConfigLocked(base))
    {
        return kStatus_XSPI_RegWriteLocked;
    }

    if (policy == kXSPI_HardwareClearPPWFlag)
    {
        base->BFGENCR |= XSPI_BFGENCR_PPWF_CLR_MASK;
    }
    else
    {
        base->BFGENCR &= ~XSPI_BFGENCR_PPWF_CLR_MASK;
    }

    return kStatus_Success;
}

/*!
 * brief Update page wait timeout counter.
 *
 * param[in] base XSPI peripheral base address.
 * param[in] countValue The value of counter, in AHB clock cycles.
 *
 * retval kStatus_XSPI_AhbReadAccessAsserted Fail due to an AHB read access already asserted
 * retval kStatus_XSPI_AhbWriteAccessAsserted Fail due to an AHB write access already asserted
 * retval kStatus_XSPI_PageProgramWaitFlagAsserted Page program wait flag already asserted.
 * retval kStatus_Success Successfully to update page wait timeout counter.
 */
status_t XSPI_UpdatePageWaitTimeCounter(XSPI_Type *base, uint32_t countValue)
{
    if (XSPI_CheckAhbReadAccessAsserted(base))
    {
        return kStatus_XSPI_AhbReadAccessAsserted;

    }
    if (XSPI_CheckAhbWriteAccessAsserted(base))
    {
        return kStatus_XSPI_AhbWriteAccessAsserted;
    }

    if ((base->FR & XSPI_FR_PPWF_MASK) != 0UL)
    {
        return kStatus_XSPI_PageProgramWaitFlagAsserted;
    }

    base->PPWF_TCNT = countValue;

    return kStatus_Success;
}

/*!
 * brief Set AHB read status register sequence Id
 *
 * note The read status sequence only triggerred when the page wait time counter expired.
 *
 * param[in] base XSPI peripheral base address.
 * param[in] seqId Specify the sequence Id in LUT used for AHB read status register.
 *
 * retval kStatus_XSPI_AhbReadAccessAsserted Fail due to an AHB read access already asserted
 * retval kStatus_XSPI_AhbWriteAccessAsserted Fail due to an AHB write access already asserted
 * retval kStatus_XSPI_RegWriteLocked Fail due to write operation to related registers is locked.
 * retval kStatus_Success Success to set AHB read status register sequence Id.
 */
status_t XSPI_SetAhbReadStatusRegSeqId(XSPI_Type *base, uint8_t seqId)
{
    if (XSPI_CheckAhbReadAccessAsserted(base))
    {
        return kStatus_XSPI_AhbReadAccessAsserted;
    }

    if (XSPI_CheckAhbWriteAccessAsserted(base))
    {
        return kStatus_XSPI_AhbWriteAccessAsserted;
    }

    if (XSPI_CheckGlobalConfigLocked(base))
    {
        return kStatus_XSPI_RegWriteLocked;
    }

    base->BFGENCR = ((base->BFGENCR & (~XSPI_BFGENCR_SEQID_RDSR_MASK)) | XSPI_BFGENCR_SEQID_RDSR(seqId));

    return kStatus_Success;
}

/*!
 * brief Set Serial flash memory status register information
 * 
 * param[in] base XSPI peripheral base address.
 * param[in] ptrStatusRegInfo Pointer to the variable which contain status register information.
 *
 * retval kStatus_XSPI_AhbReadAccessAsserted Fail due to an AHB read access already asserted
 * retval kStatus_XSPI_AhbWriteAccessAsserted Fail due to an AHB write access already asserted
 * retval kStatus_XSPI_PageProgramWaitFlagAsserted Page program wait flag already asserted.
 * retval kStatus_Success Successfully to set status register information.
 */
status_t XSPI_SetSFMStatusRegInfo(XSPI_Type *base, xspi_device_status_reg_info_t *ptrStatusRegInfo)
{
    assert(ptrStatusRegInfo != NULL);

    if (XSPI_CheckAhbReadAccessAsserted(base))
    {
        return kStatus_XSPI_AhbReadAccessAsserted;
    }

    if (XSPI_CheckAhbWriteAccessAsserted(base))
    {
        return kStatus_XSPI_AhbWriteAccessAsserted;
    }

    if ((base->FR & XSPI_FR_PPWF_MASK) != 0UL)
    {
        return kStatus_XSPI_PageProgramWaitFlagAsserted;
    }

    uint32_t tmp32 = 0UL;

    tmp32 = (base->PPW_RDSR) &
            ~(XSPI_PPW_RDSR_RDSR_VAL_CHK_MASK | XSPI_PPW_RDSR_RDSR_HWORD_SEL_MASK | XSPI_PPW_RDSR_LOC_MASK);

    tmp32 |= XSPI_PPW_RDSR_RDSR_VAL_CHK(ptrStatusRegInfo->value1Expired) |
             XSPI_PPW_RDSR_RDSR_HWORD_SEL(ptrStatusRegInfo->upperHalfWordSelected) |
             XSPI_PPW_RDSR_LOC(ptrStatusRegInfo->wipLocation);

    base->PPW_RDSR = tmp32;

    return kStatus_Success;
}

/*!
 * brief Set Buffer size for all 4 AHB buffers.
 *
 * param[in] base XSPI peripheral base address.
 * param[in] buf0Size Specify size of AHB buffer0, range of 512, 256, 128, 64, 32, 16, 8, 4, 2, 0.
 * param[in] buf1Size Specify size of AHB buffer1, range of 512, 256, 128, 64, 32, 16, 8, 4, 2, 0.
 * param[in] buf2Size Specify size of AHB buffer2, range of 512, 256, 128, 64, 32, 16, 8, 4, 2, 0.
 * param[in] buf3Size Specify size of AHB buffer3, range of 512, 256, 128, 64, 32, 16, 8, 4, 2, 0.
 *
 * retval kStatus_XSPI_AhbReadAccessAsserted Fail due to an AHB read access already asserted.
 * retval kStatus_XSPI_AhbWriteAccessAsserted Fail due to an AHB write access already asserted.
 * retval kStatus_Success Success to set AHB buffer size.
 */
status_t XSPI_UpdateAhbBufferSize(
    XSPI_Type *base, uint16_t buf0Size, uint16_t buf1Size, uint16_t buf2Size, uint16_t buf3Size)
{
    /* The total size of AHB buffer is 4KB. */
    assert((buf0Size + buf1Size + buf2Size + buf3Size) == 512U);

    if (XSPI_CheckAhbReadAccessAsserted(base))
    {
        return kStatus_XSPI_AhbReadAccessAsserted;
    }

    if (XSPI_CheckAhbWriteAccessAsserted(base))
    {
        return kStatus_XSPI_AhbWriteAccessAsserted;
    }

    base->BUFIND[0] = XSPI_BUFIND_TPINDX(buf0Size);
    base->BUFIND[1] = XSPI_BUFIND_TPINDX((uint32_t)buf0Size + (uint32_t)buf1Size);
    base->BUFIND[2] = XSPI_BUFIND_TPINDX((uint32_t)buf0Size + (uint32_t)buf1Size + (uint32_t)buf2Size);

    (void)buf3Size;

    return kStatus_Success;
}

/*!
 * brief Get status of AHB sub buffer.
 *
 * note This function only useful when sub-division feature of the selected AHB buffer is enabled.
 *
 * param[in] base XSPI peripheral base address.
 * param[in] ahbBufferId The Id of AHB buffer, range from 0 to 3.
 * param[in] subBufferId The Id of AHB buffer sub division, range from 0 to 3.
 *
 * return Current status of selected AHB sub buffer, in type of ref xspi_ahb_sub_buffer_status_t.
 */
xspi_ahb_sub_buffer_status_t XSPI_GetAhbSubBufferStatus(XSPI_Type *base, uint8_t ahbBufferId, uint8_t subBufferId)
{
    uint32_t tmp32 = 0UL;
    uint32_t shift = 8UL * (uint32_t)ahbBufferId + 2UL * (uint32_t)subBufferId;
    uint32_t mask  = 0x3UL << shift;

    tmp32 = base->AHB_BUF_STATUS;
    return (xspi_ahb_sub_buffer_status_t)(uint32_t)((tmp32 & mask) >> shift);
}

/*!
 * brief Enable AHB buffer performance monitor for selected AHB buffer's sub buffer.
 * 
 * param[in] base XSPI peripheral base address.
 * param[in] ahbBufferId Specify the selected AHB buffer.
 * param[in] subBufferId Specify the selected sub-buffer.
 */
void XSPI_EnableAhbBufferPerfMonitor(XSPI_Type *base, uint8_t ahbBufferId, uint8_t subBufferId)
{
    uint32_t tmp32 = 0UL;

    tmp32 = base->AHB_PERF_CTRL;

    tmp32 &= ~(XSPI_AHB_PERF_CTRL_SUB_BUF_SEL0_MASK << (2UL * (uint32_t)ahbBufferId));
    tmp32 |= XSPI_AHB_PERF_CTRL_SUB_BUF_SEL0(subBufferId) << (2UL * (uint32_t)ahbBufferId);

    tmp32 |= (uint32_t)XSPI_AHB_PERF_CTRL_BUF0_EN_MASK << (uint32_t)(ahbBufferId);

    base->AHB_PERF_CTRL = tmp32;
}
/***************************** AHB Access Control Low-Level Interfaces End ********************************/

/***************************** AHB Access Control Functional Interfaces Start ********************************/
/*!
 * brief Set AHB access configuration.
 * 
 * param[in] base XSPI peripheral base address.
 * param[in] ptrAhbAccessConfig Pointer to the variable which contains AHB access configurations.
 *
 * retval kStatus_XSPI_AhbReadAccessAsserted Fail due to an AHB read access already asserted
 * retval kStatus_XSPI_AhbWriteAccessAsserted Fail due to an AHB write access already asserted
 * retval kStatus_XSPI_RegWriteLocked Fail due to write operation to related registers is locked.
 * retval kStatus_Success Success to set AHB read status register sequence Id.
 */
status_t XSPI_SetAhbAccessConfig(XSPI_Type *base, xspi_ahb_access_config_t *ptrAhbAccessConfig)
{
    assert(ptrAhbAccessConfig != NULL);

    if (XSPI_CheckAhbReadAccessAsserted(base))
    {
        return kStatus_XSPI_AhbReadAccessAsserted;
    }

    if (XSPI_CheckAhbWriteAccessAsserted(base))
    {
        return kStatus_XSPI_AhbWriteAccessAsserted;
    }

    if (XSPI_CheckGlobalConfigLocked(base))
    {
        return kStatus_XSPI_RegWriteLocked;
    }

    /* Configure AHB buffers. */
    (void)XSPI_SetAhbBufferConfig(base, &(ptrAhbAccessConfig->buffer[0]), &(ptrAhbAccessConfig->buffer[1]),
                                  &(ptrAhbAccessConfig->buffer[2]), &(ptrAhbAccessConfig->buffer[3]));
    (void)XSPI_SetAhbReadDataSeqId(base, ptrAhbAccessConfig->ARDSeqIndex);
    (void)XSPI_EnableAhbBufferWriteFlush(base, ptrAhbAccessConfig->enableAHBBufferWriteFlush);
    XSPI_EnableAhbReadPrefetch(base, ptrAhbAccessConfig->enableAHBPrefetch);
    (void)XSPI_SetAhbAccessSplitSize(base, ptrAhbAccessConfig->ahbSplitSize);
    (void)XSPI_SetAhbAccessBoundary(base, ptrAhbAccessConfig->ahbAlignment);

    base->AWRCR &= ~(XSPI_AWRCR_PPW_RD_DIS_MASK | XSPI_AWRCR_PPW_WR_DIS_MASK);

    if (ptrAhbAccessConfig->ptrAhbWriteConfig != NULL)
    {
        (void)XSPI_BlockAccessAfterAhbWrite(base, ptrAhbAccessConfig->ptrAhbWriteConfig->blockSequenceWrite,
                                            ptrAhbAccessConfig->ptrAhbWriteConfig->blockRead);
        (void)XSPI_SelectPPWFlagClearPolicy(base, ptrAhbAccessConfig->ptrAhbWriteConfig->policy);

        (void)XSPI_UpdatePageWaitTimeCounter(base, ptrAhbAccessConfig->ptrAhbWriteConfig->pageWaitTimeoutValue);

        base->BFGENCR = (base->BFGENCR & ~(XSPI_BFGENCR_SEQID_WR_MASK | XSPI_BFGENCR_SEQID_RDSR_MASK)) |
                        XSPI_BFGENCR_SEQID_WR(ptrAhbAccessConfig->ptrAhbWriteConfig->AWRSeqIndex) |
                        XSPI_BFGENCR_SEQID_WR_EN_MASK |
                        XSPI_BFGENCR_SEQID_RDSR(ptrAhbAccessConfig->ptrAhbWriteConfig->ARDSRSeqIndex);
    }

    (void)XSPI_SetAhbErrorPayload(base, ptrAhbAccessConfig->ahbErrorPayload.highPayload,
                                  ptrAhbAccessConfig->ahbErrorPayload.lowPayload);

    return kStatus_Success;
}

/***************************** AHB Access Control Functional Interfaces End ********************************/

#if defined(FSL_DRIVER_TRANSFER_DOUBLE_WEAK_IRQ) && FSL_DRIVER_TRANSFER_DOUBLE_WEAK_IRQ
static void XSPI_CommonIRQHandler(XSPI_Type *base, xspi_handle_t *handle)
{
    uint32_t errFlags = XSPI_GetErrorStatusFlags(base);
    uint32_t flags = XSPI_GetCmdExecutionArbitrationStatusFlags(base);
    uint32_t i = 0UL;
    uint32_t bufferIndex = 0UL;
    uint8_t *buffer = NULL;
    size_t transferSize = 0UL;
    uint32_t tmpVal = 0UL;

    if ((errFlags & (uint32_t)kXSPI_ErrorTimeout) != 0UL)
    {
        XSPI_DisableInterrupts(base, (uint64_t)kXSPI_TimeoutErrIntEnable);
        XSPI_ClearErrorStatusFlags(base, (uint32_t)kXSPI_ErrorTimeout);
        XSPI_TransferAbort(base, handle);
        XSPI_ResetTgQueue(base);
        XSPI_ResetSfmAndAhbDomain(base);
        handle->completionCallback(base, handle, kStatus_Timeout, handle->userData);
    }

    if ((errFlags & (uint32_t)(kXSPI_ErrorTg0Sfar | kXSPI_ErrorTg1Sfar)) != 0UL)
    {
        XSPI_DisableInterrupts(base, (uint64_t)(kXSPI_Tg0SfarErrIntEnable | kXSPI_Tg1SfarErrIntEnable));
        XSPI_ClearErrorStatusFlags(base, (uint32_t)(kXSPI_ErrorTg0Sfar | kXSPI_ErrorTg1Sfar));
        g_IpAccessSfarFail = true;
        handle->completionCallback(base, handle, kStatus_XSPI_IpAccessAddrSettingInvalid, handle->userData);
    }

    if ((errFlags & (uint32_t)(kXSPI_ErrorTg0Ipcr | kXSPI_ErrorTg1Ipcr)) != 0UL)
    {
        XSPI_DisableInterrupts(base, (uint64_t)(kXSPI_Tg0IpcrErrIntEnable | kXSPI_Tg1IpcrErrIntEnable));
        XSPI_ClearErrorStatusFlags(base, (uint32_t)(kXSPI_ErrorTg0Ipcr | kXSPI_ErrorTg1Ipcr));
        g_IpAccessIpcrFail = true;
        handle->completionCallback(base, handle, kStatus_XSPI_IpAccessIPCRInvalid, handle->userData);
    }

    if ((errFlags & kXSPI_ArbitrationWin) != 0UL)
    {
        handle->state = kXSPI_StateIpAccessGranted;
        XSPI_ClearErrorStatusFlags(base, (uint32_t)kXSPI_ArbitrationWin);
        XSPI_DisableInterrupts(base, (uint64_t)kXSPI_ArbitrationWinIntEnable);
        return ;
    }

    if ((flags & (uint32_t)kXSPI_FlagTxBufferUnderRun) != 0UL)
    {
        XSPI_DisableInterrupts(base, (uint64_t)(kXSPI_TxBufferUnderrunIntEnable));
        XSPI_ClearCmdExecutionArbitrationStatusFlags(base, (uint32_t)kXSPI_FlagTxBufferUnderRun);
        handle->completionCallback(base, handle, kStatus_XSPI_TxBufferUnderrun, handle->userData);
    }

    if (((flags & (uint32_t)kXSPI_FlagTxBufferFill) != 0UL) && (handle->state == kXSPI_StateBusyWrite))
    {
        XSPI_ClearCmdExecutionArbitrationStatusFlags(base, (uint32_t)kXSPI_FlagTxBufferFill);
        XSPI_DisableInterrupts(base, (uint64_t)kXSPI_TxBufferFillIntEnable);
        bufferIndex = handle->transferTotalSize - handle->dataSize;
        buffer = (uint8_t *)(handle->data) + bufferIndex;
        transferSize = handle->curTransferSize;
        handle->dataSize -= transferSize;
        XSPI_ClearTxBuffer(base);
        
        base->TBCT = 256UL - ((uint32_t)transferSize / 4UL - 1UL);
        while(transferSize != 0UL)
        {
            for (i = 0UL; i < (transferSize / 4U); i++)
            {
                tmpVal = *(uint32_t *)buffer;
                base->TBDR = tmpVal;
                buffer += 4U;
            }

            transferSize -= 4U * i;

            if (transferSize != 0UL)
            {
                tmpVal = 0UL;

                for (uint32_t j = 0UL; j < transferSize; j++)
                {
                    tmpVal |= (uint32_t)*buffer++ << (8U *j);
                }

                base->TBDR = tmpVal;
            }
            transferSize = 0UL;
        }

        handle->state = kXSPI_StateIdle;
        if (handle->dataSize != 0UL)
        {
            XSPI_StartIpWriteNonBlocking(base, handle, handle->dataSize);
        }
        else
        {
            handle->completionCallback(base, handle, kStatus_XSPI_IpWriteFinished, handle->userData);
        }
    }

    if (((flags & (uint32_t)kXSPI_FlagRxBufferWatermarkExceeded) != 0UL) && (handle->state == kXSPI_StateBusyRead))
    {
        XSPI_DisableInterrupts(base, (uint64_t)kXSPI_RxBufferDrainIntEnable);
        /* RX buffer watermark exceeded, read data to buffer and pop RX buffer. */
        uint32_t rxDataSize = XSPI_GetRxBufferAvailableBytesCount(base);
        uint32_t rxBufferWaterMark = base->RBCT + 1UL;
        bufferIndex = handle->transferTotalSize - handle->dataSize;
        buffer = (uint8_t *)(handle->data) + bufferIndex;

        handle->dataSize -= rxDataSize;
        while(rxDataSize != 0UL)
        {
            if (rxDataSize >= 4UL * rxBufferWaterMark)
            {
                uint32_t removedCount = XSPI_GetRxBufferRemovedBytesCount(base);
                for (i = 0UL; i < rxBufferWaterMark; i++)
                {
                    *(uint32_t *)buffer = base->RBDR[i];
                    buffer += 4UL;
                }

                XSPI_TriggerRxBufferPopEvent(base);

                while((XSPI_GetRxBufferRemovedBytesCount(base) - removedCount) != (rxBufferWaterMark * 4UL))
                {}
                rxDataSize = rxDataSize - 4UL * rxBufferWaterMark;
            }
            else
            {
                for (i = 0UL; i < (rxDataSize / 4UL); i++)
                {
                    *(uint32_t *)buffer = base->RBDR[i];
                    buffer += 4UL;
                }

                rxDataSize -= 4UL * i;

                if (rxDataSize != 0UL)
                {
                    tmpVal = base->RBDR[i];

                    for (i = 0U; i < rxDataSize; i++)
                    {
                        *buffer++ = ((uint8_t)(tmpVal >> (8U * i)) & 0xFFU);
                    }
                }
            }
        }

        XSPI_ClearCmdExecutionArbitrationStatusFlags(base, kXSPI_FlagRxBufferWatermarkExceeded);
        handle->state = kXSPI_StateIdle;
        if (handle->dataSize != 0UL)
        {
            XSPI_StartIpReadNonBlocking(base, handle, handle->dataSize);
        }
        else
        {
            handle->completionCallback(base, handle, kStatus_XSPI_IpReadFinished, handle->userData);
        }
    }
}

#if defined(XSPI0)
void XSPI0_DriverIRQHandler(void);
void XSPI0_DriverIRQHandler(void)
{
    XSPI_CommonIRQHandler(XSPI0, s_xspiHandle[0]);
    SDK_ISR_EXIT_BARRIER;
}
#endif /* defined(XSPI0) */

#if defined(XSPI1)
void XSPI1_DriverIRQHandler(void);
void XSPI1_DriverIRQHandler(void)
{
    XSPI_CommonIRQHandler(XSPI1, s_xspiHandle[1]);
    SDK_ISR_EXIT_BARRIER;
}
#endif /* defined(XSPI1) */

#if defined(XSPI2)
void XSPI2_DriverIRQHandler(void);
void XSPI2_DriverIRQHandler(void)
{
    XSPI_CommonIRQHandler(XSPI2, s_xspiHandle[2]);
    SDK_ISR_EXIT_BARRIER;
}
#endif /* defined(XSPI2) */
#endif /* defined(FSL_DRIVER_TRANSFER_DOUBLE_WEAK_IRQ) && FSL_DRIVER_TRANSFER_DOUBLE_WEAK_IRQ */

#if defined(CACHE64_CTRL0_BASE)

static uint32_t XSPI_Cache64_GetInstanceByAddr(uint32_t address)
{
    uint32_t i = 0UL;
    uint32_t phyMemBase[] = CACHE64_CTRL_PHYMEM_BASES;
    uint32_t phyMemSize[] = CACHE64_CTRL_PHYMEM_SIZES;
    CACHE64_CTRL_Type *const s_xspi_cache64ctrlBases[] = CACHE64_CTRL_BASE_PTRS;

    while(i < ARRAY_SIZE(s_xspi_cache64ctrlBases))
    {
        if ((MSDK_REG_SECURE_ADDR(address) >= MSDK_REG_SECURE_ADDR(phyMemBase[i])) && (MSDK_REG_SECURE_ADDR(address) < MSDK_REG_SECURE_ADDR(phyMemBase[i] + phyMemSize[i] - 0x01U)))
        {
            return i;
        }

        i++;
    }
		
    assert(false);
    return 0xFFFFFFFFUL;
}

/*!
 * brief Enable a Cache64 instance.
 * 
 * param base CACHE64_CTRL peripheral base address.
 */
void XSPI_Cache64_EnableCache(CACHE64_CTRL_Type *base)
{
    /* if CACHE is not enabled */
    if ((base->CCR & CACHE64_CTRL_CCR_ENCACHE_MASK) == 0x00U)
    {
        /* First, invalidate the entire cache. */
        XSPI_Cache64_InvalidateCache(base);

        /* Now enable the cache. */
        base->CCR |= CACHE64_CTRL_CCR_ENCACHE_MASK;
    }
}

/*!
 * brief Disable a Cache64 instance.
 * 
 * param base CACHE64_CTRL peripheral base address.
 */
void XSPI_Cache64_DisableCache(CACHE64_CTRL_Type *base)
{
    /* if CACHE is enabled */
    if ((base->CCR & CACHE64_CTRL_CCR_ENCACHE_MASK) != 0x00U)
    {
        /* First, push any modified contents. */
        XSPI_Cache64_CleanCache(base);

        /* Now disable the cache. */
        base->CCR &= ~CACHE64_CTRL_CCR_ENCACHE_MASK;
    }
}

/*!
 * brief Invalidate whole input cache64 instance.
 * 
 * param base CACHE64_CTRL peripheral base address.
 */
void XSPI_Cache64_InvalidateCache(CACHE64_CTRL_Type *base)
{
    /* Invalidate all lines in both ways and initiate the cache command. */
    base->CCR |= CACHE64_CTRL_CCR_INVW0_MASK | CACHE64_CTRL_CCR_INVW1_MASK | CACHE64_CTRL_CCR_GO_MASK;

    /* Wait until the cache command completes. */
    while ((base->CCR & CACHE64_CTRL_CCR_GO_MASK) != 0x00U)
    {
    }

    /* As a precaution clear the bits to avoid inadvertently re-running this command. */
    base->CCR &= ~(CACHE64_CTRL_CCR_INVW0_MASK | CACHE64_CTRL_CCR_INVW1_MASK);
}

/*!
 * brief Invalidate cache64 instance based on address of size.
 * 
 * param address The value of start physical address.
 * param size Size of data in unit of byte.
 */
void XSPI_Cache64_InvalidateCacheByRange(uint32_t address, size_t size)
{
    if (size > 0UL)
    {
        uint32_t endAddr = MSDK_REG_SECURE_ADDR(address + size - 0x01U);
        uint32_t pccReg  = 0;
        /* Align address to cache line size. */
        uint32_t startAddr = MSDK_REG_SECURE_ADDR(address & ~((uint32_t)FSL_FEATURE_CACHE64_LINESIZE_BYTE - 1U));
        uint32_t instance  = XSPI_Cache64_GetInstanceByAddr(address);
        uint32_t endLim;
        CACHE64_CTRL_Type *base;
        uint32_t phyMemBase[] = CACHE64_CTRL_PHYMEM_BASES;
        uint32_t phyMemSize[] = CACHE64_CTRL_PHYMEM_SIZES;
        CACHE64_CTRL_Type *const s_xspi_cache64ctrlBases[] = CACHE64_CTRL_BASE_PTRS;

        if (instance >= ARRAY_SIZE(s_xspi_cache64ctrlBases))
        {
            return;
        }
        base    = s_xspi_cache64ctrlBases[instance];
        endLim  = MSDK_REG_SECURE_ADDR(phyMemBase[instance] + phyMemSize[instance] - 0x01U);
        endAddr = endAddr > endLim ? endLim : endAddr;

        /* Set the invalidate by line command and use the physical address. */
        pccReg = (base->CLCR & ~CACHE64_CTRL_CLCR_LCMD_MASK) | CACHE64_CTRL_CLCR_LCMD(1) | CACHE64_CTRL_CLCR_LADSEL_MASK;
        base->CLCR = pccReg;

        while (startAddr < endAddr)
        {
            /* Set the address and initiate the command. */
            base->CSAR = (startAddr & CACHE64_CTRL_CSAR_PHYADDR_MASK) | CACHE64_CTRL_CSAR_LGO_MASK;

            /* Wait until the cache command completes. */
            while ((base->CSAR & CACHE64_CTRL_CSAR_LGO_MASK) != 0x00U)
            {
            }
            startAddr += (uint32_t)FSL_FEATURE_CACHE64_LINESIZE_BYTE;
        }
    }
}

/*!
 * brief Clean whole input cache64 instance.
 * 
 * param base CACHE64_CTRL peripheral base address.
 */
void XSPI_Cache64_CleanCache(CACHE64_CTRL_Type *base)
{
    /* Enable the to push all modified lines. */
    base->CCR |= CACHE64_CTRL_CCR_PUSHW0_MASK | CACHE64_CTRL_CCR_PUSHW1_MASK | CACHE64_CTRL_CCR_GO_MASK;

    /* Wait until the cache command completes. */
    while ((base->CCR & CACHE64_CTRL_CCR_GO_MASK) != 0x00U)
    {
    }

    /* As a precaution clear the bits to avoid inadvertently re-running this command. */
    base->CCR &= ~(CACHE64_CTRL_CCR_PUSHW0_MASK | CACHE64_CTRL_CCR_PUSHW1_MASK);
}

/*!
 * brief Clean cache64 instance based on address of size.
 * 
 * param address The value of start physical address.
 * param size Size of data in unit of byte.
 */
void XSPI_Cache64_CleanCacheByRange(uint32_t address, size_t size)
{
    if (size > 0UL)
    {
        uint32_t endAddr = MSDK_REG_SECURE_ADDR(address + size - 0x01U);
        uint32_t pccReg  = 0;
        /* Align address to cache line size. */
        uint32_t startAddr = MSDK_REG_SECURE_ADDR(address & ~((uint32_t)FSL_FEATURE_CACHE64_LINESIZE_BYTE - 1U));
        uint32_t instance  = XSPI_Cache64_GetInstanceByAddr(address);
        uint32_t endLim;
        CACHE64_CTRL_Type *base;
        uint32_t phyMemBase[] = CACHE64_CTRL_PHYMEM_BASES;
        uint32_t phyMemSize[] = CACHE64_CTRL_PHYMEM_SIZES;
        CACHE64_CTRL_Type *const s_xspi_cache64ctrlBases[] = CACHE64_CTRL_BASE_PTRS;

        if (instance >= ARRAY_SIZE(s_xspi_cache64ctrlBases))
        {
            return;
        }
        base    = s_xspi_cache64ctrlBases[instance];
        endLim  = MSDK_REG_SECURE_ADDR(phyMemBase[instance] + phyMemSize[instance] - 0x01U);
        endAddr = endAddr > endLim ? endLim : endAddr;

        /* Set the push by line command. */
        pccReg = (base->CLCR & ~CACHE64_CTRL_CLCR_LCMD_MASK) | CACHE64_CTRL_CLCR_LCMD(2) | CACHE64_CTRL_CLCR_LADSEL_MASK;
        base->CLCR = pccReg;

        while (startAddr < endAddr)
        {
            /* Set the address and initiate the command. */
            base->CSAR = (startAddr & CACHE64_CTRL_CSAR_PHYADDR_MASK) | CACHE64_CTRL_CSAR_LGO_MASK;

            /* Wait until the cache command completes. */
            while ((base->CSAR & CACHE64_CTRL_CSAR_LGO_MASK) != 0x00U)
            {
            }
            startAddr += (uint32_t)FSL_FEATURE_CACHE64_LINESIZE_BYTE;
        }
    }
}

/*!
 * brief Clean and Invalidate whole input cache64 instance.
 * 
 * param base CACHE64_CTRL peripheral base address.
 */
void XSPI_Cache64_CleanInvalidateCache(CACHE64_CTRL_Type *base)
{
    /* Push and invalidate all. */
    base->CCR |= CACHE64_CTRL_CCR_PUSHW0_MASK | CACHE64_CTRL_CCR_PUSHW1_MASK | CACHE64_CTRL_CCR_INVW0_MASK |
                 CACHE64_CTRL_CCR_INVW1_MASK | CACHE64_CTRL_CCR_GO_MASK;

    /* Wait until the cache command completes. */
    while ((base->CCR & CACHE64_CTRL_CCR_GO_MASK) != 0x00U)
    {
    }

    /* As a precaution clear the bits to avoid inadvertently re-running this command. */
    base->CCR &= ~(CACHE64_CTRL_CCR_PUSHW0_MASK | CACHE64_CTRL_CCR_PUSHW1_MASK | CACHE64_CTRL_CCR_INVW0_MASK |
                   CACHE64_CTRL_CCR_INVW1_MASK);
}

/*!
 * brief Clean and invalidate cache64 instance based on address of size.
 * 
 * param address The value of start physical address.
 * param size Size of data in unit of byte.
 */
void XSPI_Cache64_CleanInvalidateByRange(uint32_t address, size_t size)
{
    if (size > 0UL)
    {
        uint32_t endAddr = MSDK_REG_SECURE_ADDR(address + size - 0x01U);
        uint32_t pccReg  = 0;
        /* Align address to cache line size. */
        uint32_t startAddr = MSDK_REG_SECURE_ADDR(address & ~((uint32_t)FSL_FEATURE_CACHE64_LINESIZE_BYTE - 1U));
        uint32_t instance  = XSPI_Cache64_GetInstanceByAddr(address);
        uint32_t endLim;
        CACHE64_CTRL_Type *base;
        uint32_t phyMemBase[] = CACHE64_CTRL_PHYMEM_BASES;
        uint32_t phyMemSize[] = CACHE64_CTRL_PHYMEM_SIZES;
        CACHE64_CTRL_Type *const s_xspi_cache64ctrlBases[] = CACHE64_CTRL_BASE_PTRS;

        if (instance >= ARRAY_SIZE(s_xspi_cache64ctrlBases))
        {
            return;
        }
        base    = s_xspi_cache64ctrlBases[instance];
        endLim  = MSDK_REG_SECURE_ADDR(phyMemBase[instance] + phyMemSize[instance] - 0x01U);
        endAddr = endAddr > endLim ? endLim : endAddr;

        /* Set the push by line command. */
        pccReg = (base->CLCR & ~CACHE64_CTRL_CLCR_LCMD_MASK) | CACHE64_CTRL_CLCR_LCMD(3) | CACHE64_CTRL_CLCR_LADSEL_MASK;
        base->CLCR = pccReg;

        while (startAddr < endAddr)
        {
            /* Set the address and initiate the command. */
            base->CSAR = (startAddr & CACHE64_CTRL_CSAR_PHYADDR_MASK) | CACHE64_CTRL_CSAR_LGO_MASK;

            /* Wait until the cache command completes. */
            while ((base->CSAR & CACHE64_CTRL_CSAR_LGO_MASK) != 0x00U)
            {
            }
            startAddr += (uint32_t)FSL_FEATURE_CACHE64_LINESIZE_BYTE;
        }
    }
}

/*!
 * brief Read data from cache.
 * 
 * param address The value of start physical address.
 *
 * return uint32_t The data of input physical address stored in cache.
 */
uint32_t XSPI_Cache64_ReadCache(uint32_t address)
{
    CACHE64_CTRL_Type *const s_xspi_cache64ctrlBases[] = CACHE64_CTRL_BASE_PTRS;
    uint32_t instance = XSPI_Cache64_GetInstanceByAddr(address);
    CACHE64_CTRL_Type *base = s_xspi_cache64ctrlBases[instance];

    base->CLCR &= ~(CACHE64_CTRL_CLCR_LCMD_MASK | CACHE64_CTRL_CLCR_LACC_MASK);
    base->CLCR |= CACHE64_CTRL_CLCR_LADSEL_MASK;

    base->CSAR = CACHE64_CTRL_CSAR_PHYADDR27_1(address) | CACHE64_CTRL_CSAR_LGO_MASK;
    while(((base->CSAR) & CACHE64_CTRL_CSAR_LGO_MASK) != 0UL)
    {}

    return (uint32_t)(base->CCVR);
}
#endif /* defined(CACHE64_CTRL0_BASE) */

#if (defined(FSL_FEATURE_SOC_CACHE64_POLSEL_COUNT) && (FSL_FEATURE_SOC_CACHE64_POLSEL_COUNT > 0))
static uint32_t XSPI_Cache64_GetPolselInstance(CACHE64_POLSEL_Type *base)
{
    uint32_t i;
    CACHE64_POLSEL_Type *const xspi_cache64polselBases[] = CACHE64_POLSEL_BASE_PTRS;

    for (i = 0UL; i < ARRAY_SIZE(xspi_cache64polselBases); i++)
    {
        if (MSDK_REG_SECURE_ADDR(base) == MSDK_REG_SECURE_ADDR(xspi_cache64polselBases[i]))
        {
            break;
        }
    }

    assert(i < ARRAY_SIZE(xspi_cache64polselBases));

    return i;
}

/*!
 * brief Set policy of cache regions.
 * 
 * param base CACHE64_POLSEL peripheral base address.
 * param config Pointer to the structure xspi_cache64_region_config_t.
 */
void XSPI_Cache64_SetRegionConfig(CACHE64_POLSEL_Type *base, xspi_cache64_region_config_t *config)
{
    assert(config != NULL);

#if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
#ifdef CACHE64_CLOCKS
    uint32_t instance = XSPI_Cache64_GetPolselInstance(base);

    /* Enable CACHE64 clock */
    CLOCK_EnableClock(s_xspi_cache64Clocks[instance]);
#endif
#endif

    base->REG0_TOP = config->region0TopAddr & 0x3FFUL;
    base->REG1_TOP = config->region1TopAddr & 0x3FFUL;

    base->POLSEL = CACHE64_POLSEL_POLSEL_REG0_POLICY(config->region0Policy) |
             CACHE64_POLSEL_POLSEL_REG1_POLICY(config->region1Policy) |
             CACHE64_POLSEL_POLSEL_REG2_POLICY(config->region2Policy);
}
#endif 
