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

/* -------------------------------------------------------------------------- */
/*                                  Includes                                  */
/* -------------------------------------------------------------------------- */

#include "fwk_config.h"
#include "fwk_platform_flash.h"
#include "board.h"
#include "board_extflash.h"
#include "fsl_nor_flash.h"

/* -------------------------------------------------------------------------- */
/*                               Private macros                               */
/* -------------------------------------------------------------------------- */

#define KB(x) (((uint32_t)x) << 10)

#define SET_BIT(bitmap, i) bitmap[((i) >> 5)] |= (1U << ((i)&0x1f))
#define CLR_BIT(bitmap, i) bitmap[((i) >> 5)] &= ~(1U << ((i)&0x1f))
#define GET_BIT(bitmap, i) (((bitmap[(i) >> 5] & (1U << ((i)&0x1f))) >> ((i)&0x1f)) != 0U)

/* -------------------------------------------------------------------------- */
/*                               Private memory                               */
/* -------------------------------------------------------------------------- */

static nor_config_t norConfig = {NULL};
static nor_handle_t norHandle = {NULL};

/* -------------------------------------------------------------------------- */
/*                              Public functions                              */
/* -------------------------------------------------------------------------- */

status_t PLATFORM_InitExternalFlash(uint32_t baudrate)
{
    status_t status;

    /* Ignoring baudrate here, it is configured by Nor_Flash_Init when calling BOARD_GetNorFlashBaudrate */
    (void)baudrate;

    BOARD_InitExternalFlash();

    status = Nor_Flash_Init(&norConfig, &norHandle);
    assert(status == kStatus_Success);

    return status;
}

status_t PLATFORM_EraseExternalFlash(uint32_t address, uint32_t size)
{
    status_t status  = kStatus_Success;
    uint32_t endAddr = address + size;
    uint32_t startBlock, endBlock;
    uint32_t index;
    if ((endAddr & (PLATFORM_EXTFLASH_SECTOR_SIZE - 1U)) != 0U)
    {
        /* If the address is in the middle of a block, round up to the next block
         * This gives the upper block limit, every blocks before this one will be erased */
        endAddr = ((endAddr / PLATFORM_EXTFLASH_SECTOR_SIZE) + 1U) * PLATFORM_EXTFLASH_SECTOR_SIZE;
    }

    startBlock = address / PLATFORM_EXTFLASH_SECTOR_SIZE;
    endBlock   = endAddr / PLATFORM_EXTFLASH_SECTOR_SIZE;
    index      = startBlock;
    while (index < endBlock)
    {
        uint32_t remainingSector = endBlock - index;
        uint32_t remainingSize   = remainingSector * PLATFORM_EXTFLASH_SECTOR_SIZE;
        uint32_t nbSectors;

        /* The external flash used on EVKs is an AT25XE161D and supports different erase size in one operation
         * So we can optimize by erasing multiple sectors in one operation instead of each sector one by one */
        if (remainingSize >= (uint32_t)KB(64U))
        {
            nbSectors = (uint32_t)(KB(64U) / ((uint32_t)PLATFORM_EXTFLASH_SECTOR_SIZE));
        }
        else if (remainingSize >= (uint32_t)KB(32U))
        {
            nbSectors = (uint32_t)(KB(32U) / ((uint32_t)PLATFORM_EXTFLASH_SECTOR_SIZE));
        }
        else if (remainingSize >= (uint32_t)KB(4U))
        {
            nbSectors = (KB(4U) / ((uint32_t)PLATFORM_EXTFLASH_SECTOR_SIZE));
        }
        else
        {
            nbSectors = 1U;
        }

        status = Nor_Flash_Erase(&norHandle, index * PLATFORM_EXTFLASH_SECTOR_SIZE,
                                 nbSectors * PLATFORM_EXTFLASH_SECTOR_SIZE);
        if (status != kStatus_Success)
        {
            break;
        }

        index += nbSectors;
    }

    return status;
}

status_t PLATFORM_ReadExternalFlash(uint8_t *dest, uint32_t length, uint32_t address, bool requestFastRead)
{
    status_t status = kStatus_Success;

    (void)requestFastRead;

    status = Nor_Flash_Read(&norHandle, address, dest, length);
    assert(status == kStatus_Success);

    return status;
}

status_t PLATFORM_WriteExternalFlash(uint8_t *data, uint32_t length, uint32_t address)
{
    status_t status = kStatus_Success;

    status = Nor_Flash_Program(&norHandle, address, data, length);
    assert(status == kStatus_Success);

    return status;
}

status_t PLATFORM_IsExternalFlashBusy(bool *isBusy)
{
    return Nor_Flash_Is_Busy(&norHandle, isBusy);
}
