/**
*   @file      mnsh_spi.c
*   @author    IPC - Industrial BU
*   @date      05 september 2013
*   @brief     Implements routines for SPI minishell module
*   @note      (C) COPYRIGHT 2013 STMicroelectronics
*
* @attention
*
* THE PRESENT SOFTWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
* WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE TIME.
* AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY DIRECT,
* INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING FROM THE
* CONTENT OF SUCH SOFTWARE AND/OR THE USE MADE BY CUSTOMERS OF THE CODING
* INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
*
*/

/*******************************************************************************
* INCLUDE FILES:
*******************************************************************************/
#include "handler_sflash.h"

#include "hal_l4xx.h"

/** @addtogroup GENERIC
  * @{
  */

/*******************************************************************************
* TYPES:
*******************************************************************************/
#define TRC_FAIL_SHORT(a, b, c)
#define TRC_INFO_BYTE(a, b, c)
#define SPI_SSOutputCmd(a)      HAL_SFLASH_SSOutputCmd(a)


/*******************************************************************************
* CONSTANTS & MACROS:
*******************************************************************************/
#define DISABLE         0
#define ENABLE          (!0)

#define SPI_IT_Mask                 ((uint32_t)(SPI_FLAG_CRCERR))

#define SPI_FLASH_CMD_SE            0xD8            /*!< Sector Erase instruction */
#define SPI_FLASH_CMD_RDSR          0x05            /*!< Status Register Read instruction */
#define SPI_FLASH_CMD_MMPP          0x02            /*!< Main Memory Page Program Through Buffer 1 instruction */
#define SPI_FLASH_CMD_MMPR          0x0B            /*!< Read instruction */
#define SPI_FLASH_CMD_WENBL         0x06            /*!< Write Enable instruction */
#define SPI_FLASH_CMD_BE            0xC7            /*!< Sector Bulk instruction */

#define SPI_FLASH_DUMMY_BYTE        ((uint8_t)0xA5) /*!< Dummy Byte */

#define SPI_FLASH_CMD_MDR           ((uint8_t)0x9F) /*!< Manufacturer and Device ID Read instruction */

#define SPI_FLASH_WRITE_MAX         16

/*******************************************************************************
* GLOBAL VARIABLES:
*******************************************************************************/

/*******************************************************************************
* LOCAL VARIABLES:
*******************************************************************************/

/*******************************************************************************
* LOCAL FUNCTION PROTOTYPES:
*******************************************************************************/
static uint8_t SFLASH_SendByte(uint8_t byte);

/*******************************************************************************
*
*                       IMPLEMENTATION: Public functions
*
*******************************************************************************/

/**
  * @brief  Initialisation for SFLASH access
  * @param  None
  * @retval None
  */
void SFLASH_Conf(void)
{
  HAL_SFLASH_Conf();
}

/**
  * @brief  Performs read from serial flash (aligned on pages)
  * @param  buf is the destination address in RAM
  * @param  address is the source address in serial flash
  * @param  size is the number of bytes to read
  * @retval None
  */
void SFLASH_Read(uint8_t *buf, uint32_t address, uint16_t size)
{
  if (size > SPI_FLASH_PAGE_SIZE)
  {
    TRC_FAIL_SHORT(TRACE_MODULE_BG, TRACE_BG_SFLASH_READ_ERR, &size);
    return;
  }
  SFLASH_ReadBuffer(buf, address, size);
#if 0
  if ((address & 0xFFFF) == 0)
  {
    uint32_t tmp;
    tmp = address >> 16;
    TRC_INFO_BYTE(TRACE_MODULE_BG, TRACE_BG_SFLASH_READ, &tmp);
  }
#endif
}

/**
  * @brief  Performs write to serial flash (aligned on pages)
  * @param  address is the destination address in serial flash
  * @param  buf is the source address in RAM
  * @param  size is the number of bytes to write
  * @retval None
  */
void SFLASH_Write(uint32_t address, uint8_t *buf, uint16_t size)
{
  if (size > SPI_FLASH_PAGE_SIZE)
  {
    TRC_FAIL_SHORT(TRACE_MODULE_BG, TRACE_BG_SFLASH_WRITE_ERR, &size);
    return;
  }
  SFLASH_WriteBuffer(buf, address, size);
#if 0
  if ((address & 0xFFFF) == 0)
  {
    uint32_t tmp;
    tmp = address >> 16;
    TRC_INFO_BYTE(TRACE_MODULE_BG, TRACE_BG_SFLASH_WRITE, &tmp);
  }
#endif
}

/**
  * @brief  Sends bytes to serial flash with no specific address
  * @param  buf is the source address in RAM
  * @param  size is the number of bytes to write
  * @retval None
  */
void SFLASH_SendBytes(uint8_t *buf, uint16_t size)
{
  int i;

  SPI_SSOutputCmd(ENABLE);
  for (i=0; i<size; i++)
  {
    SFLASH_SendByte(buf[i]);
  }
  SPI_SSOutputCmd(DISABLE);
}

/**
  * @brief  Performs erase in serial flash (aligned on sectors)
  * @param  address is the address in serial flash of the sector to erase
  * @retval None
  */
void SFLASH_Erase(uint32_t address)
{
  uint16_t sector;
  sector = (uint16_t)(address / SPI_FLASH_SECTOR_SIZE);
  SFLASH_SectorErase(sector);
  TRC_INFO_BYTE(TRACE_MODULE_BG, TRACE_BG_SFLASH_ERASE, &sector);
}

/**
  * @brief  Performs erase in serial flash (all sectors in bulk mode)
  * @param  None
  * @retval None
  */
void SFLASH_EraseAll(void)
{
  SFLASH_BulkErase();
}

/**
  * @brief  Read the contents of a small buffer on the boot flash
  * @param  buffer is the pointer onto the destination buffer in RAM
  * @param  page_id is the page number in flash
  * @param  bytes_to_read is the number of bytes to write
  * @return uint8_tean value which says if the operation completed successfully or not
  */
uint8_t SFLASH_ReadBuffer(uint8_t *buffer, uint32_t start_addr, uint16_t bytes_to_read)
{
  uint8_t status;

  if (!bytes_to_read || (bytes_to_read > SPI_FLASH_PAGE_SIZE))
        return 0;

  /* Wait until SFLASH is ready */
  do
  {
    status = SFLASH_GetStatusReg();
  }
  while ( status & 0x01 ); /* Busy */

  start_addr &= SPI_FLASH_TOT_MASK;
//  MNSH_Printf("Read start address is %x\n",start_addr);
  SPI_SSOutputCmd(ENABLE);
//  MNSH_Printf("Sector is %x, Page is %x, Offset is %x\n",((start_addr & ((uint32_t)0x1F0000)) >> 16),((start_addr & ((uint32_t)0xFF00)) >> 8),(start_addr & ((uint32_t)0xFF)));
  /* Send Read instruction */
  SFLASH_SendByte(SPI_FLASH_CMD_MMPR);
  /* Send ReadAddr high nibble address byte to write to */
  SFLASH_SendByte((start_addr & ((uint32_t)0x1F0000)) >> 16);
  /* Send ReadAddr medium nibble address byte to write to */
  SFLASH_SendByte((start_addr & ((uint32_t)0xFF00)) >> 8);
  /* Send ReadAddr low nibble address byte to write to */
  SFLASH_SendByte(start_addr & ((uint32_t)0xFF));

  /* Send some dummy bytes to initialize the read operation */
  SFLASH_SendByte(SPI_FLASH_DUMMY_BYTE);

#if defined(SPI_FLASH_AT45DB161D)
  SFLASH_SendByte(SPI_FLASH_DUMMY_BYTE);
  SFLASH_SendByte(SPI_FLASH_DUMMY_BYTE);
  SFLASH_SendByte(SPI_FLASH_DUMMY_BYTE);
#endif

  while (bytes_to_read--) /* while there is data to be read */
  {
    /* Read a byte from the FLASH */
    *buffer = SFLASH_SendByte(SPI_FLASH_DUMMY_BYTE);
    /* Point to the next location where the byte read will be saved */
    buffer++;
  }

  SPI_SSOutputCmd(DISABLE);

  return 1;
}

/**
  * @brief  Write the contents of a small buffer on the boot flash
  * @param  buffer is the pointer onto the buffer to write into the flash
  * @param  page_id is the page number for the write operation
  * @param  bytes_to_write The number of bytes to write
  * @return uint8_tean value which says if the operation completed successfully or not
  */
uint8_t SFLASH_WriteBuffer(uint8_t *buffer, uint32_t start_addr, uint16_t bytes_to_write)
{
  uint8_t status = 0;
  
  if (!bytes_to_write || (bytes_to_write > SPI_FLASH_PAGE_SIZE))
        return 0;
  start_addr &= SPI_FLASH_TOT_MASK;
  
//  MNSH_Printf("Write start address is %x\n",start_addr);
  while (bytes_to_write != 0)
  {
    uint16_t bytes_written;
    /* Wait until SFLASH is ready */
    do
    {
      status = SFLASH_GetStatusReg();
    }
    while ( status & 0x01 ); /* Busy */

    bytes_written = 0;
#ifdef BUILD_FOR_FREE_RTOS
    portENTER_CRITICAL();
#endif
    SPI_SSOutputCmd(ENABLE);

    SFLASH_SendByte(SPI_FLASH_CMD_WENBL);

    SPI_SSOutputCmd(DISABLE);
    SPI_SSOutputCmd(ENABLE);
//    MNSH_Printf("Sector is %x, Page is %x, Offset is %x\n",((start_addr & ((uint32_t)0x1F0000)) >> 16),((start_addr & ((uint32_t)0xFF00)) >> 8),(start_addr & ((uint32_t)0xFF)));
    /* Send "Main Memory Page Program Through Buffer 1" instruction */
    SFLASH_SendByte(SPI_FLASH_CMD_MMPP);
    /* Send WriteAddr high nibble address byte to write to */
    SFLASH_SendByte((start_addr & ((uint32_t)0x1F0000)) >> 16);
    /* Send WriteAddr medium nibble address byte to write to */
    SFLASH_SendByte((start_addr & ((uint32_t)0xFF00)) >> 8);
    /* Send WriteAddr low nibble address byte to write to */
    SFLASH_SendByte(start_addr & ((uint32_t)0xFF));

    /* while there is data to be written on the FLASH */
    while (bytes_to_write)
    {
      /* Send the current byte */
      SFLASH_SendByte(*buffer);
      /* Point on the next byte to be written */
      buffer++;
      bytes_to_write --;
      bytes_written ++;
      if (bytes_written == SPI_FLASH_WRITE_MAX)
        break;
    }

    SPI_SSOutputCmd(DISABLE);
#ifdef BUILD_FOR_FREE_RTOS
    portEXIT_CRITICAL();
#endif
    start_addr += SPI_FLASH_WRITE_MAX;
  }
  return 1;
}

/**
  * @brief  Erase a sector in serial flash
  * @param  sector_id is the number of the sector to erase
  * @return uint8_tean value which says if the operation completed successfully or not
  */
uint8_t SFLASH_SectorErase(uint16_t sector_id)
{
  uint8_t status;
  uint32_t start_addr;

  start_addr = sector_id * SPI_FLASH_SECTOR_SIZE;
  start_addr &= SPI_FLASH_TOT_MASK;

  if (start_addr >= SPI_FLASH_SIZE)
        return 0;
  /* Wait until SFLASH is ready */
  do
  {
    status = SFLASH_GetStatusReg();
  }
  while ( status & 0x01 ); /* Busy */

#ifdef BUILD_FOR_FREE_RTOS
  portENTER_CRITICAL();
#endif
  SPI_SSOutputCmd(ENABLE);

  SFLASH_SendByte(SPI_FLASH_CMD_WENBL);

  SPI_SSOutputCmd(DISABLE);
  SPI_SSOutputCmd(ENABLE);

  SFLASH_SendByte(SPI_FLASH_CMD_SE);
  SFLASH_SendByte((start_addr & ((uint32_t)0x1F0000)) >> 16);
  SFLASH_SendByte((start_addr & ((uint32_t)0xFF00)) >> 8);
  SFLASH_SendByte(start_addr & ((uint32_t)0xFF));

  SPI_SSOutputCmd(DISABLE);
#ifdef BUILD_FOR_FREE_RTOS
  portEXIT_CRITICAL();
#endif

  return 1;
}

/**
  * @brief  Erase all sectors in serial flash
  * @param  none
  * @return uint8_tean value which says if the operation completed successfully or not
  */
uint8_t SFLASH_BulkErase(void)
{
  uint8_t status;

  /* Wait until SFLASH is ready */
  do
  {
    status = SFLASH_GetStatusReg();
  }
  while ( status & 0x01 ); /* Busy */

#ifdef BUILD_FOR_FREE_RTOS
  portENTER_CRITICAL();
#endif
  SPI_SSOutputCmd(ENABLE);

  SFLASH_SendByte(SPI_FLASH_CMD_WENBL);

  SPI_SSOutputCmd(DISABLE);
  SPI_SSOutputCmd(ENABLE);

  SFLASH_SendByte(SPI_FLASH_CMD_BE);

  SPI_SSOutputCmd(DISABLE);
#ifdef BUILD_FOR_FREE_RTOS
  portEXIT_CRITICAL();
#endif

  return 1;
}

/**
  * @brief  Get status register of the serial flash
  * @param  sector_id is the number of the sector to erase
  * @return uint8_tean value which says if the operation completed successfully or not
  */
uint8_t SFLASH_GetStatusReg(void)
{
    uint8_t status = 0;

    SPI_SSOutputCmd(ENABLE);
    SFLASH_SendByte(SPI_FLASH_CMD_RDSR);
    status = SFLASH_SendByte(SPI_FLASH_DUMMY_BYTE);
    SPI_SSOutputCmd(DISABLE);

    return status;
}

/**
  * @brief  Get Device Id
  * @param  None
  * @return The device Id
  */
uint32_t SFLASH_GetDeviceId(void)
{
  uint32_t id;
  UART_Print("SFLASH_GetDeviceId\r\n");
  SPI_SSOutputCmd(ENABLE);

  /* Send "MDR" instruction */
  SFLASH_SendByte(SPI_FLASH_CMD_MDR);

  /* Read a byte from the FLASH */
  id = ((uint32_t)SFLASH_SendByte(SPI_FLASH_DUMMY_BYTE)) << 16;
  /* Read a byte from the FLASH */
  id |= ((uint32_t)SFLASH_SendByte(SPI_FLASH_DUMMY_BYTE)) << 8;
  /* Read a byte from the FLASH */
  id |= SFLASH_SendByte(SPI_FLASH_DUMMY_BYTE);

  SPI_SSOutputCmd(DISABLE);

  return id;
}

/*******************************************************************************
*
*                       IMPLEMENTATION: Private functions
*
*******************************************************************************/
/**
  * @brief  Sends a byte through the SPI interface and return the byte received
  *         from the SPI bus.
  * @param  byte : byte to send.
  * @return The value of the received byte.
  */
static uint8_t SFLASH_SendByte(uint8_t byte)
{
  return(HAL_SFLASH_SendByte(byte));
}




/**
  * @}
  */

/* End Of File */

