/*============================================================================*/

/*==================================================================================================
*
*   Copyright 2020-2021 NXP.
*
*   This software is owned or controlled by NXP and may only be used strictly in accordance with
*   the applicable license terms. By expressly accepting such terms or by downloading, installing,
*   activating and/or otherwise using the software, you are agreeing that you have read, and that
*   you agree to comply with and are bound by, such license terms. If you do not agree to
*   be bound by the applicable license terms, then you may not retain, install, activate or
*   otherwise use the software.
==================================================================================================*/
/*============================================================================*/

/*=============================================================================
  Description
  ============================================================================*/
/**
 *   @file    host_flash.c
 *
 *   @brief   This is a host flash driver wrapper file.
 */


/*=============================================================================
 *                               INCLUDE FILES
 =============================================================================*/
#include "host_flash.h"
#include "Mcal.h"
#include "C40_Ip.h"
#include "string.h"
/*=============================================================================
  LOCAL MACROS
  ============================================================================*/




/*=============================================================================
  LOCAL FUNCTION PROTOTYPES
  ============================================================================*/

/*=============================================================================
  GLOBAL FUNCTION PROTOTYPES
  ============================================================================*/
/* FUNCTION NAME: HostFlash_Init
 *
 * DESCRIPTION:
 * This function initializes flash driver.
 */
Fls_CheckStatusType HostFlash_Init(void)
{
    C40_Ip_StatusType eReturnCode = STATUS_C40_IP_ERROR;

    eReturnCode = C40_Ip_Init(&C40ConfigSet_BOARD_InitPeripherals_InitCfg);

    if( STATUS_C40_IP_SUCCESS == eReturnCode  )
    {
        return FLS_JOB_OK ;
    }
    else
    {
        return FLS_JOB_FAILED ;
    }
}


/* FUNCTION NAME: HostFlash_Program
 *
 * DESCRIPTION:
 * This function Programs Bytes in Flash memory.
 * length must be multiple of 8 Bytes.
 */
#if ( 1 == HOST_FLASH_RAM_CODE )
Fls_CheckStatusType __attribute__((section (".ramcode"))) HostFlash_Program( uint32_t Address,
        uint8_t* SourceAddress, uint32_t Length)
#else
Fls_CheckStatusType HostFlash_Program( uint32_t Address,
        uint8_t* SourceAddress, uint32_t Length)
#endif
{
    C40_Ip_VirtualSectorsType vSector ;
    C40_Ip_StatusType C40_Ip_Status = STATUS_C40_IP_ERROR ;
    uint32_t writeLenMax = 0u ;
    uint32_t writeLen = 0u ;
    uint32_t FlashDataRegInx = 0 ;

    uint8_t fillBuf[FLS_PROGRAM_MAX_LEN] = {0};
    uint32_t tempSrcAddr = SourceAddress;


    if ( ((Address & (C40_WRITE_DOUBLE_WORD - 1U)) != 0U ) ||
         (0U == Length)                                    ||
         (NULL_PTR == SourceAddress)
       )
    {
        return FLS_JOB_FAILED ;
    }

    vSector = C40_Ip_GetSectorNumberFromAddress(Address);

    /* Unlock sector */
    if (STATUS_C40_IP_SECTOR_PROTECTED == C40_Ip_GetLock(vSector))
    {
        C40_Ip_Status = C40_Ip_ClearLock(vSector, FLS_MASTER_ID);
    }

    while (Length > 0)
    {
        /* 32 Flash Data reg - 128Byte */
        FlashDataRegInx = (Address & (FLS_PROGRAM_MAX_LEN - 1U)) >> 2U;

        /* get the maximum write length of the current address */
        writeLenMax = FLS_PROGRAM_DATA_REG_SIZE * ( FLS_PROGRAM_DATA_REG_NUM - FlashDataRegInx ) ;

        /* if length is less than the maximum write length  */
        if( Length < writeLenMax )
        {
            /* if the writeLen need to fill 0 */
            if( Length & ( FLS_PROGRAM_MIN_LEN - 1 ) )
            {

                if( Length > FLS_PROGRAM_MIN_LEN )
                {   /* > 8 */
                    writeLen = Length - ( Length & ( FLS_PROGRAM_MIN_LEN - 1 ) );
                }
                else
                {   /* < 8 */
                    writeLen = FLS_PROGRAM_MIN_LEN ;
                }

                memset(fillBuf,0,sizeof(fillBuf));
                memcpy(fillBuf,SourceAddress,writeLen);

                tempSrcAddr = fillBuf ;
            }
            else
            {   /* do not need fill 0 */
                writeLen = Length;
                tempSrcAddr = SourceAddress;
            }

        }
        else
        {
            /* if length is more than the max write len, current write len is max the maximum write length  */
            writeLen = writeLenMax ;
            tempSrcAddr = SourceAddress ;
        }

        SuspendAllInterrupts();
        /* Write data */
        C40_Ip_Status = C40_Ip_MainInterfaceWrite(Address,writeLen,(const uint8_t *)tempSrcAddr,FLS_MASTER_ID);
        do
        {
            C40_Ip_Status = C40_Ip_MainInterfaceWriteStatus();
        }
        while (STATUS_C40_IP_BUSY == C40_Ip_Status);
        ResumeAllInterrupts();

        /* if filled? set to origin length */
        if( Length < writeLen )
        {
            writeLen = Length ;
        }

        Address += writeLen;
        SourceAddress += writeLen;
        Length -= writeLen;
    }

    if( STATUS_C40_IP_SUCCESS == C40_Ip_Status  )
    {
        return FLS_JOB_OK ;
    }
    else
    {
        return FLS_JOB_FAILED ;
    }
}


/* FUNCTION NAME: HostFlash_Erase
 *
 * DESCRIPTION:
 * This function Erases Sectors in Flash memory.
 */
#if ( 1 == HOST_FLASH_RAM_CODE )
Fls_CheckStatusType __attribute__((section (".ramcode"))) HostFlash_EraseSector( uint32_t Address,
        uint32_t NoOfSectors)
#else
Fls_CheckStatusType HostFlash_EraseSector( uint32_t Address,
        uint32_t NoOfSectors)
#endif
{
    (void)NoOfSectors ;
    C40_Ip_VirtualSectorsType vSector ;
    C40_Ip_StatusType C40_Ip_Status ;

    vSector = C40_Ip_GetSectorNumberFromAddress(Address);

    /* Unlock sector */
    if (STATUS_C40_IP_SECTOR_PROTECTED == C40_Ip_GetLock(vSector))
    {
        C40_Ip_Status = C40_Ip_ClearLock(vSector, FLS_MASTER_ID);
    }
    SuspendAllInterrupts();
    /* Erase sector */
    C40_Ip_Status = C40_Ip_MainInterfaceSectorErase(vSector, FLS_MASTER_ID);
    do
    {
        C40_Ip_Status = C40_Ip_MainInterfaceSectorEraseStatus();
    }
    while (STATUS_C40_IP_BUSY == C40_Ip_Status);
    ResumeAllInterrupts();
    if( STATUS_C40_IP_SUCCESS == C40_Ip_Status  )
    {
        return FLS_JOB_OK ;
    }
    else
    {
        return FLS_JOB_FAILED ;
    }
}

/* FUNCTION NAME: HostFlash_Erase
 *
 * DESCRIPTION:
 * This function Erases Sectors in Flash memory.
 */
#if ( 1 == HOST_FLASH_RAM_CODE )
Fls_CheckStatusType __attribute__((section (".ramcode"))) HostFlash_EraseByLen(uint32_t Address, uint32_t Length)
#else
Fls_CheckStatusType HostFlash_EraseByLen(uint32_t Address, uint32_t Length)
#endif
{
    C40_Ip_VirtualSectorsType vSector ;
    C40_Ip_StatusType C40_Ip_Status ;

    if( ( Address & (FLS_SECTOR_MIN_ERASE_SIZE - 1U) ) != 0U )
    {
        return FLS_INPUT_PARAM_FAILED ;
    }

    /* Check if the Length is sector alignment or not */
    if((Length & (FLS_SECTOR_MIN_ERASE_SIZE - 1U)) != 0U)
    {
        Length &= ~(FLS_SECTOR_MIN_ERASE_SIZE - 1U) ;
        Length += FLS_SECTOR_MIN_ERASE_SIZE ;
    }

    while( Length > 0 )
    {
        vSector = C40_Ip_GetSectorNumberFromAddress(Address);

        /*
         Hardware protection
         − 12x64KB sectors (super sectors) at first memory (768KB area of every block)
         − 32x8KB sectors at last memory (256KB area of every block)
         */
        /* Unlock sector */
        if(STATUS_C40_IP_SECTOR_PROTECTED == C40_Ip_GetLock(vSector))
        {
            C40_Ip_Status = C40_Ip_ClearLock(vSector, FLS_MASTER_ID);
        }

        SuspendAllInterrupts();
        /* Erase sector - 8 KB */
        C40_Ip_Status = C40_Ip_MainInterfaceSectorErase(vSector, FLS_MASTER_ID);
        do
        {
            C40_Ip_Status = C40_Ip_MainInterfaceSectorEraseStatus();
        } while (STATUS_C40_IP_BUSY == C40_Ip_Status);
        ResumeAllInterrupts();

        if(STATUS_C40_IP_SUCCESS == C40_Ip_Status)
        {
            Address += FLS_SECTOR_MIN_ERASE_SIZE ;
            Length -= FLS_SECTOR_MIN_ERASE_SIZE ;
        }
        else
        {
            break ;
        }
    }

    if( STATUS_C40_IP_SUCCESS == C40_Ip_Status  )
    {
        return FLS_JOB_OK ;
    }
    else
    {
        return FLS_JOB_FAILED ;
    }
}

