/* Copyright Statement:
 *
 * This software/firmware and related documentation ("AutoChips Software") are
 * protected under relevant copyright laws. The information contained herein is
 * confidential and proprietary to AutoChips Inc. and/or its licensors. Without
 * the prior written permission of AutoChips inc. and/or its licensors, any
 * reproduction, modification, use or disclosure of AutoChips Software, and
 * information contained herein, in whole or in part, shall be strictly
 * prohibited.
 *
 * AutoChips Inc. (C) 2021. All rights reserved.
 *
 * BY OPENING THIS FILE, RECEIVER HEREBY UNEQUIVOCALLY ACKNOWLEDGES AND AGREES
 * THAT THE SOFTWARE/FIRMWARE AND ITS DOCUMENTATIONS ("AUTOCHIPS SOFTWARE")
 * RECEIVED FROM AUTOCHIPS AND/OR ITS REPRESENTATIVES ARE PROVIDED TO RECEIVER
 * ON AN "AS-IS" BASIS ONLY. AUTOCHIPS EXPRESSLY DISCLAIMS ANY AND ALL
 * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
 * NONINFRINGEMENT. NEITHER DOES AUTOCHIPS PROVIDE ANY WARRANTY WHATSOEVER WITH
 * RESPECT TO THE SOFTWARE OF ANY THIRD PARTY WHICH MAY BE USED BY,
 * INCORPORATED IN, OR SUPPLIED WITH THE AUTOCHIPS SOFTWARE, AND RECEIVER AGREES
 * TO LOOK ONLY TO SUCH THIRD PARTY FOR ANY WARRANTY CLAIM RELATING THERETO.
 * RECEIVER EXPRESSLY ACKNOWLEDGES THAT IT IS RECEIVER'S SOLE RESPONSIBILITY TO
 * OBTAIN FROM ANY THIRD PARTY ALL PROPER LICENSES CONTAINED IN AUTOCHIPS
 * SOFTWARE. AUTOCHIPS SHALL ALSO NOT BE RESPONSIBLE FOR ANY AUTOCHIPS SOFTWARE
 * RELEASES MADE TO RECEIVER'S SPECIFICATION OR TO CONFORM TO A PARTICULAR
 * STANDARD OR OPEN FORUM. RECEIVER'S SOLE AND EXCLUSIVE REMEDY AND AUTOCHIPS'S
 * ENTIRE AND CUMULATIVE LIABILITY WITH RESPECT TO THE AUTOCHIPS SOFTWARE
 * RELEASED HEREUNDER WILL BE, AT AUTOCHIPS'S OPTION, TO REVISE OR REPLACE THE
 * AUTOCHIPS SOFTWARE AT ISSUE, OR REFUND ANY SOFTWARE LICENSE FEES OR SERVICE
 * CHARGE PAID BY RECEIVER TO AUTOCHIPS FOR SUCH AUTOCHIPS SOFTWARE AT ISSUE.
 */

/*!
 * @file eep_flash_drv.c
 *
 * @brief This file provides FLASH integration functions.
 *
 */

/* PRQA S 0380 EOF */ /* Number of macro definitions exceeds 4095 */
/* PRQA S 3387,3415 EOF */ /* Operator side effects. */
/* PRQA S 0306,0303,0316 EOF */ /* Type conversion. */
/* PRQA S 2889 EOF */ /* More than one 'return'  */
/* PRQA S 3408,3206 EOF */ /* Be defined without declaration. */
/* PRQA S 1338 EOF */ /* Modified parameter  */
/* PRQA S 1503,1505 EOF */ /* Is defined but not used. */

/* ===========================================  Includes  =========================================== */
#include "device_register.h"
#include "eep_drv.h"

/* ============================================  Define  ============================================ */
/* The timeout for the start bit setting of FLASH CST reggister */
#define FLASH_CST_TIMEOUT                   (2U)
/* FLASH cache clear timeout */
#define FLASH_CACHE_CLEAR_TIMEOUT           (37U)

/* ===========================================  Typedef  ============================================ */

/* ==========================================  Variables  =========================================== */

#if !defined(__ICCARM__)
uint32_t FLASH_StartCmd_WaitClear_Copy[128];
#endif

uint32_t EepFlashStartAddr = 0U;
uint32_t EepFlashEndAddr = 0U;
/* ====================================  Functions declaration  ===================================== */
extern void EepMemcpy(void *v_dst, const void *v_src, uint32_t c);

/* =====================================  Functions definition  ===================================== */

/* code in ram */
#if defined(__ICCARM__)
__ramfunc static void FLASH_StartCmd_WaitClear(void)
#else
__attribute__((section(".text"))) static void FLASH_StartCmd_WaitClear(void)
#endif
{
    volatile uint8_t timeout = 16; /* (128 + 10) */
    volatile uint32_t CST_Timeout = FLASH_CST_TIMEOUT;

    DisableInterrupts;
    /* trigger command execute */
    SET_BIT32(FLASH->CST, FLASH_CST_START_Msk);
    while ((READ_BIT32(FLASH->CST, FLASH_CST_START_Msk) != 0U) && (0U != CST_Timeout))
    {
        CST_Timeout--;
    }

    while(timeout--)
    {
        __asm("nop");
    }
    EnableInterrupts;
}
/* end */

/*!
 * @brief Perform start command sequence on FLASH.
 *
 * @param[in] config: The FLASH configuration structure pointer
 * @return operation status
 */
static Eep_Hal_StatusType Flash_StartCmdSequence(uint8_t CloseOpenCache)
{
    Eep_Hal_StatusType  Ret = EEP_HAL_SUCCESS;
    volatile uint32_t Timeout = FLASH_CST_TIMEOUT;

    if (1U == CloseOpenCache)
    {
#if defined(__ICCARM__)
        FLASH_StartCmd_WaitClear();
#else
        void (*FLASH_StartCmd_WaitClear_InRam)(void) = (void (*)(void))((uint32_t)FLASH_StartCmd_WaitClear_Copy | 0x1);
        FLASH_StartCmd_WaitClear_InRam();
#endif
    }
    else
    {
        SET_BIT32(FLASH->CST, FLASH_CST_START_Msk);
        while ((READ_BIT32(FLASH->CST, FLASH_CST_START_Msk) != 0U) && (0U != Timeout))
        {
            Timeout--;
        }
    }

    /* Wait till CCIF bit is set */
    while (0U == READ_BIT32(FLASH->STAT, FLASH_STAT_CCIF_Msk)){ /* do nothing. */ };
    /* Check command execute error status */
    if (0U != READ_BIT32(FLASH->STAT, 0x01FC0U))
    {
        /* if the write protection flag is marked, it indicates a timeout error */
        if (0x0U !=  READ_BIT32(FLASH->STAT, FLASH_STAT_PVIOLF_Pos))
        {
            return EEP_HAL_TIMEOUT;
        }
        Ret = EEP_HAL_STATUS_ERROR;
    }

    return Ret;
}

/*!
 * @brief Unlock the FLASH register.
 *
 * @param[in] none
 * @return operation status
 *        - 0U: Operation was successful
 *        - 3U: Operation timeout was occurred
 */
Eep_Hal_StatusType AC7840x_Flash_UnlockCtrl(void)
{
    Eep_Hal_StatusType Ret = EEP_HAL_SUCCESS;
    uint32_t TimeoutCount = 0U;

    while (FLASH_STAT_LOCK_Msk == READ_BIT32(FLASH->STAT, FLASH_STAT_LOCK_Msk))
    {
        WRITE_REG32(FLASH->KEYUNLK, FLASH_UNLOCK_KEY1);
        WRITE_REG32(FLASH->KEYUNLK, FLASH_UNLOCK_KEY2);

        if (TimeoutCount > 100U)
        {
            Ret = EEP_HAL_TIMEOUT;
            break;
        }
        TimeoutCount++;
    }
    /* enable LVD detect */
    SET_BIT32(FLASH->CNFG, FLASH_CNFG_LVDDETEN_Msk);

    return Ret;
}

/*!
 * @brief Lock the FLASH register.
 *
 * @param[in] none
 * @return none
 */
void Eep_Flash_LockCtrl(Eep_Hal_ConfigType *EepConfig)
{
    WRITE_REG32(FLASH->KEYUNLK, ~FLASH_UNLOCK_KEY1);
    WRITE_REG32(FLASH->KEYUNLK, ~FLASH_UNLOCK_KEY2);
}

/*!
 * @brief EEP group verify.
 *
 * @param[in] addr: Start address for verify operation
 * @param[in] size: Verify size in 64-bit
 * @return operation status
 */
Eep_Hal_StatusType AC7840x_Flash_VerifyGroup(uint32_t Addr, uint32_t Size)
{
    Eep_Hal_StatusType Ret = EEP_HAL_SUCCESS;
    uint8_t Temp = (uint8_t)DFLASH_WRITE_UNIT_SIZE;

    if (Addr >= (PFLASH_BASE_ADDRESS + PFLASH_BLOCK_SIZE))
    {
        if ((Addr < DFLASH_BASE_ADDRESS) || (Addr >= (DFLASH_BASE_ADDRESS + DFLASH_BLOCK_SIZE)))
        {
            Ret = EEP_HAL_STATUS_ERROR;
        }
        else
        {
            Temp = (uint8_t)DFLASH_WRITE_UNIT_SIZE;
        }
    }
    else
    {
        Temp = (uint8_t)PFLASH_WRITE_UNIT_SIZE;
    }

    if (EEP_HAL_SUCCESS == Ret)
    {
        /* Check if command is completed by CCIF */
        if (0U == READ_BIT32(FLASH->STAT, FLASH_STAT_CCIF_Msk))
        {
            Ret = EEP_HAL_BUSY;
        }
        else
        {
            /* Write 1 to clear COLLERR & ACCERR & FPVIOL flag in FLASH status register */
            SET_BIT32(FLASH->STAT, 0x01FC0U);

            /* Set flash execute command */
            WRITE_REG32(FLASH->CMD, 0x20U);
            WRITE_REG32(FLASH->ADDR, Addr);
            WRITE_REG32(FLASH->LEN, Size / Temp);

            /* Calling FLASH start command sequence function to execute the command */
            Ret = Flash_StartCmdSequence(0U);
        }
    }

    return Ret;
}
/*!
 * @brief EEP erase GROUP.
 *
 * @param[in] addr: Erase sector start address
 * @param[in] size: Erase size in bytes
 * @return operation status
 */

Eep_Hal_StatusType AC7840x_Flash_EraseGroup(uint32_t Addr, uint32_t Size)
{
    Eep_Hal_StatusType Ret = EEP_HAL_SUCCESS;
    uint32_t SectorSize;
    uint32_t Temp;
    uint32_t TempSize = Size;

    if ((Addr < EepFlashStartAddr) || ((Addr + Size) > EepFlashEndAddr))
    {
        Ret = EEP_HAL_STATUS_ERROR;
    }
    else
    {
        Temp = DFLASH_BASE_ADDRESS;
        if ((Addr >= Temp) && (Addr < (Temp + DFLASH_BLOCK_SIZE)))
        {
            SectorSize = (uint32_t)DFLASH_PAGE_SIZE;
        }
        else
        {
            Temp = PFLASH_BASE_ADDRESS;
            if (Addr < (Temp + PFLASH_BLOCK_SIZE))
            {
                SectorSize = (uint32_t)PFLASH_PAGE_SIZE;
            }
            else
            {
                Ret = EEP_HAL_STATUS_ERROR;
                TempSize = 0U;
                SectorSize = 0U;
            }
        }

        /* Check if the tempSize is alignment with flash sector size */
        if (0U != (TempSize & (SectorSize - 1U)))
        {
            Ret = EEP_HAL_STATUS_ERROR;
        }

        while ((TempSize > 0U) && (EEP_HAL_SUCCESS == Ret))
        {
            /* Check if command is completed by CCIF */
            if (0U == READ_BIT32(FLASH->STAT, FLASH_STAT_CCIF_Msk))
            {
                Ret = EEP_HAL_BUSY;
            }
            else
            {
                /* Write 1 to clear COLLERR & ACCERR & FPVIOL flag in FLASH status register */
                SET_BIT32(FLASH->STAT, 0x01FC0U);

                /* Set flash execute command */
                WRITE_REG32(FLASH->CMD, 0x01U);
                WRITE_REG32(FLASH->ADDR, Addr);

                /* Calling FLASH start command sequence function to execute the command */
                Ret = Flash_StartCmdSequence(1U);

                /* Update size and address */
                TempSize -= SectorSize;
                Addr += SectorSize;
            }
        }
    }

    return Ret;
}

/*!
 * @brief FLASH program.
 *
 * @param[in] addr: Program start address
 * @param[in] size: Program size in byte
 * @param[in] data: Pointer of data source address from which will be programmed
 * @return operation status
 *        - 0U: Operation was successful.
 *        - 1U: Operation was failure.
 *        - 4U: Operation was unsupported.
 *        - 2U: Operation was busy.
 */
Eep_Hal_StatusType AC7840x_Flash_Program(uint32_t Addr, uint32_t Size, const uint8_t *Data)
{
    Eep_Hal_StatusType Ret = EEP_HAL_SUCCESS;
    uint32_t Index;

    if (0U != (Size & (PFLASH_WRITE_UNIT_SIZE - 1U)) ||
       (Addr < EepFlashStartAddr) || ((Addr + Size) > EepFlashEndAddr))
    {
        Ret = EEP_HAL_STATUS_ERROR;
    }
    else
    {
        if (Addr >= (PFLASH_BASE_ADDRESS + PFLASH_BLOCK_SIZE))
        {
            if ((Addr < DFLASH_BASE_ADDRESS) || (Addr >= (DFLASH_BASE_ADDRESS + DFLASH_BLOCK_SIZE)))
            {
                Ret = EEP_HAL_STATUS_ERROR;
            }
        }

        while ((Size > 0U) && (EEP_HAL_SUCCESS == Ret))
        {
            /* Check if command is completed by CCIF */
            if (0U == READ_BIT32(FLASH->STAT, FLASH_STAT_CCIF_Msk))
            {
                Ret = EEP_HAL_BUSY;
            }
            else
            {
                /* Write 1 to clear COLLERR & ACCERR & FPVIOL flag in FLASH status register */
                SET_BIT32(FLASH->STAT, 0x01FC0U);

                /* Set flash execute command */
                WRITE_REG32(FLASH->CMD, 0x10U);
                WRITE_REG32(FLASH->ADDR, Addr);
                WRITE_REG32(FLASH->DATA0, ((uint32_t)Data[0] | ((uint32_t)Data[1] << 8U) | ((uint32_t)Data[2] << 16U) | ((uint32_t)Data[3] << 24U)));
                WRITE_REG32(FLASH->DATA1, ((uint32_t)Data[4] | ((uint32_t)Data[5] << 8U) | ((uint32_t)Data[6] << 16U) | ((uint32_t)Data[7] << 24U)));

                /* Calling FLASH start command sequence function to execute the command */
                Ret = Flash_StartCmdSequence(1U);

                /* Update program address for next operation */
                Addr += PFLASH_WRITE_UNIT_SIZE;
                /* Update size for next operation */
                Size -= PFLASH_WRITE_UNIT_SIZE;

                /* Increase the data source pointer by 8 */
                for (Index = 0; Index < PFLASH_WRITE_UNIT_SIZE; Index++)
                {
                    Data++;
                }
            }
        }
    }

    return Ret;
}

/*!
 * @brief CSE size determines whether to init eep.
 *
 * @param[in] ram base addr
 * @param[in] flash base addr
 * @return operation status
 */
Eep_Hal_StatusType Eep_Flash_PreInitCheck(Eep_Hal_ConfigType *EepConfig, uint32_t RamAddr, uint32_t FlashAddr)
{
    Eep_Hal_StatusType Ret = EEP_HAL_SUCCESS;

    if (0x0UL != READ_BIT32(FLASH->PART, FLASH_PART_CSEKEYSIZE_Msk))
    {
        if (((RamAddr >= 0x14000000U) && (RamAddr < 0x14001000U)) ||
            ((FlashAddr >= 0x1010000U) && (FlashAddr < 0x1020000U)))
        {
            Ret = EEP_HAL_STATUS_ERROR;
        }
    }

#if !defined(__ICCARM__)
    EepMemcpy(FLASH_StartCmd_WaitClear_Copy,
             (void *)((uint32_t)FLASH_StartCmd_WaitClear & (~0x1U)),
             sizeof(FLASH_StartCmd_WaitClear_Copy));
#endif

    return Ret;
}

/*!
 * @brief Check FLASH current busy status
 *
 * @param[in] none
 * @return EEP current status busy or idle
 */
Eep_Hal_StatusType Eep_Flash_CheckBusy(Eep_Hal_ConfigType *EepConfig)
{
    if (0U == READ_BIT32(FLASH->STAT, FLASH_STAT_CCIF_Msk))
    {
        return EEP_HAL_BUSY;
    }
    else
    {
        return EEP_HAL_SUCCESS;
    }
}


/* =====================================  Functions definition  ===================================== */
/*!
 * @brief Unlock the FLASH register.
 *
 * @param[in] none
 * @return operation status
 */
Eep_Hal_StatusType Eep_Flash_UnlockCtrl(const Eep_Hal_ConfigType *EepConfig)
{
    Eep_Hal_StatusType Ret = EEP_HAL_SUCCESS;
    uint32_t i;

    for (i = 0; i < EepConfig->Retry; i++)
    {
        Ret = AC7840x_Flash_UnlockCtrl();

        if (EEP_HAL_SUCCESS == Ret)
        {
            break;
        }
    }

    return Ret;
}

/*!
 * @brief EEP group verify.
 *
 * @param[in] addr: Start address for verify operation
 * @param[in] size: Verify size (byte)
 * @return operation status
 */
Eep_Hal_StatusType Eep_Flash_VerifyGroup(const Eep_Hal_ConfigType *EepConfig, uint32_t Addr, uint32_t Size)
{
    Eep_Hal_StatusType Ret = EEP_HAL_SUCCESS;
    uint32_t i;

    for (i = 0; i < EepConfig->Retry; i++)
    {
        Ret = AC7840x_Flash_VerifyGroup(Addr, Size);

        if (EEP_HAL_SUCCESS == Ret)
        {
            break;
        }
    }

    return Ret;
}
/*!
 * @brief EEP erase GROUP.
 *
 * @param[in] addr: Erase sector start address
 * @param[in] size: Erase size in bytes
 * @return operation status
 */

Eep_Hal_StatusType Eep_Flash_EraseGroup(const Eep_Hal_ConfigType *EepConfig, uint32_t Addr, uint32_t Size)
{
    Eep_Hal_StatusType Ret = EEP_HAL_SUCCESS;
    uint32_t i;

    for (i = 0; i < EepConfig->Retry; i++)
    {
        Ret = AC7840x_Flash_EraseGroup(Addr, Size);

        if (EEP_HAL_SUCCESS == Ret)
        {
            break;
        }
    }

    return Ret;
}

/*!
 * @brief FLASH program.
 *
 * @param[in] addr: Program start address
 * @param[in] size: Program size in byte
 * @param[in] data: Pointer of data source address from which will be programmed
 * @return operation status
 */
Eep_Hal_StatusType Eep_Flash_Program(const Eep_Hal_ConfigType *EepConfig, uint32_t Addr, uint32_t Size, const uint8_t *Data)
{
    Eep_Hal_StatusType Ret = EEP_HAL_SUCCESS;
    uint32_t i;

    for (i = 0; i < EepConfig->Retry; i++)
    {
        Ret = AC7840x_Flash_Program(Addr, Size, Data);

        if (EEP_HAL_SUCCESS == Ret)
        {
            break;
        }
    }
    return Ret;
}


uint32_t RamUintSize(const Eep_Hal_ConfigType *EepConfig)
{
    uint32_t Size;

    Size = 512U;

    return Size;
}

Eep_Hal_StatusType CheckEepSizeAlign(const Eep_Hal_ConfigType *EepConfig)
{
    Eep_Hal_StatusType Ret = EEP_HAL_SUCCESS;
    uint32_t UnitSize, Mod;

    UnitSize = 512U;

    Mod = 0;

    if (((EepConfig->EepSize % UnitSize) != Mod) || (EepConfig->EepSize > 4096U))
    {
        Ret = EEP_HAL_PARAM_ERROR;
    }

    return Ret;
}

/* =============================================  EOF  ============================================== */
