diff --git a/drivers/mtd/CMakeLists.txt b/drivers/mtd/CMakeLists.txt index bf8307ffdf3..af7fbcdeaf4 100644 --- a/drivers/mtd/CMakeLists.txt +++ b/drivers/mtd/CMakeLists.txt @@ -85,6 +85,10 @@ if(CONFIG_MTD) list(APPEND SRCS at24xx.c) endif() + if(CONFIG_MTD_AT25EE) + list(APPEND SRCS at25ee.c) + endif() + if(CONFIG_MTD_AT45DB) list(APPEND SRCS at45db.c) endif() diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig index 136272bdff6..bf36892c530 100644 --- a/drivers/mtd/Kconfig +++ b/drivers/mtd/Kconfig @@ -33,7 +33,8 @@ config MTD_PARTITION is described in: include/nuttx/mtd/mtd.h - FAR struct mtd_dev_s *mtd_partition(FAR struct mtd_dev_s *mtd, off_t offset, off_t nblocks); + FAR struct mtd_dev_s *mtd_partition(FAR struct mtd_dev_s *mtd, + off_t offset, off_t nblocks); Each call to mtd_partition() will create a new MTD driver instance managing the sub-region of flash beginning at 'offset' (in blocks) @@ -115,7 +116,8 @@ config MTD_READAHEAD select DRVR_INVALIDATE select DRVR_READBYTES ---help--- - Build the mtd_rwbuffer layer and enable support for read-ahead buffering. + Build the mtd_rwbuffer layer and enable support for read-ahead + buffering. if MTD_READAHEAD @@ -350,7 +352,7 @@ config NULLMTD_BLOCKSIZE default 512 config NULLMTD_ERASESIZE - int "MTD null detault erase block size" + int "MTD null default erase block size" default 4096 config NULLMTD_ERASESTATE @@ -382,10 +384,10 @@ config AT24XX_SIZE int "AT24xx size (Kbit)" default 64 ---help--- - This is the XX in the AT24Cxx part number. For example, if you have a - AT 24C512, then the correct value is 512. This value is also the capacity - of the part in kilobits. For example, the 24C512 supports 512 Kbits or - 512 /8 = 64 KiB. + This is the XX in the AT24Cxx part number. For example, if you have + an AT24C64, then the correct value is 64. + This value is also the capacity of the part in kilobits. + For example, the 64 supports 64 Kbits or 64/8 = 8 KiB. config AT24XX_ADDR hex "AT24XX I2C address" @@ -403,8 +405,8 @@ config AT24XX_EXTENDED bool "Extended memory" default n ---help--- - If the device supports extended memory, then this operation may be set - to enabled the MTDIOC_EXTENDED ioctl() operation. When the + If the device supports extended memory, then this operation may be + set to enable the MTDIOC_EXTENDED ioctl() operation. When the extended operation is selected, calls to the driver read method will return data from the extended memory region. @@ -422,12 +424,107 @@ config AT24XX_FREQUENCY int "AT24xx I2C bus frequency" default 100000 ---help--- - Set the I2C frequency to use when accessing the AT24CXX EEPROM. This value - must represent a valid I2C speed (normally less than 400.000) or the driver - might fail. + Set the I2C frequency to use when accessing the AT24CXX EEPROM. + This value must represent a valid I2C speed (normally less than + 400.000) or the driver might fail. endif # MTD_AT24XX +config MTD_AT25EE + bool "SPI-based AT25xx EEPROM" + default n + select SPI + select MTD_BYTE_WRITE + ---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. + +if MTD_AT25EE + +choice + prompt "Block Size" + default USE_NATIVE_AT25EE_BLOCK_SIZE + ---help--- + For applications where a file system is used on the AT25 EEPROM, + 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 system block size is, say, 128, 256 or + 512 bytes. + + In any event, the block size *must* be an even multiple of the + number of pages and, often, needs to be a factor 2. + + This is up to the user to check! + +config USE_NATIVE_AT25EE_BLOCK_SIZE + bool "Use EEPROM's native block size" + +config MANUALLY_SET_AT25EE_BLOCK_SIZE + bool "Manually set block size" + +endchoice # Block Size + +if MANUALLY_SET_AT25EE_BLOCK_SIZE + +config MANUAL_AT25EE_BLOCK_SIZE + int "Manually-set EEPROM block size" + default 512 + +endif # MANUALLY_SET_BLOCK_SIZE + +config AT25EE_ENABLE_BLOCK_ERASE + bool "Enabled block erase" + default n + ---help--- + EEPROM does not need to be erased before write. However, in some + applications (e.g if an erase verify is wanted, or if a particular + 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 bool "SPI-based AT25 FLASH" default n @@ -502,18 +599,20 @@ config M25P_MANUFACTURER hex "M25P manufacturers ID" default 0x20 ---help--- - Various manufacturers may have produced the parts. 0x20 is the manufacturer ID - for the STMicro MP25x serial FLASH. If, for example, you are using the a Macronix - International MX25 serial FLASH, the correct manufacturer ID would be 0xc2. + Various manufacturers may have produced the parts. + 0x20 is the manufacturer ID for the STMicro MP25x serial FLASH. + If, for example, you are using the a Macronix International MX25 + serial FLASH, the correct manufacturer ID would be 0xc2. config M25P_MEMORY_TYPE hex "M25P memory type ID" default 0x20 ---help--- - The memory type for M25 "P" series is 0x20, but the driver also supports "F" series - devices, such as the EON EN25F80 part which adds a 4K sector erase capability. The - memory type for "F" series parts from EON is 0x31. The 4K sector erase size will - automatically be enabled when filesystems that can use it are enabled, such as SMART. + The memory type for M25 "P" series is 0x20, but the driver also + supports "F" series devices, such as the EON EN25F80 part which adds + a 4K sector erase capability. The memory type for "F" series parts + from EON is 0x31. The 4K sector erase size will automatically be + enabled when filesystems that can use it are enabled, such as SMART. config MT25Q_MEMORY_TYPE hex "MT25Q memory type ID" @@ -527,7 +626,8 @@ config M25P_SUBSECTOR_ERASE ---help--- Some devices (such as the EON EN25F80) support a smaller erase block size (4K vs 64K). This option enables support for sub-sector erase. - The SMART file system can take advantage of this option if it is enabled. + The SMART file system can take advantage of this option if it is + enabled. endif # MTD_M25P @@ -558,7 +658,8 @@ config MX25L_SUBSECTOR_ERASE ---help--- Some devices (such as the EON EN25F80) support a smaller erase block size (4K vs 64K). This option enables support for sub-sector erase. - The SMART file system can take advantage of this option if it is enabled. + The SMART file system can take advantage of this option if it is + enabled. config MX25L_DEBUG bool "Enable driver debug features" @@ -672,7 +773,8 @@ config MTD_W25QXXXJV bool "QuadSPI-based Winbond W25QXXXJV family FLASH" default n ---help--- - Support the W25Q016JV, W25Q032JV, W25Q064JV, W25Q128JV, W25Q256JV, W25Q512JV, W25Q01JV + Support the W25Q016JV, W25Q032JV, W25Q064JV, W25Q128JV, W25Q256JV, + W25Q512JV, W25Q01JV if MTD_W25QXXXJV @@ -744,13 +846,14 @@ config MTD_SMART bool "Sector Mapped Allocation for Really Tiny (SMART) Flash support" default n ---help--- - The MP25x series of Flash devices are typically very small and have a very large - erase block size. This causes issues with the standard Flash Translation Layer - block driver since it tries to allocate a RAM block the size of a flash erase - block, which is typically 64K. This block driver uses a different approach - to sacrifice performance for RAM memory footprint by saving data in sectors - (typically 2K - 4K based on memory size) and relocating sectors as needed when - an erase block needs to be erased. + The MP25x series of Flash devices are typically very small and have a + very large erase block size. This causes issues with the standard + Flash Translation Layer block driver since it tries to allocate a RAM + block the size of a flash erase block, which is typically 64K. + This block driver uses a different approach to sacrifice performance + for RAM memory footprint by saving data in sectors (typically 2K - 4K + based on memory size) and relocating sectors as needed when an erase + block needs to be erased. if MTD_SMART @@ -766,9 +869,9 @@ config MTD_SMART_SECTOR_SIZE int "SMART Device sector size" default 1024 ---help--- - Sets the size of a single allocation on the SMART device. Larger sector sizes - reduce overhead per sector, but cause more wasted space with a lot of smaller - files. + Sets the size of a single allocation on the SMART device. + Larger sector sizes reduce overhead per sector, but cause more wasted + space with a lot of smaller files. config MTD_SMART_WRITEBUFFER bool "Enable SMART write buffering" @@ -785,10 +888,11 @@ config MTD_SMART_WEAR_LEVEL depends on MTD_SMART default y ---help--- - Adds extra logic and RAM to guarantee equal wear leveling of the FLASH - device by recording and monitoring erase block operations and selecting - sector allocations to ensure all erase blocks are worn evenly. This will - evenly wear both dynamic and static data on the device. + Adds extra logic and RAM to guarantee equal wear leveling of the + FLASH device by recording and monitoring erase block operations and + selecting sector allocations to ensure all erase blocks are worn + evenly. This will evenly wear both dynamic and static data on the + device. if MTD_SMART_WEAR_LEVEL && !SMART_CRC_16 @@ -796,11 +900,11 @@ config MTD_SMART_CONVERT_WEAR_FORMAT bool "Convert existing non wear leveling FLASH to wear leveling" default n ---help--- - Adds a little extra code which detects an existing SMART format on a device - that was created prior to the wear leveling implementation. This conversion - only works if either no CRC is being used or if CRC-8 is being used as other - CRC versions use a different header format and require a mksmartfs on the - device even if an existing format is there. + Adds a little extra code which detects an existing SMART format on a + device that was created prior to the wear leveling implementation. + This conversion only works if either no CRC is being used or if CRC-8 + is being used as other CRC versions use a different header format and + require a mksmartfs on the device even if an existing format is there endif # MTD_SMART_WEAR_LEVEL && !SMART_CRC_16 @@ -809,17 +913,19 @@ config MTD_SMART_ENABLE_CRC depends on MTD_SMART default n ---help--- - Enables logic to compute and validate a CRC for logical sectors. The - CRC is calculated for all bytes in the logical sector. The CRC size is - selectable (8-bit, 16-bit, 32-bit). For added protection, larger CRCs should - be used with larger (2K - 4K) sector sizes. Enabling CRC protection will - cause increased sector relocation and increased erase block erasures since - directory and wear-level status updates can no longer be performed in-place - and mandate re-writing the information to a new sector. + Enables logic to compute and validate a CRC for logical sectors. + The CRC is calculated for all bytes in the logical sector. + The CRC size is selectable (8-bit, 16-bit, 32-bit). For added + protection, larger CRCs should be used with larger (2K - 4K) sector + sizes. Enabling CRC protection will cause increased sector + relocation and increased erase block erasures since directory and + wear-level status updates can no longer be performed in-place and + mandate re-writing the information to a new sector. - An 8-bit CRC protection scheme can be added to an existing non-CRC formatted - SMART volume without needing to reformat the drive. As sectors are re-written - or relocated, they will be converted to CRC protected sectors. + An 8-bit CRC protection scheme can be added to an existing non-CRC + formatted SMART volume without needing to reformat the drive. As + sectors are re-written or relocated, they will be converted to CRC + protected sectors. choice prompt "CRC level selection" @@ -827,9 +933,9 @@ choice default SMART_CRC_8 ---help--- Select the level of CRC protection implemented in the SMART MTD layer. - Smaller CRC selection uses less overhead per logical sectors, but also has - a higher probability of not detecting multiple bit errors. Devices with - larger logical sector sizes should use a larger CRC. + Smaller CRC selection uses less overhead per logical sectors, but + also has a higher probability of not detecting multiple bit errors. + Devices with larger logical sector sizes should use a larger CRC. config SMART_CRC_8 bool "CRC-8" @@ -858,52 +964,54 @@ config MTD_SMART_MINIMIZE_RAM depends on MTD_SMART default 0 ---help--- - Reduces RAM usage in the SMART MTD layer by replacing the 1-for-1 logical to - physical sector map with a smaller cache-based structure. This can save a - considerable amount of RAM on devices with a large sector count, but at the - expense of increased read/write times when a cache miss occurs. If the - requested logical sector has not been cached, then the device will need to be - scanned to located it on the physical medium. + Reduces RAM usage in the SMART MTD layer by replacing the 1-for-1 + logical to physical sector map with a smaller cache-based structure. + This can save a considerable amount of RAM on devices with a large + sector count, but at the expense of increased read/write times when a + cache miss occurs. If the requested logical sector has not been + cached, then the device will need to be scanned to located it on the + physical medium. config MTD_SMART_SECTOR_CACHE_SIZE int "Number of entries in the SMART logical sector cache" depends on MTD_SMART_MINIMIZE_RAM default 512 ---help--- - Sets the size of the cache used for logical to physical sector mapping. A - larger number allows larger files to be "seek"ed randomly without encountering - cache misses. Any files larger than CACH_SIZE * SECTOR_SIZE that are sought - start to end will cause the cache to flush forcing manual scanning of the - MTD device to find the logical to physical mappings. + Sets the size of the cache used for logical to physical sector + mapping. A larger number allows larger files to be "seek"ed randomly + without encountering cache misses. Any files larger than + CACHE_SIZE * SECTOR_SIZE that are sought start to end will cause the + cache to flush forcing manual scanning of the MTD device to find the + logical to physical mappings. config MTD_SMART_SECTOR_PACK_COUNTS bool "Pack free and release counts when possible" depends on MTD_SMART_MINIMIZE_RAM default y ---help--- - For volumes with 16 sectors per erase block or less, this option causes the - free sector and released sector counts used for allocation and garbage - collection to be packed such that two values are stored per byte. For - volumes with 16 sectors per erase block, the 4 LSBs are packed and all of - the high-order bits are packed separately (8 per byte). This squeezes even - more RAM out. + For volumes with 16 sectors per erase block or less, this option + causes the free sector and released sector counts used for allocation + and garbage collection to be packed such that two values are stored + per byte. For volumes with 16 sectors per erase block, the 4 LSBs + are packed and all of the high-order bits are packed separately + (8 per byte). This squeezes even more RAM out. config MTD_SMART_SECTOR_ERASE_DEBUG bool "Track Erase Block erasure counts" depends on MTD_SMART default n ---help--- - Allocates an Erase Block erase count array and keeps track of the number - of erases per erase block. This data is then presented on the procfs - interface. + Allocates an Erase Block erase count array and keeps track of the + number of erases per erase block. This data is then presented on the + procfs interface. config MTD_SMART_ALLOC_DEBUG bool "RAM Allocation Debug" depends on MTD_SMART default n ---help--- - Records all SMART MTD layer allocations for debug purposes and makes them - accessible from the ProcFS interface if it is enabled. + Records all SMART MTD layer allocations for debug purposes and makes + them accessible from the ProcFS interface if it is enabled. endif # MTD_SMART @@ -938,8 +1046,8 @@ config RAMTRON_EMULATE_SECTOR_SHIFT For purpose of the VFAT file system, we emulate them. Specify sector shift value to determine emulated sector size. - The relationship between sector shift value and emulated sector size is - described in the equation: + The relationship between sector shift value and emulated sector size + is described in the equation: RAMTRON_EMULATE_SECTOR_SIZE = (1 << RAMTRON_EMULATE_SECTOR_SHIFT) sector shift value : sector size in bytes @@ -1007,11 +1115,12 @@ config SST25_SLOWWRITE bool default n ---help--- - There used to be a bug in the current code when using the higher speed AAI - write sequence. The nature of the bug is that the WRDI instruction is not - working. At the end of the AAI sequence, the status register continues to - report that the SST25 is write enabled (WEL bit) and in AAI mode (AAI - bit). This has been fixed by David Sidrane! + There used to be a bug in the current code when using the higher + speed AAI write sequence. The nature of the bug is that the WRDI + instruction is not working. At the end of the AAI sequence, the + status register continues to report that the SST25 is write enabled + (WEL bit) and in AAI mode (AAI bit). + This has been fixed by David Sidrane! config SST25_SLOWREAD bool @@ -1026,8 +1135,8 @@ config MTD_SST25XX ---help--- With the 64 MBit and larger parts, SST changed the write mechanism to support page write instead of byte/word write like the smaller parts. - As a result, the SST25 driver is not compatible with the larger density - parts, and the SST25XX driver must be used instead. + As a result, the SST25 driver is not compatible with the larger + density parts, and the SST25XX driver must be used instead. if MTD_SST25XX @@ -1043,15 +1152,16 @@ config SST25XX_MANUFACTURER hex "Manufacturers ID" default 0xBF ---help--- - Various manufacturers may have produced the parts. 0xBF is the manufacturer ID - for the parts manufactured by SST. + Various manufacturers may have produced the parts. 0xBF is the + manufacturer ID for the parts manufactured by SST. config SST25XX_MEMORY_TYPE hex "Memory type ID" default 0x25 ---help--- - The memory type for SST25VF065 series is 0x25, but this can be modified if needed - to support compatible devices from different manufacturers. + The memory type for SST25VF065 series is 0x25, but this can be + modified if needed to support compatible devices from different + manufacturers. endif # MTD_SST25XX @@ -1060,7 +1170,8 @@ config MTD_SST26 default n select SPI ---help--- - These part are also different from SST25 and SST25XX, they support both SPI and QSPI. + These part are also different from SST25 and SST25XX, they support + both SPI and QSPI. if MTD_SST26 @@ -1076,15 +1187,16 @@ config SST26_MANUFACTURER hex "Manufacturers ID" default 0xBF ---help--- - Various manufacturers may have produced the parts. 0xBF is the manufacturer ID - for the parts manufactured by SST. + Various manufacturers may have produced the parts. 0xBF is the + manufacturer ID for the parts manufactured by SST. config SST26_MEMORY_TYPE hex "Memory type ID" default 0x26 ---help--- - The memory type for SST26VF0xx series is 0x26, but this can be modified if needed - to support compatible devices from different manufacturers. + The memory type for SST26VF0xx series is 0x26, but this can be + modified if needed to support compatible devices from different + manufacturers. config SST26_DEBUG bool "Debug output from the SST26 driver" diff --git a/drivers/mtd/Make.defs b/drivers/mtd/Make.defs index 0db6d50965e..549b6c3fb89 100644 --- a/drivers/mtd/Make.defs +++ b/drivers/mtd/Make.defs @@ -86,6 +86,10 @@ ifeq ($(CONFIG_MTD_AT24XX),y) CSRCS += at24xx.c endif +ifeq ($(CONFIG_MTD_AT25EE),y) +CSRCS += at25ee.c +endif + ifeq ($(CONFIG_MTD_AT45DB),y) CSRCS += at45db.c endif diff --git a/drivers/mtd/at25ee.c b/drivers/mtd/at25ee.c new file mode 100644 index 00000000000..45873a7c46c --- /dev/null +++ b/drivers/mtd/at25ee.c @@ -0,0 +1,1083 @@ +/**************************************************************************** + * drivers/mtd/at25ee.c + * + * 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#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 + * system block size is, say, 256 or 512 bytes. + * In any event, the block size *must* be an even multiple of the pages. + */ + +/**************************************************************************** + * 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. + */ + +struct at25ee_dev_s +{ + struct mtd_dev_s mtd; /* MTD interface */ + 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 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 */ +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static void at25ee_lock(FAR struct spi_dev_s *dev); + +/* MTD driver methods */ + +static int at25ee_erase(FAR struct mtd_dev_s *dev, + off_t startblock, + size_t nblocks); +static ssize_t at25ee_bread(FAR struct mtd_dev_s *dev, + off_t startblock, + size_t nblocks, FAR uint8_t *buf); +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); +static ssize_t at25ee_write(FAR struct mtd_dev_s *dev, off_t offset, + size_t nbytes, FAR const uint8_t *buf); +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 */ + + /* STM devices */ + + { + 11, 5, 3, 0 + }, /* M95M02 262144 256 3 */ +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: at25ee_lock + * + * Description: + * On SPI buses where there are multiple devices, it will be necessary to + * lock SPI to have exclusive access to the buses for a sequence of + * transfers. The bus should be locked before the chip is selected. + * + * 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. + * + * Input Parameters: + * dev - pointer to device structure + * Returned Value: + * none + * + ****************************************************************************/ + +static void at25ee_lock(FAR struct spi_dev_s *dev) +{ + 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(0), 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(0), 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) + { + nxsig_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(0), true); + + SPI_SEND(priv->spi, enable ? AT25EE_CMD_WREN : AT25EE_CMD_WRDIS); + + SPI_SELECT(priv->spi, SPIDEV_EEPROM(0), 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(0), true); + + at25ee_sendcmd(priv->spi, AT25EE_CMD_WRITE, priv->addrlen, devaddr); + SPI_SNDBLOCK(priv->spi, data, len); + + SPI_SELECT(priv->spi, SPIDEV_EEPROM(0), 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; + + DEBUGASSERT(priv); + + buf = kmm_malloc(priv->pgsize); + if (!buf) + { + ferr("ERROR: Failed to alloc memory for at25ee eraseall!\n"); + return -ENOMEM; + } + + 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; +} + +/**************************************************************************** + * Name: at25ee_erase + * + * Description: + * Erase a number of blocks of data. + * + * Input Parameters: + * dev - a reference to the device structure + * startblock - start block of the erase + * nblocks - nblocks to erase + * + * Returned Value: + * Success (OK) or fail (negated error code) + ****************************************************************************/ + +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; + + DEBUGASSERT(dev); + + if (priv->blocksize > priv->pgsize) + { + startblock *= (priv->blocksize / priv->pgsize); + nblocks *= (priv->blocksize / priv->pgsize); + } + + blocksleft = nblocks; + + if (startblock >= priv->npages) + { + return -E2BIG; + } + + buf = kmm_malloc(priv->pgsize); + if (!buf) + { + ferr("ERROR: Failed to alloc memory for at25ee erase!\n"); + return -ENOMEM; + } + + memset(buf, 0xff, priv->pgsize); + + 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++; + } + + kmm_free(buf); + if (priv->blocksize > priv->pgsize) + { + return (int)(nblocks / (priv->blocksize / priv->pgsize)); + } + else + { + return (int)nblocks; + } +#endif +} + +/**************************************************************************** + * Name: at25ee_read + * + * Description: + * Read a number of bytes of data. + * + * Input Parameters: + * dev - a reference to the device structure + * offset - start of the memory to read + * nbytes - number of bytes to read + * buffer - pointer to variable to store the read data + * + * Returned Value: + * Size of the data read + ****************************************************************************/ + +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); + + 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(0), 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(0), false); + + at25ee_unlock(priv->spi); + + nxmutex_unlock(&priv->lock); + return nbytes; +} + +/**************************************************************************** + * Name: at25ee_write + * + * Description: + * Write a number of bytes of data. + * + * Input Parameters: + * dev - a reference to the device structure + * offset - start of the memory to write + * nbytes - number of bytes to write + * buf - pointer to buffer of data to write + * + * Returned Value: + * Size of the data written + ****************************************************************************/ + +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); + + 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; +} + +/**************************************************************************** + * Name: at25ee_bread + * + * Description: + * Read a number of blocks of data. + * + * Input Parameters: + * dev - a reference to the device structure + * startblock - start block of the read + * nblocks - nblocks to read + * buf - pointer to variable to store the read data + * + * Returned Value: + * Number of blocks written + ****************************************************************************/ + +static ssize_t at25ee_bread(FAR struct mtd_dev_s *dev, + off_t startblock, + 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; + + DEBUGASSERT(dev); + DEBUGASSERT(buf); + + if (priv->blocksize > priv->pgsize) + { + startblock *= (priv->blocksize / priv->pgsize); + nblocks *= (priv->blocksize / priv->pgsize); + } + + 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; + } +} + +/**************************************************************************** + * Name: at25ee_bwrite + * + * Description: + * Write a number of blocks of data. + * + * Input Parameters: + * dev - a reference to the device structure + * startblock - starting block to write to + * nblocks - nblocks to write + * buf - pointer to data buffer to write + * + * Returned Value: + * Size of the data written + ****************************************************************************/ + +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; + + DEBUGASSERT(dev); + DEBUGASSERT(buf); + + if (priv->blocksize > priv->pgsize) + { + startblock *= (priv->blocksize / priv->pgsize); + nblocks *= (priv->blocksize / priv->pgsize); + } + + 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; + } +} + +/**************************************************************************** + * Name: at25ee_ioctl + * * Description: + * IOCTLS relating to the EEPROM mtd device + * + * Input Parameters: + * dev - a reference to the device structure + * cmd - ioctl command + * arg - ioctl argument + * + * Returned Value: + * Success (OK) or fail (negated error code) + ****************************************************************************/ + +static int at25ee_ioctl(FAR struct mtd_dev_s *dev, + int cmd, + unsigned long arg) +{ + FAR struct at25ee_dev_s *priv = (FAR struct at25ee_dev_s *)dev; + int ret = -EINVAL; /* Assume good command with bad parameters */ + + DEBUGASSERT(dev); + + finfo("cmd: %d\n", cmd); + + switch (cmd) + { + case MTDIOC_GEOMETRY: + { + FAR struct mtd_geometry_s *geo = (FAR struct mtd_geometry_s *) + ((uintptr_t)arg); + if (geo) + { + memset(geo, 0, sizeof(*geo)); + + /* Populate the geometry structure with information need to + * know the capacity and how to access the device. + * + * NOTE: + * that the device is treated as though it where just an array + * of fixed size blocks. + * That is most likely not true, but the client will expect the + * device logic to do whatever is necessary to make it appear + * so. + * + * blocksize: + * May be user defined. + * The block size for the at24XX devices may be larger than + * the page size in order to better support file systems. + * The read and write functions translate BLOCKS to pages + * for the small flash devices + * erasesize: + * It has to be at least as big as the blocksize, bigger + * serves no purpose. + * neraseblocks + * Note that the device size is in kilobits and must be + * 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; + } + + ret = OK; + + finfo("blocksize: %" PRId32 " erasesize: %" PRId32 + " neraseblocks: %" PRId32 "\n", + geo->blocksize, geo->erasesize, geo->neraseblocks); + } + } + break; + + case BIOC_PARTINFO: + { + FAR struct partition_info_s *info = + (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->startsector = 0; + info->parent[0] = '\0'; + ret = OK; + } + } + break; + + case MTDIOC_BULKERASE: + ret = at25ee_eraseall(priv); + break; + + default: + ret = -ENOTTY; /* Bad command */ + break; + } + + return ret; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: at25ee_initialize + * + * Description: + * Create an initialized MTD device instance for an AT25 SPI EEPROM + * MTD devices are not registered in the file system, but are created + * as instances that can be bound to other functions + * (such as a block or character driver front end). + * + * Input Parameters: + * dev - a reference to the spi device structure + * devtype - device type, from include/nuttx/eeprom/spi_xx25xx.h + * readonly - sets block driver to be readonly + * + * Returned Value: + * Initialised device instance (success) or NULL (fail) + * + ****************************************************************************/ + +FAR struct mtd_dev_s *at25ee_initialize(FAR struct spi_dev_s *dev, + int devtype, int readonly) +{ + FAR struct at25ee_dev_s *priv; + + DEBUGASSERT(dev); + + /* Check device type early */ + + if ((devtype < 0) || + (devtype >= sizeof(g_at25ee_devices) / sizeof(g_at25ee_devices[0]))) + { + return NULL; + } + + priv = kmm_zalloc(sizeof(struct at25ee_dev_s)); + if (priv == NULL) + { + ferr("ERROR: Failed to allocate device structure\n"); + return NULL; + } + + /* Initialize the allocated structure */ + + nxmutex_init(&priv->lock); + + priv->spi = dev; + 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; +#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; + } + else + { + priv->blocksize = CONFIG_MANUAL_AT25EE_BLOCK_SIZE; + } + +#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); + + priv->mtd.erase = at25ee_erase; + priv->mtd.bread = at25ee_bread; + priv->mtd.bwrite = at25ee_bwrite; + priv->mtd.read = at25ee_read; + priv->mtd.write = at25ee_write; + priv->mtd.ioctl = at25ee_ioctl; + priv->mtd.name = "at25ee"; + + /* Return the implementation-specific state structure as the MTD device */ + + finfo("Return %p\n", priv); + return (FAR struct mtd_dev_s *)priv; +} + +#endif /* CONFIG_MTD_AT25EE */ diff --git a/include/nuttx/eeprom/spi_xx25xx.h b/include/nuttx/eeprom/spi_xx25xx.h index 5a1f7608293..29fcc98cbf7 100644 --- a/include/nuttx/eeprom/spi_xx25xx.h +++ b/include/nuttx/eeprom/spi_xx25xx.h @@ -25,7 +25,9 @@ * Public Types ****************************************************************************/ -/* DO NOT CHANGE ORDER, IT MATCHES CODE IN drivers/eeprom/spieeprom.c */ +/* DO NOT CHANGE ORDER, IT MATCHES CODE IN drivers/eeprom/spieeprom.c and + * drivers/mtd/at25ee.c + */ enum eeprom_25xx_e { diff --git a/include/nuttx/mtd/mtd.h b/include/nuttx/mtd/mtd.h index 1a1c4923d90..e6356e0b0e3 100644 --- a/include/nuttx/mtd/mtd.h +++ b/include/nuttx/mtd/mtd.h @@ -397,6 +397,28 @@ FAR struct mtd_dev_s *at24c_initialize(FAR struct i2c_master_s *dev, FAR struct mtd_dev_s *at24c_initialize(FAR struct i2c_master_s *dev); #endif +/**************************************************************************** + * Name: at25xx_initialize + * + * Description: + * Create an initialized MTD device instance for an AT25 SPI EEPROM + * MTD devices are not registered in the file system, but are created + * as instances that can be bound to other functions + * (such as a block or character driver front end). + * + * Input Parameters: + * dev - a reference to the spi device structure + * devtype - device type, from include/nuttx/eeprom/spi_xx25xx.h + * readonly - sets block driver to be readonly + * + * Returned Value: + * Initialised device structure (success) of NULL (fail) + * + ****************************************************************************/ + +FAR struct mtd_dev_s *at25ee_initialize(FAR struct spi_dev_s *dev, + int devtype, int readonly); + /**************************************************************************** * Name: at24c_uninitialize *