/**
  ******************************************************************************
  * @file    flash.c
  * @author  MCD Application Team
  * @brief   This example code shows how to use the Flash supported by the
  *          B-WL5M-SUBG board
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2022 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */

/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "b_wl5m_subg_bus.h"
#include "string.h"

/** @addtogroup B_WL5M_SUBG_HAL_Examples
  * @{
  */

/** @addtogroup BSP
  * @{
  */

/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/
void printSector(uint8_t idxSector);
void printSector2(uint32_t address, uint16_t size);
void printBlock(uint8_t idxBlock);
int32_t checkSectorErased(uint32_t sectorAddress);
int32_t checkBlockErased(uint32_t blockAddress);

/**
  * @brief  demo of External Flash.
  */
void Flash_demo(void)
{
  /* Init the LEDs */
  BSP_LED_Init(LED_RED);
  BSP_LED_Init(LED_GREEN);

  /* Initialize the External Flash */
  if (BSP_FLASH_Init(0) != BSP_ERROR_NONE)
  {
    printf("Error to initialize BSP Flash !!\n");
    return;
  }

  /* 1 - Read Identification (RDID) */
  uint8_t RDID[3] = {0U, 0U, 0U};
  printf("1 - Read Device IDs: ");
  if (BSP_FLASH_ReadID(0, RDID) != BSP_ERROR_NONE)
  {
    printf("Failed\n");
    BSP_LED_On(LED_RED);
  }
  else
  {
    printf("0x%02x | 0x%02x | 0x%02x ", RDID[0], RDID[1], RDID[2]);
    if ((RDID[0] != 0xC2) || (RDID[1] != 0x20) || (RDID[2] != 0x13))
    {
      printf("Failed");
      BSP_LED_On(LED_RED);
    }
    printf("\n");
  }
  
  /* 2 - Read SFDP parameters */
  uint8_t SFDP[4] = {0U, 0U, 0U, 0U};
  printf("2 - Read SFDP Parameters\n");
  printf("\t2.1 - Read SFDP Signature: ");
  if (BSP_FLASH_ReadSFDP(0, SFDP, 0x00, 4) != BSP_ERROR_NONE)
  {
    printf("BSP_FLASH_ReadSFDP error\n");
  }
  else
  {
    printf("\t%x%x%x%x\n", SFDP[3], SFDP[2], SFDP[1], SFDP[0]);
    if ((SFDP[3] != 0x50) || (SFDP[2] != 0x44) || (SFDP[1] != 0x46) || (SFDP[0] != 0x53))
    {
      printf("Failed\n");
      BSP_LED_On(LED_RED);
    }
  }
  
  printf("\t2.2 - Read SFDP revision: ");
  if (BSP_FLASH_ReadSFDP(0, SFDP, 0x04, 2) != BSP_ERROR_NONE)
  {
    printf("BSP_FLASH_ReadSFDP error\n");
  }
  else
  {
    printf("\tv%x.%x\n", SFDP[1], SFDP[0]);
    if ((SFDP[1] != 0x01) || (SFDP[0] != 0x00))
    {
      printf("Failed\n");
      BSP_LED_On(LED_RED);
    }
  }
  
  /* 3 - Read Status */
  printf("3 - Read Status");
  if (BSP_FLASH_GetStatus(0) == BSP_ERROR_NONE)
  {
    printf("\tGet Status is OK\n");
  }
  else
  {
    printf("\tGet Status error\n");
    BSP_LED_On(LED_RED);
  }
  
  /* 4 - Check Memory configuration */
  uint8_t statusRegister = 0xFF;
  printf("4 - Test to configure Memory Block Protection: ");
  printf("\t- Initial Status Register value: ");
  if (BSP_FLASH_ReadStatusRegister(0, &statusRegister) != BSP_ERROR_NONE)
  {
    printf("Failed\n");
    BSP_LED_On(LED_RED);
  }
  else
  {
    printf("0x%02x\n", statusRegister);
  }

  if (BSP_FLASH_GetStatus(0) == BSP_ERROR_NONE)
  {
    printf("Get Status is OK\n");
  }
  else
  {
    printf("Get Status error\n");
  }
  
  printf("\t- Tests all Block Protection configurations:\n");
  for (uint8_t i = 1; i < 8; i++)
  {
    uint8_t BPbits =  i << 2;
    printf("\t\t-> BP bits: 0x%02x", BPbits);
    if (BSP_FLASH_BlockProtectConfig(0U, BPbits) != BSP_ERROR_NONE)
    {
      printf("Failed\n");
      BSP_LED_On(LED_RED);
    }
    else
    {
      if (BSP_FLASH_ReadStatusRegister(0U, &statusRegister) != BSP_ERROR_NONE)
      {
        printf("Failed\n");
        BSP_LED_On(LED_RED);
      }
      else if ((statusRegister & MX25L4006_SR_BLOCKPR) == BPbits)
      {
        printf(" 0x%02x\n", statusRegister);
      }
      else
      {
        printf("Failed\n");
        BSP_LED_On(LED_RED);
      }
    }
  }

  /* Reset Block Protection bits */
  if (BSP_FLASH_BlockProtectConfig(0U, 0U) != BSP_ERROR_NONE)
  {
    printf("Failed to reset Block Protection Configuration !!\n");
    BSP_LED_On(LED_RED);
  }

  /* Erase Block */
  if (BSP_FLASH_Erase_Chip(0) != BSP_ERROR_NONE)
  {
    printf("Failed to erase whole chip !!\n");
    BSP_LED_On(LED_RED);
  }

  printf("5 - Read/Write to Memory\n");

  /* Write data in block 0 with granularity of 64B */
  uint8_t data[256] = {0};
  uint8_t data2[256] = {0};
  uint32_t sizeBlock = 256;

  for (uint32_t i = 0; i < sizeBlock; i++)
  {
    data[i] = (uint8_t)(0xFE - i);
  }

  uint32_t nbBlockPerSector = MX25L4006_SECTOR_SIZE / sizeBlock;
  for (uint32_t idSector = 0; idSector < 128; idSector++)
  {
    uint32_t sectorAddress = idSector * MX25L4006_SECTOR_SIZE;

    for (uint32_t idxBlock = 0; idxBlock < nbBlockPerSector; idxBlock++)
    {
      if (BSP_FLASH_Write(0, data, sectorAddress, sizeBlock) != BSP_ERROR_NONE)
      {
        printf("Write error at sector[0x%06x]\n", sectorAddress);
        BSP_LED_On(LED_RED);
      }

      sectorAddress += sizeBlock;
      BSP_LED_Toggle(LED_BLUE);
    }

    /* Read memory with classic function BSP_FLASH_Read */
    sectorAddress = idSector * MX25L4006_SECTOR_SIZE;

    for (uint32_t idxBlock = 0; idxBlock < nbBlockPerSector; idxBlock++)
    {
      memset(data2, 0xAA, sizeBlock);
      if (BSP_FLASH_Read(0, data2, sectorAddress, sizeBlock) != BSP_ERROR_NONE)
      {
        printf("Read error at sector[0x%06x]\n", sectorAddress);
        BSP_LED_On(LED_RED);
      }
      else
      {
        if (memcmp(data, data2, sizeBlock) != 0)
        {
          printf("Error diff at sector[0x%06x]\n", sectorAddress);
          BSP_LED_On(LED_RED);
        }
      }
      sectorAddress += sizeBlock;
      BSP_LED_Toggle(LED_GREEN);
    }
    
    /* Quickly Read memory with specific function BSP_FLASH_FastRead */
    sectorAddress = idSector * MX25L4006_SECTOR_SIZE;

    for (uint32_t idxBlock = 0; idxBlock < nbBlockPerSector; idxBlock++)
    {
      memset(data2, 0xAA, sizeBlock);
      if (BSP_FLASH_FastRead(0, data2, sectorAddress, sizeBlock) != BSP_ERROR_NONE)
      {
        printf("Read error at sector[0x%06x]\n", sectorAddress);
        BSP_LED_On(LED_RED);
      }
      else
      {
        if (memcmp(data, data2, sizeBlock) != 0)
        {
          printf("Error diff at sector[0x%06x]\n", sectorAddress);
          BSP_LED_On(LED_RED);
        }
      }
      sectorAddress += sizeBlock;
      BSP_LED_Toggle(LED_GREEN);
    }
  }

  printf("6 - Checking Erase sector at 0x000000\n");
  if (BSP_FLASH_Erase_Sector(0, 0x000000) != BSP_ERROR_NONE)
  {
    printf("failed\n");
    BSP_LED_On(LED_RED);
  }

  if (checkSectorErased(0) != 0)
  {
    BSP_LED_On(LED_RED);
  }

  printf("7 - Checking Erase block at 0x01CA32\n");
  uint32_t blockAddress = 0x01CA32;
  if (BSP_FLASH_Erase_Block(0, blockAddress, MX25L4006_ERASE_64K) != BSP_ERROR_NONE)
  {
    printf("failed\n");
    BSP_LED_On(LED_RED);
  }

  if (checkBlockErased(blockAddress & ~0xFFFF) != 0)
  {
    printf("failed\n");
    BSP_LED_On(LED_RED);
  }

  printf("8 - Checking Erase whole Chip\n");

  /* Erase full chip */
  if (BSP_FLASH_Erase_Chip(0) != BSP_ERROR_NONE)
  {
    printf("failed\n");
    BSP_LED_On(LED_RED);
  }

  for (uint32_t blockAddress = 0x00000000; blockAddress < 0x00080000; blockAddress += MX25L4006_BLOCK_SIZE)
  {
    if (checkBlockErased(blockAddress) != 0)
    {
      printf("failed\n");
      BSP_LED_On(LED_RED);
      break;
    }

    BSP_LED_Toggle(LED_BLUE);
    HAL_Delay(1);
  }

  /* DeInit the Flash */
  BSP_FLASH_DeInit(0);

  /* DeInit the LEDs */
  BSP_LED_DeInit(LED_RED);
  BSP_LED_DeInit(LED_GREEN);
}

void printSector(uint8_t idxSector)
{
  uint8_t datasRead[64];
  uint32_t idx = 0;
  uint32_t addressSector = idxSector * MX25L4006_SECTOR_SIZE; /* Sector of 4KB */
  memset(datasRead, 0, 64 * sizeof(uint8_t));

  printf("\t- Sector %02i [0x%06x] ", idxSector, addressSector);
  fflush(stdout);
  for (uint32_t idxBlock64 = 0; idxBlock64 < 64; idxBlock64++)
  {
    if (BSP_FLASH_Read(0, datasRead, addressSector, 64) != BSP_ERROR_NONE)
    {
      BSP_LED_On(LED_RED);
    }

    for (idx = 0; idx < 64; idx++)
    {
      printf("%02x ", datasRead[idx]);
      fflush(stdout);
    }
    printf("\n\t\t\t\t");

    addressSector += 64;
  }
  printf("\n");
}

void printSector2(uint32_t address, uint16_t size)
{
  uint8_t datasRead[64];
  uint32_t idx = 0;
  uint32_t addressSector = address; /* Sector of 4KB */
  memset(datasRead, 0, 64 * sizeof(uint8_t));

  printf("\t- Sector [0x%06x] %02i ", address, size);
  fflush(stdout);

  if (BSP_FLASH_Read(0, datasRead, addressSector, size) != BSP_ERROR_NONE)
  {
    BSP_LED_On(LED_RED);
  }

  for (idx = 0; idx < size; idx++)
  {
    printf("%02x ", datasRead[idx]);
    fflush(stdout);
  }

  printf("\n");
}

void printBlock(uint8_t idxBlock)
{
  uint32_t addressBlock = idxBlock * MX25L4006_BLOCK_SIZE; /* Block of 64KB */

  printf("\t- Block %i [0x%06x\n", idxBlock, addressBlock);

  for (uint8_t idxSector = 0; idxSector < 16; idxSector++)
  {
    printSector(idxBlock * 16 + idxSector);
  }
}

int32_t checkSectorErased(uint32_t sectorAddress)
{
  int32_t ret = 0;
  uint8_t dataRead[MX25L4006_PAGE_SIZE];

  uint32_t endSectorAddress = sectorAddress + MX25L4006_SECTOR_SIZE;
  for (uint32_t memoryAddress = sectorAddress; (ret == 0) && (memoryAddress < endSectorAddress); memoryAddress += MX25L4006_PAGE_SIZE)
  {
    memset(dataRead, 0x00, MX25L4006_PAGE_SIZE);
    if (BSP_FLASH_Read(0, dataRead, memoryAddress, MX25L4006_PAGE_SIZE) != BSP_ERROR_NONE)
    {
      ret = -1;
      break;
    }

    for (uint32_t i = 0; i < MX25L4006_PAGE_SIZE; i++)
    {
      if (dataRead[ i ] != 0xFF)
      {
        ret = -2;
        break;
      }
    }
  }

  return ret;
}

int32_t checkBlockErased(uint32_t blockAddress)
{
  int32_t ret = 0;

  uint32_t endBlockAddress = blockAddress + MX25L4006_BLOCK_SIZE;
  for (uint32_t memoryAddress = blockAddress; (ret == 0) && (memoryAddress < endBlockAddress); memoryAddress += MX25L4006_SECTOR_SIZE)
  {
    ret = checkSectorErased(memoryAddress);
  }

  return ret;
}

/**
  * @}
  */

/**
  * @}
  */