This is an automated email from the ASF dual-hosted git repository.
xiaoxiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git
The following commit(s) were added to refs/heads/master by this push:
new 222ca5b040 Add MTD for AT25 eeprom
222ca5b040 is described below
commit 222ca5b0403fb1f834dc2382019a85ec398f7716
Author: TimJTi <[email protected]>
AuthorDate: Tue Dec 19 14:03:58 2023 +0000
Add MTD for AT25 eeprom
---
drivers/mtd/CMakeLists.txt | 4 +
drivers/mtd/Kconfig | 300 ++++++----
drivers/mtd/Make.defs | 4 +
drivers/mtd/at25ee.c | 1083 +++++++++++++++++++++++++++++++++++++
include/nuttx/eeprom/spi_xx25xx.h | 4 +-
include/nuttx/mtd/mtd.h | 22 +
6 files changed, 1322 insertions(+), 95 deletions(-)
diff --git a/drivers/mtd/CMakeLists.txt b/drivers/mtd/CMakeLists.txt
index bf8307ffdf..af7fbcdeaf 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 136272bdff..bf36892c53 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 0db6d50965..549b6c3fb8 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 0000000000..45873a7c46
--- /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 <nuttx/config.h>
+#include <sys/types.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/kmalloc.h>
+#include <nuttx/signal.h>
+#include <nuttx/fs/ioctl.h>
+#include <nuttx/spi/spi.h>
+#include <nuttx/mtd/mtd.h>
+
+#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 5a1f760829..29fcc98cbf 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 1a1c4923d9..e6356e0b0e 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
*