/************************************************************************************
 * arch/sim/src/up_spiflash.c
 *
 *   Copyright (C) 2014 Ken Pettit. All rights reserved.
 *   Author: Ken Pettit <pettitkd@gmail.com>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 3. Neither the name NuttX nor the names of its contributors may be
 *    used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 ************************************************************************************/

/************************************************************************************
 * Included Files
 ************************************************************************************/

#include <nuttx/config.h>

#include <sys/types.h>
#include <stdint.h>
#include <stdbool.h>
#include <semaphore.h>
#include <errno.h>
#include <debug.h>
#include <string.h>

#include <nuttx/arch.h>
#include <nuttx/spi/spi.h>

#include "up_internal.h"

#if defined(CONFIG_SIM_SPIFLASH)

/************************************************************************************
 * Pre-processor Definitions
 ************************************************************************************/
/* Configuration ********************************************************************/

/* Debug ****************************************************************************/
/* Check if (non-standard) SPI debug is enabled */

#ifndef CONFIG_DEBUG
#  undef CONFIG_DEBUG_VERBOSE
#  undef CONFIG_DEBUG_SPI
#endif

#ifdef CONFIG_DEBUG_SPI
#  define spidbg lldbg
#  ifdef CONFIG_DEBUG_VERBOSE
#    define spivdbg lldbg
#  else
#    define spivdbg(x...)
#  endif
#else
#  define spidbg(x...)
#  define spivdbg(x...)
#endif

/* Define the FLASH SIZE in bytes */

#ifdef CONFIG_SIM_SPIFLASH_1M
#  define CONFIG_SPIFLASH_SIZE        (128 * 1024)
#  define CONFIG_SPIFLASH_CAPACITY    0x11

#ifndef CONFIG_SIM_SPIFLASH_SECTORSIZE
#  define CONFIG_SIM_SPIFLASH_SECTORSIZE    2048
#endif

#endif

#ifdef CONFIG_SIM_SPIFLASH_8M
#  define CONFIG_SPIFLASH_SIZE        (1024 * 1024)
#  define CONFIG_SPIFLASH_CAPACITY    0x14
#endif

#ifdef CONFIG_SIM_SPIFLASH_32M
#  define CONFIG_SPIFLASH_SIZE        (4 * 1024 * 1024)
#  define CONFIG_SPIFLASH_CAPACITY    0x16
#endif

#ifdef CONFIG_SIM_SPIFLASH_64M
#  define CONFIG_SPIFLASH_SIZE        (8 * 1024 * 1024)
#  define CONFIG_SPIFLASH_CAPACITY    0x17
#endif

#ifdef CONFIG_SIM_SPIFLASH_128M
#  define CONFIG_SPIFLASH_SIZE        (16 * 1024 * 1024)
#  define CONFIG_SPIFLASH_CAPACITY    0x18
#endif

#ifndef CONFIG_SIM_SPIFLASH_MANUFACTURER
#  define CONFIG_SIM_SPIFLASH_MANUFACTURER  0x20
#endif

#ifndef CONFIG_SIM_SPIFLASH_MEMORY_TYPE
#  define CONFIG_SIM_SPIFLASH_MEMORY_TYPE   0x20
#endif

#ifndef CONFIG_SIM_SPIFLASH_SECTORSIZE
#  define CONFIG_SIM_SPIFLASH_SECTORSIZE    65536
#endif

#ifndef CONFIG_SIM_SPIFLASH_SUBSECTORSIZE
#  define CONFIG_SIM_SPIFLASH_SUBSECTORSIZE    4096
#endif

#ifndef CONFIG_SIM_SPIFLASH_SECTORSIZE_MASK
#  define CONFIG_SIM_SPIFLASH_SECTORSIZE_MASK  (~(CONFIG_SIM_SPIFLASH_SECTORSIZE-1))
#endif

#ifndef CONFIG_SIM_SPIFLASH_SUBSECTORSIZE_MASK
#  define CONFIG_SIM_SPIFLASH_SUBSECTORSIZE_MASK  (~(CONFIG_SIM_SPIFLASH_SUBSECTORSIZE-1))
#endif

#ifndef CONFIG_SIM_SPIFLASH_PAGESIZE
#  define CONFIG_SIM_SPIFLASH_PAGESIZE    256
#endif

#ifndef CONFIG_SIM_SPIFLASH_PAGESIZE_MASK
#  define CONFIG_SIM_SPIFLASH_PAGESIZE_MASK  (CONFIG_SIM_SPIFLASH_PAGESIZE-1)
#endif

/* Define FLASH States */

#define SPIFLASH_STATE_IDLE         0
#define SPIFLASH_STATE_RDID1        1
#define SPIFLASH_STATE_RDID2        2
#define SPIFLASH_STATE_RDID3        3
#define SPIFLASH_STATE_WREN         4
#define SPIFLASH_STATE_RDSR         5
#define SPIFLASH_STATE_SE1          6
#define SPIFLASH_STATE_SE2          7
#define SPIFLASH_STATE_SE3          8
#define SPIFLASH_STATE_PP1          9
#define SPIFLASH_STATE_PP2          10
#define SPIFLASH_STATE_PP3          11
#define SPIFLASH_STATE_PP4          12
#define SPIFLASH_STATE_READ1        13
#define SPIFLASH_STATE_READ2        14
#define SPIFLASH_STATE_READ3        15
#define SPIFLASH_STATE_READ4        16
#define SPIFLASH_STATE_FREAD_WAIT   17

/* Instructions */
/*      Command            Value      N Description             Addr Dummy Data   */
#define SPIFLASH_WREN      0x06    /* 1 Write Enable              0   0     0     */
#define SPIFLASH_WRDI      0x04    /* 1 Write Disable             0   0     0     */
#define SPIFLASH_RDID      0x9f    /* 1 Read Identification       0   0     1-3   */
#define SPIFLASH_RDSR      0x05    /* 1 Read Status Register      0   0     >=1   */
#define SPIFLASH_WRSR      0x01    /* 1 Write Status Register     0   0     1     */
#define SPIFLASH_READ      0x03    /* 1 Read Data Bytes           3   0     >=1   */
#define SPIFLASH_FAST_READ 0x0b    /* 1 Higher speed read         3   1     >=1   */
#define SPIFLASH_PP        0x02    /* 1 Page Program              3   0     1-256 */
#define SPIFLASH_SE        0xd8    /* 1 Sector Erase              3   0     0     */
#define SPIFLASH_BE        0xc7    /* 1 Bulk Erase                0   0     0     */
#define SPIFLASH_DP        0xb9    /* 2 Deep power down           0   0     0     */
#define SPIFLASH_RES       0xab    /* 2 Read Electronic Signature 0   3     >=1   */
#define SPIFLASH_SSE       0x20    /* 3 Sub-Sector Erase          0   0     0     */

#define SPIFLASH_DUMMY     0xa5

/************************************************************************************
 * Private Types
 ************************************************************************************/

struct sim_spiflashdev_s
{
  struct spi_dev_s spidev;     /* Externally visible part of the SPI interface */
  uint32_t         selected;   /* SPIn base address */
  int              wren;
  int              state;
  uint16_t         read_data;
  uint8_t          last_cmd;
  unsigned long    address;
  unsigned char    data[CONFIG_SPIFLASH_SIZE];
};

/************************************************************************************
 * Private Function Prototypes
 ************************************************************************************/

/* SPI methods */

#ifndef CONFIG_SPI_OWNBUS
static int         spiflash_lock(FAR struct spi_dev_s *dev, bool lock);
#endif
static uint32_t    spiflash_setfrequency(FAR struct spi_dev_s *dev, uint32_t frequency);
static void        spiflash_setmode(FAR struct spi_dev_s *dev, enum spi_mode_e mode);
static void        spiflash_setbits(FAR struct spi_dev_s *dev, int nbits);
static uint16_t    spiflash_send(FAR struct spi_dev_s *dev, uint16_t wd);
static void        spiflash_exchange(FAR struct spi_dev_s *dev, FAR const void *txbuffer,
                                FAR void *rxbuffer, size_t nwords);
static void        spiflash_select(FAR struct spi_dev_s *dev, enum spi_dev_e devid,
                     bool selected);
static uint8_t     spiflash_status(FAR struct spi_dev_s *dev, enum spi_dev_e devid);
#ifdef CONFIG_SPI_CMDDATA
static int         spiflash_cmddata(FAR struct spi_dev_s *dev, enum spi_dev_e devid, bool cmd);
#endif
#ifndef CONFIG_SPI_EXCHANGE
static void        spiflash_sndblock(FAR struct spi_dev_s *dev, FAR const void *txbuffer,
                                size_t nwords);
static void        spiflash_recvblock(FAR struct spi_dev_s *dev, FAR void *rxbuffer,
                                 size_t nwords);
#endif

static void spiflash_writeword(FAR struct sim_spiflashdev_s *priv, uint16_t data);
static uint16_t spiflash_readword(FAR struct sim_spiflashdev_s *priv);

/************************************************************************************
 * Private Data
 ************************************************************************************/

static const struct spi_ops_s g_spiops =
{
#ifndef CONFIG_SPI_OWNBUS
  .lock              = spiflash_lock,
#endif
  .select            = spiflash_select,
  .setfrequency      = spiflash_setfrequency,
  .setmode           = spiflash_setmode,
  .setbits           = spiflash_setbits,
  .status            = spiflash_status,
#ifdef CONFIG_SPI_CMDDATA
  .cmddata           = spiflash_cmddata,
#endif
  .send              = spiflash_send,
#ifdef CONFIG_SPI_EXCHANGE
  .exchange          = spiflash_exchange,
#else
  .sndblock          = spiflash_sndblock,
  .recvblock         = spiflash_recvblock,
#endif
  .registercallback  = 0,
};

struct sim_spiflashdev_s g_spidev =
{
  .spidev   = { &g_spiops },
};

/************************************************************************************
 * Public Data
 ************************************************************************************/

/************************************************************************************
 * Private Functions
 ************************************************************************************/

/************************************************************************************
 * Name: spiflash_lock
 *
 * Description:
 *   On SPI buses where there are multiple devices, it will be necessary to
 *   lock SPI to have exclusive access to the buses for a sequence of
 *   transfers.  The bus should be locked before the chip is selected. After
 *   locking the SPI bus, the caller should then also call the setfrequency,
 *   setbits, and setmode methods to make sure that the SPI is properly
 *   configured for the device.  If the SPI buss is being shared, then it
 *   may have been left in an incompatible state.
 *
 * Input Parameters:
 *   dev  - Device-specific state data
 *   lock - true: Lock spi bus, false: unlock SPI bus
 *
 * Returned Value:
 *   None
 *
 ************************************************************************************/

#ifndef CONFIG_SPI_OWNBUS
static int spiflash_lock(FAR struct spi_dev_s *dev, bool lock)
{
  return OK;
}
#endif

/************************************************************************************
 * Name: spiflash_select
 *
 * Description:
 *   Process select logic for the FLASH.
 *
 * Returned Value:
 *   None
 *
 ************************************************************************************/

static void spiflash_select(FAR struct spi_dev_s *dev, enum spi_dev_e devid,
                            bool selected)
{
  FAR struct sim_spiflashdev_s *priv = (FAR struct sim_spiflashdev_s *)dev;

  if (devid == SPIDEV_FLASH)
    {
      priv->selected = selected;

      /* As part of an de-select, ensure the WREN bit is cleared */

      if (!selected)
        {
          if (priv->last_cmd != SPIFLASH_WREN)
            {
              priv->wren = 0;
            }

          priv->state = SPIFLASH_STATE_IDLE;
        }
    }
}

/************************************************************************************
 * Name: spiflash_cmddata
 *
 * Description:
 *   Perform SPI Command operations
 *
 * Returned Value:
 *   Always returns zero
 *
 ************************************************************************************/

#ifdef CONFIG_SPI_CMDDATA
static int spiflash_cmddata(FAR struct spi_dev_s *dev, enum spi_dev_e devid, bool cmd)
{
  return 0;
}
#endif

/************************************************************************************
 * Name: spiflash_setfrequency
 *
 * Description:
 *   Set the SPI frequency.
 *
 * Input Parameters:
 *   dev -       Device-specific state data
 *   frequency - The SPI frequency requested
 *
 * Returned Value:
 *   Returns the actual frequency selected
 *
 ************************************************************************************/

static uint32_t spiflash_setfrequency(FAR struct spi_dev_s *dev, uint32_t frequency)
{
  return frequency;
}

/************************************************************************************
 * Name: spiflash_setmode
 *
 * Description:
 *   Set the SPI mode.  see enum spi_mode_e for mode definitions
 *
 * Input Parameters:
 *   dev  - Device-specific state data
 *   mode - The SPI mode requested
 *
 * Returned Value:
 *   Returns the actual frequency selected
 *
 ************************************************************************************/

static void spiflash_setmode(FAR struct spi_dev_s *dev, enum spi_mode_e mode)
{
}

/************************************************************************************
 * Name: spiflash_setbits
 *
 * Description:
 *   Set the number of bits per word.
 *
 * Input Parameters:
 *   dev   - Device-specific state data
 *   nbits - The number of bits requested
 *
 * Returned Value:
 *   None
 *
 ************************************************************************************/

static void spiflash_setbits(FAR struct spi_dev_s *dev, int nbits)
{
}

/************************************************************************************
 * Name: spiflash_status
 *
 * Description:
 *   Set the SPI bus status
 *
 * Returned Value:
 *   Always returns zero
 *
 ************************************************************************************/

static uint8_t spiflash_status(FAR struct spi_dev_s *dev, enum spi_dev_e devid)
{
  return 0;
}

/************************************************************************************
 * Name: spiflash_send
 *
 * Description:
 *   Exchange one word on SPI
 *
 * Input Parameters:
 *   dev - Device-specific state data
 *   wd  - The word to send.  the size of the data is determined by the
 *         number of bits selected for the SPI interface.
 *
 * Returned Value:
 *   response
 *
 ************************************************************************************/

static uint16_t spiflash_send(FAR struct spi_dev_s *dev, uint16_t wd)
{
  FAR struct sim_spiflashdev_s *priv = (FAR struct sim_spiflashdev_s *)dev;
  uint16_t ret;

  if (priv->selected)
    {
      spiflash_writeword(priv, wd);
      ret = spiflash_readword(priv);
    }
  else
    {
      ret = 0xff;
    }

  return ret;
}

/************************************************************************************
 * Name: spiflash_exchange (no DMA).  aka spi_exchange_nodma
 *
 * Description:
 *   Exchange a block of data on SPI without using DMA
 *
 * Input Parameters:
 *   dev      - Device-specific state data
 *   txbuffer - A pointer to the buffer of data to be sent
 *   rxbuffer - A pointer to a buffer in which to receive data
 *   nwords   - the length of data to be exchanged in units of words.
 *              The wordsize is determined by the number of bits-per-word
 *              selected for the SPI interface.  If nbits <= 8, the data is
 *              packed into uint8_t's; if nbits >8, the data is packed into
 *              uint16_t's
 *
 * Returned Value:
 *   None
 *
 ************************************************************************************/

static void spiflash_exchange(FAR struct spi_dev_s *dev, FAR const void *txbuffer,
                              FAR void *rxbuffer, size_t nwords)
{
  spivdbg("txbuffer=%p rxbuffer=%p nwords=%d\n", txbuffer, rxbuffer, nwords);

  /* 8-bit mode */

  FAR const uint8_t *src  = (FAR const uint8_t *)txbuffer;
  FAR uint8_t *dest = (FAR uint8_t *)rxbuffer;
  uint8_t word;

  while (nwords-- > 0)
    {
      /* Get the next word to write.  Is there a source buffer? */

      if (src)
        {
          word = *src++;
        }
      else
        {
          word = 0xff;
        }

      /* Exchange one word */

      word = (uint8_t)spiflash_send(dev, (uint16_t)word);

      /* Is there a buffer to receive the return value? */

      if (dest)
        {
          *dest++ = word;
        }
    }
}

/************************************************************************************
 * Name: spi_sndblock
 *
 * Description:
 *   Send a block of data on SPI
 *
 * Input Parameters:
 *   dev      - Device-specific state data
 *   txbuffer - A pointer to the buffer of data to be sent
 *   nwords   - the length of data to send from the buffer in number of words.
 *              The wordsize is determined by the number of bits-per-word
 *              selected for the SPI interface.  If nbits <= 8, the data is
 *              packed into uint8_t's; if nbits >8, the data is packed into
 *              uint16_t's
 *
 * Returned Value:
 *   None
 *
 ************************************************************************************/

#ifndef CONFIG_SPI_EXCHANGE
static void spiflash_sndblock(FAR struct spi_dev_s *dev, FAR const void *txbuffer, size_t nwords)
{
  spivdbg("txbuffer=%p nwords=%d\n", txbuffer, nwords);
  return spiflash_exchange(dev, txbuffer, NULL, nwords);
}
#endif

/************************************************************************************
 * Name: spi_recvblock
 *
 * Description:
 *   Receive a block of data from SPI
 *
 * Input Parameters:
 *   dev      - Device-specific state data
 *   rxbuffer - A pointer to the buffer in which to receive data
 *   nwords   - the length of data that can be received in the buffer in number
 *              of words.  The wordsize is determined by the number of bits-per-word
 *              selected for the SPI interface.  If nbits <= 8, the data is
 *              packed into uint8_t's; if nbits >8, the data is packed into
 *              uint16_t's
 *
 * Returned Value:
 *   None
 *
 ************************************************************************************/

#ifndef CONFIG_SPI_EXCHANGE
static void spiflash_recvblock(FAR struct spi_dev_s *dev, FAR void *rxbuffer,
                               size_t nwords)
{
  spivdbg("rxbuffer=%p nwords=%d\n", rxbuffer, nwords);
  return spiflash_exchange(dev, NULL, rxbuffer, nwords);
}
#endif

/************************************************************************************
 * Name: spiflash_sectorerase
 *
 * Description:
 *   Erase one sector
 *
 * Input Parameters:
 *   priv - Device-specific state data
 *
 * Returned Value:
 *   None
 *
 ************************************************************************************/

static void spiflash_sectorerase(FAR struct sim_spiflashdev_s *priv)
{
  uint32_t  address;
  uint32_t  len;

  /* Ensure the WREN bit is set before any erase operation */

  if (priv->wren)
    {
      address = priv->address;
      if (priv->last_cmd == SPIFLASH_SE)
        {
          address &= CONFIG_SIM_SPIFLASH_SECTORSIZE_MASK;
          len = CONFIG_SIM_SPIFLASH_SECTORSIZE;
        }
      else if (priv->last_cmd == SPIFLASH_SSE)
        {
          address &= CONFIG_SIM_SPIFLASH_SUBSECTORSIZE_MASK;
          len = CONFIG_SIM_SPIFLASH_SUBSECTORSIZE;
        }

      /* Now perform the erase */

      memset(&priv->data[address], 0xFF, len);
    }
}

/************************************************************************************
 * Name: spiflash_writeword
 *
 * Description:
 *   Write a word (byte in our case) to the FLASH state machine.
 *
 * Input Parameters:
 *   dev      - Device-specific state data
 *   data     - the data to send to the simulated FLASH
 *
 * Returned Value:
 *   None
 *
 ************************************************************************************/

static void spiflash_writeword(FAR struct sim_spiflashdev_s *priv, uint16_t data)
{
  switch (priv->state)
    {
      case SPIFLASH_STATE_IDLE:
        priv->last_cmd = data;
        priv->read_data = 0xff;
        switch (data)
          {
            case SPIFLASH_RDID:
              priv->state = SPIFLASH_STATE_RDID1;
              break;

            case SPIFLASH_WREN:
              priv->wren = 1;
              priv->state = SPIFLASH_STATE_WREN;
              break;

            case SPIFLASH_RDSR:
              priv->state = SPIFLASH_STATE_RDSR;
              break;

            /* Sector / Subsector erase */

            case SPIFLASH_SE:
            case SPIFLASH_SSE:
              priv->state = SPIFLASH_STATE_SE1;
              break;

            /* Bulk Erase */

            case SPIFLASH_BE:
              priv->state = SPIFLASH_STATE_IDLE;
              if (priv->wren)
                {
                  memset(priv->data, 0xff, CONFIG_SPIFLASH_SIZE);
                }
              break;

            case SPIFLASH_PP:
              priv->state = SPIFLASH_STATE_PP1;
              break;

            case SPIFLASH_READ:
            case SPIFLASH_FAST_READ:
              priv->state = SPIFLASH_STATE_READ1;
              break;

            default:
              break;
          }
        break;

      /* Read ID States */

      case SPIFLASH_STATE_RDID1:
        priv->read_data = CONFIG_SIM_SPIFLASH_MANUFACTURER;
        priv->state = SPIFLASH_STATE_RDID2;
        break;

      case SPIFLASH_STATE_RDID2:
        priv->read_data = CONFIG_SIM_SPIFLASH_MEMORY_TYPE;
        priv->state = SPIFLASH_STATE_RDID3;
        break;

      case SPIFLASH_STATE_RDID3:
        priv->read_data = CONFIG_SPIFLASH_CAPACITY;
        priv->state = SPIFLASH_STATE_IDLE;
        break;

      /* WREN state - if we receive any bytes here, then we abort the WREN */

      case SPIFLASH_STATE_WREN:
        priv->wren = 0;
        break;

      /* Read Status Register state */

      case SPIFLASH_STATE_RDSR:
        priv->read_data = 0;
        priv->state = SPIFLASH_STATE_IDLE;
        break;

      /* Sector and Sub-Sector erase states - Read the address */

      case SPIFLASH_STATE_SE1:
        priv->address = data << 16;
        priv->state = SPIFLASH_STATE_SE2;
        break;

      case SPIFLASH_STATE_SE2:
        priv->address |= data << 8;
        priv->state = SPIFLASH_STATE_SE3;
        break;

      case SPIFLASH_STATE_SE3:
        priv->address |= data;

        /* Now perform the sector or sub-sector erase.  Really this should
         * be done during the deselect, but this is just a simulation .
         */

        spiflash_sectorerase(priv);
        break;

      /* Page Program.  We could reuse the SE states, but let's keep it clean. */

      case SPIFLASH_STATE_PP1:
        priv->address = data << 16;
        priv->state = SPIFLASH_STATE_PP2;
        break;

      case SPIFLASH_STATE_PP2:
        priv->address |= data << 8;
        priv->state = SPIFLASH_STATE_PP3;
        break;

      case SPIFLASH_STATE_PP3:
        priv->address |= data;
        priv->state = SPIFLASH_STATE_PP4;
        break;

      case SPIFLASH_STATE_PP4:
        /* In this state we actually write data (if WREN enabled) */

        if (priv->wren)
          {
            priv->data[priv->address] = data;
          }

        /* Now increment the address.  We do a page wrap here to simulate
         * the actual FLASH.
         */

        if ((priv->address & CONFIG_SIM_SPIFLASH_PAGESIZE_MASK) ==
              CONFIG_SIM_SPIFLASH_PAGESIZE_MASK)
          {
            priv->address &= !CONFIG_SIM_SPIFLASH_PAGESIZE_MASK;
          }
        else
          {
            priv->address++;
          }
        break;

      /* Read data */

      case SPIFLASH_STATE_READ1:
        priv->address = data << 16;
        priv->state = SPIFLASH_STATE_READ2;
        break;

      case SPIFLASH_STATE_READ2:
        priv->address |= data << 8;
        priv->state = SPIFLASH_STATE_READ3;
        break;

      case SPIFLASH_STATE_READ3:
        priv->address |= data;
        if (priv->last_cmd == SPIFLASH_FAST_READ)
          {
            priv->state = SPIFLASH_STATE_FREAD_WAIT;
          }
        else
          {
            priv->state = SPIFLASH_STATE_READ4;
          }
        break;

      case SPIFLASH_STATE_FREAD_WAIT:
        priv->read_data = 0xff;
        priv->state = SPIFLASH_STATE_READ4;
        break;

      case SPIFLASH_STATE_READ4:
        /* In this state perform data reads until de-selected. */

        priv->read_data = priv->data[priv->address++];
        if (priv->address == CONFIG_SPIFLASH_SIZE)
          {
            priv->address = 0;
          }
        break;

      default:
        priv->state = SPIFLASH_STATE_IDLE;
        priv->read_data = 0xFF;
        break;
    }
}

/************************************************************************************
 * Name: spiflash_readword
 *
 * Description:
 *   Read a word (byte in our case) from the simulated FLASH.
 *
 * Input Parameters:
 *   priv - Device-specific state data
 *
 * Returned Value:
 *   Byte read from the simulated FLASH device
 *
 ************************************************************************************/

static uint16_t spiflash_readword(FAR struct sim_spiflashdev_s *priv)
{
  return priv->read_data;
}

/************************************************************************************
 * Public Functions
 ************************************************************************************/

/************************************************************************************
 * Name: up_spiinitialize
 *
 * Description:
 *   Initialize the selected SPI port
 *
 * Input Parameter:
 *   Port number (for hardware that has multiple SPI interfaces)
 *
 * Returned Value:
 *   Valid SPI device structure reference on success; a NULL on failure
 *
 ************************************************************************************/

FAR struct spi_dev_s *up_spiflashinitialize()
{
  FAR struct sim_spiflashdev_s *priv = NULL;

  irqstate_t flags = irqsave();

  priv = &g_spidev;
  priv->selected = 0;
  priv->wren = 0;
  priv->address = 0;
  priv->state = SPIFLASH_STATE_IDLE;
  priv->read_data = 0xFF;
  priv->last_cmd = 0xFF;
  memset(&priv->data[0], 0xFF, sizeof(priv->data));

  irqrestore(flags);
  return (FAR struct spi_dev_s *)priv;
}

#endif /* CONFIG_SIM_SPIFLASH */
