diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index afc09f5b10d..a88a52cb58d 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -501,7 +501,6 @@ Documentation/components/binfmt.rst matias@protobits.dev xiaoxiang@xiaomi.com co Documentation/components/boards.rst raiden00@railab.me Documentation/components/cmake.rst raiden00@railab.me Documentation/components/crypto.rst raiden00@railab.me -Documentation/components/drivers/block/eeprom.rst raiden00@railab.me Documentation/components/drivers/block/index.rst raiden00@railab.me matias@protobits.dev bashton@brennanashton.com xiaoxiang@xiaomi.com Documentation/components/drivers/block/ramdisk.rst raiden00@railab.me Documentation/components/drivers/character/1wire.rst raiden00@railab.me @@ -525,6 +524,7 @@ Documentation/components/drivers/character/can.rst matias@protobits.dev raiden00 Documentation/components/drivers/character/contactless.rst raiden00@railab.me Documentation/components/drivers/character/crypto/index.rst raiden00@railab.me Documentation/components/drivers/character/crypto/se05x.rst raiden00@railab.me +Documentation/components/drivers/character/eeprom.rst raiden00@railab.me Documentation/components/drivers/character/efuse.rst raiden00@railab.me Documentation/components/drivers/character/i2s.rst raiden00@railab.me Documentation/components/drivers/character/index.rst matias@protobits.dev raiden00@railab.me bashton@brennanashton.com xiaoxiang@xiaomi.com michallenc@seznam.cz @@ -19674,8 +19674,7 @@ include/nuttx/drivers/ramdisk.h alin.jerpelea@sony.com include/nuttx/drivers/rpmsgblk.h wangbowen6@xiaomi.com devel@sumpfralle.de alin.jerpelea@sony.com include/nuttx/drivers/rpmsgdev.h wangbowen6@xiaomi.com zhanghongyu@xiaomi.com devel@sumpfralle.de alin.jerpelea@sony.com wangjianyu3@xiaomi.com include/nuttx/drivers/rwbuffer.h alin.jerpelea@sony.com xiaoxiang@xiaomi.com anjiahao@xiaomi.com spudarnia@yahoo.com -include/nuttx/eeprom/i2c_xx24xx.h acassis@gmail.com sebastien@lorquet.fr xiaoxiang@xiaomi.com alin.jerpelea@sony.com -include/nuttx/eeprom/spi_xx25xx.h alin.jerpelea@sony.com petro.karashchenko@gmail.com xiaoxiang@xiaomi.com 56726697+TimJTi@users.noreply.github.com +include/nuttx/eeprom/eeprom.h acassis@gmail.com alin.jerpelea@sony.com petro.karashchenko@gmail.com sebastien@lorquet.fr xiaoxiang@xiaomi.com 56726697+TimJTi@users.noreply.github.com include/nuttx/efuse/efuse.h acassis@gmail.com 56726697+TimJTi@users.noreply.github.com gustavo.nihei@espressif.com alin.jerpelea@sony.com include/nuttx/efuse/sama5_sfc_fuses.h 56726697+TimJTi@users.noreply.github.com alin.jerpelea@sony.com include/nuttx/elf.h xiaoxiang@xiaomi.com anchao@xiaomi.com masayuki.ishikawa@gmail.com anthony@vergeaero.com diff --git a/Documentation/components/drivers/special/mtd.rst b/Documentation/components/drivers/special/mtd.rst index 5f56de240bc..d165ca70b61 100644 --- a/Documentation/components/drivers/special/mtd.rst +++ b/Documentation/components/drivers/special/mtd.rst @@ -145,6 +145,9 @@ character driver interface may also be preferred to reduce footprint or for very trivial usage of the EEPROM (e.g. storing parameters without having to rely on a filesystem). +The MTD driver for 25xxxx family EEPROMs is a wrapper around the character +driver, their performance is therefore almost identical. + CFI FLASH ========= diff --git a/boards/arm/stm32/stm32f401rc-rs485/src/stm32_at24.c b/boards/arm/stm32/stm32f401rc-rs485/src/stm32_at24.c index 42371568c5d..3f072f236fc 100644 --- a/boards/arm/stm32/stm32f401rc-rs485/src/stm32_at24.c +++ b/boards/arm/stm32/stm32f401rc-rs485/src/stm32_at24.c @@ -34,7 +34,7 @@ #include #include -#include +#include #include "stm32f401rc-rs485.h" diff --git a/boards/arm/stm32h7/linum-stm32h753bi/src/stm32_at24.c b/boards/arm/stm32h7/linum-stm32h753bi/src/stm32_at24.c index ac2627d2cb4..9527fc959e9 100644 --- a/boards/arm/stm32h7/linum-stm32h753bi/src/stm32_at24.c +++ b/boards/arm/stm32h7/linum-stm32h753bi/src/stm32_at24.c @@ -34,7 +34,7 @@ #include #include -#include +#include #include "stm32_i2c.h" #include "linum-stm32h753bi.h" diff --git a/drivers/eeprom/i2c_xx24xx.c b/drivers/eeprom/i2c_xx24xx.c index f3c7558c6d8..94f51c1f7cb 100644 --- a/drivers/eeprom/i2c_xx24xx.c +++ b/drivers/eeprom/i2c_xx24xx.c @@ -86,7 +86,6 @@ #include #include -#include #include #include #include @@ -164,7 +163,7 @@ static ssize_t at24cs_read_uuid(FAR struct file *filep, FAR char *buffer, /* Supported device geometries. * One geometry can fit more than one device. - * The user will use an enum'ed index from include/eeprom/i2c_xx24xx.h + * The user will use an enum'ed index from include/eeprom/eeprom.h */ static const struct ee24xx_geom_s g_ee24xx_devices[] = diff --git a/drivers/eeprom/spi_xx25xx.c b/drivers/eeprom/spi_xx25xx.c index 9c336e8d63c..de08a907a53 100644 --- a/drivers/eeprom/spi_xx25xx.c +++ b/drivers/eeprom/spi_xx25xx.c @@ -22,79 +22,6 @@ /* This is a driver for SPI EEPROMs that use 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) - * 20 MHz for <1Mbit STM devices (>4.5V) - * 16 MHz for 1Mbit STM devices (>4.5V) - * 10 MHz for all STM devices (>2.5V) - * 5 MHz for 1Mbit STM devices (>1.8V) - * 2 MHz for 1Mbit STM devices (>1.7V) - * 5 MHz for 2Mbit STM devices - * All devices have the same instruction set. - * - * The following devices should be supported: - * - * Manufacturer Device Bytes PgSize SecSize AddrLen - * Microchip - * 25xx010A 128 16 16 1 - * 25xx020A 256 16 16 1 - * 25AA02UID 256 16 16 1 - * 25AA02E48 256 16 16 1 - * 25AA02E64 256 16 16 1 - * 25xx040 512 16 16 1+bit - * 25xx040A 512 16 16 1+bit - * 25xx080 1024 16 16 1 - * 25xx080A 1024 16 16 2 - * 25xx080B 1024 32 32 2 - * 25xx080C 1024 16 16 x - * 25xx080D 1024 32 32 x - * 25xx160 2048 16 16 2 - * 25xx160A/C 2048 16 16 2 - * 25xx160B/D 2048 32 32 2 - * 25xx160C 2048 16 16 2 - * 25xx160D 2048 32 32 2 - * 25xx320 4096 32 32 2 - * 25xx320A 4096 32 32 2 - * 25xx640 8192 32 32 2 - * 25xx640A 8192 32 32 2 - * 25xx128 16384 64 64 2 - * 25xx256 32768 64 64 2 - * 25xx512 65536 128 16384 2 - * 25xx1024 131072 256 32768 3 - * Atmel - * AT25010B 128 8 8 1 - * AT25020B 256 8 8 1 - * AT25040B 512 8 8 1+bit - * AT25080B 1024 32 32 2 - * AT25160B 2048 32 32 2 - * AT25320B 4096 32 32 2 - * AT25640B 8192 32 32 2 - * AT25128B 16384 64 64 2 - * AT25256B 32768 64 64 2 - * AT25512 65536 128 128 2 - * AT25M01 131072 256 256 3 - * ST Microelectronics - * M95010 128 16 16 1 - * M95020 256 16 16 1 - * M95040 512 16 16 1+bit - * M95080 1024 32 32 2 - * M95160 2048 32 32 2 - * M95320 4096 32 32 2 - * M95640 8192 32 32 2 - * M95128 16384 64 64 2 - * M95256 32768 64 64 2 - * M95512 65536 128 128 2 - * M95M01 131072 256 256 3 - * M95M02 262144 256 256 3 */ /**************************************************************************** @@ -175,6 +102,13 @@ struct ee25xx_geom_s struct ee25xx_dev_s { +#ifdef CONFIG_MTD_AT25EE + struct eeprom_dev_s eepdev; /* Must appear at the beginning to make it + * possible to cast between struct eeprom_dev_s + * and struct ee25xx_dev_s + */ +#endif + FAR struct spi_dev_s *spi; /* SPI device where the EEPROM is attached */ uint16_t devid; /* SPI device ID to manage CS lines in board */ uint32_t freq; /* SPI bus frequency in Hz */ @@ -193,14 +127,67 @@ struct ee25xx_dev_s * Private Function Prototypes ****************************************************************************/ +/* SPI lock/unlock */ + +static void ee25xx_lock(FAR struct ee25xx_dev_s *eedev); +static void ee25xx_unlock(FAR struct ee25xx_dev_s *eedev); + +/* Trigger a read/write operation */ + +static void ee25xx_sendcmd(FAR struct spi_dev_s *spi, uint8_t cmd, + uint8_t addrlen, uint32_t addr); + +/* Write/erase related functions */ + +static void ee25xx_waitwritecomplete(FAR struct ee25xx_dev_s *eedev); +static void ee25xx_writeenable(FAR struct ee25xx_dev_s *spi, int enable); +static void ee25xx_writepage(FAR struct ee25xx_dev_s *eedev, + uint32_t devaddr, FAR const char *data, + size_t len); +static int ee25xx_eraseall(FAR struct ee25xx_dev_s *eedev); +static int ee25xx_erasepage(FAR struct ee25xx_dev_s *eedev, + unsigned long index); +static int ee25xx_erasesector(FAR struct ee25xx_dev_s *eedev, + unsigned long index); +static uint8_t ee25xx_rdsr(FAR struct ee25xx_dev_s *eedev); +static void ee25xx_wrsr(FAR struct ee25xx_dev_s *eedev, uint8_t what); + +/* Initialization function */ + +static int ee25xx_populatedev(FAR struct ee25xx_dev_s **eedev, + FAR struct spi_dev_s *dev, uint16_t spi_devid, + enum eeprom_25xx_e devtype, int readonly); + +/* File operations */ + 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, +static ssize_t ee25xx_fread(FAR struct file *filep, FAR char *buffer, size_t buflen); -static int ee25xx_ioctl(FAR struct file *filep, int cmd, +static ssize_t ee25xx_fwrite(FAR struct file *filep, FAR const char *buffer, + size_t buflen); +static int ee25xx_fioctl(FAR struct file *filep, int cmd, + unsigned long arg); + +/* EEPROM operations (only needed for the MTD driver) */ + +#ifdef CONFIG_MTD_AT25EE +static ssize_t ee25xx_eepread(FAR struct eeprom_dev_s *eedev, off_t offset, + size_t nbytes, FAR uint8_t *buffer); +static ssize_t ee25xx_eepwrite(FAR struct eeprom_dev_s *eedev, off_t offset, + size_t nbytes, FAR const uint8_t *buffer); +static int ee25xx_eepioctl(FAR struct eeprom_dev_s *dev, int cmd, + unsigned long arg); +#endif + +/* Actual read, write and ioctl functions */ + +static ssize_t ee25xx_read(FAR struct ee25xx_dev_s *eedev, off_t offset, + size_t nbytes, FAR char *buffer); +static ssize_t ee25xx_write(FAR struct ee25xx_dev_s *eedev, off_t offset, + size_t nbytes, FAR const char *buffer); +static int ee25xx_ioctl(FAR struct ee25xx_dev_s *dev, int cmd, unsigned long arg); /**************************************************************************** @@ -209,7 +196,7 @@ static int ee25xx_ioctl(FAR struct file *filep, int cmd, /* 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 + * The user will use an enum'ed index from include/eeprom/eeprom.h */ static const struct ee25xx_geom_s g_ee25xx_devices[] = @@ -285,12 +272,12 @@ static const struct ee25xx_geom_s g_ee25xx_devices[] = static const struct file_operations g_ee25xx_fops = { - ee25xx_open, /* open */ - ee25xx_close, /* close */ - ee25xx_read, /* read */ - ee25xx_write, /* write */ - ee25xx_seek, /* seek */ - ee25xx_ioctl, /* ioctl */ + ee25xx_open, /* open */ + ee25xx_close, /* close */ + ee25xx_fread, /* read */ + ee25xx_fwrite, /* write */ + ee25xx_seek, /* seek */ + ee25xx_fioctl, /* ioctl */ }; /**************************************************************************** @@ -357,8 +344,15 @@ static inline void ee25xx_unlock(FAR struct ee25xx_dev_s *priv) /**************************************************************************** * 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. + * Description: + * Send command and address on SPI as one transaction to take advantage of + * possible faster DMA transfers. Sending byte per byte is FAR FAR slower. + * + * Input Parameters: + * spi - SPI bus + * cmd - Command to be sent + * addrlen - Length of the address field in bits + * addr - Address to be sent * ****************************************************************************/ @@ -397,11 +391,15 @@ static void ee25xx_sendcmd(FAR struct spi_dev_s *spi, uint8_t cmd, /**************************************************************************** * Name: ee25xx_waitwritecomplete * - * Description: loop until the write operation is done. + * Description: + * Loop until the write operation is done + * + * Input Parameters: + * eedev - Device structure * ****************************************************************************/ -static void ee25xx_waitwritecomplete(struct ee25xx_dev_s *priv) +static void ee25xx_waitwritecomplete(FAR struct ee25xx_dev_s *eedev) { uint8_t status; @@ -411,23 +409,23 @@ static void ee25xx_waitwritecomplete(struct ee25xx_dev_s *priv) { /* Select this FLASH part */ - ee25xx_lock(priv); - SPI_SELECT(priv->spi, SPIDEV_EEPROM(priv->devid), true); + ee25xx_lock(eedev); + SPI_SELECT(eedev->spi, SPIDEV_EEPROM(eedev->devid), true); /* Send "Read Status Register (RDSR)" command */ - SPI_SEND(priv->spi, EE25XX_CMD_RDSR); + SPI_SEND(eedev->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); + status = SPI_SEND(eedev->spi, EE25XX_DUMMY); /* Deselect the FLASH */ - SPI_SELECT(priv->spi, SPIDEV_EEPROM(priv->devid), false); - ee25xx_unlock(priv); + SPI_SELECT(eedev->spi, SPIDEV_EEPROM(eedev->devid), false); + ee25xx_unlock(eedev); /* Given that writing could take up to a few milliseconds, * the following short delay in the "busy" case will allow @@ -445,9 +443,14 @@ static void ee25xx_waitwritecomplete(struct ee25xx_dev_s *priv) /**************************************************************************** * 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. + * Description: + * Enable or disable write operations. + * This is required before any write, since a lot of operations + * automatically disable the write latch. + * + * Input Parameters: + * eedev - Device structure + * enable - Whether to enable or disable the write latch * ****************************************************************************/ @@ -465,7 +468,14 @@ static void ee25xx_writeenable(FAR struct ee25xx_dev_s *eedev, int enable) /**************************************************************************** * Name: ee25xx_writepage * - * Description: Write data to the EEPROM, NOT crossing page boundaries. + * Description: + * Write data to the EEPROM, NOT crossing page boundaries. + * + * Input Parameters: + * eedev - Device structure + * devaddr - Address where to start writing + * data - Data to be written + * len - Length of the data to be written in bytes * ****************************************************************************/ @@ -506,7 +516,7 @@ static int ee25xx_eraseall(FAR struct ee25xx_dev_s *eedev) if (eedev->readonly) { - return -EACCES; + return -EROFS; } /* Devices with different page and sector sizes support a dedicated command @@ -599,12 +609,12 @@ static int ee25xx_erasepage(FAR struct ee25xx_dev_s *eedev, if (eedev->readonly) { - return -EACCES; + return -EROFS; } if (index >= (eedev->size / eedev->pgsize)) { - return -EFBIG; + return -EFAULT; } /* Devices with different page and sector sizes support a dedicated command @@ -692,6 +702,11 @@ static int ee25xx_erasesector(FAR struct ee25xx_dev_s *eedev, DEBUGASSERT(eedev); DEBUGASSERT(eedev->secsize > 0); + if (eedev->readonly) + { + return -EROFS; + } + if (eedev->pgsize == eedev->secsize) { return ee25xx_erasepage(eedev, index); @@ -704,7 +719,7 @@ static int ee25xx_erasesector(FAR struct ee25xx_dev_s *eedev, if (index >= (eedev->size / eedev->secsize)) { - return -EFBIG; + return -EFAULT; } ret = nxmutex_lock(&eedev->lock); @@ -734,7 +749,14 @@ static int ee25xx_erasesector(FAR struct ee25xx_dev_s *eedev, /**************************************************************************** * Name: ee25xx_rdsr * - * Description: Read status register. + * Description: + * Read status register + * + * Input Parameters: + * eedev - Device structure + * + * Returned Value: + * Content of the status register * ****************************************************************************/ @@ -757,7 +779,12 @@ static uint8_t ee25xx_rdsr(FAR struct ee25xx_dev_s *eedev) /**************************************************************************** * Name: ee25xx_wrsr * - * Description: Write status register. + * Description: + * Write status register + * + * Input Parameters: + * eedev - Device structure + * what - Value to be written to the status register * ****************************************************************************/ @@ -782,15 +809,22 @@ static void ee25xx_wrsr(FAR struct ee25xx_dev_s *eedev, uint8_t what) /**************************************************************************** * Name: ee25xx_open * - * Description: Open the block device + * Description: + * Open the character device + * + * Input Parameters + * filep - File structure + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. * ****************************************************************************/ static int ee25xx_open(FAR struct file *filep) { - FAR struct inode *inode = filep->f_inode; + FAR struct inode * inode = filep->f_inode; FAR struct ee25xx_dev_s *eedev; - int ret = OK; + int ret; DEBUGASSERT(inode->i_private); eedev = inode->i_private; @@ -819,15 +853,22 @@ static int ee25xx_open(FAR struct file *filep) /**************************************************************************** * Name: ee25xx_close * - * Description: Close the block device + * Description: + * Close the character device + * + * Input Parameters + * filep - File structure + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. * ****************************************************************************/ static int ee25xx_close(FAR struct file *filep) { - FAR struct inode *inode = filep->f_inode; + FAR struct inode * inode = filep->f_inode; FAR struct ee25xx_dev_s *eedev; - int ret = OK; + int ret; DEBUGASSERT(inode->i_private); eedev = inode->i_private; @@ -858,15 +899,25 @@ static int ee25xx_close(FAR struct file *filep) /**************************************************************************** * Name: ee25xx_seek * - * Remark: Copied from bchlib + * Description: + * Reposition the offset of the given file structure + * + * Input Parameters + * filep - File structure + * offset - Offset with respect to whence where to reposition + * whence - Reference point for the offset + * + * Returned Value: + * Offset location in bytes from the beginning of the EEPROM on success. + * Negated errno value on failure. * ****************************************************************************/ 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; + off_t newpos; + int ret; FAR struct inode *inode = filep->f_inode; DEBUGASSERT(inode->i_private); @@ -931,88 +982,224 @@ static off_t ee25xx_seek(FAR struct file *filep, off_t offset, int whence) } /**************************************************************************** - * Name: ee25xx_read + * Name: ee25xx_fread + * + * Description: + * Read bytes from the EEPROM at the file structure offset + * The offset is updated on success + * + * Input Parameters: + * filep - File structure + * buffer - Buffer that will be populated with the read data + * len - Number of bytes to read + * + * Returned Value: + * Number of read bytes on success; a negated error code on failure + * ****************************************************************************/ -static ssize_t ee25xx_read(FAR struct file *filep, FAR char *buffer, +static ssize_t ee25xx_fread(FAR struct file *filep, FAR char *buffer, size_t len) { - FAR struct ee25xx_dev_s *eedev; - FAR struct inode *inode = filep->f_inode; - int ret; + ssize_t ret; - DEBUGASSERT(inode->i_private); - eedev = inode->i_private; + DEBUGASSERT(filep->f_inode); + DEBUGASSERT(filep->f_inode->i_private); - ret = nxmutex_lock(&eedev->lock); + ret = ee25xx_read(filep->f_inode->i_private, filep->f_pos, len, buffer); + + if (ret > 0) + { + /* Update the file position */ + + filep->f_pos += ret; + } + + return ret; +} + +/**************************************************************************** + * Name: ee25xx_eepread + * + * Description: + * Read bytes from the EEPROM at the given offset + * + * Input Parameters: + * dev - Generic EEPROM device structure + * offset - Absolute offset where to start reading data + * nbytes - Number of bytes to read + * buffer - Buffer that will be populated with the read data + * + * Returned Value: + * Number of read bytes on success; a negated error code on failure + * + ****************************************************************************/ + +#ifdef CONFIG_MTD_AT25EE +static ssize_t ee25xx_eepread(FAR struct eeprom_dev_s *dev, off_t offset, + size_t nbytes, FAR uint8_t *buffer) +{ + return ee25xx_read((FAR struct ee25xx_dev_s *)dev, offset, nbytes, + (FAR char *)buffer); +} +#endif + +/**************************************************************************** + * Name: ee25xx_read + * + * Description: + * Read bytes from the EEPROM at the given offset + * + * Input Parameters: + * eedev - Device structure + * offset - Absolute offset where to start reading data + * nbytes - Number of bytes to read + * buffer - Buffer that will be populated with the read data + * + * Returned Value: + * Number of read bytes on success; a negated error code on failure + * + ****************************************************************************/ + +static ssize_t ee25xx_read(FAR struct ee25xx_dev_s *eedev, off_t offset, + size_t nbytes, FAR char *buffer) +{ + if (offset > eedev->size) + { + return 0; + } + + const int ret = nxmutex_lock(&eedev->lock); if (ret < 0) { return ret; } - /* trim len if read would go beyond end of device */ + /* trim nbytes if read would go beyond end of device */ - if ((filep->f_pos + len) > eedev->size) + if ((offset + nbytes) > eedev->size) { - len = eedev->size - filep->f_pos; + nbytes = eedev->size - offset; } ee25xx_lock(eedev); SPI_SELECT(eedev->spi, SPIDEV_EEPROM(eedev->devid), true); - /* STM32F4Disco: There is a 25 us delay here */ + ee25xx_sendcmd(eedev->spi, EE25XX_CMD_READ, eedev->addrlen, offset); - 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_RECVBLOCK(eedev->spi, buffer, nbytes); SPI_SELECT(eedev->spi, SPIDEV_EEPROM(eedev->devid), false); ee25xx_unlock(eedev); - /* Update the file position */ - - filep->f_pos += len; nxmutex_unlock(&eedev->lock); - return len; + return nbytes; } /**************************************************************************** - * Name: ee25xx_write + * Name: ee25xx_fwrite + * + * Description: + * Write bytes to the EEPROM at the file structure offset + * The offset is updated on success + * + * Input Parameters: + * filep - File structure + * buffer - Buffer containing the data to be written + * len - Number of bytes to write + * + * Returned Value: + * Number of written bytes on success; a negated error code on failure + * ****************************************************************************/ -static ssize_t ee25xx_write(FAR struct file *filep, FAR const char *buffer, - size_t len) +static ssize_t ee25xx_fwrite(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; + ssize_t ret; - DEBUGASSERT(inode->i_private); - eedev = inode->i_private; + DEBUGASSERT(filep->f_inode); + DEBUGASSERT(filep->f_inode->i_private); + + ret = ee25xx_write(filep->f_inode->i_private, filep->f_pos, len, buffer); + + if (ret > 0) + { + /* Update the file position */ + + filep->f_pos += ret; + } + + return ret; +} + +/**************************************************************************** + * Name: ee25xx_eepwrite + * + * Description: + * Write bytes to the EEPROM at the given offset + * + * Input Parameters: + * dev - Generic EEPROM device structure + * offset - Absolute offset where to start writing data + * nbytes - Number of bytes to write + * buffer - Data to be written + * + * Returned Value: + * Number of written bytes on success; a negated error code on failure + * + ****************************************************************************/ + +#ifdef CONFIG_MTD_AT25EE +static ssize_t ee25xx_eepwrite(FAR struct eeprom_dev_s *dev, off_t offset, + size_t nbytes, FAR const uint8_t *buffer) +{ + return ee25xx_write((FAR struct ee25xx_dev_s *)dev, offset, nbytes, + (FAR const char *)buffer); +} +#endif + +/**************************************************************************** + * Name: ee25xx_write + * + * Description: + * Write bytes to the EEPROM at the given offset + * + * Input Parameters: + * dev - Device structure + * offset - Absolute offset where to start writing data + * nbytes - Number of bytes to write + * buffer - Data to be written + * + * Returned Value: + * Number of written bytes on success; a negated error code on failure + * + ****************************************************************************/ + +static ssize_t ee25xx_write(FAR struct ee25xx_dev_s *eedev, off_t offset, + size_t nbytes, FAR const char *buffer) +{ + size_t cnt; + int pageoff; + int ret; if (eedev->readonly) { - return ret; + return -EROFS; } /* Forbid writes past the end of the device */ - if (filep->f_pos >= eedev->size) + if (offset >= eedev->size) { - return -EFBIG; + return 0; } - /* Clamp len to avoid crossing the end of the memory */ + /* Clamp nbytes to avoid crossing the end of the memory */ - if ((len + filep->f_pos) > eedev->size) + if ((nbytes + offset) > eedev->size) { - len = eedev->size - filep->f_pos; + nbytes = eedev->size - offset; } ret = nxmutex_lock(&eedev->lock); @@ -1025,7 +1212,7 @@ static ssize_t ee25xx_write(FAR struct file *filep, FAR const char *buffer, * The user should verify the write by rereading memory. */ - ret = len; /* save number of bytes written */ + ret = nbytes; /* save number of bytes written */ /* Writes can't happen in a row like the read does. * The EEPROM is made of pages, and write sequences @@ -1037,39 +1224,39 @@ static ssize_t ee25xx_write(FAR struct file *filep, FAR const char *buffer, /* First, write some page-unaligned data */ - pageoff = filep->f_pos & (eedev->pgsize - 1); + pageoff = offset & (eedev->pgsize - 1); cnt = eedev->pgsize - pageoff; - if (cnt > len) + if (cnt > nbytes) { - cnt = len; + cnt = nbytes; } if (pageoff > 0) { ee25xx_writeenable(eedev, true); - ee25xx_writepage(eedev, filep->f_pos, buffer, cnt); + ee25xx_writepage(eedev, offset, buffer, cnt); ee25xx_waitwritecomplete(eedev); - len -= cnt; - buffer += cnt; - filep->f_pos += cnt; + nbytes -= cnt; + buffer += cnt; + offset += cnt; } /* Then, write remaining bytes at page-aligned addresses */ - while (len > 0) + while (nbytes > 0) { - cnt = len; + cnt = nbytes; if (cnt > eedev->pgsize) { cnt = eedev->pgsize; } ee25xx_writeenable(eedev, true); - ee25xx_writepage(eedev, filep->f_pos, buffer, cnt); + ee25xx_writepage(eedev, offset, buffer, cnt); ee25xx_waitwritecomplete(eedev); - len -= cnt; - buffer += cnt; - filep->f_pos += cnt; + nbytes -= cnt; + buffer += cnt; + offset += cnt; } nxmutex_unlock(&eedev->lock); @@ -1077,21 +1264,72 @@ static ssize_t ee25xx_write(FAR struct file *filep, FAR const char *buffer, } /**************************************************************************** - * Name: ee25xx_ioctl + * Name: ee25xx_fioctl * - * Description: TODO: Read device ID. - * This is completely optional and only applies to bigger devices. + * Description: + * Ioctl control for the device, the same as ioctl(). + * + * Input Parameters: + * filep - File structure + * cmd - Ioctl command. + * arg - Ioctl argument. + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. * ****************************************************************************/ -static int ee25xx_ioctl(FAR struct file *filep, int cmd, unsigned long arg) +static int ee25xx_fioctl(FAR struct file *filep, int cmd, unsigned long arg) { - FAR struct ee25xx_dev_s *eedev; - FAR struct inode *inode = filep->f_inode; - int ret = -EINVAL; + DEBUGASSERT(filep->f_inode); + DEBUGASSERT(filep->f_inode->i_private); + return ee25xx_ioctl(filep->f_inode->i_private, cmd, arg); +} - DEBUGASSERT(inode->i_private); - eedev = inode->i_private; +/**************************************************************************** + * Name: ee25xx_eepioctl + * + * Description: + * Ioctl control for the device, the same as ioctl(). + * + * Input Parameters: + * filep - Generic EEPROM device structure + * cmd - Ioctl command. + * arg - Ioctl argument. + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ****************************************************************************/ + +#ifdef CONFIG_MTD_AT25EE +static int ee25xx_eepioctl(FAR struct eeprom_dev_s *dev, int cmd, + unsigned long arg) +{ + return ee25xx_ioctl((FAR struct ee25xx_dev_s *)dev, cmd, arg); +} +#endif + +/**************************************************************************** + * Name: ee25xx_fioctl + * + * Description: + * Ioctl control for the device, the same as ioctl(). + * + * Input Parameters: + * filep - Device structure + * cmd - Ioctl command. + * arg - Ioctl argument. + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ****************************************************************************/ + +static int ee25xx_ioctl(FAR struct ee25xx_dev_s *eedev, int cmd, + unsigned long arg) +{ + int ret = -EINVAL; switch (cmd) { @@ -1172,13 +1410,70 @@ static int ee25xx_ioctl(FAR struct file *filep, int cmd, unsigned long arg) * Public Functions ****************************************************************************/ +/**************************************************************************** + * Name: ee25xx_populatedev + * + * Description: + * Populate a device structure + * + * Input Parameters: + * eedev - Pointer to the device structure to be populated + * dev - SPI device instance + * spi_devid - SPI device ID to manage CS lines in board + * devtype - 25xx device type, the geometry is derived from it + * readonly - Sets driver to be readonly + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ****************************************************************************/ + +static int ee25xx_populatedev(FAR struct ee25xx_dev_s **eedev, + FAR struct spi_dev_s *dev, uint16_t spi_devid, + enum eeprom_25xx_e devtype, int readonly) +{ + /* Check the device type early */ + + const int devtype_idx = (int)devtype; + if (devtype_idx >= + (sizeof(g_ee25xx_devices) / sizeof(g_ee25xx_devices[0]))) + { + return -EINVAL; + } + + *eedev = kmm_zalloc(sizeof(struct ee25xx_dev_s)); + + if (!(*eedev)) + { + return -ENOMEM; + } + + nxmutex_init(&(*eedev)->lock); + + (*eedev)->spi = dev; + (*eedev)->devid = spi_devid; + (*eedev)->freq = CONFIG_EE25XX_FREQUENCY; + (*eedev)->readonly = !!readonly; + + (*eedev)->size = 128 << g_ee25xx_devices[devtype_idx].bytes; + (*eedev)->pgsize = 8 << g_ee25xx_devices[devtype_idx].pagesize; + (*eedev)->addrlen = g_ee25xx_devices[devtype_idx].addrlen << 3; + if ((g_ee25xx_devices[devtype_idx].flags & 1)) + { + ++(*eedev)->addrlen; + } + + (*eedev)->secsize = + (*eedev)->pgsize << g_ee25xx_devices[devtype_idx].secsize; + + return OK; +} + /**************************************************************************** * Name: ee25xx_initialize * * Description: - * Bind an 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). + * Bind an EEPROM driver to an SPI bus * * Parameters: * dev - Pointer to the SPI device instance @@ -1188,50 +1483,90 @@ static int ee25xx_ioctl(FAR struct file *filep, int cmd, unsigned long arg) * readonly - Sets driver to be readonly * * Returned Values: - * OK on success; A negated errno value is returned on any failure. + * Zero (OK) on success; a negated errno value on failure. * ****************************************************************************/ int ee25xx_initialize(FAR struct spi_dev_s *dev, uint16_t spi_devid, - FAR char *devname, int devtype, int readonly) + FAR char *devname, enum eeprom_25xx_e devtype, + int readonly) +{ + FAR struct ee25xx_dev_s *eedev; + int ret; + + ret = ee25xx_populatedev(&eedev, dev, spi_devid, devtype, readonly); + + if (ret != OK) + { + return ret; + } + + finfo("EEPROM device %s, %"PRIu32" bytes, %u per page, addrlen %u, " + "readonly %d\n", devname, eedev->size, eedev->pgsize, + eedev->addrlen, eedev->readonly); + + return register_driver(devname, &g_ee25xx_fops, 0666, eedev); +} + +/**************************************************************************** + * Name: ee25xx_initialize_as_eeprom_dev + * + * Description: + * Create an initialized EEPROM device instance for an xx25xx SPI EEPROM. + * The device is not registered in the file system, but created as an + * instance that can be bound to other functions. + * + * Parameters: + * dev - Pointer to the SPI device instance + * spi_devid - SPI device ID to manage CS lines in board + * devtype - 25xx device type, the geometry is derived from it + * readonly - Sets driver to be readonly + * + * Returned Values: + * Initialised device structure (success) or NULL (fail) + * + ****************************************************************************/ + +#ifdef CONFIG_MTD_AT25EE + +FAR struct eeprom_dev_s *ee25xx_initialize_as_eeprom_dev( + FAR struct spi_dev_s *dev, uint16_t spi_devid, + enum eeprom_25xx_e 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]))) + if (ee25xx_populatedev(&eedev, dev, spi_devid, devtype, readonly) != OK) { - return -EINVAL; + return NULL; } - eedev = kmm_zalloc(sizeof(struct ee25xx_dev_s)); + /* Populate the eeprom_dev_s struct */ - if (!eedev) - { - return -ENOMEM; - } + eedev->eepdev.read = ee25xx_eepread; + eedev->eepdev.write = ee25xx_eepwrite; + eedev->eepdev.ioctl = ee25xx_eepioctl; - nxmutex_init(&eedev->lock); - - eedev->spi = dev; - eedev->devid = spi_devid; - eedev->freq = CONFIG_EE25XX_FREQUENCY; - eedev->size = 128 << g_ee25xx_devices[devtype].bytes; - eedev->pgsize = 8 << g_ee25xx_devices[devtype].pagesize; - eedev->secsize = eedev->pgsize << g_ee25xx_devices[devtype].secsize; - eedev->addrlen = g_ee25xx_devices[devtype].addrlen << 3; - if ((g_ee25xx_devices[devtype].flags & 1)) - { - eedev->addrlen = 9; - } - - eedev->readonly = !!readonly; - - finfo("EEPROM device %s, %"PRIu32" bytes, " - "%u per page, addrlen %u, readonly %d\n", - devname, eedev->size, eedev->pgsize, eedev->addrlen, eedev->readonly); - - return register_driver_with_size(devname, &g_ee25xx_fops, 0666, eedev, - eedev->size); + return (FAR struct eeprom_dev_s *)eedev; } + +#endif + +/**************************************************************************** + * Name: ee25xx_teardown + * + * Description: + * Teardown a previously created ee25xx device. + * + * Input Parameters: + * dev - Pointer to the driver instance. + * + ****************************************************************************/ + +#ifdef CONFIG_MTD_AT25EE + +void ee25xx_teardown(FAR struct eeprom_dev_s *dev) +{ + kmm_free(dev); +} + +#endif diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig index 0a70ccbd313..2cb211274f3 100644 --- a/drivers/mtd/Kconfig +++ b/drivers/mtd/Kconfig @@ -516,12 +516,17 @@ config MTD_AT25EE default n select SPI select MTD_BYTE_WRITE + select EEPROM + select SPI_EE_25XX ---help--- Build support for SPI-based AT25xx type EEPROMs. MTD on EEPROM can perform poorly, so it is possible only usable if the EEPROM has a clock speed 10MHz or higher. EEPROMs that use the same commands as the 25AA160 should work OK. + This is a convenience wrapper around the eeprom/spi_xx25xx driver. + It can be configured from the "EEPROM support" menu. + if MTD_AT25EE choice @@ -564,46 +569,6 @@ config AT25EE_ENABLE_BLOCK_ERASE file system requires this) block erase (i.e. writing each byte to 0xff) can be enabled here. -config AT25EE_SPIMODE - int "AT25EE SPI Mode" - default 0 - -config AT25EE_SPIFREQUENCY - int "AT25EE SPI Frequency" - default 10000000 - -config AT25EE_START_DELAY - int "AT25EE startdelay" - ---help--- - The delay between CS active and first CLK. In ns. - depends on SPI_DELAY_CONTROL - range 0 1000000 - default 5000 - -config AT25EE_STOP_DELAY - int "AT25EE stopdelay" - ---help--- - The delay between last CLK and CS inactive. In ns. - depends on SPI_DELAY_CONTROL - range 0 1000000 - default 5000 - -config AT25EE_CS_DELAY - int "AT25EE csdelay" - ---help--- - The delay between CS inactive and CS active again. In ns. - depends on SPI_DELAY_CONTROL - range 0 1000000 - default 5000 - -config AT25EE_IFDELAY - int "AT25EE ifdelay" - ---help--- - The delay between frames. In ns. - depends on SPI_DELAY_CONTROL - range 0 1000000 - default 5000 - endif # MTD_AT25EE config MTD_AT25 diff --git a/drivers/mtd/at25ee.c b/drivers/mtd/at25ee.c index a5d17f58caf..c89d05bd2ec 100644 --- a/drivers/mtd/at25ee.c +++ b/drivers/mtd/at25ee.c @@ -25,66 +25,28 @@ ****************************************************************************/ #include -#include -#include -#include -#include -#include -#include -#include + #include -#include #include +#include +#include +#include +#include +#include +#include +#include +#include -#include -#include +#include +#include #include -#include +#include #include -#ifdef CONFIG_MTD_AT25EE - /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ -#ifndef CONFIG_AT25EE_SPIMODE -# define CONFIG_AT25EE_SPIMODE 0 -#endif - -/* EEPROM commands - * High bit of low nibble used for A8 in 25xx040/at25040 products - */ - -#define AT25EE_CMD_WRSR 0x01 -#define AT25EE_CMD_WRITE 0x02 -#define AT25EE_CMD_READ 0x03 -#define AT25EE_CMD_WRDIS 0x04 -#define AT25EE_CMD_RDSR 0x05 -#define AT25EE_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) - * - * Identification page access for ST devices - * RDID/RDLS 0x83 Read identification page / Read ID page lock status - * WRID/LID 0x82 Write identification page / Lock ID page - */ - -/* SR bits definitions */ - -#define AT25EE_SR_WIP 0x01 /* Write in Progress */ -#define AT25EE_SR_WEL 0x02 /* Write Enable Latch */ -#define AT25EE_SR_BP0 0x04 /* First Block Protect bit */ -#define AT25EE_SR_BP1 0x08 /* Second Block Protect bit */ -#define AT25EE_SR_WPEN 0x80 /* Write Protect Enable */ - -#define AT25EE_DUMMY 0xFF - /* For applications where a file system is used on the AT25EE, the tiny page * sizes will result in very inefficient EEPROM usage. In such cases, it is * better if blocks are comprised of "clusters" of pages so that the file @@ -96,16 +58,6 @@ * Private Types ****************************************************************************/ -/* Device geometry description, compact form (2 bytes per entry) */ - -struct at25ee_geom_s -{ - uint8_t bytes : 4; /* Power of 2 of 128 bytes (0:128 1:256 2:512 etc) */ - uint8_t pagesize : 4; /* Power of 2 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; /* Addr. management for 25xx040, 1=A8 in inst */ -}; - /* This type represents the state of the MTD device. The struct mtd_dev_s * must appear at the beginning of the definition so that you can freely * cast between pointers to struct mtd_dev_s and struct at25ee_dev_s. @@ -113,25 +65,23 @@ struct at25ee_geom_s struct at25ee_dev_s { - struct mtd_dev_s mtd; /* MTD interface */ - struct spi_dev_s *spi; /* SPI device where the EEPROM is attached */ - uint16_t devid; /* SPI device ID to manage CS lines in board */ - uint32_t size; /* in bytes, expanded from geometry */ - uint16_t pgsize; /* write block size, in bytes, expanded from - * geometry - */ - uint16_t npages; /* numpages, derived from geometry */ - uint16_t addrlen; /* number of BITS in data addresses */ - uint16_t blocksize; /* Block sized to report */ - mutex_t lock; /* file access serialization */ - uint8_t readonly; /* Flags */ + struct mtd_dev_s mtd; /* MTD interface */ + FAR struct eeprom_dev_s *eepdev; /* eeprom/spi_xx25xx device */ + + size_t size; /* Size in bytes, expanded from geometry */ + blksize_t pgsize; /* Write block size (in bytes), from geometry */ + blksize_t sectsize; /* Sector size (in bytes), expanded from geometry */ + uint16_t blocksize; /* Block size to report */ }; /**************************************************************************** * Private Function Prototypes ****************************************************************************/ -static void at25ee_lock(FAR struct spi_dev_s *dev); +#ifdef CONFIG_AT25EE_ENABLE_BLOCK_ERASE +static int at25ee_erasepages(FAR struct at25ee_dev_s *priv, off_t startpage, + blkcnt_t npages); +#endif /* MTD driver methods */ @@ -145,362 +95,64 @@ static ssize_t at25ee_bwrite(FAR struct mtd_dev_s *dev, off_t startblock, size_t nblocks, FAR const uint8_t *buf); static ssize_t at25ee_read(FAR struct mtd_dev_s *dev, off_t offset, size_t nbytes, FAR uint8_t *buf); +#ifdef CONFIG_MTD_BYTE_WRITE static ssize_t at25ee_write(FAR struct mtd_dev_s *dev, off_t offset, size_t nbytes, FAR const uint8_t *buf); +#endif static int at25ee_ioctl(FAR struct mtd_dev_s *dev, int cmd, unsigned long arg); -static void at25ee_writepage(FAR struct at25ee_dev_s *priv, uint32_t devaddr, - FAR const uint8_t *data, size_t len); -static void at25ee_writeenable(FAR struct at25ee_dev_s *priv, int enable); -static void at25ee_waitwritecomplete(struct at25ee_dev_s *priv); -static void at25ee_sendcmd(FAR struct spi_dev_s *spi, uint8_t cmd, - uint8_t addrlen, uint32_t addr); -static inline void at25ee_unlock(FAR struct spi_dev_s *dev); -static void at25ee_lock(FAR struct spi_dev_s *dev); /**************************************************************************** * Private Data ****************************************************************************/ -/* Supported device geometries. - * One geometry can fit more than one device. - * The user will use an enum'd index from include/eeprom/spi_xx25xx.h - */ - -static const struct at25ee_geom_s g_at25ee_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 */ - { - 9, 4, 2, 0 - }, /* AT25512 65536 128 2 */ - { - 10, 5, 3, 0 - }, /* AT25M01 131072 256 3 */ - - /* STM devices */ - - { - 11, 5, 3, 0 - }, /* M95M02 262144 256 3 */ -}; - /**************************************************************************** * Private Functions ****************************************************************************/ /**************************************************************************** - * Name: at25ee_lock + * Name: at25ee_erasepages * * 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. + * Erase a number of pages of data * - * 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. - * - * 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. + * Remark; + * The startpage and npages parameter are assumed to be valid. If they are + * not, the underlying EEPROM character driver will return an error. * * Input Parameters: - * dev - pointer to device structure + * priv - a reference to the device structure + * startpage - start page of the erase + * npages - number of pages to erase + * * Returned Value: - * none + * Success (OK), or fail (negated error code) * ****************************************************************************/ -static void at25ee_lock(FAR struct spi_dev_s *dev) +#ifdef CONFIG_AT25EE_ENABLE_BLOCK_ERASE +static int at25ee_erasepages(FAR struct at25ee_dev_s *priv, off_t startpage, + blkcnt_t npages) { - SPI_LOCK(dev, true); - SPI_SETMODE(dev, CONFIG_AT25EE_SPIMODE); - SPI_SETBITS(dev, 8); - SPI_HWFEATURES(dev, 0); - SPI_SETFREQUENCY(dev, CONFIG_AT25EE_SPIFREQUENCY); -#ifdef CONFIG_SPI_DELAY_CONTROL - SPI_SETDELAY(dev, CONFIG_AT25EE_START_DELAY, CONFIG_AT25EE_STOP_DELAY, - CONFIG_AT25EE_CS_DELAY, CONFIG_AT25EE_IFDELAY); -#endif -} - -/**************************************************************************** - * Name: at25ee_unlock - * - * Description: - * Unlocks the SPI bus - * - * Input Parameters: - * dev - pointer to device structure - * Returned Value: - * none - * - ****************************************************************************/ - -static inline void at25ee_unlock(FAR struct spi_dev_s *dev) -{ - SPI_LOCK(dev, false); -} - -/**************************************************************************** - * Name: at25ee_sendcmd - * - * Description: - * Send command and address as one transaction to take advantage - * of possible faster DMA transfers. - * Sending byte per byte is MUCH slower. - * - * Input Parameters: - * spi - a reference to the spi device - * cmd - SPI command to send - * addrlen - length of the address, in bits - * addr - address to write to - * - * Returned Value: - * none - * - ****************************************************************************/ - -static void at25ee_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: at25ee_waitwritecomplete - * - * Description: - * loop until the write operation is done. - * - * Input Parameters: - * priv - a reference to the device structure - * - * Returned Value: - * none - * - ****************************************************************************/ - -static void at25ee_waitwritecomplete(struct at25ee_dev_s *priv) -{ - uint8_t status; - - /* Loop as long as the memory is busy with a write cycle */ - - do - { - /* Select this FLASH part */ - - at25ee_lock(priv->spi); - SPI_SELECT(priv->spi, SPIDEV_EEPROM(priv->devid), true); - - /* Send "Read Status Register (RDSR)" command */ - - SPI_SEND(priv->spi, AT25EE_CMD_RDSR); - - /* Send a dummy byte to generate the clock needed to shift out the - * status - */ - - status = SPI_SEND(priv->spi, AT25EE_DUMMY); - - /* Deselect the FLASH */ - - SPI_SELECT(priv->spi, SPIDEV_EEPROM(priv->devid), false); - at25ee_unlock(priv->spi); - - /* 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 & AT25EE_SR_WIP) != 0) - { - nxsched_usleep(1000); - } - } - while ((status & AT25EE_SR_WIP) != 0); -} - -/**************************************************************************** - * Name: at25ee_writeenable - * - * Description: - * Enable or disable write operations. - * This is required before any write, since a lot of operations - * automatically disable the write latch. - * - * Input Parameters: - * priv - a reference to the device structure - * enable - enable (true) or disable(false) write operations - * - * Returned Value: - * none - * - ****************************************************************************/ - -static void at25ee_writeenable(FAR struct at25ee_dev_s *priv, int enable) -{ - at25ee_lock(priv->spi); - SPI_SELECT(priv->spi, SPIDEV_EEPROM(priv->devid), true); - - SPI_SEND(priv->spi, enable ? AT25EE_CMD_WREN : AT25EE_CMD_WRDIS); - - SPI_SELECT(priv->spi, SPIDEV_EEPROM(priv->devid), false); - at25ee_unlock(priv->spi); -} - -/**************************************************************************** - * Name: at25ee_writepage - * - * Description: - * Write data to the EEPROM, NOT crossing page boundaries. - * - * Input Parameters: - * priv - a reference to the device structure - * devaddr - the address to start the write - * data - pointer to data buffer to write - * len - length of the data to write - * - * Returned Value: - * none - * - ****************************************************************************/ - -static void at25ee_writepage(FAR struct at25ee_dev_s *priv, uint32_t devaddr, - FAR const uint8_t *data, size_t len) -{ - at25ee_lock(priv->spi); - SPI_SELECT(priv->spi, SPIDEV_EEPROM(priv->devid), true); - - at25ee_sendcmd(priv->spi, AT25EE_CMD_WRITE, priv->addrlen, devaddr); - SPI_SNDBLOCK(priv->spi, data, len); - - SPI_SELECT(priv->spi, SPIDEV_EEPROM(priv->devid), false); - at25ee_unlock(priv->spi); -} - -/**************************************************************************** - * Name: at25ee_eraseall - * - * Description: - * Erase all data in the device - * - * Input Parameters: - * priv - a reference to the device structure - * devaddr - the address to start the write - * data - pointer to data buffer to write - * len - length of the data to write - * - * Returned Value: - * none - * - ****************************************************************************/ - -static int at25ee_eraseall(FAR struct at25ee_dev_s *priv) -{ - uint8_t *buf; - int startblock = 0; + blkcnt_t i; + int ret = OK; DEBUGASSERT(priv); - buf = kmm_malloc(priv->pgsize); - if (!buf) + for (i = 0; i < npages; ++i) { - ferr("ERROR: Failed to alloc memory for at25ee eraseall!\n"); - return -ENOMEM; + const unsigned long page = (unsigned long)(startpage + i); + ret = priv->eepdev->ioctl(priv->eepdev, EEPIOC_PAGEERASE, page); + if (ret < 0) + { + ferr("ERROR while erasing page #%ld (ret = %d)\n", page, ret); + break; + } } - memset(buf, 0xff, priv->pgsize); - - for (startblock = 0; startblock < priv->npages; startblock++) - { - uint16_t offset = startblock * priv->pgsize; - at25ee_write(&priv->mtd, offset, priv->pgsize, buf); - } - - kmm_free(buf); - return OK; + return ret; } +#endif /* CONFIG_AT25EE_ENABLE_BLOCK_ERASE */ /**************************************************************************** * Name: at25ee_erase @@ -514,70 +166,112 @@ static int at25ee_eraseall(FAR struct at25ee_dev_s *priv) * nblocks - nblocks to erase * * Returned Value: - * Success (OK) or fail (negated error code) + * Number of blocks erased, or fail (negated error code) + * ****************************************************************************/ -static int at25ee_erase(FAR struct mtd_dev_s *dev, - off_t startblock, - size_t nblocks) +static int at25ee_erase(FAR struct mtd_dev_s *dev, off_t startblock, + size_t nblocks) { #ifndef CONFIG_AT25EE_ENABLE_BLOCK_ERASE return (int)nblocks; #else - FAR struct at25ee_dev_s *priv = (FAR struct at25ee_dev_s *)dev; - uint8_t *buf; - size_t blocksleft; + FAR struct at25ee_dev_s *priv = (FAR struct at25ee_dev_s *)dev; + blkcnt_t npages = (blkcnt_t)nblocks; + off_t startpg = startblock; DEBUGASSERT(dev); + if ((startblock == 0) && ((nblocks * priv->blocksize) >= priv->size)) + { + return priv->eepdev->ioctl(priv->eepdev, EEPIOC_CHIPERASE, 0); + } + if (priv->blocksize > priv->pgsize) { - startblock *= (priv->blocksize / priv->pgsize); - nblocks *= (priv->blocksize / priv->pgsize); + startpg *= (priv->blocksize / priv->pgsize); + npages *= (priv->blocksize / priv->pgsize); } - blocksleft = nblocks; - - if (startblock >= priv->npages) + if ((startpg * priv->pgsize) >= priv->size) { - return -E2BIG; + return -EFAULT; } - buf = kmm_malloc(priv->pgsize); - if (!buf) + if (((startpg + npages) * priv->pgsize) > priv->size) { - ferr("ERROR: Failed to alloc memory for at25ee erase!\n"); - return -ENOMEM; + npages = (blkcnt_t)(priv->size / (size_t)priv->pgsize) - startpg; } - memset(buf, 0xff, priv->pgsize); + finfo("startpg: %08lx npages: %d\n", (long)startpg, (int)npages); - if (startblock + nblocks > priv->npages) + if (priv->pgsize != priv->sectsize) { - nblocks = priv->npages - startblock; + const blkcnt_t pgpersect = priv->sectsize / priv->pgsize; + const off_t startsect = (startpg + (pgpersect - 1)) / pgpersect; + const off_t endsect = (startpg + npages) / pgpersect; + int i; + int ret; + + /* Erase the pages not aligned to a sector */ + + if ((startpg % pgpersect) != 0) + { + const blkcnt_t pgcnt = pgpersect - (startpg % pgpersect); + ret = at25ee_erasepages(priv, startpg, pgcnt); + if (ret < 0) + { + return ret; + } + } + + /* Erase by sectors */ + + for (i = 0; i < (endsect - startsect); ++i) + { + const unsigned long sectorno = (unsigned long)(startsect + i); + ret = priv->eepdev->ioctl(priv->eepdev, EEPIOC_SECTORERASE, + sectorno); + if (ret < 0) + { + ferr("ERROR while erasing sector #%ld (ret = %d)\n", + sectorno, ret); + return ret; + } + } + + /* Erase the remaining pages */ + + if ((endsect * priv->sectsize) < ((startpg + npages) * priv->pgsize)) + { + const off_t start = endsect * pgpersect; + const blkcnt_t pgcnt = npages - (start - startpg); + ret = at25ee_erasepages(priv, endsect * pgpersect, pgcnt); + if (ret < 0) + { + return ret; + } + } } - finfo("startblock: %08lx nblocks: %d\n", (long)startblock, (int)nblocks); - - while (blocksleft-- > 0) + else { - off_t offset = startblock * priv->pgsize; - - finfo("startblock: %08lx offset: %d\n", (long)startblock, (int)offset); - at25ee_write(dev, offset, priv->pgsize, buf); - startblock++; + const int ret = at25ee_erasepages(priv, startpg, npages); + if (ret < 0) + { + return ret; + } } - kmm_free(buf); if (priv->blocksize > priv->pgsize) { - return (int)(nblocks / (priv->blocksize / priv->pgsize)); + return (int)((npages * priv->pgsize) / priv->blocksize); } else { return (int)nblocks; } -#endif +#endif /* CONFIG_AT25EE_ENABLE_BLOCK_ERASE */ } /**************************************************************************** @@ -593,45 +287,19 @@ static int at25ee_erase(FAR struct mtd_dev_s *dev, * buffer - pointer to variable to store the read data * * Returned Value: - * Size of the data read + * Size of the data read on success, a negated error code on failure + * ****************************************************************************/ static ssize_t at25ee_read(FAR struct mtd_dev_s *dev, off_t offset, size_t nbytes, FAR uint8_t *buf) { - int ret; FAR struct at25ee_dev_s *priv = (FAR struct at25ee_dev_s *)dev; - DEBUGASSERT(buf); DEBUGASSERT(dev); + DEBUGASSERT(buf); - ret = nxmutex_lock(&priv->lock); - if (ret < 0) - { - return ret; - } - - if ((offset + nbytes) > priv->size) - { - return 0; /* end-of-file */ - } - - at25ee_lock(priv->spi); - - SPI_SELECT(priv->spi, SPIDEV_EEPROM(priv->devid), true); - - /* STM32F4Disco: There is a 25 us delay here */ - - at25ee_sendcmd(priv->spi, AT25EE_CMD_READ, priv->addrlen, offset); - - SPI_RECVBLOCK(priv->spi, buf, nbytes); - - SPI_SELECT(priv->spi, SPIDEV_EEPROM(priv->devid), false); - - at25ee_unlock(priv->spi); - - nxmutex_unlock(&priv->lock); - return nbytes; + return priv->eepdev->read(priv->eepdev, offset, nbytes, buf); } /**************************************************************************** @@ -647,91 +315,19 @@ static ssize_t at25ee_read(FAR struct mtd_dev_s *dev, off_t offset, * buf - pointer to buffer of data to write * * Returned Value: - * Size of the data written + * Size of the data written on success, a negated error code on failure + * ****************************************************************************/ static ssize_t at25ee_write(FAR struct mtd_dev_s *dev, off_t offset, size_t nbytes, FAR const uint8_t *buf) { - int ret = -EACCES; FAR struct at25ee_dev_s *priv = (FAR struct at25ee_dev_s *)dev; - int pageoff; - size_t cnt; - DEBUGASSERT(buf); DEBUGASSERT(dev); + DEBUGASSERT(buf); - if (priv->readonly) - { - return -EPERM; - } - - /* Forbid writes past the end of the device */ - - if (nbytes + offset > priv->size) - { - return 0; - } - - ret = nxmutex_lock(&priv->lock); - if (ret < 0) - { - return 0; - } - - /* From this point no failure cannot be detected anymore. - * The user should verify the write by rereading memory. - */ - - ret = nbytes; /* save number of bytes written */ - - /* Writes can't 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 = offset & (priv->pgsize - 1); - cnt = priv->pgsize - pageoff; - if (cnt > nbytes) - { - cnt = nbytes; - } - - if (pageoff > 0) - { - at25ee_writeenable(priv, true); - at25ee_writepage(priv, offset, buf, cnt); - at25ee_waitwritecomplete(priv); - nbytes -= cnt; - buf += cnt; - offset += cnt; - } - - /* Then, write remaining bytes at page-aligned addresses */ - - while (nbytes > 0) - { - cnt = nbytes; - if (cnt > priv->pgsize) - { - cnt = priv->pgsize; - } - - at25ee_writeenable(priv, true); - at25ee_writepage(priv, offset, buf, cnt); - at25ee_waitwritecomplete(priv); - nbytes -= cnt; - buf += cnt; - offset += cnt; - } - - nxmutex_unlock(&priv->lock); - return ret; + return priv->eepdev->write(priv->eepdev, offset, nbytes, buf); } /**************************************************************************** @@ -747,7 +343,8 @@ static ssize_t at25ee_write(FAR struct mtd_dev_s *dev, off_t offset, * buf - pointer to variable to store the read data * * Returned Value: - * Number of blocks written + * Number of blocks read on success, a negated error code on failure + * ****************************************************************************/ static ssize_t at25ee_bread(FAR struct mtd_dev_s *dev, @@ -755,60 +352,23 @@ static ssize_t at25ee_bread(FAR struct mtd_dev_s *dev, size_t nblocks, FAR uint8_t *buf) { FAR struct at25ee_dev_s *priv = (FAR struct at25ee_dev_s *)dev; - off_t offset; - ssize_t nread; - size_t i; + off_t offset; + ssize_t nbytes; + ssize_t ret; DEBUGASSERT(dev); - DEBUGASSERT(buf); + DEBUGASSERT(priv->blocksize > 0); - if (priv->blocksize > priv->pgsize) + offset = startblock * priv->blocksize; + nbytes = nblocks * priv->blocksize; + + ret = at25ee_read(dev, offset, nbytes, buf); + if (ret > 0) { - startblock *= (priv->blocksize / priv->pgsize); - nblocks *= (priv->blocksize / priv->pgsize); + ret /= priv->blocksize; } - finfo("startblock: %08lx nblocks: %lu\n", - (unsigned long)startblock, (unsigned long)nblocks); - - if (startblock >= priv->npages) - { - return 0; - } - - if (startblock + nblocks > priv->npages) - { - nblocks = priv->npages - startblock; - } - - /* Convert the access from startblock and number of blocks to a byte - * offset and number of bytes. - */ - - offset = startblock * priv->pgsize; - - /* Then perform the byte-oriented read for each block separately */ - - for (i = 0; i < nblocks; i++) - { - nread = at25ee_read(dev, offset, priv->pgsize, buf); - if (nread < 0) - { - return nread; - } - - offset += priv->pgsize; - buf += priv->pgsize; - } - - if (priv->blocksize > priv->pgsize) - { - return nblocks / (priv->blocksize / priv->pgsize); - } - else - { - return nblocks; - } + return ret; } /**************************************************************************** @@ -824,61 +384,37 @@ static ssize_t at25ee_bread(FAR struct mtd_dev_s *dev, * buf - pointer to data buffer to write * * Returned Value: - * Size of the data written + * Number of blocks written on success, a negated error code on failure + * ****************************************************************************/ static ssize_t at25ee_bwrite(FAR struct mtd_dev_s *dev, off_t startblock, size_t nblocks, FAR const uint8_t *buf) { FAR struct at25ee_dev_s *priv = (FAR struct at25ee_dev_s *)dev; - size_t blocksleft; + off_t offset; + ssize_t nbytes; + ssize_t ret; DEBUGASSERT(dev); - DEBUGASSERT(buf); + DEBUGASSERT(priv->blocksize > 0); - if (priv->blocksize > priv->pgsize) + offset = startblock * priv->blocksize; + nbytes = nblocks * priv->blocksize; + + ret = at25ee_write(dev, offset, nbytes, buf); + if (ret > 0) { - startblock *= (priv->blocksize / priv->pgsize); - nblocks *= (priv->blocksize / priv->pgsize); + ret /= priv->blocksize; } - blocksleft = nblocks; - - if (startblock >= priv->npages) - { - return 0; - } - - if (startblock + nblocks > priv->npages) - { - nblocks = priv->npages - startblock; - } - - finfo("startblock: %08lx nblocks: %d\n", (long)startblock, (int)nblocks); - - while (blocksleft-- > 0) - { - off_t offset = startblock * priv->pgsize; - - finfo("startblock: %08lx offset: %d\n", (long)startblock, (int)offset); - at25ee_write(dev, offset, priv->pgsize, buf); - startblock++; - buf += priv->pgsize; - } - - if (priv->blocksize > priv->pgsize) - { - return nblocks / (priv->blocksize / priv->pgsize); - } - else - { - return nblocks; - } + return ret; } /**************************************************************************** * Name: at25ee_ioctl - * * Description: + * + * Description: * IOCTLS relating to the EEPROM mtd device * * Input Parameters: @@ -888,6 +424,7 @@ static ssize_t at25ee_bwrite(FAR struct mtd_dev_s *dev, off_t startblock, * * Returned Value: * Success (OK) or fail (negated error code) + * ****************************************************************************/ static int at25ee_ioctl(FAR struct mtd_dev_s *dev, @@ -895,7 +432,7 @@ static int at25ee_ioctl(FAR struct mtd_dev_s *dev, unsigned long arg) { FAR struct at25ee_dev_s *priv = (FAR struct at25ee_dev_s *)dev; - int ret = -EINVAL; /* Assume good command with bad parameters */ + int ret = -EINVAL; DEBUGASSERT(dev); @@ -935,18 +472,9 @@ static int at25ee_ioctl(FAR struct mtd_dev_s *dev, * scaled by 1024 / 8 */ - if (priv->blocksize > priv->pgsize) - { - geo->blocksize = priv->blocksize; - geo->erasesize = priv->blocksize; - geo->neraseblocks = priv->size / priv->blocksize; - } - else - { - geo->blocksize = priv->pgsize; - geo->erasesize = priv->pgsize; - geo->neraseblocks = priv->npages; - } + geo->blocksize = priv->blocksize; + geo->erasesize = priv->blocksize; + geo->neraseblocks = priv->size / priv->blocksize; ret = OK; @@ -963,16 +491,8 @@ static int at25ee_ioctl(FAR struct mtd_dev_s *dev, (FAR struct partition_info_s *)arg; if (info != NULL) { - if (priv->blocksize > priv->pgsize) - { - info->numsectors = priv->size / priv->blocksize; - info->sectorsize = priv->blocksize; - } - else - { - info->numsectors = priv->npages; - info->sectorsize = priv->pgsize; - } + info->numsectors = priv->size / priv->blocksize; + info->sectorsize = priv->blocksize; info->startsector = 0; info->parent[0] = '\0'; @@ -982,11 +502,23 @@ static int at25ee_ioctl(FAR struct mtd_dev_s *dev, break; case MTDIOC_BULKERASE: - ret = at25ee_eraseall(priv); + { + ret = priv->eepdev->ioctl(priv->eepdev, EEPIOC_CHIPERASE, 0); + } + break; + + case MTDIOC_SETSPEED: + { + ret = priv->eepdev->ioctl(priv->eepdev, EEPIOC_SETSPEED, arg); + } break; default: - ret = -ENOTTY; /* Bad command */ + { + /* Tunnel the command to the EEPROM driver */ + + ret = priv->eepdev->ioctl(priv->eepdev, cmd, arg); + } break; } @@ -1009,56 +541,69 @@ static int at25ee_ioctl(FAR struct mtd_dev_s *dev, * Input Parameters: * dev - a reference to the spi device structure * spi_devid - SPI device ID to manage CS lines in board - * devtype - device type, from include/nuttx/eeprom/spi_xx25xx.h + * devtype - device type, see eeprom/eeprom.h * readonly - sets block driver to be readonly * * Returned Value: - * Initialised device structure (success) of NULL (fail) + * Initialized device instance (success) or NULL (fail) * ****************************************************************************/ FAR struct mtd_dev_s *at25ee_initialize(FAR struct spi_dev_s *dev, - uint16_t spi_devid, int devtype, + uint16_t spi_devid, + enum eeprom_25xx_e devtype, int readonly) { FAR struct at25ee_dev_s *priv; + struct eeprom_geometry_s geo; + int ret; - DEBUGASSERT(dev); + /* Initialize the EEPROM driver */ - /* Check device type early */ + FAR struct eeprom_dev_s *eepdev = + ee25xx_initialize_as_eeprom_dev(dev, spi_devid, devtype, readonly); - if ((devtype < 0) || - (devtype >= sizeof(g_at25ee_devices) / sizeof(g_at25ee_devices[0]))) + if (eepdev == NULL) { + ferr("ERROR: Failed to initialize the EEPROM driver\n"); return NULL; } - priv = kmm_zalloc(sizeof(struct at25ee_dev_s)); + /* Allocate the device structure */ + + priv = (FAR struct at25ee_dev_s *)kmm_zalloc(sizeof(struct at25ee_dev_s)); if (priv == NULL) { ferr("ERROR: Failed to allocate device structure\n"); return NULL; } + /* Retrieve the geometry */ + + ret = eepdev->ioctl(eepdev, EEPIOC_GEOMETRY, + (unsigned long)((uintptr_t)&geo)); + if (ret < 0) + { + ferr("ERROR retrieving the EEPROM geometry (ret = %d)\n", ret); + goto cleanup; + } + /* Initialize the allocated structure */ - nxmutex_init(&priv->lock); + priv->eepdev = eepdev; + priv->size = geo.pagesize * geo.npages; + priv->pgsize = geo.pagesize; + priv->sectsize = geo.sectsize; - priv->spi = dev; - priv->devid = spi_devid; - priv->size = 128 << g_at25ee_devices[devtype].bytes; - priv->pgsize = 8 << g_at25ee_devices[devtype].pagesize; - priv->addrlen = g_at25ee_devices[devtype].addrlen << 3; - priv->npages = priv->size / priv->pgsize; #ifdef CONFIG_USE_NATIVE_AT25EE_BLOCK_SIZE - priv->blocksize = priv->pgsize; + priv->blocksize = geo.pagesize; #else if ((CONFIG_MANUAL_AT25EE_BLOCK_SIZE % priv->pgsize) || (CONFIG_MANUAL_AT25EE_BLOCK_SIZE > priv->size)) { ferr("ERROR: Configured block size is incorrect!\n"); DEBUGASSERT(0); - priv->blocksize = priv->pgsize; + priv->blocksize = geo.pagesize; } else { @@ -1066,23 +611,19 @@ FAR struct mtd_dev_s *at25ee_initialize(FAR struct spi_dev_s *dev, } #endif - if ((g_at25ee_devices[devtype].flags & 1)) - { - priv->addrlen = 9; - } - priv->readonly = !!readonly; - - finfo("EEPROM device, %"PRIu32" bytes, " - "%u per page, addrlen %u, readonly %d\n", - priv->size, priv->pgsize, priv->addrlen, - priv->readonly); + finfo("MTD wrapper around EEPROM device: %zd bytes, %" PRIu16 + " per page, %" PRIu16 " per block, readonly %s\n", + priv->size, (uint16_t)priv->pgsize, priv->blocksize, + readonly ? "ON" : "OFF"); priv->mtd.erase = at25ee_erase; priv->mtd.bread = at25ee_bread; priv->mtd.bwrite = at25ee_bwrite; priv->mtd.read = at25ee_read; +#ifdef CONFIG_MTD_BYTE_WRITE priv->mtd.write = at25ee_write; +#endif priv->mtd.ioctl = at25ee_ioctl; priv->mtd.name = "at25ee"; @@ -1090,6 +631,37 @@ FAR struct mtd_dev_s *at25ee_initialize(FAR struct spi_dev_s *dev, finfo("Return %p\n", priv); return (FAR struct mtd_dev_s *)priv; + +cleanup: + ee25xx_teardown(eepdev); + kmm_free(priv); + return NULL; +} + +/**************************************************************************** + * Name: at25ee_teardown + * + * Description: + * Teardown a previously created at25ee device. + * + * Input Parameters: + * mtd - Pointer to the mtd driver instance. + * + ****************************************************************************/ + +void at25ee_teardown(FAR struct mtd_dev_s *mtd) +{ + FAR struct at25ee_dev_s *priv = (FAR struct at25ee_dev_s *)mtd; + + if (priv != NULL) + { + /* Destroy the underlying EEPROM driver */ + + ee25xx_teardown(priv->eepdev); + + /* Free the memory */ + + kmm_free(priv); + } } -#endif /* CONFIG_MTD_AT25EE */ diff --git a/include/nuttx/eeprom/eeprom.h b/include/nuttx/eeprom/eeprom.h index a9f50b12b0a..65b86e7fcdb 100644 --- a/include/nuttx/eeprom/eeprom.h +++ b/include/nuttx/eeprom/eeprom.h @@ -1,10 +1,8 @@ -/************************************************************************************ +/**************************************************************************** * include/nuttx/eeprom/eeprom.h * * SPDX-License-Identifier: Apache-2.0 * - * EEPROM IOCTL commands - * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. The @@ -20,7 +18,7 @@ * License for the specific language governing permissions and limitations * under the License. * - ************************************************************************************/ + ****************************************************************************/ /* This file includes common definitions to be used in all EEPROM drivers * (when applicable). @@ -29,9 +27,9 @@ #ifndef __INCLUDE_NUTTX_EEPROM_EEPROM_H #define __INCLUDE_NUTTX_EEPROM_EEPROM_H -/************************************************************************************ +/**************************************************************************** * Included Files - ************************************************************************************/ + ****************************************************************************/ #include @@ -40,11 +38,11 @@ #include -/************************************************************************************ +/**************************************************************************** * Pre-processor Definitions - ************************************************************************************/ + ****************************************************************************/ -/* EEPROM IOCTL Commands ************************************************************/ +/* EEPROM IOCTL Commands ****************************************************/ #define EEPIOC_GEOMETRY _EEPIOC(0x000) /* Similar to BIOC_GEOMETRY: * Return the geometry of the @@ -89,9 +87,9 @@ * IN: Which blocks as integer. * OUT: OK (0) on success. */ -/************************************************************************************ +/**************************************************************************** * Type Definitions - ************************************************************************************/ + ****************************************************************************/ struct eeprom_geometry_s { @@ -100,4 +98,246 @@ struct eeprom_geometry_s blksize_t pagesize; /* Size of one page in bytes */ }; +#if defined(CONFIG_MTD_AT25EE) || defined(CONFIG_MTD_AT24XX) +struct eeprom_dev_s +{ + /* The following methods operate on the EEPROM */ + + CODE ssize_t (*read)(FAR struct eeprom_dev_s *dev, off_t offset, + size_t nbytes, FAR uint8_t *buffer); + CODE ssize_t (*write)(FAR struct eeprom_dev_s *dev, off_t offset, + size_t nbytes, FAR const uint8_t *buffer); + + CODE int (*ioctl)(FAR struct eeprom_dev_s *dev, int cmd, + unsigned long arg); +}; +#endif + +#ifdef CONFIG_I2C_EE_24XX +enum eeprom_24xx_e +{ + /* Microchip geometries */ + + EEPROM_24XX00, + EEPROM_24XX01, + EEPROM_24XX02, + EEPROM_24XX04, + EEPROM_24XX08, + EEPROM_24XX16, + EEPROM_24XX32, + EEPROM_24XX64, + EEPROM_24XX128, + EEPROM_24XX256, + EEPROM_24XX512, + EEPROM_24XX1025, + EEPROM_24XX1026, + EEPROM_24CM02, + + /* Atmel geometries - none... */ + + /* STM geometries */ + + EEPROM_M24C01, + EEPROM_M24C02, + EEPROM_M24M02, + + /* Aliases (devices similar to previously defined ones) */ + + EEPROM_AT24C01 = EEPROM_24XX01, + EEPROM_AT24C02 = EEPROM_24XX02, + EEPROM_AT24C04 = EEPROM_24XX04, + EEPROM_AT24C08 = EEPROM_24XX08, + EEPROM_AT24C16 = EEPROM_24XX16, + EEPROM_AT24C32 = EEPROM_24XX32, + EEPROM_AT24C64 = EEPROM_24XX64, + EEPROM_AT24C128 = EEPROM_24XX128, + EEPROM_AT24C256 = EEPROM_24XX256, + EEPROM_AT24C512 = EEPROM_24XX512, + EEPROM_AT24C1024 = EEPROM_24XX1026, + EEPROM_AT24CM02 = EEPROM_24CM02, + + EEPROM_M24C04 = EEPROM_24XX04, + EEPROM_M24C08 = EEPROM_24XX08, + EEPROM_M24C16 = EEPROM_24XX16, + EEPROM_M24C32 = EEPROM_24XX32, + EEPROM_M24C64 = EEPROM_24XX64, + EEPROM_M24128 = EEPROM_24XX128, + EEPROM_M24256 = EEPROM_24XX256, + EEPROM_M24512 = EEPROM_24XX512, + EEPROM_M24M01 = EEPROM_24XX1026, +}; +#endif + +#ifdef CONFIG_SPI_EE_25XX +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, /* Additional erase commands */ + EEPROM_25XX1024, /* Additional erase commands */ + + /* Atmel geometries */ + + EEPROM_AT25010B, + EEPROM_AT25020B, + EEPROM_AT25040B, + EEPROM_AT25512, + EEPROM_AT25M01, + + /* STM geometries */ + + EEPROM_M95M02, + + /* 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_AT25M02 = EEPROM_25XX1024, + EEPROM_M95010 = EEPROM_25XX010, + EEPROM_M95020 = EEPROM_25XX020, + EEPROM_M95040 = EEPROM_25XX040, + EEPROM_M95080 = EEPROM_25XX080B, + EEPROM_M95160 = EEPROM_25XX160B, + EEPROM_M95320 = EEPROM_25XX320, + EEPROM_M95640 = EEPROM_25XX640, + EEPROM_M95128 = EEPROM_25XX128, + EEPROM_M95256 = EEPROM_25XX256, + EEPROM_M95512 = EEPROM_AT25512, + EEPROM_M95M01 = EEPROM_AT25M01, + EEPROM_BR25G256 = EEPROM_25XX256, +}; +#endif + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +#ifndef __ASSEMBLY__ + +#ifdef __cplusplus +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: ee24xx_initialize + * + * Description: + * Bind a EEPROM driver to an I2C 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). + * + * Parameters: + * bus - Pointer to the I2C device instance + * devaddr - I2C device address + * devaddr - Device name + * devtype - 24xx device type, the geometry is derived from it + * readonly - Flag indicating whether the device should be opened read-only + * + * Returned Values: + * OK on success; A negated errno value is returned on any failure. + * + ****************************************************************************/ + +#ifdef CONFIG_I2C_EE_24XX +struct i2c_master_s; +int ee24xx_initialize(FAR struct i2c_master_s *bus, uint8_t devaddr, + FAR char *devname, int devtype, int readonly); +#endif + +/**************************************************************************** + * Name: ee25xx_initialize + * + * Description: + * Bind an 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). + * + * Parameters: + * dev - Pointer to the SPI device instance + * spi_devid - SPI device ID to manage CS lines in board + * devname - Device name + * devtype - 25xx device type, the geometry is derived from it + * readonly - Sets driver to be readonly + * + * Returned Values: + * Zero (OK) on success; a negated errno value on failure. + * + ****************************************************************************/ + +#ifdef CONFIG_SPI_EE_25XX +struct spi_dev_s; +int ee25xx_initialize(FAR struct spi_dev_s *dev, uint16_t spi_devid, + FAR char *devname, enum eeprom_25xx_e devtype, + int readonly); + +/**************************************************************************** + * Name: ee25xx_initialize_as_eeprom_dev + * + * Description: + * Create an initialized EEPROM device instance for an xx25xx SPI EEPROM. + * The device is not registered in the file system, but created as an + * instance that can be bound to other functions. + * + * Parameters: + * dev - Pointer to the SPI device instance + * spi_devid - SPI device ID to manage CS lines in board + * devtype - 25xx device type, the geometry is derived from it + * readonly - Sets driver to be readonly + * + * Returned Values: + * Initialized device structure (success) or NULL (fail) + * + ****************************************************************************/ + +#ifdef CONFIG_MTD_AT25EE +FAR struct eeprom_dev_s *ee25xx_initialize_as_eeprom_dev( + FAR struct spi_dev_s *dev, uint16_t spi_devid, + enum eeprom_25xx_e devtype, int readonly); + +/**************************************************************************** + * Name: ee25xx_teardown + * + * Description: + * Teardown a previously created ee25xx device. + * + * Input Parameters: + * dev - Pointer to the driver instance. + * + ****************************************************************************/ + +void ee25xx_teardown(FAR struct eeprom_dev_s *dev); + +#endif /* CONFIG_MTD_AT25EE */ +#endif /* CONFIG_SPI_EE_25XX */ + +#undef EXTERN +#ifdef __cplusplus +} +#endif + +#endif /* __ASSEMBLY__ */ + #endif /* __INCLUDE_NUTTX_EEPROM_EEPROM_H */ diff --git a/include/nuttx/eeprom/i2c_xx24xx.h b/include/nuttx/eeprom/i2c_xx24xx.h deleted file mode 100644 index 7761c454214..00000000000 --- a/include/nuttx/eeprom/i2c_xx24xx.h +++ /dev/null @@ -1,102 +0,0 @@ -/**************************************************************************** - * include/nuttx/eeprom/i2c_xx24xx.h - * - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. The - * ASF licenses this file to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance with the - * License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - ****************************************************************************/ - -#ifndef __INCLUDE_NUTTX_EEPROM_I2C_XX24XX_H -#define __INCLUDE_NUTTX_EEPROM_I2C_XX24XX_H - -/**************************************************************************** - * Public Types - ****************************************************************************/ - -/* DO NOT CHANGE ORDER, IT MATCHES CODE IN drivers/eeprom/i2c_xx24xx.c */ - -enum eeprom_24xx_e -{ - /* Microchip geometries */ - - EEPROM_24XX00, - EEPROM_24XX01, - EEPROM_24XX02, - EEPROM_24XX04, - EEPROM_24XX08, - EEPROM_24XX16, - EEPROM_24XX32, - EEPROM_24XX64, - EEPROM_24XX128, - EEPROM_24XX256, - EEPROM_24XX512, - EEPROM_24XX1025, - EEPROM_24XX1026, - EEPROM_24CM02, - - /* Atmel geometries - none... */ - - /* STM geometries */ - - EEPROM_M24C01, - EEPROM_M24C02, - EEPROM_M24M02, - - /* Aliases (devices similar to previously defined ones) */ - - EEPROM_AT24C01 = EEPROM_24XX01, - EEPROM_AT24C02 = EEPROM_24XX02, - EEPROM_AT24C04 = EEPROM_24XX04, - EEPROM_AT24C08 = EEPROM_24XX08, - EEPROM_AT24C16 = EEPROM_24XX16, - EEPROM_AT24C32 = EEPROM_24XX32, - EEPROM_AT24C64 = EEPROM_24XX64, - EEPROM_AT24C128 = EEPROM_24XX128, - EEPROM_AT24C256 = EEPROM_24XX256, - EEPROM_AT24C512 = EEPROM_24XX512, - EEPROM_AT24C1024 = EEPROM_24XX1026, - EEPROM_AT24CM02 = EEPROM_24CM02, - - EEPROM_M24C04 = EEPROM_24XX04, - EEPROM_M24C08 = EEPROM_24XX08, - EEPROM_M24C16 = EEPROM_24XX16, - EEPROM_M24C32 = EEPROM_24XX32, - EEPROM_M24C64 = EEPROM_24XX64, - EEPROM_M24128 = EEPROM_24XX128, - EEPROM_M24256 = EEPROM_24XX256, - EEPROM_M24512 = EEPROM_24XX512, - EEPROM_M24M01 = EEPROM_24XX1026, -}; - -/**************************************************************************** - * Public Function Prototypes - ****************************************************************************/ - -/**************************************************************************** - * Name: ee24xx_initialize - * - * Description: Bind a EEPROM driver to an I2C 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 i2c_master_s; -int ee24xx_initialize(FAR struct i2c_master_s *bus, uint8_t devaddr, - FAR char *devname, int devtype, int readonly); - -#endif /* __INCLUDE__NUTTX_EEPROM_I2C_XX24XX_H */ diff --git a/include/nuttx/eeprom/spi_xx25xx.h b/include/nuttx/eeprom/spi_xx25xx.h deleted file mode 100644 index 55dfc487285..00000000000 --- a/include/nuttx/eeprom/spi_xx25xx.h +++ /dev/null @@ -1,114 +0,0 @@ -/**************************************************************************** - * include/nuttx/eeprom/spi_xx25xx.h - * - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. The - * ASF licenses this file to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance with the - * License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - ****************************************************************************/ - -#ifndef __INCLUDE_NUTTX_EEPROM_SPI_XX25XX_H -#define __INCLUDE_NUTTX_EEPROM_SPI_XX25XX_H - -/**************************************************************************** - * Public Types - ****************************************************************************/ - -/* DO NOT CHANGE ORDER, IT MATCHES CODE IN drivers/eeprom/spieeprom.c and - * drivers/mtd/at25ee.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, /* Additional erase commands */ - EEPROM_25XX1024, /* Additional erase commands */ - - /* Atmel geometries */ - - EEPROM_AT25010B, - EEPROM_AT25020B, - EEPROM_AT25040B, - EEPROM_AT25512, - EEPROM_AT25M01, - - /* STM geometries */ - - EEPROM_M95M02, - - /* 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_AT25M02 = EEPROM_25XX1024, - EEPROM_M95010 = EEPROM_25XX010, - EEPROM_M95020 = EEPROM_25XX020, - EEPROM_M95040 = EEPROM_25XX040, - EEPROM_M95080 = EEPROM_25XX080B, - EEPROM_M95160 = EEPROM_25XX160B, - EEPROM_M95320 = EEPROM_25XX320, - EEPROM_M95640 = EEPROM_25XX640, - EEPROM_M95128 = EEPROM_25XX128, - EEPROM_M95256 = EEPROM_25XX256, - EEPROM_M95512 = EEPROM_AT25512, - EEPROM_M95M01 = EEPROM_AT25M01, -}; - -/**************************************************************************** - * Public Function Prototypes - ****************************************************************************/ - -/**************************************************************************** - * Name: ee25xx_initialize - * - * Description: - * Bind an 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). - * - * Parameters: - * dev - Pointer to the SPI device instance - * spi_devid - SPI device ID to manage CS lines in board - * devname - Device name - * devtype - 25xx device type, the geometry is derived from it - * readonly - Sets driver to be readonly - * - * Returned Values: - * OK on success; A negated errno value is returned on any failure. - * - ****************************************************************************/ - -struct spi_dev_s; -int ee25xx_initialize(FAR struct spi_dev_s *dev, uint16_t spi_devid, - FAR char *devname, int devtype, int readonly); - -#endif /* __INCLUDE_NUTTX_EEPROM_SPI_XX25XX_H */ diff --git a/include/nuttx/mtd/mtd.h b/include/nuttx/mtd/mtd.h index 0795713fd19..7af9af5707a 100644 --- a/include/nuttx/mtd/mtd.h +++ b/include/nuttx/mtd/mtd.h @@ -35,6 +35,10 @@ #include +#ifdef CONFIG_EEPROM +#include +#endif + /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ @@ -429,17 +433,35 @@ FAR struct mtd_dev_s *at24c_initialize(FAR struct i2c_master_s *dev); * Input Parameters: * dev - a reference to the spi device structure * spi_devid - SPI device ID to manage CS lines in board - * devtype - device type, from include/nuttx/eeprom/spi_xx25xx.h + * devtype - device type, see eeprom/eeprom.h * readonly - sets block driver to be readonly * * Returned Value: - * Initialised device structure (success) of NULL (fail) + * Initialized device structure (success) or NULL (fail) * ****************************************************************************/ +#ifdef CONFIG_MTD_AT25EE FAR struct mtd_dev_s *at25ee_initialize(FAR struct spi_dev_s *dev, - uint16_t spi_devid, int devtype, + uint16_t spi_devid, + enum eeprom_25xx_e devtype, int readonly); +#endif + +/**************************************************************************** + * Name: at25ee_teardown + * + * Description: + * Teardown a previously created at25ee device. + * + * Input Parameters: + * dev - Pointer to the mtd driver instance. + * + ****************************************************************************/ + +#ifdef CONFIG_MTD_AT25EE +void at25ee_teardown(FAR struct mtd_dev_s *mtd); +#endif /**************************************************************************** * Name: at24c_uninitialize