/**
* @file    Fls_Lld_QspiFls.c
*==================================================================================================
*   Project              : YTMicro AUTOSAR 4.4.0 MCAL
*   Platform             : ARM
*   Peripheral           : Fls_Lld_QspiFls
*   Dependencies         : none
*
*   Autosar Version      : V4.4.0
*   Autosar Revision     : ASR_REL_4_4_REV_0000
*   SW Version           : V2.3.0
*
*   (c) Copyright 2020-2025 Yuntu Microelectronics co.,ltd.
*   All Rights Reserved.
==================================================================================================*/

#ifdef __cplusplus
extern "C"
{
#endif

/*==================================================================================================
                                              INCLUDE FILES
==================================================================================================*/
#include "Fls_Cfg.h"
#if (FLS_QSPI_SECTORS_CONFIGURED == STD_ON)
#include "Fls_Lld_QspiFls.h"
#include "Fls_Qspi_Cfg.h"
#include "Fls_Lld_Qspi.h"
#include "Fls.h"
#include "OsIf.h"
/*==================================================================================================
                                      SOURCE FILE VERSION INFORMATION
==================================================================================================*/
#define FLS_LLD_QSPIFLS_VENDOR_ID_C                     (180)
#define FLS_LLD_QSPIFLS_AR_REL_MAJOR_VER_C              (4)
#define FLS_LLD_QSPIFLS_AR_REL_MINOR_VER_C              (4)
#define FLS_LLD_QSPIFLS_AR_REL_REVISION_VER_C           (0)
#define FLS_LLD_QSPIFLS_SW_MAJOR_VER_C                  (2)
#define FLS_LLD_QSPIFLS_SW_MINOR_VER_C                  (3)
#define FLS_LLD_QSPIFLS_SW_PATCH_VER_C                  (0)
/*==================================================================================================
                                            FILE VERSION CHECKS
==================================================================================================*/
/* Check if source file and FLS_LLD_QSPIFLS header file are of the same vendor */
#if (FLS_LLD_QSPIFLS_VENDOR_ID_C != FLS_LLD_QSPIFLS_VENDOR_ID)
#error "Fls_Lld_QspiFls.c and Fls_Lld_QspiFls.h have different vendor ids"
#endif

/* Check if source file and FLS_LLD_QSPIFLS header file are of the same Autosar version */
#if (( FLS_LLD_QSPIFLS_AR_REL_MAJOR_VER_C != FLS_LLD_QSPIFLS_AR_REL_MAJOR_VER) || \
      ( FLS_LLD_QSPIFLS_AR_REL_MINOR_VER_C != FLS_LLD_QSPIFLS_AR_REL_MINOR_VER) || \
      ( FLS_LLD_QSPIFLS_AR_REL_REVISION_VER_C != FLS_LLD_QSPIFLS_AR_REL_REVISION_VER))
#error "AutoSar Version Numbers of Fls_Lld_QspiFls.c and Fls_Lld_QspiFls.h are different"
#endif

/* Check if source file and FLS_LLD_QSPIFLS header file are of the same Software version */
#if (( FLS_LLD_QSPIFLS_SW_MAJOR_VER_C != FLS_LLD_QSPIFLS_SW_MAJOR_VER) || \
      ( FLS_LLD_QSPIFLS_SW_MINOR_VER_C != FLS_LLD_QSPIFLS_SW_MINOR_VER) || \
      ( FLS_LLD_QSPIFLS_SW_PATCH_VER_C != FLS_LLD_QSPIFLS_SW_PATCH_VER))
#error "Software Version Numbers of Fls_Lld_QspiFls.c and Fls_Lld_QspiFls.h are different"
#endif

/*Check if source file and Fls_Qspi_Cfg.h are of the same vendor */
#if (FLS_LLD_QSPIFLS_VENDOR_ID_C != FLS_QSPI_VENDOR_ID_CFG)
#error "Fls_Lld_QspiFls.c and Fls_Qspi_Cfg.h have different vendor ids"
#endif

/*Check if source file and Fls_Qspi_Cfg.h are of the same Autosar version */
#if (( FLS_LLD_QSPIFLS_AR_REL_MAJOR_VER_C != FLS_QSPI_AR_REL_MAJOR_VER_CFG) || \
      ( FLS_LLD_QSPIFLS_AR_REL_MINOR_VER_C != FLS_QSPI_AR_REL_MINOR_VER_CFG) || \
      ( FLS_LLD_QSPIFLS_AR_REL_REVISION_VER_C != FLS_QSPI_AR_REL_REVISION_VER_CFG))
#error "AutoSar Version Numbers of Fls_Lld_QspiFls.c and Fls_Lld_QspiFls.h are different"
#endif

/*Check if source file and Fls_Qspi_Cfg.h are of the same Software version */
#if (( FLS_LLD_QSPIFLS_SW_MAJOR_VER_C != FLS_QSPI_SW_MAJOR_VER_CFG) || \
      ( FLS_LLD_QSPIFLS_SW_MINOR_VER_C != FLS_QSPI_SW_MINOR_VER_CFG) || \
      ( FLS_LLD_QSPIFLS_SW_PATCH_VER_C != FLS_QSPI_SW_PATCH_VER_CFG))
#error "Software Version Numbers of Fls_Lld_QspiFls.c and Fls_Lld_QspiFls.h are different"
#endif


/*==================================================================================================
                                                GLOBAL VARIABLES
==================================================================================================*/
extern Fls_JobStateType Fls_JobState;
extern const Fls_ConfigType *Fls_ConfigPtr;
extern volatile MemIf_JobResultType Fls_LLDJobResult;          /* implicit zero initialization: MEMIF_JOB_OK */
extern Fls_Lld_JobType Fls_LLDJob;    /* implicit zero initialization: FLASH_JOB_NONE */

#define FLS_START_SEC_VAR_CLEARED_32
#include "Fls_MemMap.h"
/* Counters for timeout detection */
FLS_VAR uint32  Fls_Qspi_ElapsedTicks;

FLS_VAR uint32  Fls_Qspi_TimeoutTicks;

#define FLS_STOP_SEC_VAR_CLEARED_32
#include "Fls_MemMap.h"
/*==================================================================================================
                                                LOCAL VARIABLES
==================================================================================================*/
#define FLS_START_SEC_VAR_CLEARED_32
#include "Fls_MemMap.h"
/* The external job start address */
FLS_VAR static Fls_AddressType Fls_Qspi_ExtJobStartAddr;
/* The external job size */
FLS_VAR static Fls_AddressType Fls_Qspi_ExtJobSize;
/* The external job current chunk size, for jobs which need to be split in chunks */
FLS_VAR static Fls_AddressType Fls_Qspi_ExtJobChunkSize;
/* Source data buffer for the current write job. */
FLS_VAR static const uint8 * Fls_Qspi_ExtJobBuffAddr;

/* The external job start address */
FLS_VAR static Fls_AddressType Fls_Qspi_ExtJobStartAddr;
/* The external job size */
FLS_VAR static Fls_AddressType Fls_Qspi_ExtJobSize;
/* The external job current chunk size, for jobs which need to be split in chunks */
FLS_VAR static Fls_AddressType Fls_Qspi_ExtJobChunkSize;
/* Source data buffer for the current write job. */
FLS_VAR static const uint8 * Fls_Qspi_ExtJobBuffAddr;
#define FLS_STOP_SEC_VAR_CLEARED_32
#include "Fls_MemMap.h"
/*==================================================================================================
                                                LOCAL CONSTANTS
==================================================================================================*/
/*==================================================================================================
                                                LOCAL MACROS
==================================================================================================*/
/*! Invalid configuration, specifies unused device */
#define FLS_QSPI_CFG_INVALID          (uint8)0xFFU

#define FLS_START_SEC_CODE
#include "Fls_MemMap.h"
/*==================================================================================================
                                                LOCAL FUNCTIONS
==================================================================================================*/

/*FUNCTION**********************************************************************
 *
 * Function Name : Fls_Lld_ComputeWriteChunkSize
 * Description   : Computes the size of the next chunk of a write job.
 *
 *END**************************************************************************/
FLS_FUNC static Fls_AddressType  Fls_Lld_ComputeWriteChunkSize(Fls_AddressType JobSize,
                                                                Fls_AddressType BaseAddr,
                                                                Fls_AddressType PageSize,
                                                                uint32 MemAlignment)
{
    Fls_AddressType ChunkSize;

    /* Reserve size in case writing from odd starting address */
    uint32 StartAddrOffset  = BaseAddr & (MemAlignment - 1U);
    uint32 QspiMaxWriteSize = QSPI_MAX_WRITE_SIZE - StartAddrOffset;

    /* Check external device restrictions: ChunkSize can not exceed the device page size */
    /* If address is not aligned, max. ChunkSize is the amount left in the current page */
    ChunkSize = PageSize - (BaseAddr % PageSize);
    /* Check if chunk does not exceed IP driver capabilities */
    if (ChunkSize > QspiMaxWriteSize)
    {
        ChunkSize = QspiMaxWriteSize;
    }
    /* Check if entire job fits in current page */
    if (ChunkSize > JobSize)
    {
        ChunkSize = JobSize;
    }
    return ChunkSize;
}

/**
* @brief          Writes a single data chunk.
* @details        This function initiates a write for a data chunk - amount of data than can be written in a single operation.
*/
FLS_FUNC static Fls_Lld_ReturnType Fls_Lld_SectorWriteChunk(const boolean AsynchEnable)
{
    Fls_Lld_ReturnType LldRetVal = FLASH_E_FAILED;
    uint32 FlashInstance;
    Qspi_StatusType Status = STATUS_QSPI_SUCCESS;

    /* Get external flash instance */
    FlashInstance = Fls_ConfigPtr->SectorList[Fls_JobState.JobSectorIt].SectFlashUnit;

#if (FLS_ERASE_VERIFICATION_ENABLED == STD_ON)
#ifdef FLS_USER_MODE_SUPPORTED
    if (STATUS_QSPI_SUCCESS != (Qspi_StatusType)OsIf_Trusted_Call_Return3param(Qspi_Lld_EraseVerify, FlashInstance, Fls_Qspi_ExtJobStartAddr, Fls_Qspi_ExtJobChunkSize))
#else
    if (STATUS_QSPI_SUCCESS != Qspi_Lld_EraseVerify(FlashInstance, Fls_Qspi_ExtJobStartAddr, Fls_Qspi_ExtJobChunkSize))
#endif
    {
        (void)Det_ReportRuntimeError((uint16)FLS_MODULE_ID, FLS_INSTANCE_ID, FLS_SID_MAIN_FUNCTION, FLS_E_VERIFY_ERASE_FAILED);
        LldRetVal = FLASH_E_FAILED;
    }
    else
    {
#endif /* (FLS_ERASE_VERIFICATION_ENABLED == STD_ON) */
        /* Call IP write routine. */
#ifdef FLS_USER_MODE_SUPPORTED
        if (STATUS_QSPI_SUCCESS != (Qspi_StatusType)OsIf_Trusted_Call_Return4param(Qspi_Lld_Program, FlashInstance, Fls_Qspi_ExtJobStartAddr, Fls_Qspi_ExtJobBuffAddr, Fls_Qspi_ExtJobChunkSize))
#else
        if (STATUS_QSPI_SUCCESS != Qspi_Lld_Program(FlashInstance, Fls_Qspi_ExtJobStartAddr, Fls_Qspi_ExtJobBuffAddr, Fls_Qspi_ExtJobChunkSize))
#endif
        {
            LldRetVal = FLASH_E_FAILED;
        }
        else
        {
            if ((boolean)FALSE == AsynchEnable) /*SYNC Mode*/
            {
                LldRetVal = FLASH_E_OK;
                /* Wait for the write to finish. */
                do
                {
#if ( (FLS_TIMEOUT_SUPERVISION_ENABLED == STD_ON))
                    if (Fls_Qspi_ElapsedTicks >= Fls_Qspi_TimeoutTicks)
                    {
                        Status = STATUS_QSPI_TIMEOUT;
                        (void)Det_ReportRuntimeError((uint16)FLS_MODULE_ID, FLS_INSTANCE_ID, FLS_SID_MAIN_FUNCTION, FLS_E_TIMEOUT);
                        break;
                    }
                    ++Fls_Qspi_ElapsedTicks;
#endif /* (FLS_TIMEOUT_SUPERVISION_ENABLED == STD_ON) */
#ifdef FLS_USER_MODE_SUPPORTED
                    Status = (Qspi_StatusType)OsIf_Trusted_Call_Return1param(Qspi_Lld_GetMemoryStatus,FlashInstance);
#else
                    Status = Qspi_Lld_GetMemoryStatus(FlashInstance);
#endif
                }
                while (STATUS_QSPI_BUSY == Status);

                if (STATUS_QSPI_SUCCESS != Status)
                {
                    LldRetVal = FLASH_E_FAILED;
                }
#if (FLS_WRITE_VERIFICATION_ENABLED == STD_ON)
                /*GCOVR_EXCL_START*/
                /*The flash device can't occur to erasing fault in integration test*/
#ifdef FLS_USER_MODE_SUPPORTED
                else if (STATUS_QSPI_SUCCESS != (Qspi_StatusType)OsIf_Trusted_Call_Return4param( Qspi_Lld_ProgramVerify, FlashInstance, Fls_Qspi_ExtJobStartAddr, Fls_Qspi_ExtJobBuffAddr, Fls_Qspi_ExtJobChunkSize))
#else
                else if (STATUS_QSPI_SUCCESS != Qspi_Lld_ProgramVerify(FlashInstance, Fls_Qspi_ExtJobStartAddr, Fls_Qspi_ExtJobBuffAddr, Fls_Qspi_ExtJobChunkSize))
#endif
                {
                    (void)Det_ReportRuntimeError((uint16)FLS_MODULE_ID, FLS_INSTANCE_ID, FLS_SID_MAIN_FUNCTION, FLS_E_VERIFY_WRITE_FAILED);
                    LldRetVal = FLASH_E_FAILED;
                }
                /*GCOVR_EXCL_STOP*/
#endif /* (FLS_WRITE_VERIFICATION_ENABLED == STD_ON) */
                else
                {
                    /* Empty clause added to fulfill MISRA. */
                }
            }
            else
            {
                /* Schedule async write job. */
                Fls_LLDJobResult = MEMIF_JOB_PENDING;
                Fls_LLDJob = FLASH_JOB_WRITE;
                LldRetVal = FLASH_E_PENDING;
            }
        }
#if (FLS_ERASE_VERIFICATION_ENABLED == STD_ON)
    }
#endif /* (FLS_ERASE_VERIFICATION_ENABLED == STD_ON) */

    return LldRetVal;
}

/*
 * Function Name : Fls_Lld_LLDMainFunctionQspiErase
 * Description   : Handles ASYNC QSPI erase
*/
FLS_FUNC static void Fls_Lld_LLDMainFunctionQspiErase(uint32 FlashInstance)
{
#if (FLS_ERASE_VERIFICATION_ENABLED == STD_OFF)
    (void)FlashInstance;
#else
    Fls_AddressType ChunkSize = FLS_MAX_ERASE_BLANK_CHECK;

    if (Fls_Qspi_ExtJobSize < FLS_MAX_ERASE_BLANK_CHECK)
    {
        ChunkSize = Fls_Qspi_ExtJobSize;
    }
    Fls_Qspi_ExtJobSize -= ChunkSize;
    /* Verify that the sector was successfully erased. */
#ifdef FLS_USER_MODE_SUPPORTED
    if (STATUS_QSPI_SUCCESS != (Qspi_StatusType)OsIf_Trusted_Call_Return3param(Qspi_Lld_EraseVerify, FlashInstance, Fls_Qspi_ExtJobStartAddr, ChunkSize))
#else
    if (STATUS_QSPI_SUCCESS != Qspi_Lld_EraseVerify(FlashInstance, Fls_Qspi_ExtJobStartAddr, ChunkSize))
#endif
    {
        /*GCOVR_EXCL_START*/
        /*The flash device can't occur to erasing fault in integration test,so the integration not cover it*/
        /* Error, the memory locations are not erased or there was an error when attempting to read it. */
        (void)Det_ReportRuntimeError((uint16)FLS_MODULE_ID, FLS_INSTANCE_ID, FLS_SID_MAIN_FUNCTION, FLS_E_VERIFY_ERASE_FAILED);
        Fls_LLDJob = FLASH_JOB_NONE;
        Fls_LLDJobResult = MEMIF_JOB_FAILED;
        /*GCOVR_EXCL_STOP*/
    }
    if ((Fls_Qspi_ExtJobSize == 0U) && (Fls_LLDJob != FLASH_JOB_NONE))
#endif /* (FLS_ERASE_VERIFICATION_ENABLED == STD_ON) */
    {
        /* Erase operation succeeded */
        Fls_LLDJob = FLASH_JOB_NONE;
        Fls_LLDJobResult = MEMIF_JOB_OK;
        if (Fls_JobState.JobCurrentAddr >= (Fls_ConfigPtr->SectorList[Fls_JobState.JobSectorIt].SectorStartAddress + \
                                            Fls_ConfigPtr->SectorList[Fls_JobState.JobSectorIt].SectorSize))
        {
            /* Move on to the next sector */
            Fls_JobState.JobSectorIt += 1U;
        }
    }
}


/*
 * Function Name : Fls_Lld_GetExtFlashConfig
 * Description   : Gets the external flash configuration
*/
FLS_FUNC static const Qspi_MemoryConfigType * Fls_Lld_GetExtFlashConfig(uint32 FlashInstance)
{
    const Qspi_MemoryConfigType *FlashConfig = NULL_PTR;
    uint32 FlashConfigNo;

    /* Get external flash configuration */
    FlashConfigNo = Fls_ConfigPtr->ExtFlsCfgConfig->FlashMemCfg[FlashInstance].FlashUnitId;
    if (FlashConfigNo != FLS_QSPI_CFG_INVALID)
    {
        FlashConfig =  &(Fls_ConfigPtr->ExtFlsCfgConfig->MemCfg[FlashConfigNo]);
    }
#if (defined(FLS_DEVICES_USING_SFDP))
#if (FLS_DEVICES_USING_SFDP > 0)
    else
    {
        /* SFDP auto-filled configuration */
        FlashConfigNo = Fls_Qspi_SfdpConfigsIndex[FlashInstance];
        FlashConfig = &(Fls_Qspi_SfdpConfigs[FlashConfigNo].sfdpCfg);
    }
#endif
#endif
    return FlashConfig;
}


/*
 * Function Name : Fls_Lld_LLDMainFunctionQspiWrite
 * Description   : Handles ASYNC QSPI write
*/
FLS_FUNC static void Fls_Lld_LLDMainFunctionQspiWrite(uint32 FlashInstance)
{
    const Qspi_MemoryConfigType *FlashConfig;
    Fls_Lld_ReturnType LldRetVal = FLASH_E_FAILED;
    uint32 MemAlignment;

#if (FLS_WRITE_VERIFICATION_ENABLED == STD_ON)
#ifdef FLS_USER_MODE_SUPPORTED
    if (STATUS_QSPI_SUCCESS != (Qspi_StatusType)OsIf_Trusted_Call_Return4param( Qspi_Lld_ProgramVerify, FlashInstance, Fls_Qspi_ExtJobStartAddr, Fls_Qspi_ExtJobBuffAddr, Fls_Qspi_ExtJobChunkSize))
#else
    if (STATUS_QSPI_SUCCESS != Qspi_Lld_ProgramVerify(FlashInstance, Fls_Qspi_ExtJobStartAddr, Fls_Qspi_ExtJobBuffAddr, Fls_Qspi_ExtJobChunkSize))
#endif
    {
        /* Error, the memory location was not programmed with the desired data. */
        (void)Det_ReportRuntimeError((uint16)FLS_MODULE_ID, FLS_INSTANCE_ID, FLS_SID_MAIN_FUNCTION, FLS_E_VERIFY_WRITE_FAILED);
        Fls_LLDJob = FLASH_JOB_NONE;
        Fls_LLDJobResult = MEMIF_JOB_FAILED;
    }
    else
#endif /* (FLS_WRITE_VERIFICATION_ENABLED == STD_ON) */
    {
        /* Chunk write succeeded, check if there are more chunks */
        if (Fls_Qspi_ExtJobSize == Fls_Qspi_ExtJobChunkSize)
        {
            /* This was the last chunk, operation is completed */
            Fls_LLDJob = FLASH_JOB_NONE;
            Fls_LLDJobResult = MEMIF_JOB_OK;
            /* For a Write Job in ASYNC mode check if Fls_JobSectorIt should be increased */
            if (Fls_JobState.JobCurrentAddr  >= (Fls_ConfigPtr->SectorList[Fls_JobState.JobSectorIt].SectorStartAddress + \
                                                 Fls_ConfigPtr->SectorList[Fls_JobState.JobSectorIt].SectorSize))
            {
                /* Move on to the next sector */
                Fls_JobState.JobSectorIt += 1U;
            }
        }
        else
        {
            /* Get external flash configuration */
            FlashConfig = Fls_Lld_GetExtFlashConfig(FlashInstance);

            /* Get memory alignement */
            MemAlignment = (uint32)Fls_ConfigPtr->ExtFlsCfgConfig->FlashMemCfg[FlashInstance].FlashConnectionCfg->MemAlignment;

            /* Update job parameters */
            Fls_Qspi_ExtJobStartAddr += Fls_Qspi_ExtJobChunkSize;

            Fls_Qspi_ExtJobBuffAddr = &(Fls_Qspi_ExtJobBuffAddr[Fls_Qspi_ExtJobChunkSize]);

            Fls_Qspi_ExtJobSize -= Fls_Qspi_ExtJobChunkSize;

            Fls_Qspi_ExtJobChunkSize = Fls_Lld_ComputeWriteChunkSize(Fls_Qspi_ExtJobSize, Fls_Qspi_ExtJobStartAddr, FlashConfig->PageSize, MemAlignment);
            /* Launch async write of next chunk */
            LldRetVal = Fls_Lld_SectorWriteChunk((boolean)TRUE);
            if (FLASH_E_FAILED == LldRetVal)
            {
                /* Error, cannot launch write for next chunk */
                Fls_LLDJob = FLASH_JOB_NONE;
                Fls_LLDJobResult = MEMIF_JOB_FAILED;
            }
        }
    }
}



/*==================================================================================================
                                                GLOBAL FUNCTIONS
==================================================================================================*/
/*FUNCTION**********************************************************************
 * Function Name : Fls_Lld_LLDMainFunctionQspiJobs
 * Description   : Handles ASYNC QSPI jobs.
 * @implements     Fls_Lld_LLDMainFunctionQspiJobs_Activity
 ******************************************************************************/
FLS_FUNC void Fls_Lld_LLDMainFunctionQspiJobs(void)
{
    Qspi_StatusType Status;
    uint32 FlashInstance;

    /* Get external flash instance */
    FlashInstance = Fls_ConfigPtr->SectorList[Fls_JobState.JobSectorIt].SectFlashUnit;
#ifdef FLS_USER_MODE_SUPPORTED
    Status = (Qspi_StatusType)OsIf_Trusted_Call_Return1param(Qspi_Lld_GetMemoryStatus,FlashInstance);
#else
    Status = Qspi_Lld_GetMemoryStatus(FlashInstance);
#endif
    if (STATUS_QSPI_BUSY != Status)
    {
        if (STATUS_QSPI_SUCCESS != Status)
        {
            /*GCOVR_EXCL_START*/
            /*The flash device can't occur to hw fault in integration test,so the integration not cover it*/
            /* Error, unable to retrieve flash device Status */
            if (FLASH_JOB_ERASE == Fls_LLDJob)
            {
                (void)Det_ReportTransientFault((uint16)FLS_MODULE_ID, FLS_INSTANCE_ID, FLS_SID_MAIN_FUNCTION, FLS_E_ERASE_FAILED);
            }
            else if (FLASH_JOB_WRITE == Fls_LLDJob )
            {

                (void)Det_ReportTransientFault((uint16)FLS_MODULE_ID, FLS_INSTANCE_ID, FLS_SID_MAIN_FUNCTION, FLS_E_WRITE_FAILED);
            }
            else
            {
                /* Do nothing - should not happen in Fully Trusted Environment;
                'else' clause added to fulfil MISRA Rule 14.10 */
            }

            Fls_LLDJob = FLASH_JOB_NONE;
            Fls_LLDJobResult = MEMIF_JOB_FAILED;
            /*GCOVR_EXCL_STOP*/
        }
        else
        {
            if (FLASH_JOB_ERASE == Fls_LLDJob)
            {
                Fls_Lld_LLDMainFunctionQspiErase(FlashInstance);
            }
            else if (FLASH_JOB_WRITE == Fls_LLDJob )
            {
                Fls_Lld_LLDMainFunctionQspiWrite(FlashInstance);
            }
            else
            {
                /* Do nothing - should not happen in Fully Trusted Environment;
                'else' clause added to fulfil MISRA Rule 14.10 */
            }
        }
    }   /* if (STATUS_QSPI_BUSY != Status ) */
    else
    {
        /* device busy, check timeout */
#if ( (FLS_TIMEOUT_SUPERVISION_ENABLED == STD_ON))
        ++Fls_Qspi_ElapsedTicks;
        if (Fls_Qspi_ElapsedTicks >= Fls_Qspi_TimeoutTicks)
        {
            /* operation timed out */
            (void)Det_ReportRuntimeError((uint16)FLS_MODULE_ID, FLS_INSTANCE_ID, FLS_SID_MAIN_FUNCTION, FLS_E_TIMEOUT);

            if (FLASH_JOB_ERASE == Fls_LLDJob)
            {
                (void)Det_ReportTransientFault((uint16)FLS_MODULE_ID, FLS_INSTANCE_ID, FLS_SID_MAIN_FUNCTION, FLS_E_ERASE_FAILED);
            }
            else if (FLASH_JOB_WRITE == Fls_LLDJob )
            {
                (void)Det_ReportTransientFault((uint16)FLS_MODULE_ID, FLS_INSTANCE_ID, FLS_SID_MAIN_FUNCTION, FLS_E_WRITE_FAILED);
            }
            else
            {
                /* Do nothing - should not happen in Fully Trusted Environment;
                'else' clause added to fulfil MISRA Rule 14.10 */
            }

            Fls_LLDJob = FLASH_JOB_NONE;
            Fls_LLDJobResult = MEMIF_JOB_FAILED;
        }
#endif /* (FLS_TIMEOUT_SUPERVISION_ENABLED == STD_ON) */
    }   /* if (STATUS_QSPI_BUSY != Status ) */
}

/*FUNCTION**********************************************************************
 * Function Name : Fls_Lld_SectorEraseQspiJobs
 * Description   : Erase a sector in extern flash device.
 * @implements     Fls_Lld_SectorEraseQspiJobs_Activity
 ******************************************************************************/
FLS_FUNC Fls_Lld_ReturnType Fls_Lld_SectorEraseQspiJobs(boolean AsynchEnable, Fls_AddressType SectorOffset, const Fls_LengthType PhysicalSectorSize)
{
    Fls_Lld_ReturnType LldRetVal = FLASH_E_FAILED;
    Qspi_StatusType Status = STATUS_QSPI_SUCCESS;
    uint32 FlashInstance;

    /* Get external flash instance */
    FlashInstance = Fls_ConfigPtr->SectorList[Fls_JobState.JobSectorIt].SectFlashUnit;
    /* Compute target address : sector start address */
    Fls_Qspi_ExtJobStartAddr = Fls_ConfigPtr->SectorList[Fls_JobState.JobSectorIt].SectorHwStartAddress + SectorOffset;
    /* Record job size */
    Fls_Qspi_ExtJobSize = PhysicalSectorSize;

    /* Call IP routine to erase external sector. */
    if (STATUS_QSPI_SUCCESS != Qspi_Lld_EraseBlock(FlashInstance, Fls_Qspi_ExtJobStartAddr, Fls_Qspi_ExtJobSize))
    {
        LldRetVal = FLASH_E_FAILED;
    }
    else
    {
#if ( (FLS_TIMEOUT_SUPERVISION_ENABLED == STD_ON))
        /* Prepare timeout counter */
        Fls_Qspi_ElapsedTicks = 0U;
        if(FALSE==AsynchEnable)
        {
            Fls_Qspi_TimeoutTicks = FLS_QSPI_SYNC_ERASE_TIMEOUT;
        }
        else
        {
            Fls_Qspi_TimeoutTicks = FLS_QSPI_ASYNC_ERASE_TIMEOUT;
        }

#endif /* (FLS_TIMEOUT_SUPERVISION_ENABLED == STD_ON) */
        if ((boolean)FALSE == AsynchEnable) /*SYNC Mode*/
        {
            LldRetVal = FLASH_E_OK;
            /* Wait for the erase to finish. */
            do
            {
#if ( (FLS_TIMEOUT_SUPERVISION_ENABLED == STD_ON))

                if (Fls_Qspi_ElapsedTicks >= Fls_Qspi_TimeoutTicks)
                {
                    Status = STATUS_QSPI_TIMEOUT;
                    (void)Det_ReportRuntimeError((uint16)FLS_MODULE_ID, FLS_INSTANCE_ID, FLS_SID_MAIN_FUNCTION, FLS_E_TIMEOUT);
                    break;
                }
                ++Fls_Qspi_ElapsedTicks;
#endif /* (FLS_TIMEOUT_SUPERVISION_ENABLED == STD_ON) */
#ifdef FLS_USER_MODE_SUPPORTED
                Status = (Qspi_StatusType)OsIf_Trusted_Call_Return1param(Qspi_Lld_GetMemoryStatus,FlashInstance);
#else
                Status = Qspi_Lld_GetMemoryStatus(FlashInstance);
#endif
            }
            while (STATUS_QSPI_BUSY == Status);

            if (STATUS_QSPI_SUCCESS != Status)
            {
                LldRetVal = FLASH_E_FAILED;
            }
#if (FLS_ERASE_VERIFICATION_ENABLED == STD_ON)
            /* Verify that the sector was succesfully erased. */
#ifdef FLS_USER_MODE_SUPPORTED
            else if (STATUS_QSPI_SUCCESS != (Qspi_StatusType)OsIf_Trusted_Call_Return3param(Qspi_Lld_EraseVerify, FlashInstance, Fls_Qspi_ExtJobStartAddr, Fls_Qspi_ExtJobSize))
#else
            else if (STATUS_QSPI_SUCCESS != Qspi_Lld_EraseVerify(FlashInstance, Fls_Qspi_ExtJobStartAddr, Fls_Qspi_ExtJobSize))
#endif
            {
                (void)Det_ReportRuntimeError((uint16)FLS_MODULE_ID, FLS_INSTANCE_ID, FLS_SID_MAIN_FUNCTION, FLS_E_VERIFY_ERASE_FAILED);
                LldRetVal = FLASH_E_FAILED;
            }
#endif
            else
            {
                ; /* Empty clause added to fulfill MISRA. */
            }
        }
        else
        {
            /* Schedule async erase job. */
            Fls_LLDJobResult = MEMIF_JOB_PENDING;
            Fls_LLDJob = FLASH_JOB_ERASE;
            LldRetVal = FLASH_E_PENDING;
        }
    }

    return LldRetVal;
}

/*FUNCTION**********************************************************************
 * Function Name : Fls_Lld_SectorWriteQspiJobs
 * Description   : Write data to extern flash device.
 * @implements     Fls_Lld_SectorWriteQspiJobs_Activity
 ******************************************************************************/
FLS_FUNC Fls_Lld_ReturnType Fls_Lld_SectorWriteQspiJobs(const Fls_AddressType SectorOffset,
        const Fls_AddressType Length,
        const uint8 *JobDataSrcPtr,
        const boolean AsynchEnable
                                                       )
{
    Fls_Lld_ReturnType LldRetVal;
    const Qspi_MemoryConfigType *FlashConfig;
    uint32 FlashInstance;
    uint32 MemAlignment;

    /* Get external flash instance */
    FlashInstance = Fls_ConfigPtr->SectorList[Fls_JobState.JobSectorIt].SectFlashUnit;
    /* Get external flash configuration */
    FlashConfig = Fls_Lld_GetExtFlashConfig(FlashInstance);
    /* Get memory alignement */
    MemAlignment = (uint32)Fls_ConfigPtr->ExtFlsCfgConfig->FlashMemCfg[FlashInstance].FlashConnectionCfg->MemAlignment;

    /* Compute target address : sector address + offset */
    Fls_Qspi_ExtJobStartAddr = Fls_ConfigPtr->SectorList[Fls_JobState.JobSectorIt].SectorHwStartAddress + SectorOffset;
    /* Record source pointer */
    Fls_Qspi_ExtJobBuffAddr = JobDataSrcPtr;
    /* Record job size */
    Fls_Qspi_ExtJobSize = Length;
    /* Compute next chunk size - consider QSPI and external device restrictions */
    Fls_Qspi_ExtJobChunkSize = Fls_Lld_ComputeWriteChunkSize(Fls_Qspi_ExtJobSize, Fls_Qspi_ExtJobStartAddr, FlashConfig->PageSize, MemAlignment);

#if ( (FLS_TIMEOUT_SUPERVISION_ENABLED == STD_ON))
    /* Prepare timeout counter */
    Fls_Qspi_ElapsedTicks = 0U;
    if(FALSE==AsynchEnable)
    {
        Fls_Qspi_TimeoutTicks = FLS_QSPI_SYNC_WRITE_TIMEOUT;
    }
    else
    {
        Fls_Qspi_TimeoutTicks = FLS_QSPI_ASYNC_WRITE_TIMEOUT;
    }
#endif /* (FLS_TIMEOUT_SUPERVISION_ENABLED == STD_ON) */

    /* Write the first chunk */
    LldRetVal = Fls_Lld_SectorWriteChunk(AsynchEnable);
    if ((boolean)FALSE == AsynchEnable)
    {   /* SYNC Mode - complete operation */
        while ((FLASH_E_OK == LldRetVal) && (Fls_Qspi_ExtJobSize > Fls_Qspi_ExtJobChunkSize))
        {
            /* Update job parameters */
            Fls_Qspi_ExtJobStartAddr += Fls_Qspi_ExtJobChunkSize;
            Fls_Qspi_ExtJobBuffAddr = &(Fls_Qspi_ExtJobBuffAddr[Fls_Qspi_ExtJobChunkSize]);
            Fls_Qspi_ExtJobSize -= Fls_Qspi_ExtJobChunkSize;
            Fls_Qspi_ExtJobChunkSize = Fls_Lld_ComputeWriteChunkSize(Fls_Qspi_ExtJobSize, Fls_Qspi_ExtJobStartAddr, FlashConfig->PageSize, MemAlignment);
            /* Write current chunk */
            LldRetVal = Fls_Lld_SectorWriteChunk(AsynchEnable);
        }
    }

    return LldRetVal;
}

/*FUNCTION**********************************************************************
 * Function Name : Fls_Lld_InitControllers
 * Description   : Initialize QSPI controllers.
 * @implements     Fls_Lld_InitControllers_Activity
 ******************************************************************************/
FLS_FUNC Qspi_StatusType Fls_Lld_InitControllers(void)
{
    Qspi_StatusType Status = STATUS_QSPI_SUCCESS;
    uint8 QspiChannel = 0U;
    uint8 LoopCnt = 0U;

    for (LoopCnt = 0U; LoopCnt < Fls_ConfigPtr->ExtFlsCfgConfig->QspiUnitsCount; ++LoopCnt)
    {
        /* Get configuration for current QSPI device */
        QspiChannel = Fls_ConfigPtr->ExtFlsCfgConfig->QspiConfig[LoopCnt];
        if (QspiChannel != FLS_QSPI_CFG_INVALID)
        {
            /* Use configuration "QspiChannel" to initialize QSPI controller "LoopCnt" */
#ifdef FLS_USER_MODE_SUPPORTED
            Status = (Qspi_StatusType)OsIf_Trusted_Call_Return2param(Qspi_Lld_ControllerInit,
                     LoopCnt, &(Fls_ConfigPtr->ExtFlsCfgConfig->QspiUnitCfg[QspiChannel]));
#else
            Status = Qspi_Lld_ControllerInit(LoopCnt, &(Fls_ConfigPtr->ExtFlsCfgConfig->QspiUnitCfg[QspiChannel]));
#endif
            if (STATUS_QSPI_SUCCESS != Status)
            {
                break;
            }
        }
    }

    return Status;
}

/*FUNCTION**********************************************************************
 * Function Name : Fls_Lld_InitMemories
 * Description   : Initialize Qspi external flash devices.
 * @implements     Fls_Lld_InitMemories_Activity
 ******************************************************************************/
FLS_FUNC Qspi_StatusType Fls_Lld_InitMemories(void)
{
    Qspi_StatusType Status = STATUS_QSPI_SUCCESS;
    uint8 MemConfig = 0U;
    uint8 LoopCnt = 0U;
#if (defined(FLS_DEVICES_USING_SFDP))
#if (FLS_DEVICES_USING_SFDP > 0)
    Qspi_MemoryConfigType *flashCfg;
    /* Current SFDP configuration  */
    uint8 Fls_Qspi_SfdpConfigsCount = 0U;
#endif
#endif

    for (LoopCnt = 0U; LoopCnt < Fls_ConfigPtr->ExtFlsCfgConfig->FlashUnitsCount; ++LoopCnt)
    {
        /* Get configuration for current QSPI device */
        MemConfig = Fls_ConfigPtr->ExtFlsCfgConfig->FlashMemCfg[LoopCnt].FlashUnitId;
        if (MemConfig != FLS_QSPI_CFG_INVALID)
        {
            /* Use configuration "MemConfig" to initialize flash device "LoopCnt" */
#ifdef FLS_USER_MODE_SUPPORTED
            Status = (Qspi_StatusType)OsIf_Trusted_Call_Return3param(Qspi_Lld_Init,
                     LoopCnt, &(Fls_ConfigPtr->ExtFlsCfgConfig->MemCfg[MemConfig]),
                     Fls_ConfigPtr->ExtFlsCfgConfig->FlashMemCfg[LoopCnt].FlashConnectionCfg);
#else
            Status = Qspi_Lld_Init(LoopCnt, &(Fls_ConfigPtr->ExtFlsCfgConfig->MemCfg[MemConfig]), Fls_ConfigPtr->ExtFlsCfgConfig->FlashMemCfg[LoopCnt].FlashConnectionCfg);
#endif
        }
#if (defined(FLS_DEVICES_USING_SFDP))
#if (FLS_DEVICES_USING_SFDP > 0)
        else
        {
            /* This device must auto-configure using SFDP */
            /* Prepare configuration structure, link lut and init operations arrays */
            flashCfg = &(Fls_Qspi_SfdpConfigs[Fls_Qspi_SfdpConfigsCount].sfdpCfg);
            flashCfg->lutSequences.opCount = FLS_QSPI_SFDP_LUT_SIZE;
            flashCfg->lutSequences.lutOps = Fls_Qspi_SfdpConfigs[Fls_Qspi_SfdpConfigsCount].lutOps;
            flashCfg->initConfiguration.opCount = FLS_QSPI_SFDP_INIT_OP_SIZE;
            flashCfg->initConfiguration.operations = Fls_Qspi_SfdpConfigs[Fls_Qspi_SfdpConfigsCount].initOps;
            Status = Qspi_Lld_ReadSfdp(flashCfg, &((*(Fls_pConfigPtr->pFlsQspiCfgConfig->paFlashConnectionCfg))[LoopCnt]));
            if (STATUS_QSPI_SUCCESS == Status)
            {
                /* Qspi_Lld_ReadSfdp auto-filled configuration, use it to initialize flash device */
#ifdef FLS_USER_MODE_SUPPORTED
                Status = (Qspi_StatusType)OsIf_Trusted_Call_Return3param(Qspi_Lld_Init,
                         LoopCnt, flashCfg,
                         &((*(Fls_pConfigPtr->pFlsQspiCfgConfig->paFlashConnectionCfg))[LoopCnt]));
#else
                Status = Qspi_Lld_Init(LoopCnt, flashCfg, &((*(Fls_pConfigPtr->pFlsQspiCfgConfig->paFlashConnectionCfg))[LoopCnt]));
#endif
            }
            /* Store index to allow configuration to be retrieved later */
            Fls_Qspi_SfdpConfigsIndex[LoopCnt] = Fls_Qspi_SfdpConfigsCount;
            Fls_Qspi_SfdpConfigsCount++;
        }
#endif
#endif

        /* Configure the AHB reads for flash unit "LoopCnt" */
        if ((STATUS_QSPI_SUCCESS == Status) && (TRUE == Fls_ConfigPtr->ExtFlsCfgConfig->FlashMemCfg[LoopCnt].AHBReadCfg))
        {
#ifdef FLS_USER_MODE_SUPPORTED
            Status = (Qspi_StatusType)OsIf_Trusted_Call_Return1param(Qspi_Lld_AhbReadEnable,Fls_ConfigPtr->ExtFlsCfgConfig->FlashMemCfg[LoopCnt].FlashConnectionCfg->QspiInstance);
#else
            Status = Qspi_Lld_AhbReadEnable(Fls_ConfigPtr->ExtFlsCfgConfig->FlashMemCfg[LoopCnt].FlashConnectionCfg->QspiInstance);
#endif
        }

        if (STATUS_QSPI_SUCCESS != Status)
        {
            break;
        }
    }

    return Status;
}

/*FUNCTION**********************************************************************
 * Function Name : Fls_Lld_CheckDevicesId
 * Description   : Check the identification of the external flash device
                   against the configured one.
 * @implements     Fls_Lld_CheckDevicesId_Activity
 ******************************************************************************/
FLS_FUNC boolean Fls_Lld_CheckDevicesId(void)
{
    Qspi_StatusType Status = STATUS_QSPI_SUCCESS;
    boolean RetVal = (boolean)TRUE;
    const Qspi_MemoryConfigType * PtrConfig;
    uint32 ReadId = 0U;
    uint8 LoopCnt;

    for (LoopCnt = 0U; LoopCnt < Fls_ConfigPtr->ExtFlsCfgConfig->FlashUnitsCount; ++LoopCnt)
    {
        /* Get configuration for current QSPI device */
        PtrConfig = Fls_Lld_GetExtFlashConfig(LoopCnt);

        /* If enabled, check identification of the external flash device */
        if (QSPI_LUT_INVALID != PtrConfig->ReadIdSettings.ReadIdLut)
        {
            Status = Qspi_Lld_ReadId(LoopCnt, (uint8 *)&ReadId);
            /* Compare current device's ID with the corresponding configuration */
            if ( (STATUS_QSPI_SUCCESS != Status) || (ReadId != PtrConfig->ReadIdSettings.ReadIdExpected) )
            {
                (void)Det_ReportTransientFault((uint16)FLS_MODULE_ID, FLS_INSTANCE_ID, FLS_SID_INIT, FLS_E_UNEXPECTED_FLASH_ID);
                RetVal = (boolean)FALSE;
                break;
            }
        }
    }
    return RetVal;
}

#define FLS_STOP_SEC_CODE
#include "Fls_MemMap.h"

#endif
#ifdef __cplusplus
}
#endif

/* End of file Fls_Lld_QspiFls.c */
