Add an erase block cache to the SST 25 driver

git-svn-id: https://nuttx.svn.sourceforge.net/svnroot/nuttx/trunk@4869 7fd9a85b-ad96-42d3-883c-3090e2eb8679
This commit is contained in:
patacongo 2012-06-25 23:40:39 +00:00
parent 7ec3df6877
commit 32c100bf94

View File

@ -46,6 +46,7 @@
#include <stdbool.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include <debug.h>
@ -149,6 +150,26 @@
# define SST25_SECTOR_SIZE 512 /* Sector size = 512 bytes */
#endif
#define SST25_ERASE_STATE 0xff /* State of FLASH when erased */
/* Cache flags */
#define SST25_CACHE_VALID (1 << 0) /* 1=Cache has valid data */
#define SST25_CACHE_DIRTY (1 << 1) /* 1=Cache is dirty */
#define SST25_CACHE_ERASED (1 << 2) /* 1=Backing FLASH is erased */
#define IS_VALID(p) ((((p)->flags) & SST25_CACHE_VALID) != 0)
#define IS_DIRTY(p) ((((p)->flags) & SST25_CACHE_DIRTY) != 0)
#define IS_ERASED(p) ((((p)->flags) & SST25_CACHE_DIRTY) != 0)
#define SET_VALID(p) do { (p)->flags |= SST25_CACHE_VALID; } while (0)
#define SET_DIRTY(p) do { (p)->flags |= SST25_CACHE_DIRTY; } while (0)
#define SET_ERASED(p) do { (p)->flags |= SST25_CACHE_DIRTY; } while (0)
#define CLR_VALID(p) do { (p)->flags &= ~SST25_CACHE_VALID; } while (0)
#define CLR_DIRTY(p) do { (p)->flags &= ~SST25_CACHE_DIRTY; } while (0)
#define CLR_ERASED(p) do { (p)->flags &= ~SST25_CACHE_DIRTY; } while (0)
/************************************************************************************
* Private Types
************************************************************************************/
@ -166,7 +187,7 @@ struct sst25_dev_s
uint8_t sectorshift; /* Log2 of erase sector size */
#ifdef CONFIG_SST25_SECTOR512 /* Simulate a 512 byte sector */
bool valid; /* Buffered sector valid */
uint8_t flags; /* Buffered sector flags */
uint16_t esectno; /* Erase sector number in the cache*/
FAR uint8_t *sector; /* Allocated sector data */
#endif
@ -184,15 +205,18 @@ static inline int sst25_readid(FAR struct sst25_dev_s *priv);
static void sst25_waitwritecomplete(FAR struct sst25_dev_s *priv);
static inline void sst25_wren(FAR struct sst25_dev_s *priv);
static inline void sst25_wrdi(FAR struct sst25_dev_s *priv);
static inline void sst25_sectorerase(FAR struct sst25_dev_s *priv, off_t offset);
static void sst25_sectorerase(FAR struct sst25_dev_s *priv, off_t offset);
static inline int sst25_chiperase(FAR struct sst25_dev_s *priv);
static void sst25_byteread(FAR struct sst25_dev_s *priv, FAR uint8_t *buffer,
off_t address, size_t nbytes);
static void sst32_wordwrite(FAR struct sst25_dev_s *priv, FAR const uint8_t *buffer,
off_t address, size_t nwords);
#ifdef CONFIG_SST25_SECTOR512 /* Simulate a 512 byte sector */
static FAR uint8_t *sst25_cache(struct sst25_dev_s *priv, off_t address);
#ifdef CONFIG_SST25_SECTOR512
static void sst25_cacheflush(struct sst25_dev_s *priv);
static FAR uint8_t *sst25_cacheread(struct sst25_dev_s *priv, off_t sector);
static void sst25_cacheerase(struct sst25_dev_s *priv, off_t sector);
static void sst32_cachewrite(FAR struct sst25_dev_s *priv, FAR const uint8_t *buffer,
off_t sector, size_t nsectors);
#endif
/* MTD driver methods */
@ -421,7 +445,7 @@ static inline void sst25_wrdi(struct sst25_dev_s *priv)
* Name: sst25_sectorerase
************************************************************************************/
static inline void sst25_sectorerase(struct sst25_dev_s *priv, off_t sector)
static void sst25_sectorerase(struct sst25_dev_s *priv, off_t sector)
{
off_t address = sector << priv->sectorshift;
@ -604,14 +628,40 @@ static void sst32_wordwrite(struct sst25_dev_s *priv, FAR const uint8_t *buffer,
}
/************************************************************************************
* Name: sst25_cache
* Name: sst25_cacheflush
************************************************************************************/
#ifdef CONFIG_SST25_SECTOR512
static FAR uint8_t *sst25_cache(struct sst25_dev_s *priv, off_t sector)
static void sst25_cacheflush(struct sst25_dev_s *priv)
{
/* If the cached is dirty (meaning that it no longer matches the old FLASH contents)
* or was erased (with the cache containing the correct FLASH contents), then write
* the cached erase block to FLASH.
*/
if (IS_DIRTY(priv) || IS_ERASED(priv))
{
/* Write entire erase block to FLASH */
sst32_wordwrite(priv, priv->sector, (off_t)priv->esectno << priv->sectorshift,
(1 << (priv->sectorshift - 1)));
/* The case is no long dirty and the FLASH is no longer erased */
CLR_DIRTY(priv);
CLR_ERASED(priv);
}
}
#endif
/************************************************************************************
* Name: sst25_cacheread
************************************************************************************/
#ifdef CONFIG_SST25_SECTOR512
static FAR uint8_t *sst25_cacheread(struct sst25_dev_s *priv, off_t sector)
{
off_t esectno;
off_t address;
int shift;
int index;
@ -622,20 +672,27 @@ static FAR uint8_t *sst25_cache(struct sst25_dev_s *priv, off_t sector)
shift = priv->sectorshift - SST25_SECTOR_SHIFT;
esectno = sector >> shift;
fvdbg("sector: %ld esectno: %d shift=%d\n", sector, sectno, shift);
fvdbg("sector: %ld esectno: %d shift=%d\n", sector, esectno, shift);
/* Check if this erase block is alread in the cache */
/* Check if the requested erase block is already in the cache */
if (!priv->valid || esectno != priv->esectno)
if (!IS_VALID(priv) || esectno != priv->esectno)
{
/* No.. Flush any dirty erase block currently in the cache */
sst25_cacheflush(priv);
/* Read the erase block into the cache */
sst325_byteread(priv, priv->sector, (esectno << priv->sectorshift), 1 << priv->sectorshift);
sst25_byteread(priv, priv->sector, (esectno << priv->sectorshift), 1 << priv->sectorshift);
/* Mark the sector as cached */
priv->valid = true;
priv->esectno = esectno;
SET_VALID(priv); /* The data in the cache is valid */
CLR_DIRTY(priv); /* It should match the FLASH contents */
CLR_ERASED(priv); /* The underlying FLASH has not been erased */
}
/* Get the index to the 512 sector in the erase block that holds the argument */
@ -648,6 +705,94 @@ static FAR uint8_t *sst25_cache(struct sst25_dev_s *priv, off_t sector)
}
#endif
/************************************************************************************
* Name: sst25_cacheerase
************************************************************************************/
#ifdef CONFIG_SST25_SECTOR512
static void sst25_cacheerase(struct sst25_dev_s *priv, off_t sector)
{
FAR uint8_t *dest;
/* First, make sure that the erase block containing the 512 byte sector is in
* the cache.
*/
dest = sst25_cacheread(priv, sector);
/* Erase the block containing this sector if it is not already erased.
* The erased indicated will be cleared when the data from the erase sector
* is read into the cache and set here when we erase the block.
*/
if (!IS_ERASED(priv))
{
off_t esectno = sector >> (priv->sectorshift - SST25_SECTOR_SHIFT);
fvdbg("sector: %ld esectno: %d\n", sector, esectno);
sst25_sectorerase(priv, esectno);
SET_ERASED(priv);
}
/* Put the cached sector data into the erase state and mart the cache as dirty
* (but don't update the FLASH yet. The caller will do that at a more optimal
* time).
*/
memset(dest, SST25_ERASE_STATE, SST25_SECTOR_SIZE);
SET_DIRTY(priv);
}
#endif
/************************************************************************************
* Name: sst25_cachewrite
************************************************************************************/
#ifdef CONFIG_SST25_SECTOR512
static void sst32_cachewrite(FAR struct sst25_dev_s *priv, FAR const uint8_t *buffer,
off_t sector, size_t nsectors)
{
FAR uint8_t *dest;
for (; nsectors > 0; nsectors--)
{
/* First, make sure that the erase block containing 512 byte sector is in
* memory.
*/
dest = sst25_cacheread(priv, sector);
/* Erase the block containing this sector if it is not already erased.
* The erased indicated will be cleared when the data from the erase sector
* is read into the cache and set here when we erase the sector.
*/
if (!IS_ERASED(priv))
{
off_t esectno = sector >> (priv->sectorshift - SST25_SECTOR_SHIFT);
fvdbg("sector: %ld esectno: %d\n", sector, esectno);
sst25_sectorerase(priv, esectno);
SET_ERASED(priv);
}
/* Copy the new sector data into cached erase block */
memcpy(dest, buffer, SST25_SECTOR_SIZE);
SET_DIRTY(priv);
/* Set up for the next 512 byte sector */
buffer += SST25_SECTOR_SIZE;
sector++;
}
/* Flush the last erase block left in the cache */
sst25_cacheflush(priv);
}
#endif
/************************************************************************************
* Name: sst25_erase
************************************************************************************/
@ -662,14 +807,25 @@ static int sst25_erase(FAR struct mtd_dev_s *dev, off_t startblock, size_t nbloc
/* Lock access to the SPI bus until we complete the erase */
sst25_lock(priv->dev);
while (blocksleft-- > 0)
{
/* Erase each sector */
#ifdef CONFIG_SST25_SECTOR512
sst25_cacheerase(priv, startblock);
#else
sst25_sectorerase(priv, startblock);
#endif
startblock++;
}
#ifdef CONFIG_SST25_SECTOR512
/* Flush the last erase block left in the cache */
sst25_cacheflush(priv);
#endif
sst25_unlock(priv->dev);
return (int)nblocks;
}
@ -681,6 +837,21 @@ static int sst25_erase(FAR struct mtd_dev_s *dev, off_t startblock, size_t nbloc
static ssize_t sst25_bread(FAR struct mtd_dev_s *dev, off_t startblock, size_t nblocks,
FAR uint8_t *buffer)
{
#ifdef CONFIG_SST25_SECTOR512
ssize_t nbytes;
fvdbg("startblock: %08lx nblocks: %d\n", (long)startblock, (int)nblocks);
/* On this device, we can handle the block read just like the byte-oriented read */
nbytes = sst25_read(dev, startblock << SST25_SECTOR_SHIFT, nblocks << SST25_SECTOR_SHIFT, buffer);
if (nbytes > 0)
{
return nbytes >> SST25_SECTOR_SHIFT;
}
return (int)nbytes;
#else
FAR struct sst25_dev_s *priv = (FAR struct sst25_dev_s *)dev;
ssize_t nbytes;
@ -688,21 +859,14 @@ static ssize_t sst25_bread(FAR struct mtd_dev_s *dev, off_t startblock, size_t n
/* On this device, we can handle the block read just like the byte-oriented read */
#ifdef CONFIG_SST25_SECTOR512
nbytes = sst25_read(dev, startblock << SST25_SECTOR_SHIFT, nblocks << SST25_SECTOR_SHIFT, buffer);
if (nbytes > 0)
{
return nbytes >> SST25_SECTOR_SHIFT;
}
#else
nbytes = sst25_read(dev, startblock << priv->sectorshift, nblocks << priv->sectorshift, buffer);
if (nbytes > 0)
{
return nbytes >> priv->sectorshift;
}
#endif
return (int)nbytes;
#endif
}
/************************************************************************************
@ -713,7 +877,6 @@ static ssize_t sst25_bwrite(FAR struct mtd_dev_s *dev, off_t startblock, size_t
FAR const uint8_t *buffer)
{
FAR struct sst25_dev_s *priv = (FAR struct sst25_dev_s *)dev;
size_t nwords;
fvdbg("startblock: %08lx nblocks: %d\n", (long)startblock, (int)nblocks);
@ -721,11 +884,12 @@ static ssize_t sst25_bwrite(FAR struct mtd_dev_s *dev, off_t startblock, size_t
sst25_lock(priv->dev);
#ifdef CONFIG_SST25_SECTOR512
nwords = (nblocks << (SST25_SECTOR_SHIFT - 1));
sst32_wordwrite(priv, buffer, startblock << SST25_SECTOR_SHIFT, nwords);
sst32_cachewrite(priv, buffer, startblock, nblocks);
#else
nwords = (nblocks << (priv->sectorshift - 1));
sst32_wordwrite(priv, buffer, startblock << priv->sectorshift, nwords);
{
size_t nwords = (nblocks << (priv->sectorshift - 1));
sst32_wordwrite(priv, buffer, startblock << priv->sectorshift, nwords);
}
#endif
sst25_unlock(priv->dev);
@ -873,7 +1037,7 @@ FAR struct mtd_dev_s *sst25_initialize(FAR struct spi_dev_s *dev)
#ifdef CONFIG_SST25_SECTOR512 /* Simulate a 512 byte sector */
else
{
/* Allocate a sector buffer */
/* Allocate a buffer for the erase block cache */
priv->sector = (FAR uint8_t *)kmalloc(1 << priv->sectorshift);
if (!priv->sector)