summaryrefslogtreecommitdiff
path: root/nuttx/drivers/mtd/m25px.c
diff options
context:
space:
mode:
Diffstat (limited to 'nuttx/drivers/mtd/m25px.c')
-rw-r--r--nuttx/drivers/mtd/m25px.c330
1 files changed, 166 insertions, 164 deletions
diff --git a/nuttx/drivers/mtd/m25px.c b/nuttx/drivers/mtd/m25px.c
index bfe21a7dd..17f7a0672 100644
--- a/nuttx/drivers/mtd/m25px.c
+++ b/nuttx/drivers/mtd/m25px.c
@@ -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,55 +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 */
-
- (void)SPI_SEND(priv->dev, M25P_SE);
-
- /* 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");
-}
-
-/************************************************************************************
- * 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.
+ /* Send the "Sector Erase (SE)" or Sub-Sector Erase (SSE) instruction
+ * that was passed in as the erase type.
*/
- 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);
+ (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
@@ -566,7 +535,6 @@ static inline void m25p_subsectorerase(struct m25p_dev_s *priv, off_t subsector)
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;
+
+ /* 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;
- m25p_sectorerase(priv, startblock);
+ /* 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;
}
@@ -818,6 +829,78 @@ static ssize_t m25p_read(FAR struct mtd_dev_s *dev, off_t offset, size_t 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;