/* 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) 2024. 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.
 */
/******************************************************************************
 * @version: V3.1.0  AC7840x  Demo
 ******************************************************************************/

#include "AC784x_Flash_Drv.h"


/*!
 * @brief Unlock the FLASH register.
 *
 * This API sets key value 0xac7840 and 0x01234567 to KEYUNLK register to unlock FLASH.
 * Unlock must be done firstly before write FLASH register. if the key value is wrong,
 * FLASH will be locked and can't write FLASH register
 *
 * @param[in] none
 * @return operation status
 *        - STATUS_SUCCESS: Operation was successful
 *        - STATUS_TIMEOUT: Operation timeout was occurred
 */
status_t FLASH_DRV_UnlockCtrl(void)
{
    uint32_t    timeoutCount  = 0U;
    status_t    ret = STATUS_SUCCESS;
    uint8_t     ctrlLockBit = FLASH_GetLockStatus();

    while (1U == ctrlLockBit)
    {
        FLASH_SetKey(FLASH_UNLOCK_KEY1);
        FLASH_SetKey(FLASH_UNLOCK_KEY2);

        if (timeoutCount > 100U)
        {
            ret = STATUS_TIMEOUT;
            break;
        }
        ctrlLockBit = FLASH_GetLockStatus();
        timeoutCount++;
    }

    return ret;
}

/*!
 * @brief Lock the FLASH register.
 *
 * This API sets any key value except(0xac7840 and 0x01234567) to KEYUNLK register
 * to lock FLASH.
 *
 * @param[in] none
 * @return none
 */
void FLASH_DRV_LockCtrl(void)
{
    FLASH_SetKey(~FLASH_UNLOCK_KEY1);
    FLASH_SetKey(~FLASH_UNLOCK_KEY2);
}

/*!
 * @brief Perform start command sequence on FLASH.
 *
 * @param[in] config: The FLASH configuration structure pointer
 * @return operation status
 *        - STATUS_SUCCESS: Operation was successful
 *        - STATUS_ERROR:   Operation was failure
 */
static status_t FLASH_StartCmdSequence(void)
{
    status_t  ret = STATUS_SUCCESS;
    uint32_t timeout = 2;

    SET_BIT32(MCM->MCCR, MCM_MCCR_CADIS_Msk);
    /* trigger command execute */
    FLASH_TrigCmdExecute();
    while (FLASH_GetTrigCmdExecuteStatus() && (timeout--));
    /* Wait till CCIF bit is set */
    while (0U == FLASH_GetCmdCompleteStatus())
    {
       ;
    }
    CLEAR_BIT32(MCM->MCCR, MCM_MCCR_CADIS_Msk);
    /* Check command execute error status */
    if (0U != FLASH_GetStatus())
    {
        ret = STATUS_ERROR;
    }
    return ret;
}


/*!
 * @brief Flash erase sector.
 *
 * @param[in] addr: Erase sector start address
 * @param[in] size: Erase size in bytes
 * @return operation status
 *        - STATUS_SUCCESS:     Operation was successful.
 *        - STATUS_ERROR:       Operation was failure.
 *        - STATUS_UNSUPPORTED: Operation was unsupported.
 *        - STATUS_BUSY:        Operation was busy.
 */
status_t FLASH_DRV_EraseSector(uint32_t addr, uint32_t size)
{
    status_t ret = STATUS_SUCCESS;
    uint32_t tempSize = size;
    uint32_t sectorSize = 0;

    /* Dflash [0, 0xF_FFFF]*/
    if((addr >= PFLASH_START_ADDR) && addr <= (PFLASH_END_ADDR))
    {
        sectorSize = (PFLASH_PAGE_SIZE);
    }
    else 
    {
       ret =STATUS_UNSUPPORTED;
       return ret;
    }
    
    /* Check if the tempSize is alignment with flash sector size */
    if (0U != (tempSize & (sectorSize - 1U)))
    {
        ret = STATUS_UNSUPPORTED;
        return ret;
    }
    
    while(tempSize > 0U && ((STATUS_SUCCESS == ret)))
    {
        /* Check if command is completed by CCIF */
        if (0U == FLASH_GetCmdCompleteStatus())
        {
            ret = STATUS_BUSY;;
        }
        else
        {
            /* Write 1 to clear COLLERR & ACCERR & FPVIOL flag in FLASH status register */
            FLASH_CLEAR_STAT_ERROR_BITS();

            /* Set flash execute command */
            FLASH_SetCommand(FLASH_ERASE_PAGE);
            FLASH_SetAddress(addr);

            /* Calling FLASH start command sequence function to execute the command */
            ret = FLASH_StartCmdSequence();

            /* Update size and address */
            tempSize -= PFLASH_PAGE_SIZE;
            addr += PFLASH_PAGE_SIZE;
        }
    }

    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
 *        - STATUS_SUCCESS:     Operation was successful.
 *        - STATUS_ERROR:       Operation was failure.
 *        - STATUS_UNSUPPORTED: Operation was unsupported.
 *        - STATUS_BUSY:        Operation was busy.
 */
status_t FLASH_DRV_Program(uint32_t addr, uint32_t size, const uint8_t *data)
{
    status_t ret = STATUS_SUCCESS;

    if(0 == data)
    {
        ret = STATUS_UNSUPPORTED;
    }
    else if (0U != (size & (8U - 1U)))
    {
        ret = STATUS_UNSUPPORTED;
    }
    else 
    {
        if( addr > PFLASH_END_ADDR)
        {
            ret = STATUS_UNSUPPORTED;
        }
    }

    while ((size > 0U) && (STATUS_SUCCESS == ret))
    {
        /* Check if command is completed by CCIF */
        if (0U == FLASH_GetCmdCompleteStatus())
        {
            ret = STATUS_ERROR;
        }
        else
        {
            /* Write 1 to clear COLLERR & ACCERR & FPVIOL flag in FLASH status register */
            FLASH_CLEAR_STAT_ERROR_BITS();

            /* Set flash execute command */
            FLASH_SetCommand(FLASH_PROGRAM_PHRASE);
            FLASH_SetAddress(addr);

            FLASH_SetLowData(data[0] | (data[1] << 8U) | (data[2] << 16U) | (data[3] << 24U));
            FLASH_SetHighData(data[4] | (data[5] << 8U) | (data[6] << 16U) | (data[7] << 24U));

            /* Calling FLASH start command sequence function to execute the command */
            ret = FLASH_StartCmdSequence();

            /* 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 */
            data += PFLASH_WRITE_UNIT_SIZE;
        }
    }
    return ret;
}

/*!
 * @brief Read a certain size of data from the flash start specified address.
 *
 * @param[in] config: The FLASH configuration structure pointer
 * @param[in] addr: Read start address
 * @param[out] data: Buffer for saving read data
 * @param[in] size: read size in bytes
 * @return operation status
 *        - STATUS_SUCCESS: Operation was successful
 *        - STATUS_ERROR:   Operation was failure
 */
status_t FLASH_DRV_Read(uint32_t addr, uint8_t *data, uint32_t size)
{
    status_t ret = STATUS_SUCCESS;
    uint32_t i = 0U;

    if( 0 == data)
    {
        ret = STATUS_UNSUPPORTED;
    }
    else 
    {
    /* Check if the destination is aligned or not */
        if((addr >= PFLASH_START_ADDR) && addr <= (PFLASH_END_ADDR))
        {
           
        }
        else 
        {
            ret = STATUS_UNSUPPORTED;
        }
    }

    if (STATUS_SUCCESS == ret)
    {
        for (i = 0U; i < size; i++)
        {
            data[i] = *(uint8_t*)(addr + i);
        }
    }

    return ret;
}

/*!
 * @brief FLASH section verify.
 *
 * @param[in] config: The FLASH configuration structure pointer
 * @param[in] addr: Start address for verify operation
 * @param[in] size: Verify size in 64-bit
 * @return operation status
 *        - STATUS_SUCCESS: Operation was successful.
 *        - STATUS_ERROR:   Operation was failure.
 *        - STATUS_BUSY:    Operation was busy.
 */
status_t FLASH_DRV_VerifySection(uint32_t addr, uint32_t size)
{
    status_t ret = STATUS_SUCCESS;

    if((addr >= PFLASH_START_ADDR) && addr <= (PFLASH_END_ADDR))
    {
           
    }
    else 
    {
         ret = STATUS_UNSUPPORTED;
    }

    if (STATUS_SUCCESS == ret)
    {
        /* Check if command is completed by CCIF */
        if (0U == FLASH_GetCmdCompleteStatus())
        {
            ret = STATUS_BUSY;
        }
        else
        {
            /* Write 1 to clear COLLERR & ACCERR & FPVIOL flag in FLASH status register */
            FLASH_CLEAR_STAT_ERROR_BITS();

            /* Set flash execute command */
            FLASH_SetCommand(FLASH_VERIFY_SECTION);
            FLASH_SetAddress(addr);
            FLASH_SetLength(size);

            /* Calling FLASH start command sequence function to execute the command */
            ret = FLASH_StartCmdSequence();
        }
    }

    return ret;
}


