diff --git a/arch/sim/Kconfig b/arch/sim/Kconfig index 356044e85c0..8152679f06d 100644 --- a/arch/sim/Kconfig +++ b/arch/sim/Kconfig @@ -114,4 +114,79 @@ config SIM_TCNWAITERS The maximum number of threads that can be waiting on poll() for a touchscreen event. Default: 4 +config SIM_SPIFLASH + bool "Simulated SPI FLASH with SMARTFS" + default n + select FS_SMARTFS + select MTD_SMART + ---help--- + Adds a simulated SPI FLASH that responds to standard M25 style + commands on the SPI bus. + +choice + prompt "Simulated SPI FLASH Size" + default SIM_SPIFLASH_1M + depends on SIM_SPIFLASH + +config SIM_SPIFLASH_1M + bool "1 MBit (128K Byte)" + +config SIM_SPIFLASH_8M + bool "8 MBit (1M Byte)" + +config SIM_SPIFLASH_32M + bool "32 MBit (4M Byte)" + +config SIM_SPIFLASH_64M + bool "64 MBit (8M Byte)" + +config SIM_SPIFLASH_128M + bool "128 MBit (16M Byte)" + +endchoice + +config SIM_SPIFLASH_MANUFACTURER + hex "Hex ID of the FLASH manufacturer code" + default 0x20 + depends on SIM_SPIFLASH + ---help--- + Allows the simulated FLASH Manufacturer ID to be set. + +config SIM_SPIFLASH_MEMORY_TYPE + hex "Hex ID of the FLASH Memory Type code" + default 0x20 + depends on SIM_SPIFLASH + ---help--- + Allows the simulated FLASH Memory Type code to be set. + +config SIM_SPIFLASH_SECTORSIZE + int "FLASH Sector Erase Size" + default 65536 + depends on SIM_SPIFLASH + ---help--- + Sets the large sector erase size that the part simulates. + This driver simulates SPI devices that have both a large + sector erase as well as a "sub-sector" (per the datasheet) + erase size (typically 4K bytes). + +config SIM_SPIFLASH_SUBSECTORSIZE + int "FLASH Sub-Sector Erase Size" + default 4096 + depends on SIM_SPIFLASH + ---help--- + Sets the smaller sub-sector erase size supported by the + FLASH emulation + +config SIM_SPIFLASH_PAGESIZE + int "FLASH Write / Program Page Size + default 256 + depends on SIM_SPIFLASH + ---help--- + Sets the size of a page program operation. The page size + represents the maximum number of bytes that can be sent + for a program operation. If more bytes than this are + sent on a single Page Program, then the address will + "wrap" causing the initial data sent to be overwritten. + This is consistent with standard SPI FLASH operation. + endif diff --git a/arch/sim/src/Makefile b/arch/sim/src/Makefile index 8bbf67a8fc6..2514861de4b 100644 --- a/arch/sim/src/Makefile +++ b/arch/sim/src/Makefile @@ -43,7 +43,7 @@ AOBJS = $(ASRCS:.S=$(OBJEXT)) CSRCS = up_initialize.c up_idle.c up_interruptcontext.c up_initialstate.c CSRCS += up_createstack.c up_usestack.c up_releasestack.c up_stackframe.c CSRCS += up_unblocktask.c up_blocktask.c up_releasepending.c -CSRCS += up_reprioritizertr.c up_exit.c up_schedulesigaction.c +CSRCS += up_reprioritizertr.c up_exit.c up_schedulesigaction.c up_spiflash.c CSRCS += up_allocateheap.c up_devconsole.c HOSTSRCS = up_stdio.c up_hostusleep.c diff --git a/arch/sim/src/up_initialize.c b/arch/sim/src/up_initialize.c index b8409e0c254..0623f44d9b8 100644 --- a/arch/sim/src/up_initialize.c +++ b/arch/sim/src/up_initialize.c @@ -43,6 +43,8 @@ #include #include +#include +#include #include #include "up_internal.h" @@ -59,6 +61,32 @@ * Private Functions ****************************************************************************/ +/**************************************************************************** + * Name: up_init_smartfs + * + * Description: + * Initialize a simulated SPI FLASH block device m25p MTD driver and bind + * it to a SMART Flash block device. + * + ****************************************************************************/ + +#if defined(CONFIG_FS_SMARTFS) && defined(CONFIG_SIM_SPIFLASH) +static void up_init_smartfs(void) +{ + FAR struct mtd_dev_s *mtd; + FAR struct spi_dev_s *spi; + + /* Initialize a simulated SPI FLASH block device m25p MTD driver */ + + spi = up_spiflashinitialize(); + mtd = m25p_initialize(spi); + + /* Now initialize a SMART Flash block device and bind it to the MTD device */ + + smart_initialize(0, mtd, NULL); +} +#endif + /**************************************************************************** * Public Functions ****************************************************************************/ @@ -130,4 +158,8 @@ void up_initialize(void) #ifdef CONFIG_NET netdriver_init(); /* Our "real" network driver */ #endif + +#if defined(CONFIG_FS_SMARTFS) && defined(CONFIG_SIM_SPIFLASH) + up_init_smartfs(); +#endif } diff --git a/arch/sim/src/up_internal.h b/arch/sim/src/up_internal.h index 9287b6b672c..5e62a6d051b 100644 --- a/arch/sim/src/up_internal.h +++ b/arch/sim/src/up_internal.h @@ -236,5 +236,9 @@ int netdriver_setmacaddr(unsigned char *macaddr); void netdriver_loop(void); #endif +#ifdef CONFIG_SIM_SPIFLASH +struct spi_dev_s *up_spiflashinitialize(void); +#endif + #endif /* __ASSEMBLY__ */ #endif /* __ARCH_UP_INTERNAL_H */ diff --git a/arch/sim/src/up_spiflash.c b/arch/sim/src/up_spiflash.c new file mode 100644 index 00000000000..7d122344750 --- /dev/null +++ b/arch/sim/src/up_spiflash.c @@ -0,0 +1,858 @@ +/************************************************************************************ + * arch/sim/src/up_spiflash.c + * + * Copyright (C) 2014 Ken Pettit. All rights reserved. + * Author: Ken Pettit + * + * 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 + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "up_internal.h" + +#if defined(CONFIG_SIM_SPIFLASH) + +/************************************************************************************ + * 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 +#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 + +/* 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 + +void spiflash_writeword(FAR struct sim_spiflashdev_s *priv, uint16_t data); +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 */ + + const uint8_t *src = (const uint8_t*)txbuffer;; + uint8_t *dest = (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 + * + ************************************************************************************/ + +stataic 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: + 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; + 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; + + irqrestore(flags); + return (FAR struct spi_dev_s *)priv; +} + +#endif /* CONFIG_SIM_SPIFLASH */