drivers/mtd/gd25: add QSPI support

Add QSPI mode to the GD25 MTD driver alongside the existing SPI path.
When CONFIG_GD25_QSPI is selected, sector erase, chip erase, byte read,
page write, and byte write all use the QSPI command/memory interfaces
instead of SPI. Reads use the quad I/O fast-read command (1-4-4) and
writes use the quad page-program command (1-1-4) via QSPIMEM_QUADDATA.
A new Kconfig option enables the QE bit in SR2 at initialisation so the
quad I/O pins are active.

Signed-off-by: Sammy Tran <sammytran@geotab.com>
This commit is contained in:
Sammy Tran 2026-06-19 16:42:25 -04:00 committed by Alan C. Assis
parent c9caf2c5dc
commit 2a7cf05a20
3 changed files with 416 additions and 65 deletions

View file

@ -1393,19 +1393,39 @@ config W25_DEBUG
endif # MTD_W25
config MTD_GD25
bool "SPI-based GD25 FLASH"
bool "SPI/QuadSPI-based GigaDevice GD25 FLASH"
default n
select SPI
if MTD_GD25
config GD25_QSPI
bool "Use QuadSPI interface for GD25"
default n
select QSPI
---help---
Enable to use the QuadSPI (QSPI) interface instead of SPI.
When enabled, gd25_initialize() accepts a qspi_dev_s pointer
and bool unprotect. When disabled (default), SPI is used.
config GD25_SPIMODE
int "GD25 SPI Mode"
default 0
depends on !GD25_QSPI
config GD25_SPIFREQUENCY
int "GD25 SPI Frequency"
default 20000000
depends on !GD25_QSPI
config GD25_QSPIMODE
int "GD25 QuadSPI Mode"
default 0
depends on GD25_QSPI
config GD25_QSPIFREQUENCY
int "GD25 QuadSPI Frequency"
default 20000000
depends on GD25_QSPI
config GD25_READONLY
bool "GD25 Read-Only FLASH"

View file

@ -41,7 +41,11 @@
#include <nuttx/kmalloc.h>
#include <nuttx/signal.h>
#include <nuttx/fs/ioctl.h>
#include <nuttx/spi/spi.h>
#ifdef CONFIG_GD25_QSPI
# include <nuttx/spi/qspi.h>
#else
# include <nuttx/spi/spi.h>
#endif
#include <nuttx/mtd/mtd.h>
/***************************************************************************
@ -56,6 +60,14 @@
# define CONFIG_GD25_SPIFREQUENCY 20000000
#endif
#ifndef CONFIG_GD25_QSPIMODE
# define CONFIG_GD25_QSPIMODE 0
#endif
#ifndef CONFIG_GD25_QSPIFREQUENCY
# define CONFIG_GD25_QSPIFREQUENCY 20000000
#endif
/***************************************************************************
* GD25 Instructions
***************************************************************************/
@ -65,20 +77,36 @@
#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_RDSR1 0x35 /* Read status register-2 */
#define GD25_WRSR 0x01 /* Write Status Register */
#define GD25_RDDATA 0x03 /* Read data bytes */
#define GD25_FRD 0x0b /* Higher speed read */
#define GD25_FRD 0x0b /* Fast read (1-wire) */
#define GD25_FRDD 0x3b /* Fast read, dual output */
#define GD25_PP 0x02 /* Program page */
#define GD25_SE 0x20 /* Sector erase (4KB) */
#define GD25_BE 0xd8 /* Block Erase (64KB) */
#define GD25_QOFRD 0x6b /* Quad output fast read */
#define GD25_QIOFRD 0xeb /* Quad I/O fast read, 3-byte */
#define GD25_QIOFRD4B 0xec /* Quad I/O fast read, 4-byte */
#define GD25_PP 0x02 /* Program page (1-1-1), 3-byte */
#define GD25_PP4B 0x12 /* Program page (1-1-1), 4-byte */
#define GD25_QPP 0x32 /* Quad page program (1-1-4), 3-byte */
#define GD25_QPP4B 0x34 /* Quad page program (1-1-4), 4-byte */
#define GD25_SE 0x20 /* Sector erase (4KB), 3-byte */
#define GD25_SE4B 0x21 /* Sector erase (4KB), 4-byte */
#define GD25_BE 0xd8 /* Block Erase (64KB), 3-byte */
#define GD25_BE4B 0xdc /* Block Erase (64KB), 4-byte */
#define GD25_CE 0xc7 /* Chip erase */
#define GD25_PD 0xb9 /* Power down */
#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 */
#define GD25_4BEXT 0xe9 /* Exit 4-byte Mode */
/* Dummy clock cycles for Quad I/O Fast Read (EBh/ECh).
* Mode byte (2 clocks on 4-wire) + 4 additional = 6 total, matching
* the MX25RXX driver's default and safe up to ~80MHz at VCC=3.3V.
*/
#define GD25_QIOFRD_DUMMIES 6
/***************************************************************************
* GD25 Registers
@ -112,6 +140,11 @@
#define GD25_SR1_EN4B (1 << 3) /* Bit 3: Enable 4byte address */
#define GD25Q_SR1_EN4B (1 << 0) /* Bit 0: Enable 4byte address GD25Q memories */
/* Status Register 2 (SR2) bit definitions */
#define GD25_SR2_QE (1 << 1) /* Bit 1 of SR2 (S9): Quad Enable, non-volatile */
#define GD25_SR2_PRESERVE_MASK 0x78 /* Preserve SRP1 (bit6) and LB3-LB1 (bits5-3) */
#define GD25_DUMMY 0x00
/***************************************************************************
@ -138,13 +171,18 @@
struct gd25_dev_s
{
struct mtd_dev_s mtd; /* MTD interface */
FAR struct spi_dev_s *spi; /* Saved SPI interface instance */
uint32_t spi_devid; /* Chip select inputs */
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 */
uint8_t memory; /* memory type read from device */
struct mtd_dev_s mtd; /* MTD interface */
#ifdef CONFIG_GD25_QSPI
FAR struct qspi_dev_s *qspi; /* Saved QSPI interface instance */
FAR uint8_t *cmdbuf; /* DMA-safe command buffer */
#else
FAR struct spi_dev_s *spi; /* Saved SPI interface instance */
uint32_t spi_devid; /* Chip select inputs */
#endif
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 */
uint8_t memory; /* memory type read from device */
};
/***************************************************************************
@ -153,10 +191,13 @@ struct gd25_dev_s
/* Helpers */
static inline void gd25_purdid(FAR struct gd25_dev_s *priv);
static inline void gd25_pd(FAR struct gd25_dev_s *priv);
#ifdef CONFIG_GD25_QSPI
static void gd25_lock(FAR struct qspi_dev_s *qspi);
static inline void gd25_unlock(FAR struct qspi_dev_s *qspi);
#else
static void gd25_lock(FAR struct spi_dev_s *spi);
static inline void gd25_unlock(FAR struct spi_dev_s *spi);
#endif
static inline int gd25_readid(FAR struct gd25_dev_s *priv);
#ifndef CONFIG_GD25_READONLY
static void gd25_unprotect(FAR struct gd25_dev_s *priv);
@ -198,33 +239,24 @@ static ssize_t gd25_write(FAR struct mtd_dev_s *dev, off_t offset,
* Private Functions
***************************************************************************/
/***************************************************************************
* Name: gd25_purdid
***************************************************************************/
static inline void gd25_purdid(FAR struct gd25_dev_s *priv)
{
SPI_SELECT(priv->spi, SPIDEV_FLASH(priv->spi_devid), true);
SPI_SEND(priv->spi, GD25_PURDID);
SPI_SELECT(priv->spi, SPIDEV_FLASH(priv->spi_devid), false);
up_udelay(20);
}
/***************************************************************************
* Name: gd25_pd
***************************************************************************/
static inline void gd25_pd(FAR struct gd25_dev_s *priv)
{
SPI_SELECT(priv->spi, SPIDEV_FLASH(priv->spi_devid), true);
SPI_SEND(priv->spi, GD25_PD);
SPI_SELECT(priv->spi, SPIDEV_FLASH(priv->spi_devid), false);
}
/***************************************************************************
* Name: gd25_lock
***************************************************************************/
#ifdef CONFIG_GD25_QSPI
static void gd25_lock(FAR struct qspi_dev_s *qspi)
{
QSPI_LOCK(qspi, true);
QSPI_SETMODE(qspi, CONFIG_GD25_QSPIMODE);
QSPI_SETBITS(qspi, 8);
QSPI_SETFREQUENCY(qspi, CONFIG_GD25_QSPIFREQUENCY);
}
static inline void gd25_unlock(FAR struct qspi_dev_s *qspi)
{
QSPI_LOCK(qspi, false);
}
#else
static void gd25_lock(FAR struct spi_dev_s *spi)
{
SPI_LOCK(spi, true);
@ -239,14 +271,11 @@ static void gd25_lock(FAR struct spi_dev_s *spi)
#endif
}
/***************************************************************************
* Name: gd25_unlock
***************************************************************************/
static inline void gd25_unlock(FAR struct spi_dev_s *spi)
{
SPI_LOCK(spi, false);
}
#endif /* CONFIG_GD25_QSPI */
/***************************************************************************
* Name: gd25_readid
@ -259,10 +288,26 @@ static inline int gd25_readid(FAR struct gd25_dev_s *priv)
uint16_t capacity;
int ret = -ENODEV;
#ifdef CONFIG_GD25_QSPI
struct qspi_cmdinfo_s cmdinfo;
gd25_lock(priv->qspi);
cmdinfo.flags = QSPICMD_READDATA;
cmdinfo.addrlen = 0;
cmdinfo.cmd = GD25_JEDEC_ID;
cmdinfo.buflen = 3;
cmdinfo.addr = 0;
cmdinfo.buffer = priv->cmdbuf;
QSPI_COMMAND(priv->qspi, &cmdinfo);
manufacturer = priv->cmdbuf[0];
memory = priv->cmdbuf[1];
capacity = priv->cmdbuf[2];
#else
/* Lock and configure the SPI bus */
gd25_lock(priv->spi);
gd25_purdid(priv);
/* Select this FLASH part. */
@ -278,6 +323,7 @@ static inline int gd25_readid(FAR struct gd25_dev_s *priv)
/* Deselect the FLASH and unlock the bus */
SPI_SELECT(priv->spi, SPIDEV_FLASH(priv->spi_devid), false);
#endif /* CONFIG_GD25_QSPI */
finfo("manufacturer: %02x memory: %02x capacity: %02x\n",
manufacturer, memory, capacity);
@ -344,8 +390,11 @@ out:
* Or success.
*/
gd25_pd(priv);
#ifdef CONFIG_GD25_QSPI
gd25_unlock(priv->qspi);
#else
gd25_unlock(priv->spi);
#endif
return ret;
}
@ -356,10 +405,39 @@ out:
#ifndef CONFIG_GD25_READONLY
static void gd25_unprotect(FAR struct gd25_dev_s *priv)
{
#ifdef CONFIG_GD25_QSPI
struct qspi_cmdinfo_s cmdinfo;
uint8_t sr2;
gd25_lock(priv->qspi);
gd25_waitwritecomplete(priv);
/* Read SR2 to preserve OTP/SRP bits before writing */
sr2 = gd25_rdsr(priv, 1);
gd25_wren(priv);
/* SR1 = 0 (clear all block-protect bits); SR2 = preserve non-OTP bits,
* set QE (S9 = bit1 of SR2) so quad I/O pins are active.
*/
priv->cmdbuf[0] = 0;
priv->cmdbuf[1] = (sr2 & GD25_SR2_PRESERVE_MASK) | GD25_SR2_QE;
cmdinfo.flags = QSPICMD_WRITEDATA;
cmdinfo.addrlen = 0;
cmdinfo.cmd = GD25_WRSR;
cmdinfo.buflen = 2;
cmdinfo.addr = 0;
cmdinfo.buffer = priv->cmdbuf;
QSPI_COMMAND(priv->qspi, &cmdinfo);
gd25_waitwritecomplete(priv);
gd25_unlock(priv->qspi);
#else
/* Lock and configure the SPI bus */
gd25_lock(priv->spi);
gd25_purdid(priv);
/* Wait for any preceding write or erase operation to complete. */
@ -384,8 +462,8 @@ static void gd25_unprotect(FAR struct gd25_dev_s *priv)
/* Unlock the SPI bus */
gd25_pd(priv);
gd25_unlock(priv->spi);
#endif /* CONFIG_GD25_QSPI */
}
#endif
@ -400,11 +478,19 @@ static uint8_t gd25_waitwritecomplete(FAR struct gd25_dev_s *priv)
do
{
status = gd25_rdsr(priv, 0);
if (priv->prev_instr != GD25_PP && (status & GD25_SR_WIP) != 0)
if (priv->prev_instr != GD25_PP && priv->prev_instr != GD25_PP4B &&
priv->prev_instr != GD25_QPP && priv->prev_instr != GD25_QPP4B &&
(status & GD25_SR_WIP) != 0)
{
#ifdef CONFIG_GD25_QSPI
gd25_unlock(priv->qspi);
nxsig_usleep(1000);
gd25_lock(priv->qspi);
#else
gd25_unlock(priv->spi);
nxsched_usleep(1000);
nxsig_usleep(1000);
gd25_lock(priv->spi);
#endif
}
}
while ((status & GD25_SR_WIP) != 0);
@ -418,18 +504,29 @@ static uint8_t gd25_waitwritecomplete(FAR struct gd25_dev_s *priv)
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
};
#ifdef CONFIG_GD25_QSPI
struct qspi_cmdinfo_s cmdinfo;
cmdinfo.flags = QSPICMD_READDATA;
cmdinfo.addrlen = 0;
cmdinfo.cmd = rdsr[id];
cmdinfo.buflen = 1;
cmdinfo.addr = 0;
cmdinfo.buffer = priv->cmdbuf;
QSPI_COMMAND(priv->qspi, &cmdinfo);
return priv->cmdbuf[0];
#else
uint8_t status;
SPI_SELECT(priv->spi, SPIDEV_FLASH(priv->spi_devid), true);
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;
#endif
}
/***************************************************************************
@ -442,9 +539,20 @@ static inline uint8_t gd25_rdsr(FAR struct gd25_dev_s *priv, uint32_t id)
static inline bool gd25_4ben(FAR struct gd25_dev_s *priv)
{
#ifdef CONFIG_GD25_QSPI
struct qspi_cmdinfo_s cmdinfo;
cmdinfo.flags = 0;
cmdinfo.addrlen = 0;
cmdinfo.cmd = GD25_4BEN;
cmdinfo.buflen = 0;
cmdinfo.addr = 0;
cmdinfo.buffer = NULL;
QSPI_COMMAND(priv->qspi, &cmdinfo);
#else
SPI_SELECT(priv->spi, SPIDEV_FLASH(priv->spi_devid), true);
SPI_SEND(priv->spi, GD25_4BEN);
SPI_SELECT(priv->spi, SPIDEV_FLASH(priv->spi_devid), false);
#endif
if (priv->memory == GD25Q_JEDEC_MEMORY_TYPE)
{
return ((gd25_rdsr(priv, 1) & GD25Q_SR1_EN4B) == GD25Q_SR1_EN4B);
@ -461,9 +569,20 @@ static inline bool gd25_4ben(FAR struct gd25_dev_s *priv)
static inline void gd25_wren(FAR struct gd25_dev_s *priv)
{
#ifdef CONFIG_GD25_QSPI
struct qspi_cmdinfo_s cmdinfo;
cmdinfo.flags = 0;
cmdinfo.addrlen = 0;
cmdinfo.cmd = GD25_WREN;
cmdinfo.buflen = 0;
cmdinfo.addr = 0;
cmdinfo.buffer = NULL;
QSPI_COMMAND(priv->qspi, &cmdinfo);
#else
SPI_SELECT(priv->spi, SPIDEV_FLASH(priv->spi_devid), true);
SPI_SEND(priv->spi, GD25_WREN);
SPI_SELECT(priv->spi, SPIDEV_FLASH(priv->spi_devid), false);
#endif
}
/***************************************************************************
@ -472,9 +591,20 @@ static inline void gd25_wren(FAR struct gd25_dev_s *priv)
static inline void gd25_wrdi(FAR struct gd25_dev_s *priv)
{
#ifdef CONFIG_GD25_QSPI
struct qspi_cmdinfo_s cmdinfo;
cmdinfo.flags = 0;
cmdinfo.addrlen = 0;
cmdinfo.cmd = GD25_WRDI;
cmdinfo.buflen = 0;
cmdinfo.addr = 0;
cmdinfo.buffer = NULL;
QSPI_COMMAND(priv->qspi, &cmdinfo);
#else
SPI_SELECT(priv->spi, SPIDEV_FLASH(priv->spi_devid), true);
SPI_SEND(priv->spi, GD25_WRDI);
SPI_SELECT(priv->spi, SPIDEV_FLASH(priv->spi_devid), false);
#endif
}
/***************************************************************************
@ -526,6 +656,9 @@ static bool gd25_is_erased(FAR struct gd25_dev_s *priv, off_t address,
static void gd25_sectorerase(FAR struct gd25_dev_s *priv, off_t sector)
{
off_t address = sector << GD25_SECTOR_SHIFT;
#ifdef CONFIG_GD25_QSPI
struct qspi_cmdinfo_s cmdinfo;
#endif
finfo("sector: %08lx\n", (long)sector);
@ -546,6 +679,16 @@ static void gd25_sectorerase(FAR struct gd25_dev_s *priv, off_t sector)
gd25_wren(priv);
#ifdef CONFIG_GD25_QSPI
cmdinfo.flags = QSPICMD_ADDRESS;
cmdinfo.addrlen = priv->addr_4byte ? 4 : 3;
cmdinfo.cmd = priv->addr_4byte ? GD25_SE4B : GD25_SE;
cmdinfo.buflen = 0;
cmdinfo.addr = address;
cmdinfo.buffer = NULL;
QSPI_COMMAND(priv->qspi, &cmdinfo);
priv->prev_instr = cmdinfo.cmd;
#else
SPI_SELECT(priv->spi, SPIDEV_FLASH(priv->spi_devid), true);
/* Send the "Sector Erase (SE)" instruction */
@ -567,6 +710,7 @@ static void gd25_sectorerase(FAR struct gd25_dev_s *priv, off_t sector)
SPI_SEND(priv->spi, address & 0xff);
SPI_SELECT(priv->spi, SPIDEV_FLASH(priv->spi_devid), false);
#endif /* CONFIG_GD25_QSPI */
}
/***************************************************************************
@ -575,6 +719,10 @@ static void gd25_sectorerase(FAR struct gd25_dev_s *priv, off_t sector)
static inline int gd25_chiperase(FAR struct gd25_dev_s *priv)
{
#ifdef CONFIG_GD25_QSPI
struct qspi_cmdinfo_s cmdinfo;
#endif
/* Wait for any preceding write or erase operation to complete. */
gd25_waitwritecomplete(priv);
@ -583,6 +731,16 @@ static inline int gd25_chiperase(FAR struct gd25_dev_s *priv)
gd25_wren(priv);
#ifdef CONFIG_GD25_QSPI
cmdinfo.flags = 0;
cmdinfo.addrlen = 0;
cmdinfo.cmd = GD25_CE;
cmdinfo.buflen = 0;
cmdinfo.addr = 0;
cmdinfo.buffer = NULL;
QSPI_COMMAND(priv->qspi, &cmdinfo);
priv->prev_instr = GD25_CE;
#else
SPI_SELECT(priv->spi, SPIDEV_FLASH(priv->spi_devid), true);
/* Send the "Chip Erase (CE)" instruction */
@ -591,6 +749,7 @@ static inline int gd25_chiperase(FAR struct gd25_dev_s *priv)
priv->prev_instr = GD25_CE;
SPI_SELECT(priv->spi, SPIDEV_FLASH(priv->spi_devid), false);
#endif /* CONFIG_GD25_QSPI */
return OK;
}
@ -601,6 +760,10 @@ 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,
off_t address, size_t nbytes)
{
#ifdef CONFIG_GD25_QSPI
struct qspi_meminfo_s meminfo;
#endif
finfo("address: %08lx nbytes: %d\n", (long)address, (int)nbytes);
/* Wait for any preceding write or erase operation to complete. */
@ -611,6 +774,17 @@ static void gd25_byteread(FAR struct gd25_dev_s *priv, FAR uint8_t *buffer,
gd25_wrdi(priv);
#ifdef CONFIG_GD25_QSPI
meminfo.flags = QSPIMEM_READ | QSPIMEM_QUADIO;
meminfo.addrlen = priv->addr_4byte ? 4 : 3;
meminfo.dummies = GD25_QIOFRD_DUMMIES;
meminfo.buflen = nbytes;
meminfo.cmd = priv->addr_4byte ? GD25_QIOFRD4B : GD25_QIOFRD;
meminfo.addr = address;
meminfo.buffer = buffer;
QSPI_MEMORY(priv->qspi, &meminfo);
priv->prev_instr = meminfo.cmd;
#else
SPI_SELECT(priv->spi, SPIDEV_FLASH(priv->spi_devid), true);
/* Send "Read from Memory " instruction */
@ -645,6 +819,7 @@ static void gd25_byteread(FAR struct gd25_dev_s *priv, FAR uint8_t *buffer,
SPI_RECVBLOCK(priv->spi, buffer, nbytes);
SPI_SELECT(priv->spi, SPIDEV_FLASH(priv->spi_devid), false);
#endif /* CONFIG_GD25_QSPI */
}
/***************************************************************************
@ -656,6 +831,17 @@ static void gd25_pagewrite(FAR struct gd25_dev_s *priv,
FAR const uint8_t *buffer, off_t address,
size_t nbytes)
{
#ifdef CONFIG_GD25_QSPI
/* GD25 Quad Page Program (0x32/0x34) is a 1-1-4 transfer: the address
* is clocked on a single line and only the data is quad.
* QSPIMEM_QUADIO cannot express this (it forces the address quad too),
* so use QSPIMEM_QUADDATA, which sets the data phase quad while leaving
* the address phase single-line.
*/
struct qspi_meminfo_s meminfo;
#endif
finfo("address: %08lx nwords: %d\n", (long)address, (int)nbytes);
DEBUGASSERT(priv && buffer && (address & 0xff) == 0 &&
(nbytes & 0xff) == 0);
@ -670,6 +856,17 @@ static void gd25_pagewrite(FAR struct gd25_dev_s *priv,
gd25_wren(priv);
#ifdef CONFIG_GD25_QSPI
meminfo.flags = QSPIMEM_WRITE | QSPIMEM_QUADDATA;
meminfo.cmd = priv->addr_4byte ? GD25_QPP4B : GD25_QPP;
meminfo.addrlen = priv->addr_4byte ? 4 : 3;
meminfo.buflen = GD25_PAGE_SIZE;
meminfo.dummies = 0;
meminfo.addr = address;
meminfo.buffer = (FAR void *)buffer;
QSPI_MEMORY(priv->qspi, &meminfo);
priv->prev_instr = meminfo.cmd;
#else
SPI_SELECT(priv->spi, SPIDEV_FLASH(priv->spi_devid), true);
/* Send the "Page Program (GD25_PP)" Command */
@ -693,6 +890,7 @@ static void gd25_pagewrite(FAR struct gd25_dev_s *priv,
SPI_SNDBLOCK(priv->spi, buffer, GD25_PAGE_SIZE);
SPI_SELECT(priv->spi, SPIDEV_FLASH(priv->spi_devid), false);
#endif /* CONFIG_GD25_QSPI */
/* Update addresses */
@ -711,6 +909,14 @@ static inline void gd25_bytewrite(FAR struct gd25_dev_s *priv,
FAR const uint8_t *buffer, off_t offset,
uint16_t count)
{
#ifdef CONFIG_GD25_QSPI
/* See gd25_pagewrite: GD25 Quad Page Program (0x32/0x34) is 1-1-4.
* QSPIMEM_QUADDATA selects quad data with a single-line address.
*/
struct qspi_meminfo_s meminfo;
#endif
finfo("offset: %08lx count:%d\n", (long)offset, count);
/* Wait for any preceding write to complete. We could simplify things by
@ -725,6 +931,17 @@ static inline void gd25_bytewrite(FAR struct gd25_dev_s *priv,
gd25_wren(priv);
#ifdef CONFIG_GD25_QSPI
meminfo.flags = QSPIMEM_WRITE | QSPIMEM_QUADDATA;
meminfo.cmd = priv->addr_4byte ? GD25_QPP4B : GD25_QPP;
meminfo.addrlen = priv->addr_4byte ? 4 : 3;
meminfo.buflen = count;
meminfo.dummies = 0;
meminfo.addr = offset;
meminfo.buffer = (FAR void *)buffer;
QSPI_MEMORY(priv->qspi, &meminfo);
priv->prev_instr = meminfo.cmd;
#else
SPI_SELECT(priv->spi, SPIDEV_FLASH(priv->spi_devid), true);
/* Send "Page Program (PP)" command */
@ -748,6 +965,7 @@ static inline void gd25_bytewrite(FAR struct gd25_dev_s *priv,
SPI_SNDBLOCK(priv->spi, buffer, count);
SPI_SELECT(priv->spi, SPIDEV_FLASH(priv->spi_devid), false);
#endif /* CONFIG_GD25_QSPI */
finfo("Written\n");
}
#endif /* defined(CONFIG_MTD_BYTE_WRITE) && !defined(CONFIG_GD25_READONLY) */
@ -767,10 +985,13 @@ static int gd25_erase(FAR struct mtd_dev_s *dev, off_t startblock,
finfo("startblock: %08lx nblocks: %d\n", (long)startblock, (int)nblocks);
/* Lock access to the SPI bus until we complete the erase */
/* Lock access to the bus until we complete the erase */
#ifdef CONFIG_GD25_QSPI
gd25_lock(priv->qspi);
#else
gd25_lock(priv->spi);
gd25_purdid(priv);
#endif
while (blocksleft-- > 0)
{
@ -780,8 +1001,11 @@ static int gd25_erase(FAR struct mtd_dev_s *dev, off_t startblock,
startblock++;
}
gd25_pd(priv);
#ifdef CONFIG_GD25_QSPI
gd25_unlock(priv->qspi);
#else
gd25_unlock(priv->spi);
#endif
return (int)nblocks;
#endif
}
@ -821,14 +1045,20 @@ static ssize_t gd25_bwrite(FAR struct mtd_dev_s *dev, off_t startblock,
finfo("startblock: %08lx nblocks: %d\n", (long)startblock, (int)nblocks);
/* Lock the SPI bus and write all of the pages to FLASH */
/* Lock the bus and write all of the pages to FLASH */
#ifdef CONFIG_GD25_QSPI
gd25_lock(priv->qspi);
#else
gd25_lock(priv->spi);
gd25_purdid(priv);
#endif
gd25_pagewrite(priv, buffer, startblock << GD25_PAGE_SHIFT,
nblocks << GD25_PAGE_SHIFT);
gd25_pd(priv);
#ifdef CONFIG_GD25_QSPI
gd25_unlock(priv->qspi);
#else
gd25_unlock(priv->spi);
#endif
return nblocks;
#endif
@ -845,13 +1075,19 @@ static ssize_t gd25_read(FAR struct mtd_dev_s *dev, off_t offset,
finfo("offset: %08lx nbytes: %d\n", (long)offset, (int)nbytes);
/* Lock the SPI bus and select this FLASH part */
/* Lock the bus and select this FLASH part */
#ifdef CONFIG_GD25_QSPI
gd25_lock(priv->qspi);
#else
gd25_lock(priv->spi);
gd25_purdid(priv);
#endif
gd25_byteread(priv, buffer, offset, nbytes);
gd25_pd(priv);
#ifdef CONFIG_GD25_QSPI
gd25_unlock(priv->qspi);
#else
gd25_unlock(priv->spi);
#endif
finfo("return nbytes: %d,%x,%x\n", (int)nbytes, buffer[0], buffer[1]);
return nbytes;
@ -885,8 +1121,11 @@ static ssize_t gd25_write(FAR struct mtd_dev_s *dev, off_t offset,
startpage = offset / GD25_PAGE_SIZE;
endpage = (offset + nbytes) / GD25_PAGE_SIZE;
#ifdef CONFIG_GD25_QSPI
gd25_lock(priv->qspi);
#else
gd25_lock(priv->spi);
gd25_purdid(priv);
#endif
if (startpage == endpage)
{
/* All bytes within one programmable page. Just do the write. */
@ -928,8 +1167,11 @@ static ssize_t gd25_write(FAR struct mtd_dev_s *dev, off_t offset,
}
}
gd25_pd(priv);
#ifdef CONFIG_GD25_QSPI
gd25_unlock(priv->qspi);
#else
gd25_unlock(priv->spi);
#endif
return nbytes;
#endif
}
@ -988,11 +1230,17 @@ static int gd25_ioctl(FAR struct mtd_dev_s *dev, int cmd, unsigned long arg)
{
/* Erase the entire device */
#ifdef CONFIG_GD25_QSPI
gd25_lock(priv->qspi);
#else
gd25_lock(priv->spi);
gd25_purdid(priv);
#endif
ret = gd25_chiperase(priv);
gd25_pd(priv);
#ifdef CONFIG_GD25_QSPI
gd25_unlock(priv->qspi);
#else
gd25_unlock(priv->spi);
#endif
}
break;
@ -1028,6 +1276,83 @@ static int gd25_ioctl(FAR struct mtd_dev_s *dev, int cmd, unsigned long arg)
*
***************************************************************************/
#ifdef CONFIG_GD25_QSPI
FAR struct mtd_dev_s *gd25_initialize(FAR struct qspi_dev_s *qspi,
bool unprotect)
{
FAR struct gd25_dev_s *priv;
int ret;
priv = (FAR struct gd25_dev_s *)kmm_zalloc(sizeof(struct gd25_dev_s));
if (priv)
{
priv->mtd.erase = gd25_erase;
priv->mtd.bread = gd25_bread;
priv->mtd.bwrite = gd25_bwrite;
priv->mtd.read = gd25_read;
priv->mtd.ioctl = gd25_ioctl;
#ifdef CONFIG_MTD_BYTE_WRITE
priv->mtd.write = gd25_write;
#endif
priv->mtd.name = "gd25";
priv->qspi = qspi;
priv->cmdbuf = (FAR uint8_t *)QSPI_ALLOC(qspi, 4);
if (!priv->cmdbuf)
{
kmm_free(priv);
return NULL;
}
ret = gd25_readid(priv);
if (ret != OK)
{
ferr("ERROR: Unrecognized\n");
QSPI_FREE(qspi, priv->cmdbuf);
kmm_free(priv);
return NULL;
}
/* QE (Quad Enable) must be set before any quad I/O command is issued.
* It is non-volatile, so only write if currently clear to avoid
* unnecessary write cycles.
*/
if (!(gd25_rdsr(priv, 1) & GD25_SR2_QE))
{
uint8_t sr2 = gd25_rdsr(priv, 1);
gd25_lock(priv->qspi);
gd25_waitwritecomplete(priv);
gd25_wren(priv);
priv->cmdbuf[0] = 0;
priv->cmdbuf[1] = (sr2 & GD25_SR2_PRESERVE_MASK) | GD25_SR2_QE;
struct qspi_cmdinfo_s cmdinfo;
cmdinfo.flags = QSPICMD_WRITEDATA;
cmdinfo.addrlen = 0;
cmdinfo.cmd = GD25_WRSR;
cmdinfo.buflen = 2;
cmdinfo.addr = 0;
cmdinfo.buffer = priv->cmdbuf;
QSPI_COMMAND(priv->qspi, &cmdinfo);
gd25_waitwritecomplete(priv);
gd25_unlock(priv->qspi);
}
#ifndef CONFIG_GD25_READONLY
if (unprotect)
{
gd25_unprotect(priv);
}
#endif
}
return (FAR struct mtd_dev_s *)priv;
}
#else
FAR struct mtd_dev_s *gd25_initialize(FAR struct spi_dev_s *spi,
uint32_t spi_devid)
{
@ -1082,3 +1407,4 @@ FAR struct mtd_dev_s *gd25_initialize(FAR struct spi_dev_s *spi,
return (FAR struct mtd_dev_s *)priv;
}
#endif /* CONFIG_GD25_QSPI */

View file

@ -649,8 +649,13 @@ FAR struct mtd_dev_s *w25_initialize(FAR struct spi_dev_s *dev);
*
****************************************************************************/
#ifdef CONFIG_GD25_QSPI
FAR struct mtd_dev_s *gd25_initialize(FAR struct qspi_dev_s *qspi,
bool unprotect);
#else
FAR struct mtd_dev_s *gd25_initialize(FAR struct spi_dev_s *dev,
uint32_t spi_devid);
#endif
/****************************************************************************
* Name: gd55_initialize