/************************************************************************************ * 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) /************************************************************************************ * 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 #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 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 */ 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 * ************************************************************************************/ 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: 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 */