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

#include "fwk_platform_ota.h"
#include "fsl_adapter_flash.h"
#include "fsl_gpio.h"
#include "fsl_port.h"
#include "pin_mux.h"

/************************************************************************************
*************************************************************************************
* Private type definitions and macros
*************************************************************************************
************************************************************************************/
/* Stuff related to OTA */
#define IFR0_BASE             (0x02000000u)
#define FLASH_SECTOR_SIZE     (8192u)
#define IFR0_OTACFG_DATA      ((ifr0_otacfg_data_t *)IFR0_OTACFG_PAGE_ADDR)
#define IFR0_OTACFG_PAGE_ADDR (IFR0_BASE + (3u * FLASH_SECTOR_SIZE))

#define UPDATE_REQ_MAGIC_WORD         (0x746f4278u) /* "xBot" characters in little endian format */
#define UPDATE_SB_AVAILABLE_EXT_FLASH (0x74784578u) /* "xExt" characters in little endian format */
#define UPD_KEY_SZ                    16
#define UPD_KEY_PATTERN                                                                       \
    0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x78, \
        0x4D /* activateSecretxM */

#define OTACFG_FW_UDPATE_SUCCESS 0x5ac3c35aU /*!< Indicates update was success */
#define OTACFG_FW_UDPATE_FAILURE \
    0x4412d283U /*!<Indicates failure to process sb3 file OR failure to erase/write update status to OTACFG page */

typedef struct
{
    uint32_t updateAvailable; /*!< 0x0: UPDATE_REQ_MAGIC_WORD */
    uint32_t reserved0[3];
    uint32_t intOrExt;        /*!< 0x10: UPDATE_SB_AVAILBLE_EXT_FLASH */
    uint32_t baudRate;        /*!< 0x14: Baud rate in bps */
    uint32_t updateDumpAddr;  /*!< 0x18: Start address sb3 dumped */
    uint32_t updateFileBytes; /*!< 0x1C: NoOfBytes of sb3 update file */
    uint32_t updateStatus;    /*!< 0x20: Status after sb3 update by xBoot */
    uint32_t reserved1[3];
    uint8_t  updKey[UPD_KEY_SZ]; /*!< 0x30: secret key to enable ROM FW update instead of xBoot one */
    // uint8_t otaConfig[8128u];
} ifr0_otacfg_data_t;

/************************************************************************************
 * Private memory declarations
 ************************************************************************************/
#if (!(defined(__CC_ARM) || defined(__UVISION_VERSION)))
extern uint32_t NV_STORAGE_START_ADDRESS[];
#else
extern uint32_t Image$$NVM_region$$ZI$$Base[];
#endif /* __CC_ARM */
/************************************************************************************
*************************************************************************************
* Private functions
*************************************************************************************
************************************************************************************/

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

int PLATFORM_OtaBootDataUpdateOnCommit(const OtaLoaderInfo_t *ota_loader_info)
{
    NOT_USED(ota_loader_info);
    return 0;
}

int PLATFORM_OtaUpdateBootFlags(const OtaLoaderInfo_t *ota_loader_info)
{
    int                st     = -1;
    ifr0_otacfg_data_t otacfg = {
        .updateAvailable = UPDATE_REQ_MAGIC_WORD,
        .reserved0       = {0xffffffffu, 0xffffffffu, 0xffffffffu},
        .intOrExt        = ~UPDATE_SB_AVAILABLE_EXT_FLASH, /* Internal flash OTA by default */
        .baudRate        = 0,
        .updateStatus    = 0xffffffffu, /* Clear it so that xBoot can update Status after sb3 update */
        .reserved1       = {0xffffffffu, 0xffffffffu, 0xffffffffu},
        .updKey          = {UPD_KEY_PATTERN},
    };
    if (ota_loader_info->sb_arch_in_ext_flash)
    {
        otacfg.intOrExt = UPDATE_SB_AVAILABLE_EXT_FLASH;
        otacfg.baudRate = ota_loader_info->spi_baudrate;
    }
    otacfg.updateDumpAddr  = ota_loader_info->image_addr; /* SB3 Start address */
    otacfg.updateFileBytes = ota_loader_info->image_sz;   /* SB3 byte size */
    do
    {
        hal_flash_status_t status;
        status = HAL_FlashInit();
        if (kStatus_HAL_Flash_Success != status)
        {
            break;
        }
        status = HAL_FlashEraseSector((uint32_t)IFR0_OTACFG_DATA, sizeof(ifr0_otacfg_data_t));
        if (kStatus_HAL_Flash_Success != status)
        {
            break;
        }
        status = HAL_FlashProgram((uint32_t)IFR0_OTACFG_DATA, sizeof(ifr0_otacfg_data_t), (uint8_t *)&otacfg);
        if (kStatus_HAL_Flash_Success != status)
        {
            break;
        }
        st = 0;
    } while (false);
    return st;
}

int PLATFORM_OtaClearBootFlags(void)
{
    int st = -1;
    do
    {
        hal_flash_status_t status;
        uint32_t           updateStatus = IFR0_OTACFG_DATA->updateStatus;
        if (updateStatus == ~0UL)
        {
            st = 1; /* Update Status has remained unscathed */
            break;
        }
        status = HAL_FlashInit();
        if (kStatus_HAL_Flash_Success != status)
        {
            break;
        }
        status = HAL_FlashEraseSector((uint32_t)IFR0_OTACFG_DATA, sizeof(ifr0_otacfg_data_t));
        if (kStatus_HAL_Flash_Success != status)
        {
            break;
        }
        switch (updateStatus)
        {
            case OTACFG_FW_UDPATE_SUCCESS:
                st = 0;
                break;
            case OTACFG_FW_UDPATE_FAILURE:
                st = 2;
                break;
            default:
                st = 3;
                break;
        }
    } while (false);
    return st;
}

uint32_t PLATFORM_OtaGetImageOffset(bool internal_storage, uint32_t partition_offset)
{
    uint32_t val;
    if (internal_storage)
    {
        val = 0;
    }
    else
    {
        /* Need to make sure there is enough space between gOtaExtFlashPartitionOffset_c and
         * top of flash to hold half of internal flash */
        val = partition_offset;
    }
    return val;
}

uint32_t PLATFORM_OtaGetMaxImageSize(bool internal_storage)
{
    uint32_t           base_addr = 0U;
    uint32_t           max_sz    = 0U;
    hal_flash_status_t st;
    do
    {
        st = HAL_FlashGetProperty(kHAL_Flash_PropertyPflashBlockBaseAddr, &base_addr);
        if (st != kStatus_HAL_Flash_Success)
        {
            assert(0);
            break;
        }
        if ((uint32_t)NV_STORAGE_START_ADDRESS <= base_addr)
        {
            assert(0);
            break;
        }
        max_sz = (uint32_t)(NV_STORAGE_START_ADDRESS - base_addr);
        if (internal_storage)
        {
            /* Half the size up to NVM */
            max_sz /= 2U;
        }
    } while (false);
    return max_sz;
}
