summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGregory Nutt <gnutt@nuttx.org>2014-11-26 13:55:34 -0600
committerGregory Nutt <gnutt@nuttx.org>2014-11-26 13:55:34 -0600
commit2f82c57b83185d2fa3346e2c6c105d2712afbfad (patch)
tree35523f341ca067727402c66cbac1338de01efbf4
parent83e24c358e0971ba9637bfd170c23a002e8a8190 (diff)
downloadnuttx-2f82c57b83185d2fa3346e2c6c105d2712afbfad.tar.gz
nuttx-2f82c57b83185d2fa3346e2c6c105d2712afbfad.tar.bz2
nuttx-2f82c57b83185d2fa3346e2c6c105d2712afbfad.zip
Add support for generic EEPROM access via a character driver. Add also the EEPROM driver itself. From Sebastien Lorquet
-rw-r--r--nuttx/drivers/Kconfig11
-rw-r--r--nuttx/drivers/Makefile1
-rw-r--r--nuttx/drivers/eeprom/Kconfig21
-rw-r--r--nuttx/drivers/eeprom/Make.defs46
-rw-r--r--nuttx/drivers/eeprom/spi_xx25xx.c788
-rw-r--r--nuttx/drivers/mtd/README.txt4
-rw-r--r--nuttx/include/nuttx/eeprom/spi_xx25xx.h99
-rw-r--r--nuttx/include/nuttx/spi/spi.h3
8 files changed, 970 insertions, 3 deletions
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 <gnutt@nuttx.org>
+#
+# 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 <sebastien@lorquet.fr>
+ *
+ * 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 <nuttx/config.h>
+
+#include <sys/types.h>
+#include <debug.h>
+#include <errno.h>
+#include <nuttx/fs/fs.h>
+
+#include <nuttx/kmalloc.h>
+#include <nuttx/spi/spi.h>
+
+/****************************************************************************
+ * 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 <sebastien@lorquet.fr>
+ *
+ * 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 */