Rearchitecting of some MTD, partition, SMART interfaces, and FLASH drivers to: Better use the byte write capbility when available and to use smaller erase sectors for the erase sector size when available).

This commit is contained in:
Gregory Nutt
2013-05-03 12:52:33 -06:00
parent b934926bd2
commit 72179b7773
17 changed files with 643 additions and 452 deletions
+2 -25
View File
@@ -64,13 +64,6 @@ config RAMMTD_FLASHSIM
RAMMTD_FLASHSIM will add some extra logic to improve the level of
FLASH simulation.
config RAMMTD_SMART
bool "SMART block driver support in the RAM MTD driver"
default n
---help---
Builds in additional ioctl and interface code to support the
SMART block driver / filesystem.
endif
config MTD_AT24XX
@@ -139,30 +132,14 @@ config M25P_MEMORY_TYPE
---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
ID for "F" series parts from EON is 0x31.
config M25P_SUBSECTOR_ERASE
bool "Sub-Sector Erase"
default n
---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.
config M25P_BYTEWRITE
bool "Enable ByteWrite ioctl support"
default n
---help---
The M25P series of devices allow writing to a page with less than a full-page
size of data. In this case, only the written bytes are updated without affecting
the other bytes in the page. The SMART FS requires this option for proper operation.
memory type for "F" series parts from EON is 0x31. The 4K sector erase size will
automatically be enabled when filessytems that can use it are enabled, such as SMART.
endif
config MTD_SMART
bool "Sector Mapped Allocation for Really Tiny (SMART) Flash support"
default y
select M25P_BYTEWRITE
---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
+167 -165
View File
@@ -3,7 +3,7 @@
* Driver for SPI-based M25P1 (128Kbit), M25P64 (32Mbit), M25P64 (64Mbit), and
* M25P128 (128Mbit) FLASH (and compatible).
*
* Copyright (C) 2009-2011 Gregory Nutt. All rights reserved.
* Copyright (C) 2009-2011, 2013 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* Redistribution and use in source and binary forms, with or without
@@ -114,6 +114,7 @@
#define M25P_EN25F80_PAGE_SHIFT 8 /* Page size 1 << 8 = 256 */
#define M25P_EN25F80_NPAGES 4096
#define M25P_EN25F80_SUBSECT_SHIFT 12 /* Sub-Sector size 1 << 12 = 4,096 */
#define M25P_EN25F80_NSUBSECTORS 256
/* M25P32 capacity is 4,194,304 bytes:
* (64 sectors) * (65,536 bytes per sector)
@@ -203,7 +204,7 @@ struct m25p_dev_s
uint8_t pageshift; /* 8 */
uint16_t nsectors; /* 128 or 64 */
uint32_t npages; /* 32,768 or 65,536 */
#ifdef CONFIG_M25P_SUBSECTOR_ERASE
#ifdef CONFIG_MTD_SMART
uint8_t subsectorshift; /* 0, 12 or 13 (4K or 8K) */
#endif
};
@@ -219,13 +220,10 @@ static inline void m25p_unlock(FAR struct spi_dev_s *dev);
static inline int m25p_readid(struct m25p_dev_s *priv);
static void m25p_waitwritecomplete(struct m25p_dev_s *priv);
static void m25p_writeenable(struct m25p_dev_s *priv);
static inline void m25p_sectorerase(struct m25p_dev_s *priv, off_t offset);
static inline void m25p_sectorerase(struct m25p_dev_s *priv, off_t offset, uint8_t type);
static inline int m25p_bulkerase(struct m25p_dev_s *priv);
static inline void m25p_pagewrite(struct m25p_dev_s *priv, FAR const uint8_t *buffer,
off_t offset);
#ifdef CONFIG_M25P_SUBSECTOR_ERASE
static inline void m25p_subsectorerase(struct m25p_dev_s *priv, off_t offset);
#endif
/* MTD driver methods */
@@ -236,6 +234,10 @@ static ssize_t m25p_bwrite(FAR struct mtd_dev_s *dev, off_t startblock,
size_t nblocks, FAR const uint8_t *buf);
static ssize_t m25p_read(FAR struct mtd_dev_s *dev, off_t offset, size_t nbytes,
FAR uint8_t *buffer);
#ifdef CONFIG_MTD_BYTE_WRITE
static ssize_t m25p_write(FAR struct mtd_dev_s *dev, off_t offset, size_t nbytes,
FAR const uint8_t *buffer);
#endif
static int m25p_ioctl(FAR struct mtd_dev_s *dev, int cmd, unsigned long arg);
/************************************************************************************
@@ -320,7 +322,7 @@ static inline int m25p_readid(struct m25p_dev_s *priv)
{
/* Okay.. is it a FLASH capacity that we understand? */
#ifdef CONFIG_M25P_SUBSECTOR_ERASE
#ifdef CONFIG_MTD_SMART
priv->subsectorshift = 0;
#endif
@@ -338,11 +340,11 @@ static inline int m25p_readid(struct m25p_dev_s *priv)
{
/* Save the FLASH geometry */
priv->sectorshift = M25P_EN25F80_SECTOR_SHIFT;
priv->nsectors = M25P_EN25F80_NSECTORS;
priv->pageshift = M25P_EN25F80_PAGE_SHIFT;
priv->npages = M25P_EN25F80_NPAGES;
#ifdef CONFIG_M25P_SUBSECTOR_ERASE
priv->sectorshift = M25P_EN25F80_SECTOR_SHIFT;
priv->nsectors = M25P_EN25F80_NSECTORS;
#ifdef CONFIG_MTD_SMART
priv->subsectorshift = M25P_EN25F80_SUBSECT_SHIFT;
#endif
return OK;
@@ -480,9 +482,20 @@ static void m25p_writeenable(struct m25p_dev_s *priv)
* Name: m25p_sectorerase
************************************************************************************/
static inline void m25p_sectorerase(struct m25p_dev_s *priv, off_t sector)
static void m25p_sectorerase(struct m25p_dev_s *priv, off_t sector, uint8_t type)
{
off_t offset = sector << priv->sectorshift;
off_t offset;
#ifdef CONFIG_MTD_SMART
if (priv->subsectorshift > 0)
{
offset = sector << priv->subsectorshift;
}
else
#endif
{
offset = sector << priv->sectorshift;
}
fvdbg("sector: %08lx\n", (long)sector);
@@ -502,9 +515,11 @@ static inline void m25p_sectorerase(struct m25p_dev_s *priv, off_t sector)
SPI_SELECT(priv->dev, SPIDEV_FLASH, true);
/* Send the "Sector Erase (SE)" instruction */
/* Send the "Sector Erase (SE)" or Sub-Sector Erase (SSE) instruction
* that was passed in as the erase type.
*/
(void)SPI_SEND(priv->dev, M25P_SE);
(void)SPI_SEND(priv->dev, type);
/* Send the sector offset high byte first. For all of the supported
* parts, the sector number is completely contained in the first byte
@@ -521,53 +536,6 @@ static inline void m25p_sectorerase(struct m25p_dev_s *priv, off_t sector)
fvdbg("Erased\n");
}
/************************************************************************************
* Name: m25p_subsectorerase
************************************************************************************/
#ifdef CONFIG_M25P_SUBSECTOR_ERASE
static inline void m25p_subsectorerase(struct m25p_dev_s *priv, off_t subsector)
{
off_t offset = subsector << priv->subsectorshift;
fvdbg("subsector: %9lx\n", (long)subsector);
/* Wait for any preceding write to complete. We could simplify things by
* perform this wait at the end of each write operation (rather than at
* the beginning of ALL operations), but have the wait first will slightly
* improve performance.
*/
m25p_waitwritecomplete(priv);
/* Send write enable instruction */
m25p_writeenable(priv);
/* Select this FLASH part */
SPI_SELECT(priv->dev, SPIDEV_FLASH, true);
/* Send the "Sub-Sector Erase (SSE)" instruction */
(void)SPI_SEND(priv->dev, M25P_SSE);
/* Send the sector offset high byte first. For all of the supported
* parts, the sector number is completely contained in the first byte
* and the values used in the following two bytes don't really matter.
*/
(void)SPI_SEND(priv->dev, (offset >> 16) & 0xff);
(void)SPI_SEND(priv->dev, (offset >> 8) & 0xff);
(void)SPI_SEND(priv->dev, offset & 0xff);
/* Deselect the FLASH */
SPI_SELECT(priv->dev, SPIDEV_FLASH, false);
fvdbg("Erased\n");
}
#endif
/************************************************************************************
* Name: m25p_bulkerase
************************************************************************************/
@@ -654,7 +622,7 @@ static inline void m25p_pagewrite(struct m25p_dev_s *priv, FAR const uint8_t *bu
* Name: m25p_bytewrite
************************************************************************************/
#ifdef CONFIG_M25P_BYTEWRITE
#ifdef CONFIG_MTD_BYTE_WRITE
static inline void m25p_bytewrite(struct m25p_dev_s *priv, FAR const uint8_t *buffer,
off_t offset, uint16_t count)
{
@@ -711,13 +679,55 @@ static int m25p_erase(FAR struct mtd_dev_s *dev, off_t startblock, size_t nblock
/* Lock access to the SPI bus until we complete the erase */
m25p_lock(priv->dev);
while (blocksleft-- > 0)
while (blocksleft > 0)
{
/* Erase each sector */
#ifdef CONFIG_MTD_SMART
size_t sectorboundry;
size_t blkper;
m25p_sectorerase(priv, startblock);
/* If we have a smaller erase size, then we will find as many full
* sector erase blocks as possible to speed up the process instead of
* erasing everything in smaller chunks.
*/
if (priv->subsectorshift > 0)
{
blkper = 1 << (priv->sectorshift - priv->subsectorshift);
sectorboundry = (startblock + blkper - 1) / blkper;
sectorboundry *= blkper;
/* If we are on a sector boundry and have at least a full sector
* of blocks left to erase, then we can do a full sector erase.
*/
if (startblock == sectorboundry && blocksleft >= blkper)
{
/* Do a full sector erase */
m25p_sectorerase(priv, startblock / blkper, M25P_SE);
startblock += blkper;
blocksleft -= blkper;
continue;
}
else
{
/* Just do a sub-sector erase */
m25p_sectorerase(priv, startblock, M25P_SSE);
startblock++;
blocksleft--;
continue;
}
}
#endif
/* Not using sub-sector erase. Erase each full sector */
m25p_sectorerase(priv, startblock, M25P_SE);
startblock++;
blocksleft--;
}
m25p_unlock(priv->dev);
return (int)nblocks;
}
@@ -741,6 +751,7 @@ static ssize_t m25p_bread(FAR struct mtd_dev_s *dev, off_t startblock, size_t nb
{
return nbytes >> priv->pageshift;
}
return (int)nbytes;
}
@@ -766,8 +777,8 @@ static ssize_t m25p_bwrite(FAR struct mtd_dev_s *dev, off_t startblock, size_t n
buffer += pagesize;
startblock++;
}
m25p_unlock(priv->dev);
m25p_unlock(priv->dev);
return nblocks;
}
@@ -817,6 +828,78 @@ static ssize_t m25p_read(FAR struct mtd_dev_s *dev, off_t offset, size_t nbytes,
return nbytes;
}
/************************************************************************************
* Name: m25p_write
************************************************************************************/
#ifdef CONFIG_MTD_BYTE_WRITE
static ssize_t m25p_write(FAR struct mtd_dev_s *dev, off_t offset, size_t nbytes,
FAR const uint8_t *buffer)
{
FAR struct m25p_dev_s *priv = (FAR struct m25p_dev_s *)dev;
int startpage;
int endpage;
int count;
int index;
int pagesize;
int bytestowrite;
fvdbg("offset: %08lx nbytes: %d\n", (long)offset, (int)nbytes);
/* We must test if the offset + count crosses one or more pages
* and perform individual writes. The devices can only write in
* page increments.
*/
startpage = offset / (1 << priv->pageshift);
endpage = (offset + nbytes) / (1 << priv->pageshift);
if (startpage == endpage)
{
/* All bytes within one programmable page. Just do the write. */
m25p_bytewrite(priv, buffer, offset, nbytes);
}
else
{
/* Write the 1st partial-page */
count = nbytes;
pagesize = (1 << priv->pageshift);
bytestowrite = pagesize - (offset & (pagesize-1));
m25p_bytewrite(priv, buffer, offset, bytestowrite);
/* Update offset and count */
offset += bytestowrite;
count -= bytestowrite;
index = bytestowrite;
/* Write full pages */
while (count >= pagesize)
{
m25p_bytewrite(priv, &buffer[index], offset, pagesize);
/* Update offset and count */
offset += pagesize;
count -= pagesize;
index += pagesize;
}
/* Now write any partial page at the end */
if (count > 0)
{
m25p_bytewrite(priv, &buffer[index], offset, count);
}
}
return nbytes;
}
#endif /* CONFIG_MTD_BYTE_WRITE */
/************************************************************************************
* Name: m25p_ioctl
************************************************************************************/
@@ -844,15 +927,22 @@ static int m25p_ioctl(FAR struct mtd_dev_s *dev, int cmd, unsigned long arg)
* appear so.
*/
geo->blocksize = (1 << priv->pageshift);
geo->erasesize = (1 << priv->sectorshift);
geo->neraseblocks = priv->nsectors;
#ifdef CONFIG_M25P_SUBSECTOR_ERASE
geo->subsectorsize = (1 << priv->subsectorshift);
geo->nsubsectors = priv->nsectors * (1 << (priv->sectorshift -
priv->subsectorshift));
geo->blocksize = (1 << priv->pageshift);
#ifdef CONFIG_MTD_SMART
if (priv->subsectorshift > 0)
{
geo->erasesize = (1 << priv->subsectorshift);
geo->neraseblocks = priv->nsectors * (1 << (priv->sectorshift -
priv->subsectorshift));
}
else
#endif
ret = OK;
{
geo->erasesize = (1 << priv->sectorshift);
geo->neraseblocks = priv->nsectors;
}
ret = OK;
fvdbg("blocksize: %d erasesize: %d neraseblocks: %d\n",
geo->blocksize, geo->erasesize, geo->neraseblocks);
@@ -870,97 +960,6 @@ static int m25p_ioctl(FAR struct mtd_dev_s *dev, int cmd, unsigned long arg)
}
break;
#ifdef CONFIG_FS_SMARTFS
case MTDIOC_GETCAPS:
{
ret = 0;
#ifdef CONFIG_M25P_BYTEWRITE
ret |= MTDIOC_CAPS_BYTEWRITE;
#endif
#ifdef CONFIG_M25P_SUBSECTOR_ERASE
ret |= MTDIOC_CAPS_SECTERASE;
#endif
break;
}
#endif /* CONFIG_FS_SMARTFS */
#ifdef CONFIG_M25P_SUBSECTOR_ERASE
case MTDIOC_SECTERASE:
{
m25p_subsectorerase(priv, (off_t) arg);
ret = OK;
break;
}
#endif
#ifdef CONFIG_M25P_BYTEWRITE
case MTDIOC_BYTEWRITE:
{
struct mtd_byte_write_s *bytewrite = (struct mtd_byte_write_s *) arg;
int startpage;
int endpage;
int count;
int index;
int pagesize;
int bytestowrite;
size_t offset;
/* We must test if the offset + count crosses one or more pages
* and perform individual writes. The devices can only write in
* page increments.
*/
startpage = bytewrite->offset / (1 << priv->pageshift);
endpage = (bytewrite->offset + bytewrite->count) / (1 << priv->pageshift);
if (startpage == endpage)
{
m25p_bytewrite(priv, bytewrite->buffer, bytewrite->offset,
bytewrite->count);
}
else
{
/* Write the 1st partial-page */
count = bytewrite->count;
pagesize = (1 << priv->pageshift);
offset = bytewrite->offset;
bytestowrite = pagesize - (bytewrite->offset & (pagesize-1));
m25p_bytewrite(priv, bytewrite->buffer, offset, bytestowrite);
/* Update offset and count */
offset += bytestowrite;
count -= bytestowrite;
index = bytestowrite;
/* Write full pages */
while (count >= pagesize)
{
m25p_bytewrite(priv, &bytewrite->buffer[index], offset, pagesize);
/* Update offset and count */
offset += pagesize;
count -= pagesize;
index += pagesize;
}
/* Now write any partial page at the end */
if (count > 0)
{
m25p_bytewrite(priv, &bytewrite->buffer[index], offset, count);
}
}
ret = OK;
break;
}
#endif
case MTDIOC_XIPBASE:
default:
ret = -ENOTTY; /* Bad command */
@@ -1010,6 +1009,9 @@ FAR struct mtd_dev_s *m25p_initialize(FAR struct spi_dev_s *dev)
priv->mtd.bread = m25p_bread;
priv->mtd.bwrite = m25p_bwrite;
priv->mtd.read = m25p_read;
#ifdef CONFIG_MTD_BYTE_WRITE
priv->mtd.write = m25p_write;
#endif
priv->mtd.ioctl = m25p_ioctl;
priv->dev = dev;
+10 -1
View File
@@ -137,7 +137,7 @@ static bool part_bytecheck(FAR struct mtd_partition_s *priv, off_t byoff)
erasesize = priv->blocksize * priv->blkpererase;
readend = (byoff + erasesize - 1) / erasesize;
return readend < priv->neraseblocks;
return readend <= priv->neraseblocks;
}
/****************************************************************************
@@ -410,6 +410,12 @@ static int part_ioctl(FAR struct mtd_dev_s *dev, int cmd, unsigned long arg)
* sector count (where the size of a sector is provided the by parent MTD
* driver).
*
* NOTE: Since there may be a number of MTD partition drivers operating on
* the same, underlying FLASH driver, that FLASH driver must be capable
* of enforcing mutually exclusive access to the FLASH device. Without
* partitions, that mutual exclusion would be provided by the file system
* above the FLASH driver.
*
****************************************************************************/
FAR struct mtd_dev_s *mtd_partition(FAR struct mtd_dev_s *mtd, off_t firstblock,
@@ -483,6 +489,9 @@ FAR struct mtd_dev_s *mtd_partition(FAR struct mtd_dev_s *mtd, off_t firstblock,
part->child.bwrite = part_bwrite;
part->child.read = mtd->read ? part_read : NULL;
part->child.ioctl = part_ioctl;
#ifdef CONFIG_MTD_BYTE_WRITE
part->child.write = mtd->write ? part_write : NULL;
#endif
part->parent = mtd;
part->firstblock = erasestart * blkpererase;
+35 -20
View File
@@ -228,7 +228,7 @@ static int ram_erase(FAR struct mtd_dev_s *dev, off_t startblock, size_t nblocks
* Name: ram_readbytes
****************************************************************************/
#ifdef CONFIG_RAMMTD_SMART
#ifdef CONFIG_MTD_SMART
static ssize_t ram_read_bytes(FAR struct mtd_dev_s *dev, off_t offset,
size_t nbytes, FAR uint8_t *buf)
{
@@ -328,6 +328,34 @@ static ssize_t ram_bwrite(FAR struct mtd_dev_s *dev, off_t startblock,
return nblocks;
}
/****************************************************************************
* Name: ram_bytewrite
****************************************************************************/
#ifdef CONFIG_MTD_BYTE_WRITE
static ssize_t ram_bytewrite(FAR struct mtd_dev_s *dev, off_t offset,
size_t nbytes, FAR const uint8_t *buf)
{
FAR struct ram_dev_s *priv = (FAR struct ram_dev_s *)dev;
off_t maxaddr;
DEBUGASSERT(dev && buf);
/* Don't let the write exceed the size of the ram buffer */
maxaddr = priv->nblocks * CONFIG_RAMMTD_ERASESIZE;
if (offset + nbytes > maxaddr)
{
return 0;
}
/* Then write the data to RAM */
ram_write(&priv->start[offset], buf, nbytes);
return nbytes;
}
#endif
/****************************************************************************
* Name: ram_ioctl
****************************************************************************/
@@ -380,24 +408,6 @@ static int ram_ioctl(FAR struct mtd_dev_s *dev, int cmd, unsigned long arg)
}
break;
#ifdef CONFIG_RAMMTD_SMART
case MTDIOC_GETCAPS:
{
ret = MTDIOC_CAPS_BYTEWRITE;
break;
}
case MTDIOC_BYTEWRITE:
{
struct mtd_byte_write_s *bytewrite = (struct mtd_byte_write_s *) arg;
ram_write(&priv->start[bytewrite->offset], bytewrite->buffer,
bytewrite->count);
ret = OK;
break;
}
#endif
default:
ret = -ENOTTY; /* Bad command */
break;
@@ -454,9 +464,14 @@ FAR struct mtd_dev_s *rammtd_initialize(FAR uint8_t *start, size_t size)
priv->mtd.bwrite = ram_bwrite;
priv->mtd.ioctl = ram_ioctl;
priv->mtd.erase = ram_erase;
#ifdef CONFIG_MTD_BYTE_WRITE
priv->mtd.write = ram_bytewrite;
#endif
#ifdef CONFIG_RAMMTD_SMART
#ifdef CONFIG_MTD_SMART
priv->mtd.read = ram_read_bytes;
#else
priv->mtd.read = NULL;
#endif
priv->start = start;
+178 -135
View File
File diff suppressed because it is too large Load Diff