From 2f82c57b83185d2fa3346e2c6c105d2712afbfad Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Wed, 26 Nov 2014 13:55:34 -0600 Subject: Add support for generic EEPROM access via a character driver. Add also the EEPROM driver itself. From Sebastien Lorquet --- nuttx/drivers/Kconfig | 11 + nuttx/drivers/Makefile | 1 + nuttx/drivers/eeprom/Kconfig | 21 + nuttx/drivers/eeprom/Make.defs | 46 ++ nuttx/drivers/eeprom/spi_xx25xx.c | 788 ++++++++++++++++++++++++++++++++ nuttx/drivers/mtd/README.txt | 4 +- nuttx/include/nuttx/eeprom/spi_xx25xx.h | 99 ++++ nuttx/include/nuttx/spi/spi.h | 3 +- 8 files changed, 970 insertions(+), 3 deletions(-) create mode 100644 nuttx/drivers/eeprom/Kconfig create mode 100644 nuttx/drivers/eeprom/Make.defs create mode 100644 nuttx/drivers/eeprom/spi_xx25xx.c create mode 100644 nuttx/include/nuttx/eeprom/spi_xx25xx.h (limited to 'nuttx') diff --git a/nuttx/drivers/Kconfig b/nuttx/drivers/Kconfig index 08ca03cc1..f2eb26efb 100644 --- a/nuttx/drivers/Kconfig +++ b/nuttx/drivers/Kconfig @@ -207,6 +207,17 @@ menuconfig SPI if SPI source drivers/spi/Kconfig + +menuconfig SPI_EEPROM + bool "SPI EEPROM support" + default n + ---help--- + This directory holds implementations of SPI EEPROM drivers + +if SPI_EEPROM +source drivers/eeprom/Kconfig +endif + endif # SPI menuconfig I2S diff --git a/nuttx/drivers/Makefile b/nuttx/drivers/Makefile index 05aed285b..229b3d6f6 100644 --- a/nuttx/drivers/Makefile +++ b/nuttx/drivers/Makefile @@ -52,6 +52,7 @@ VPATH = . include analog$(DELIM)Make.defs include audio$(DELIM)Make.defs include bch$(DELIM)Make.defs +include eeprom$(DELIM)Make.defs include input$(DELIM)Make.defs include lcd$(DELIM)Make.defs include mmcsd$(DELIM)Make.defs diff --git a/nuttx/drivers/eeprom/Kconfig b/nuttx/drivers/eeprom/Kconfig new file mode 100644 index 000000000..14c31b3e8 --- /dev/null +++ b/nuttx/drivers/eeprom/Kconfig @@ -0,0 +1,21 @@ +# +# For a description of the syntax of this configuration file, +# see misc/tools/kconfig-language.txt. +# + +config SPI_EE_25XX + bool "Microchip 25xxNNN / Atmel AT25NNN EEPROM devices" + default n + depends on SPI_EEPROM + ---help--- + This selection enables support for the Microchip/Atmel SPI EEPROM + devices + +if SPI_EE_25XX + +config EE25XX_SPIMODE + int "SPI mode (0-3)" + default 0 + depends on SPI_EE_25XX + +endif diff --git a/nuttx/drivers/eeprom/Make.defs b/nuttx/drivers/eeprom/Make.defs new file mode 100644 index 000000000..ce301be87 --- /dev/null +++ b/nuttx/drivers/eeprom/Make.defs @@ -0,0 +1,46 @@ +############################################################################ +# drivers/eeprom/Make.defs +# +# Copyright (C) 2014 Gregory Nutt. All rights reserved. +# Author: Gregory Nutt +# +# 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. +# +############################################################################ + +ifneq ($(CONFIG_SPI_EE_25XX),0) + +# Include the Microchip/Atmel xx25xx driver + +CSRCS += spi_xx25xx.c + +# Include build support + +DEPPATH += --dep-path eeprom +VPATH += :eeprom +endif diff --git a/nuttx/drivers/eeprom/spi_xx25xx.c b/nuttx/drivers/eeprom/spi_xx25xx.c new file mode 100644 index 000000000..41623ea8f --- /dev/null +++ b/nuttx/drivers/eeprom/spi_xx25xx.c @@ -0,0 +1,788 @@ +/**************************************************************************** + * drivers/eeprom/spi_xx25xx.c + * + * Copyright (C) 2014 Gregory Nutt. All rights reserved. + * Author: Sebastien Lorquet + * + * 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. + * + ****************************************************************************/ + +/* This is a driver for SPI EEPROMos that uses the same commands as the 25AA160. + * Write time 5ms, 6ms for 25xx1025 (determined automatically with polling) + * Max SPI speed is : + * 10 MHz for -A/B/C/D/E/UID versions + * 1 MHz for 25AA versions + * 2 MHz for 25LC versions + * 3 MHz for 25C versions + * 10 MHz for 25xxN versions where N=128 and more + * 20 MHz for 25AA512, 25LC512, 25xx1024 + * 20 MHz for Atmel devices (>4.5V) + * 10 MHz for Atmel devices (>2.5V) + * All devices have the same instruction set. + * + * The following devices should be supported: + * + * Manufacturer Device Bytes PgSize AddrLen + * Microchip + * 25xx010A 128 16 1 + * 25xx020A 256 16 1 + * 25AA02UID 256 16 1 + * 25AA02E48 256 16 1 + * 25AA02E64 256 16 1 + * 25xx040 512 16 1+bit + * 25xx040A 512 16 1+bit + * 25xx080 1024 16 1 + * 25xx080A 1024 16 2 + * 25xx080B 1024 32 2 + * 25xx080C 1024 16 x + * 25xx080D 1024 32 x + * 25xx160 2048 16 2 + * 25xx160A/C 2048 16 2 TESTED + * 25xx160B/D 2048 32 2 + * 25xx160C 2048 16 2 + * 25xx160D 2048 32 2 + * 25xx320 4096 32 2 + * 25xx320A 4096 32 2 + * 25xx640 8192 32 2 + * 25xx640A 8192 32 2 + * 25xx128 16384 64 2 + * 25xx256 32768 64 2 + * 25xx512 65536 128 2 + * 25xx1024 131072 256 3 + * Atmel + * AT25010B 128 8 1 + * AT25020B 256 8 1 + * AT25040B 512 8 1+bit + * AT25080B 1024 32 2 + * AT25160B 2048 32 2 + * AT25320B 4096 32 2 + * AT25640B 8192 32 2 + * AT25128B 16384 64 2 + * AT25256B 32768 64 2 + * AT25512 65536 128 2 + * AT25M01 131072 256 3 + */ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include + +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#ifndef CONFIG_EE25XX_SPIMODE +# define CONFIG_EE25XX_SPIMODE 0 +#endif + +/* EEPROM commands + * High bit of low nibble used for A8 in 25xx040/at25040 products + */ + +#define EE25XX_CMD_WRSR 0x01 +#define EE25XX_CMD_WRITE 0x02 +#define EE25XX_CMD_READ 0x03 +#define EE25XX_CMD_WRDIS 0x04 +#define EE25XX_CMD_RDSR 0x05 +#define EE25XX_CMD_WREN 0x06 + +/* Following commands will be available some day via IOCTLs + * PE 0x42 Page erase (25xx512/1024) + * SE 0xD8 Sector erase (25xx512/1024) + * CE 0xC7 Chip erase (25xx512/1024) + * RDID 0xAB Wake up and read electronic signature (25xx512/1024) + * DPD 0xB9 Sleep (25xx512/1024) + */ + +/* SR bits definitions */ + +#define EE25XX_SR_WIP 0x01 /* Write in Progress */ +#define EE25XX_SR_WEL 0x02 /* Write Enable Latch */ +#define EE25XX_SR_BP0 0x04 /* First Block Protect bit */ +#define EE25XX_SR_BP1 0x08 /* Second Block Protect bit */ +#define EE25XX_SR_WPEN 0x80 /* Write Protect Enable */ + +#define EE25XX_DUMMY 0xFF + +/**************************************************************************** + * Types + ****************************************************************************/ + +/* Device geometry description, compact form (2 bytes per entry) */ + +struct ee25xx_geom_s +{ + uint8_t bytes : 4; /*power of two of 128 bytes (0:128 1:256 2:512 etc) */ + uint8_t pagesize : 4; /*power of two of 8 bytes (0:8 1:16 2:32 3:64 etc)*/ + uint8_t addrlen : 4; /*number of bytes in command address field */ + uint8_t flags : 4; /*special address management for 25xx040, 1=A8 in inst*/ +}; + +/* Private data attached to the inode */ + +struct ee25xx_dev_s +{ + struct spi_dev_s *spi; /* spi device where the EEPROM is attached */ + uint32_t size; /* in bytes, expanded from geometry */ + uint16_t pgsize; /* write block size, in bytes, expanded from geometry */ + uint16_t addrlen; /* number of BITS in data addresses */ + sem_t sem; /* file access serialization */ + uint8_t refs; /* The number of times the device has been opened */ + uint8_t readonly; /* Flags */ +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int ee25xx_open(FAR struct file *filep); +static int ee25xx_close(FAR struct file *filep); +static off_t ee25xx_seek(FAR struct file *filep, off_t offset, int whence); +static ssize_t ee25xx_read(FAR struct file *filep, FAR char *buffer, + size_t buflen); +static ssize_t ee25xx_write(FAR struct file *filep, FAR const char *buffer, + size_t buflen); +static int ee25xx_ioctl(FAR struct file *filep, int cmd, + unsigned long arg); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* Supported device geometries. + * One geometry can fit more than one device. + * The user will use an enum'ed index from include/eeprom/spi_xx25xx.h + */ + +static const struct ee25xx_geom_s g_ee25xx_devices[] = +{ + /* Microchip devices */ + + { 0, 1, 1, 0}, /*25xx010A 128 16 1*/ + { 1, 1, 1, 0}, /*25xx020A 256 16 1*/ + { 2, 1, 1, 1}, /*25xx040 512 16 1+bit*/ + { 3, 1, 1, 0}, /*25xx080 1024 16 1*/ + { 3, 2, 2, 0}, /*25xx080B 1024 32 2*/ + { 4, 1, 2, 0}, /*25xx160 2048 16 2*/ + { 4, 2, 2, 0}, /*25xx160B/D 2048 32 2*/ + { 5, 2, 2, 0}, /*25xx320 4096 32 2*/ + { 6, 2, 2, 0}, /*25xx640 8192 32 2*/ + { 7, 3, 2, 0}, /*25xx128 16384 64 2*/ + { 8, 3, 2, 0}, /*25xx256 32768 64 2*/ + { 9, 4, 2, 0}, /*25xx512 65536 128 2*/ + {10, 5, 3, 0}, /*25xx1024 131072 256 3*/ + + /* Atmel devices */ + + { 0, 0, 1, 0}, /*AT25010B 128 8 1*/ + { 1, 0, 1, 0}, /*AT25020B 256 8 1*/ + { 2, 0, 1, 1}, /*AT25040B 512 8 1+bit*/ +}; + +/* Driver operations */ + +static const struct file_operations ee25xx_fops = +{ + ee25xx_open, /* open */ + ee25xx_close, /* close */ + ee25xx_read, /* read */ + ee25xx_write, /* write */ + ee25xx_seek, /* seek */ + ee25xx_ioctl /* ioctl */ +#ifndef CONFIG_DISABLE_POLL + , 0 /* poll */ +#endif +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: ee25xx_lock + ****************************************************************************/ + +static void ee25xx_lock(FAR struct spi_dev_s *dev) +{ + /* 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. + * + * This is a blocking call and will not return until we have exclusive + * access to the SPI bus. We will retain that exclusive access until the + * bus is unlocked. + */ + + (void)SPI_LOCK(dev, true); + + /* After locking the SPI bus, the we also need call the setfrequency, + * setbits, and setmode methods to make sure that the SPI is properly + * configured for the device. If the SPI bus is being shared, then it may + * have been left in an incompatible state. + */ + + SPI_SETMODE(dev, CONFIG_EE25XX_SPIMODE); + SPI_SETBITS(dev, 8); + (void)SPI_SETFREQUENCY(dev, 10000000); /* This is the default speed */ +} + +/**************************************************************************** + * Name: ee25xx_unlock + ****************************************************************************/ + +static inline void ee25xx_unlock(FAR struct spi_dev_s *dev) +{ + (void)SPI_LOCK(dev, false); +} + +/**************************************************************************** + * Name: ee25xx_sendcmd + * + * Description: Send command and address as one transaction to take advantage + * of possible faster DMA transfers. Sending byte per byte is FAR FAR slower. + * + ****************************************************************************/ + +static void ee25xx_sendcmd(FAR struct spi_dev_s *spi, uint8_t cmd, + uint8_t addrlen, uint32_t addr) +{ + uint8_t buf[4]; + int cmdlen = 1; + + /* Store command */ + + buf[0] = cmd; + + /* Store address according to its length */ + + if (addrlen == 9) + { + buf[0] |= (((addr >> 8) & 1) << 3); + } + + if (addrlen > 16) + { + buf[cmdlen++] = (addr >> 16) & 0xff; + } + + if (addrlen > 9) + { + buf[cmdlen++] = (addr >> 8) & 0xff; + } + + buf[cmdlen++] = addr & 0xff; + + SPI_SNDBLOCK(spi,buf,cmdlen); +} + +/**************************************************************************** + * Name: ee25xx_waitwritecomplete + * + * Description: loop until the write operation is done. + * + ****************************************************************************/ + +static void ee25xx_waitwritecomplete(struct ee25xx_dev_s *priv) +{ + uint8_t status; + + /* Loop as long as the memory is busy with a write cycle */ + + do + { + /* Select this FLASH part */ + + SPI_SELECT(priv->spi, SPIDEV_EEPROM, true); + + /* Send "Read Status Register (RDSR)" command */ + + (void)SPI_SEND(priv->spi, EE25XX_CMD_RDSR); + + /* Send a dummy byte to generate the clock needed to shift out the + * status + */ + + status = SPI_SEND(priv->spi, EE25XX_DUMMY); + + /* Deselect the FLASH */ + + SPI_SELECT(priv->spi, SPIDEV_EEPROM, false); + + /* Given that writing could take up to a few milliseconds, + * the following short delay in the "busy" case will allow + * other peripherals to access the SPI bus. + */ + + if ((status & EE25XX_SR_WIP) != 0) + { + ee25xx_unlock(priv->spi); + usleep(1000); + ee25xx_lock(priv->spi); + } + } + while ((status & EE25XX_SR_WIP) != 0); + + fvdbg("Complete\n"); +} + +/**************************************************************************** + * Name: ee25xx_writeenable + * + * Description: Enable or disable write operations. + * This is required before any write, since a lot of operations automatically + * disables the write latch. + * + ****************************************************************************/ + +static void ee25xx_writeenable(FAR struct spi_dev_s *spi, int enable) +{ + ee25xx_lock(spi); + SPI_SELECT(spi, SPIDEV_EEPROM, true); + + SPI_SEND(spi, enable ? EE25XX_CMD_WREN : EE25XX_CMD_WRDIS); + + SPI_SELECT(spi, SPIDEV_EEPROM, false); + ee25xx_unlock(spi); +} + +/**************************************************************************** + * Name: ee25xx_writepage + * + * Description: Write data to the EEPROM, NOT crossing page boundaries. + * + ****************************************************************************/ + +static void ee25xx_writepage(FAR struct ee25xx_dev_s *eedev, uint32_t devaddr, + FAR const char *data, size_t len) +{ + ee25xx_lock(eedev->spi); + SPI_SELECT(eedev->spi, SPIDEV_EEPROM, true); + + ee25xx_sendcmd(eedev->spi, EE25XX_CMD_WRITE, eedev->addrlen, devaddr); + SPI_SNDBLOCK(eedev->spi, data, len); + + SPI_SELECT(eedev->spi, SPIDEV_EEPROM, false); + ee25xx_unlock(eedev->spi); +} + +/**************************************************************************** + * Name: ee25xx_semtake + * + * Acquire a resource to access the device. + * The purpose of the semaphore is to block tasks that try to access the + * EEPROM while another task is actively using it. + * + ****************************************************************************/ + +static void ee25xx_semtake(FAR struct ee25xx_dev_s *eedev) +{ + /* Take the semaphore (perhaps waiting) */ + + while (sem_wait(&eedev->sem) != 0) + { + /* The only case that an error should occur here is if + * the wait was awakened by a signal. + */ + + ASSERT(errno == EINTR); + } +} + +/**************************************************************************** + * Name: ee25xx_semgive + * + * Release a resource to access the device. + * + ****************************************************************************/ + +static inline void ee25xx_semgive(FAR struct ee25xx_dev_s *eedev) +{ + sem_post(&eedev->sem); +} + +/**************************************************************************** + * Driver Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: ee25xx_open + * + * Description: Open the block device + * + ****************************************************************************/ + +static int ee25xx_open(FAR struct file *filep) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct ee25xx_dev_s *eedev; + int ret = OK; + + DEBUGASSERT(inode && inode->i_private); + eedev = (FAR struct ee25xx_dev_s *)inode->i_private; + ee25xx_semtake(eedev); + + /* Increment the reference count */ + + if ( (eedev->refs + 1) == 0) + { + ret = -EMFILE; + } + else + { + eedev->refs += 1; + } + + ee25xx_semgive(eedev); + return ret; +} + +/**************************************************************************** + * Name: ee25xx_close + * + * Description: Close the block device + * + ****************************************************************************/ + +static int ee25xx_close(FAR struct file *filep) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct ee25xx_dev_s *eedev; + int ret = OK; + + DEBUGASSERT(inode && inode->i_private); + eedev = (FAR struct ee25xx_dev_s *)inode->i_private; + ee25xx_semtake(eedev); + + /* Decrement the reference count. I want the entire close operation + * to be atomic wrt other driver operations. + */ + + if (eedev->refs == 0) + { + ret = -EIO; + } + else + { + eedev->refs -= 1; + } + + ee25xx_semgive(eedev); + return ret; +} + +/**************************************************************************** + * Name: ee25xx_seek + * + * Remark: Copied from bchlib + * + ****************************************************************************/ + +static off_t ee25xx_seek(FAR struct file *filep, off_t offset, int whence) +{ + FAR struct ee25xx_dev_s *eedev; + off_t newpos; + int ret; + FAR struct inode *inode = filep->f_inode; + + DEBUGASSERT(inode && inode->i_private); + eedev = (FAR struct ee25xx_dev_s *)inode->i_private; + ee25xx_semtake(eedev); + + /* Determine the new, requested file position */ + + switch (whence) + { + case SEEK_CUR: + newpos = filep->f_pos + offset; + break; + + case SEEK_SET: + newpos = offset; + break; + + case SEEK_END: + newpos = eedev->size + offset; + break; + + default: + /* Return EINVAL if the whence argument is invalid */ + + ee25xx_semgive(eedev); + return -EINVAL; + } + + /* Opengroup.org: + * + * "The lseek() function shall allow the file offset to be set beyond the end + * of the existing data in the file. If data is later written at this point, + * subsequent reads of data in the gap shall return bytes with the value 0 + * until data is actually written into the gap." + * + * We can conform to the first part, but not the second. But return EINVAL if + * + * "...the resulting file offset would be negative for a regular file, block + * special file, or directory." + */ + + if (newpos >= 0) + { + filep->f_pos = newpos; + ret = newpos; + } + else + { + ret = -EINVAL; + } + + ee25xx_semgive(eedev); + return ret; +} + +/**************************************************************************** + * Name: ee25xx_read + ****************************************************************************/ + +static ssize_t ee25xx_read(FAR struct file *filep, FAR char *buffer, + size_t len) +{ + FAR struct ee25xx_dev_s *eedev; + FAR struct inode *inode = filep->f_inode; + + DEBUGASSERT(inode && inode->i_private); + eedev = (FAR struct ee25xx_dev_s *)inode->i_private; + + ee25xx_semtake(eedev); + + /* trim len if read would go beyond end of device */ + + if ((filep->f_pos + len) > eedev->size) + { + len = eedev->size - filep->f_pos; + } + + ee25xx_lock(eedev->spi); + SPI_SELECT(eedev->spi, SPIDEV_EEPROM, true); + + /* STM32F4Disco: There is a 25 us delay here */ + + ee25xx_sendcmd(eedev->spi, EE25XX_CMD_READ, eedev->addrlen, filep->f_pos); + + /* STM32F4Disco: There is a 42 us delay here */ + + SPI_RECVBLOCK(eedev->spi, buffer, len); + + /* STM32F4Disco: There is a 20 us delay here */ + + SPI_SELECT(eedev->spi, SPIDEV_EEPROM, false); + ee25xx_unlock(eedev->spi); + + if (ret > 0) + { + filep->f_pos += len; + } + + ee25xx_semgive(eedev); + return len; +} + +/**************************************************************************** + * Name: ee25xx_write + ****************************************************************************/ + +static ssize_t ee25xx_write(FAR struct file *filep, FAR const char *buffer, + size_t len) +{ + FAR struct ee25xx_dev_s *eedev; + size_t cnt; + int pageoff; + FAR struct inode *inode = filep->f_inode; + int ret = -EACCES; + + DEBUGASSERT(inode && inode->i_private); + eedev = (FAR struct ee25xx_dev_s *)inode->i_private; + + if (eedev->readonly) + { + return ret; + } + + /* Clamp len to avoid crossing the end of the memory */ + + if ( (len + filep->f_pos) > eedev->size) + { + len = eedev->size - filep->f_pos; + } + + /* From this point no failure cannot be detected anymore. + * The user should verify the write by rereading memory. + */ + + ret = len; /* save number of bytes written */ + + ee25xx_semtake(eedev); + + /* Writes cant happen in a row like the read does. + * The EEPROM is made of pages, and write sequences + * cannot cross page boundaries. So every time the last + * byte of a page is programmed, the SPI transaction is + * stopped, and the status register is read until the + * write operation has completed. + */ + + /* First, write some page-unaligned data */ + + pageoff = filep->f_pos & (eedev->pgsize - 1); + cnt = eedev->pgsize - pageoff; + if (cnt > len) + { + cnt = len; + } + + if (pageoff > 0) + { + ee25xx_writeenable(eedev->spi, true); + ee25xx_writepage(eedev, filep->f_pos, buffer, cnt); + ee25xx_waitwritecomplete(eedev); + len -= cnt; + buffer += cnt; + filep->f_pos += cnt; + } + + /* Then, write remaining bytes at page-aligned addresses */ + + while (len > 0) + { + cnt = len; + if (cnt > eedev->pgsize) + { + cnt = eedev->pgsize; + } + + ee25xx_writeenable(eedev->spi, true); + ee25xx_writepage(eedev, filep->f_pos, buffer, cnt); + ee25xx_waitwritecomplete(eedev); + len -= cnt; + buffer += cnt; + filep->f_pos += cnt; + } + + ee25xx_semgive(eedev); + + return ret; +} + +/**************************************************************************** + * Name: ee25xx_ioctl + * + * Description: TODO: Erase a sector/page/device or read device ID. + * This is completely optional and only applies to bigger devices. + * + ****************************************************************************/ + +static int ee25xx_ioctl(FAR struct file *filep, int cmd, unsigned long arg) +{ + FAR struct ee25xx_dev_s *eedev; + FAR struct inode *inode = filep->f_inode; + int ret = 0; + + DEBUGASSERT(inode && inode->i_private); + eedev = (FAR struct ee25xx_dev_s *)inode->i_private; + + switch (cmd) + { + default: + (void)eedev; + ret = -EINVAL; + } + + return ret; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: ee25xx_initialize + * + * Description: Bind a EEPROM driver to an SPI bus. The user MUST provide + * a description of the device geometry, since it is not possible to read + * this information from the device (contrary to the SPI flash devices). + * + ****************************************************************************/ + +int ee25xx_initialize(FAR struct spi_dev_s *dev, FAR char *devname, + int devtype, int readonly) +{ + FAR struct ee25xx_dev_s *eedev; + + /* Check device type early */ + + if ((devtype < 0) || + (devtype >= sizeof(g_ee25xx_devices) / sizeof(g_ee25xx_devices[0]))) + { + return -EINVAL; + } + + eedev = kmm_zalloc(sizeof(struct ee25xx_dev_s)); + + if (!eedev) + { + return -ENOMEM; + } + + sem_init(&eedev->sem, 0, 1); + + eedev->spi = dev; + eedev->size = 128 << g_ee25xx_devices[devtype].bytes; + eedev->pgsize = 8 << g_ee25xx_devices[devtype].pagesize; + eedev->addrlen = g_ee25xx_devices[devtype].addrlen << 3; + if ( (g_ee25xx_devices[devtype].flags & 1)) + { + eedev->addrlen = 9; + } + + eedev->readonly = !!readonly; + + lowsyslog(LOG_NOTICE, + "EEPROM device %s, %d bytes, %d per page, addrlen %d, readonly %d\n", + devname, eedev->size, eedev->pgsize, eedev->addrlen, eedev->readonly); + + return register_driver(devname, &ee25xx_fops, 0666, eedev); +} diff --git a/nuttx/drivers/mtd/README.txt b/nuttx/drivers/mtd/README.txt index 76498096b..5331155f8 100644 --- a/nuttx/drivers/mtd/README.txt +++ b/nuttx/drivers/mtd/README.txt @@ -2,8 +2,8 @@ MTD README ========== MTD stands for "Memory Technology Devices". This directory contains - drivers that operate on various memory technoldogy devices and provide an - MTD interface. That MTD interface may then by use by higher level logic + drivers that operate on various memory technology devices and provide an + MTD interface. That MTD interface may then be used by higher level logic to control access to the memory device. See include/nuttx/mtd/mtd.h for additional information. diff --git a/nuttx/include/nuttx/eeprom/spi_xx25xx.h b/nuttx/include/nuttx/eeprom/spi_xx25xx.h new file mode 100644 index 000000000..ec3fcaa2a --- /dev/null +++ b/nuttx/include/nuttx/eeprom/spi_xx25xx.h @@ -0,0 +1,99 @@ +/**************************************************************************** + * include/nuttx/eeprom/m25xx.h + * + * Copyright (C) 2014 Gregory Nutt. All rights reserved. + * Author: Sebastien Lorquet + * + * 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. + * + ****************************************************************************/ + +#ifndef __INCLUDE_NUTTX_EEPROM_M25XX_H +#define __INCLUDE_NUTTX_EEPROM_M25XX_H 1 + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* DO NOT CHANGE ORDER, IT MACHES CODE IN drivers/eeprom/spieeprom.c */ + +enum eeprom_25xx_e +{ + /* Microchip geometries */ + + EEPROM_25xx010, + EEPROM_25xx020, + EEPROM_25xx040, + EEPROM_25xx080A, /* 16 bytes pages */ + EEPROM_25xx080B, /* 32 bytes pages */ + EEPROM_25xx160A, /* 16 bytes pages */ + EEPROM_25xx160B, /* 32 bytes pages */ + EEPROM_25xx320, + EEPROM_25xx640, + EEPROM_25xx128, + EEPROM_25xx256, + EEPROM_25xx512, + EEPROM_25xx1024, + + /* Atmel geometries */ + + EEPROM_AT25010B, + EEPROM_AT25020B, + EEPROM_AT25040B, + + /* Aliases (devices similar to previously defined ones)*/ + + EEPROM_AT25080B = EEPROM_25xx080B, + EEPROM_AT25160B = EEPROM_25xx160B, + EEPROM_AT25320B = EEPROM_25xx320, + EEPROM_AT25640B = EEPROM_25xx640, + EEPROM_AT25128B = EEPROM_25xx128, + EEPROM_AT225256B = EEPROM_25xx256, + EEPROM_AT25512 = EEPROM_25xx512, + EEPROM_AT25M02 = EEPROM_25xx1024, +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: ee25xx_initialize + * + * Description: Bind a EEPROM driver to an SPI bus. The user MUST provide + * a description of the device geometry, since it is not possible to read + * this information from the device (contrary to the SPI flash devices). + * + ****************************************************************************/ + +struct spi_dev_s; +int ee25xx_initialize(FAR struct spi_dev_s *dev, FAR char *devname, + int devtype, int readonly); + +#endif /* __INCLUDE__NUTTX_EEPROM_M25XX_H */ + diff --git a/nuttx/include/nuttx/spi/spi.h b/nuttx/include/nuttx/spi/spi.h index ace8799cf..7f1c4c784 100644 --- a/nuttx/include/nuttx/spi/spi.h +++ b/nuttx/include/nuttx/spi/spi.h @@ -360,7 +360,8 @@ enum spi_dev_e SPIDEV_EXPANDER, /* Select SPI I/O expander device */ SPIDEV_MUX, /* Select SPI multiplexer device */ SPIDEV_AUDIO_DATA, /* Select SPI audio codec device data port */ - SPIDEV_AUDIO_CTRL /* Select SPI audio codec device control port */ + SPIDEV_AUDIO_CTRL, /* Select SPI audio codec device control port */ + SPIDEV_EEPROM /* Select SPI EEPROM device */ }; /* Certain SPI devices may required differnt clocking modes */ -- cgit v1.2.3