drivers/mtd/gd25.c: Add 4byte address operation for capacity larger than 128Mbit.
This commit is contained in:
parent
267b440f0a
commit
6d63ba711b
@ -78,6 +78,7 @@
|
||||
#define GD25_WREN 0x06 /* Write enable */
|
||||
#define GD25_WRDI 0x04 /* Write Disable */
|
||||
#define GD25_RDSR 0x05 /* Read status register */
|
||||
#define GD25_RDSR1 0x35 /* Read status register-1 */
|
||||
#define GD25_WRSR 0x01 /* Write Status Register */
|
||||
#define GD25_RDDATA 0x03 /* Read data bytes */
|
||||
#define GD25_FRD 0x0b /* Higher speed read */
|
||||
@ -90,6 +91,7 @@
|
||||
#define GD25_PURDID 0xab /* Release PD, Device ID */
|
||||
#define GD25_RDMFID 0x90 /* Read Manufacturer / Device */
|
||||
#define GD25_JEDEC_ID 0x9f /* JEDEC ID read */
|
||||
#define GD25_4BEN 0xb7 /* Enable 4-byte Mode */
|
||||
|
||||
/**************************************************************************
|
||||
* GD25 Registers
|
||||
@ -120,6 +122,7 @@
|
||||
|
||||
#define GD25_SR_WIP (1 << 0) /* Bit 0: Write in Progress */
|
||||
#define GD25_SR_WEL (1 << 1) /* Bit 1: Write Enable Latch */
|
||||
#define GD25_SR1_EN4B (1 << 3) /* Bit 3: Enable 4byte address */
|
||||
|
||||
#define GD25_DUMMY 0x00
|
||||
|
||||
@ -151,6 +154,7 @@ struct gd25_dev_s
|
||||
FAR struct spi_dev_s *spi; /* Saved SPI interface instance */
|
||||
uint16_t nsectors; /* Number of erase sectors */
|
||||
uint8_t prev_instr; /* Previous instruction given to GD25 device */
|
||||
bool addr_4byte; /* True: Use Four-byte address */
|
||||
};
|
||||
|
||||
/**************************************************************************
|
||||
@ -168,7 +172,8 @@ static void gd25_unprotect(FAR struct gd25_dev_s *priv);
|
||||
static uint8_t gd25_waitwritecomplete(FAR struct gd25_dev_s *priv);
|
||||
static inline void gd25_wren(FAR struct gd25_dev_s *priv);
|
||||
static inline void gd25_wrdi(FAR struct gd25_dev_s *priv);
|
||||
static bool gd25_is_erased(struct gd25_dev_s *priv, off_t address, off_t size);
|
||||
static bool gd25_is_erased(FAR struct gd25_dev_s *priv, off_t address,
|
||||
off_t size);
|
||||
static void gd25_sectorerase(FAR struct gd25_dev_s *priv, off_t offset);
|
||||
static inline int gd25_chiperase(FAR struct gd25_dev_s *priv);
|
||||
static void gd25_byteread(FAR struct gd25_dev_s *priv, FAR uint8_t *buffer,
|
||||
@ -177,6 +182,8 @@ static void gd25_byteread(FAR struct gd25_dev_s *priv, FAR uint8_t *buffer,
|
||||
static void gd25_pagewrite(FAR struct gd25_dev_s *priv,
|
||||
FAR const uint8_t *buffer, off_t address, size_t nbytes);
|
||||
#endif
|
||||
static inline uint8_t gd25_rdsr(FAR struct gd25_dev_s *priv, uint32_t id);
|
||||
static inline void gd25_4ben(FAR struct gd25_dev_s *priv);
|
||||
|
||||
/* MTD driver methods */
|
||||
|
||||
@ -188,16 +195,13 @@ static ssize_t gd25_bwrite(FAR struct mtd_dev_s *dev, off_t startblock,
|
||||
size_t nblocks, FAR const uint8_t *buf);
|
||||
static ssize_t gd25_read(FAR struct mtd_dev_s *dev, off_t offset,
|
||||
size_t nbytes, FAR uint8_t *buffer);
|
||||
static int gd25_ioctl(FAR struct mtd_dev_s *dev, int cmd, unsigned long arg);
|
||||
static int gd25_ioctl(FAR struct mtd_dev_s *dev, int cmd,
|
||||
unsigned long arg);
|
||||
#ifdef CONFIG_MTD_BYTE_WRITE
|
||||
static ssize_t gd25_write(FAR struct mtd_dev_s *dev, off_t offset,
|
||||
size_t nbytes, FAR const uint8_t *buffer);
|
||||
#endif
|
||||
|
||||
/**************************************************************************
|
||||
* Private Data
|
||||
**************************************************************************/
|
||||
|
||||
/**************************************************************************
|
||||
* Private Functions
|
||||
**************************************************************************/
|
||||
@ -229,7 +233,7 @@ static inline void gd25_unlock(FAR struct spi_dev_s *spi)
|
||||
* Name: gd25_readid
|
||||
**************************************************************************/
|
||||
|
||||
static inline int gd25_readid(struct gd25_dev_s *priv)
|
||||
static inline int gd25_readid(FAR struct gd25_dev_s *priv)
|
||||
{
|
||||
uint16_t manufacturer;
|
||||
uint16_t memory;
|
||||
@ -294,6 +298,22 @@ static inline int gd25_readid(struct gd25_dev_s *priv)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* Capacity greater than 16MB, Enable four-byte address */
|
||||
|
||||
if (priv->nsectors > GD25_NSECTORS_128MBIT)
|
||||
{
|
||||
gd25_4ben(priv);
|
||||
|
||||
if ((gd25_rdsr(priv, 1) & GD25_SR1_EN4B) != GD25_SR1_EN4B)
|
||||
{
|
||||
ferr("ERROR: capacity %02x: Can't enable 4-byte mode!\n",
|
||||
capacity);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
priv->addr_4byte = true;
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
@ -344,20 +364,13 @@ static void gd25_unprotect(FAR struct gd25_dev_s *priv)
|
||||
* Name: gd25_waitwritecomplete
|
||||
**************************************************************************/
|
||||
|
||||
static uint8_t gd25_waitwritecomplete(struct gd25_dev_s *priv)
|
||||
static uint8_t gd25_waitwritecomplete(FAR struct gd25_dev_s *priv)
|
||||
{
|
||||
uint8_t status;
|
||||
|
||||
do
|
||||
{
|
||||
SPI_SELECT(priv->spi, SPIDEV_FLASH(0), true);
|
||||
|
||||
(void)SPI_SEND(priv->spi, GD25_RDSR);
|
||||
|
||||
status = SPI_SEND(priv->spi, GD25_DUMMY);
|
||||
|
||||
SPI_SELECT(priv->spi, SPIDEV_FLASH(0), false);
|
||||
|
||||
status = gd25_rdsr(priv, 0);
|
||||
if (priv->prev_instr != GD25_PP && (status & GD25_SR_WIP) != 0)
|
||||
{
|
||||
gd25_unlock(priv->spi);
|
||||
@ -370,11 +383,42 @@ static uint8_t gd25_waitwritecomplete(struct gd25_dev_s *priv)
|
||||
return status;
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
* Name: gd25_rdsr
|
||||
**************************************************************************/
|
||||
|
||||
static inline uint8_t gd25_rdsr(FAR struct gd25_dev_s *priv, uint32_t id)
|
||||
{
|
||||
uint8_t status;
|
||||
uint8_t rdsr[2] =
|
||||
{
|
||||
GD25_RDSR, GD25_RDSR1
|
||||
};
|
||||
|
||||
SPI_SELECT(priv->spi, SPIDEV_FLASH(priv->spi_devid), true);
|
||||
(void)SPI_SEND(priv->spi, rdsr[id]);
|
||||
status = SPI_SEND(priv->spi, GD25_DUMMY);
|
||||
SPI_SELECT(priv->spi, SPIDEV_FLASH(priv->spi_devid), false);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
* Name: gd25_4ben
|
||||
**************************************************************************/
|
||||
|
||||
static inline void gd25_4ben(FAR struct gd25_dev_s *priv)
|
||||
{
|
||||
SPI_SELECT(priv->spi, SPIDEV_FLASH(priv->spi_devid), true);
|
||||
(void)SPI_SEND(priv->spi, GD25_4BEN);
|
||||
SPI_SELECT(priv->spi, SPIDEV_FLASH(priv->spi_devid), false);
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
* Name: gd25_wren
|
||||
**************************************************************************/
|
||||
|
||||
static inline void gd25_wren(struct gd25_dev_s *priv)
|
||||
static inline void gd25_wren(FAR struct gd25_dev_s *priv)
|
||||
{
|
||||
SPI_SELECT(priv->spi, SPIDEV_FLASH(0), true);
|
||||
(void)SPI_SEND(priv->spi, GD25_WREN);
|
||||
@ -385,7 +429,7 @@ static inline void gd25_wren(struct gd25_dev_s *priv)
|
||||
* Name: gd25_wrdi
|
||||
**************************************************************************/
|
||||
|
||||
static inline void gd25_wrdi(struct gd25_dev_s *priv)
|
||||
static inline void gd25_wrdi(FAR struct gd25_dev_s *priv)
|
||||
{
|
||||
SPI_SELECT(priv->spi, SPIDEV_FLASH(0), true);
|
||||
(void)SPI_SEND(priv->spi, GD25_WRDI);
|
||||
@ -396,7 +440,8 @@ static inline void gd25_wrdi(struct gd25_dev_s *priv)
|
||||
* Name: gd25_is_erased
|
||||
**************************************************************************/
|
||||
|
||||
static bool gd25_is_erased(struct gd25_dev_s *priv, off_t address, off_t size)
|
||||
static bool gd25_is_erased(FAR struct gd25_dev_s *priv, off_t address,
|
||||
off_t size)
|
||||
{
|
||||
size_t npages = size >> GD25_PAGE_SHIFT;
|
||||
uint32_t erased_32;
|
||||
@ -437,7 +482,7 @@ static bool gd25_is_erased(struct gd25_dev_s *priv, off_t address, off_t size)
|
||||
* Name: gd25_sectorerase
|
||||
**************************************************************************/
|
||||
|
||||
static void gd25_sectorerase(struct gd25_dev_s *priv, off_t sector)
|
||||
static void gd25_sectorerase(FAR struct gd25_dev_s *priv, off_t sector)
|
||||
{
|
||||
off_t address = sector << GD25_SECTOR_SHIFT;
|
||||
|
||||
@ -471,6 +516,11 @@ static void gd25_sectorerase(struct gd25_dev_s *priv, off_t sector)
|
||||
* bits (those corresponding to the sector) have any meaning.
|
||||
*/
|
||||
|
||||
if (priv->addr_4byte)
|
||||
{
|
||||
(void)SPI_SEND(priv->spi, (address >> 24) & 0xff);
|
||||
}
|
||||
|
||||
(void)SPI_SEND(priv->spi, (address >> 16) & 0xff);
|
||||
(void)SPI_SEND(priv->spi, (address >> 8) & 0xff);
|
||||
(void)SPI_SEND(priv->spi, address & 0xff);
|
||||
@ -482,7 +532,7 @@ static void gd25_sectorerase(struct gd25_dev_s *priv, off_t sector)
|
||||
* Name: gd25_chiperase
|
||||
**************************************************************************/
|
||||
|
||||
static inline int gd25_chiperase(struct gd25_dev_s *priv)
|
||||
static inline int gd25_chiperase(FAR struct gd25_dev_s *priv)
|
||||
{
|
||||
/* Wait for any preceding write or erase operation to complete. */
|
||||
|
||||
@ -508,7 +558,7 @@ static inline int gd25_chiperase(struct gd25_dev_s *priv)
|
||||
**************************************************************************/
|
||||
|
||||
static void gd25_byteread(FAR struct gd25_dev_s *priv, FAR uint8_t *buffer,
|
||||
off_t address, size_t nbytes)
|
||||
off_t address, size_t nbytes)
|
||||
{
|
||||
finfo("address: %08lx nbytes: %d\n", (long)address, (int)nbytes);
|
||||
|
||||
@ -534,6 +584,11 @@ static void gd25_byteread(FAR struct gd25_dev_s *priv, FAR uint8_t *buffer,
|
||||
|
||||
/* Send the address high byte first. */
|
||||
|
||||
if (priv->addr_4byte)
|
||||
{
|
||||
(void)SPI_SEND(priv->spi, (address >> 24) & 0xff);
|
||||
}
|
||||
|
||||
(void)SPI_SEND(priv->spi, (address >> 16) & 0xff);
|
||||
(void)SPI_SEND(priv->spi, (address >> 8) & 0xff);
|
||||
(void)SPI_SEND(priv->spi, address & 0xff);
|
||||
@ -556,11 +611,13 @@ static void gd25_byteread(FAR struct gd25_dev_s *priv, FAR uint8_t *buffer,
|
||||
**************************************************************************/
|
||||
|
||||
#ifndef CONFIG_GD25_READONLY
|
||||
static void gd25_pagewrite(struct gd25_dev_s *priv, FAR const uint8_t *buffer,
|
||||
off_t address, size_t nbytes)
|
||||
static void gd25_pagewrite(FAR struct gd25_dev_s *priv,
|
||||
FAR const uint8_t *buffer, off_t address,
|
||||
size_t nbytes)
|
||||
{
|
||||
finfo("address: %08lx nwords: %d\n", (long)address, (int)nbytes);
|
||||
DEBUGASSERT(priv && buffer && (address & 0xff) == 0 && (nbytes & 0xff) == 0);
|
||||
DEBUGASSERT(priv && buffer && (address & 0xff) == 0 &&
|
||||
(nbytes & 0xff) == 0);
|
||||
|
||||
for (; nbytes > 0; nbytes -= GD25_PAGE_SIZE)
|
||||
{
|
||||
@ -581,6 +638,11 @@ static void gd25_pagewrite(struct gd25_dev_s *priv, FAR const uint8_t *buffer,
|
||||
|
||||
/* Send the address high byte first. */
|
||||
|
||||
if (priv->addr_4byte)
|
||||
{
|
||||
(void)SPI_SEND(priv->spi, (address >> 24) & 0xff);
|
||||
}
|
||||
|
||||
(void)SPI_SEND(priv->spi, (address >> 16) & 0xff);
|
||||
(void)SPI_SEND(priv->spi, (address >> 8) & 0xff);
|
||||
(void)SPI_SEND(priv->spi, address & 0xff);
|
||||
@ -604,8 +666,9 @@ static void gd25_pagewrite(struct gd25_dev_s *priv, FAR const uint8_t *buffer,
|
||||
**************************************************************************/
|
||||
|
||||
#if defined(CONFIG_MTD_BYTE_WRITE) && !defined(CONFIG_GD25_READONLY)
|
||||
static inline void gd25_bytewrite(struct gd25_dev_s *priv,
|
||||
FAR const uint8_t *buffer, off_t offset, uint16_t count)
|
||||
static inline void gd25_bytewrite(FAR struct gd25_dev_s *priv,
|
||||
FAR const uint8_t *buffer, off_t offset,
|
||||
uint16_t count)
|
||||
{
|
||||
finfo("offset: %08lx count:%d\n", (long)offset, count);
|
||||
|
||||
@ -630,6 +693,11 @@ static inline void gd25_bytewrite(struct gd25_dev_s *priv,
|
||||
|
||||
/* Send the page offset high byte first. */
|
||||
|
||||
if (priv->addr_4byte)
|
||||
{
|
||||
(void)SPI_SEND(priv->spi, (offset >> 24) & 0xff);
|
||||
}
|
||||
|
||||
(void)SPI_SEND(priv->spi, (offset >> 16) & 0xff);
|
||||
(void)SPI_SEND(priv->spi, (offset >> 8) & 0xff);
|
||||
(void)SPI_SEND(priv->spi, offset & 0xff);
|
||||
@ -648,7 +716,7 @@ static inline void gd25_bytewrite(struct gd25_dev_s *priv,
|
||||
**************************************************************************/
|
||||
|
||||
static int gd25_erase(FAR struct mtd_dev_s *dev, off_t startblock,
|
||||
size_t nblocks)
|
||||
size_t nblocks)
|
||||
{
|
||||
#ifdef CONFIG_GD25_READONLY
|
||||
return -EACESS
|
||||
@ -680,13 +748,14 @@ static int gd25_erase(FAR struct mtd_dev_s *dev, off_t startblock,
|
||||
**************************************************************************/
|
||||
|
||||
static ssize_t gd25_bread(FAR struct mtd_dev_s *dev, off_t startblock,
|
||||
size_t nblocks, FAR uint8_t *buffer)
|
||||
size_t nblocks, FAR uint8_t *buffer)
|
||||
{
|
||||
ssize_t nbytes;
|
||||
|
||||
finfo("startblock: %08lx nblocks: %d\n", (long)startblock, (int)nblocks);
|
||||
|
||||
nbytes = gd25_read(dev, startblock << GD25_PAGE_SHIFT, nblocks << GD25_PAGE_SHIFT, buffer);
|
||||
nbytes = gd25_read(dev, startblock << GD25_PAGE_SHIFT,
|
||||
nblocks << GD25_PAGE_SHIFT, buffer);
|
||||
if (nbytes > 0)
|
||||
{
|
||||
nbytes >>= GD25_PAGE_SHIFT;
|
||||
@ -700,7 +769,7 @@ static ssize_t gd25_bread(FAR struct mtd_dev_s *dev, off_t startblock,
|
||||
**************************************************************************/
|
||||
|
||||
static ssize_t gd25_bwrite(FAR struct mtd_dev_s *dev, off_t startblock,
|
||||
size_t nblocks, FAR const uint8_t *buffer)
|
||||
size_t nblocks, FAR const uint8_t *buffer)
|
||||
{
|
||||
#ifdef CONFIG_GD25_READONLY
|
||||
return -EACCESS;
|
||||
@ -724,8 +793,8 @@ static ssize_t gd25_bwrite(FAR struct mtd_dev_s *dev, off_t startblock,
|
||||
* Name: gd25_read
|
||||
**************************************************************************/
|
||||
|
||||
static ssize_t gd25_read(FAR struct mtd_dev_s *dev, off_t offset, size_t nbytes,
|
||||
FAR uint8_t *buffer)
|
||||
static ssize_t gd25_read(FAR struct mtd_dev_s *dev, off_t offset,
|
||||
size_t nbytes, FAR uint8_t *buffer)
|
||||
{
|
||||
FAR struct gd25_dev_s *priv = (FAR struct gd25_dev_s *)dev;
|
||||
|
||||
@ -747,7 +816,7 @@ static ssize_t gd25_read(FAR struct mtd_dev_s *dev, off_t offset, size_t nbytes,
|
||||
|
||||
#ifdef CONFIG_MTD_BYTE_WRITE
|
||||
static ssize_t gd25_write(FAR struct mtd_dev_s *dev, off_t offset,
|
||||
size_t nbytes, FAR const uint8_t *buffer)
|
||||
size_t nbytes, FAR const uint8_t *buffer)
|
||||
{
|
||||
#ifdef CONFIG_GD25_READONLY
|
||||
return -EACCESS;
|
||||
@ -781,7 +850,7 @@ static ssize_t gd25_write(FAR struct mtd_dev_s *dev, off_t offset,
|
||||
/* Write the 1st partial-page */
|
||||
|
||||
count = nbytes;
|
||||
bytestowrite = GD25_PAGE_SIZE - (offset & (GD25_PAGE_SIZE-1));
|
||||
bytestowrite = GD25_PAGE_SIZE - (offset & (GD25_PAGE_SIZE - 1));
|
||||
gd25_bytewrite(priv, buffer, offset, bytestowrite);
|
||||
|
||||
/* Update offset and count */
|
||||
|
Loading…
x
Reference in New Issue
Block a user