drivers/mtd/gd55.c (1,064 lines of code) (raw):
/****************************************************************************
* drivers/mtd/gd55.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <assert.h>
#include <errno.h>
#include <debug.h>
#include <inttypes.h>
#include <stdbool.h>
#include <stdint.h>
#ifdef CONFIG_MTD_GD55_SECTOR512
# include <stdlib.h>
# include <string.h>
#endif
#include <nuttx/kmalloc.h>
#include <nuttx/signal.h>
#include <nuttx/fs/ioctl.h>
#include <nuttx/spi/qspi.h>
#include <nuttx/mtd/mtd.h>
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* 4 byte addressing is needed for addresses needing more than a 3 byte
* address, i.e. 16Mbyte
*/
#define MODE_3BYTE_LIMIT ((16 * 1024 * 1024))
/* GD55 Commands */
#define GD55_QREAD 0x6b /* Quad output fast read */
#define GD55_QREAD_DUMMIES 8
#define GD55_QC_READ 0xeb /* Quad output continuous fast read */
#define GD55_QC_READ_DUMMIES 6
#define GD55_EQPP 0xc2 /* Extended quad page program */
#define GD55_EQPP_DUMMIES 0 /* No dummy clocks */
#define GD55_SE 0x20 /* 4Kb Sector erase */
#define GD55_BE32 0x52 /* 32Kbit block Erase */
#define GD55_BE64 0xd8 /* 64Kbit block Erase */
#define GD55_CE 0x60 /* Chip erase (alternate) */
#define GD55_WREN 0x06 /* Write Enable */
#define GD55_WRDI 0x04 /* Write Disable */
#define GD55_RDSR1 0x05 /* Read status register 1 */
#define GD55_EN4B 0xb7 /* Enable 4 byte Addressing Mode */
#define GD55_DIS4B 0xe9 /* Disable 4 byte Addressing Mode */
#define GD55_IBSL 0x36 /* Individual block/sector lock */
#define GD55_IBSUL 0x39 /* Individual block/sector unlock */
#define GD55_RIBSL 0x3d /* Read individual block/sector lock */
#define GD55_RDNVCR 0xb5 /* Read Non-Volatile config register */
#define GD55_RD_NVCR_DUMMIES 8
#define GD55_RDSR2 0x35 /* Read status register 2 */
#define GD55_WRSR1 0x01 /* Write status register 1 */
#define GD55_SE_ALT 0x21 /* Alternate 4Kb Sector erase */
#define GD55_QC_READ_ALT 0xec /* Quad output continuous fast read */
#define GD55_4B_QDTR_READ 0xed /* Quad I/O DTR read */
#define GD55_4B_QDTR_READ_ALT 0xee /* Alternate quad I/O DTR read */
#define GD55_PP 0x02 /* Page program (SPI, not used) */
#define GD55_PP_ALT 0x12 /* Aternate page program (SPI) */
#define GD55_BE32_ALT 0x5c /* Alternate 32Kbit block Erase */
#define GD55_BE64_ALT 0xd8 /* ALternate 64Kbit block Erase */
#define GD55_CE_ALT 0xc7 /* Alternate chip erase */
#define GD55_QPP 0x32 /* Quad page program */
#define GD55_QPP_ALT 0x34 /* ALternate quad page program */
#define GD55_QPP_DUMMIES 0 /* No dummy clocks */
#define GD55_QPIEN 0x38 /* Enable QPI Operation */
#define GD55_QPIDIS 0xff /* Disable QPI Operation */
#define GD55_DP 0xb9 /* Deep power down */
#define GD55_RDP 0xab /* Release deep power down */
#define GD55_RUID 0x4b /* Read Unique ID */
#define GD55_RDID 0x9e /* Read identification */
#define GD55_RDID_ALT 0x9f /* Read identification (alternate) */
#define GD55_PE_SUSPEND 0x75 /* Suspends program/erase */
#define GD55_PE_RESUME 0x7a /* Resume program */
#define GD55_RDVCR 0x85 /* Read Volatile config register */
#define GD55_RD_VCR_DUMMIES 1
#define GD55_WRSR2 0x31 /* Write status register 2 */
#define GD55_WRNVCR 0xb1 /* Write Non-Volatile config register */
#define GD55_WRENVSC 0x50 /* Write en. Volatile config register */
#define GD55_WRVCR 0x91 /* Write Volatile config register */
#define GD55_WREAR 0xc5 /* Write Extended address register */
#define GD55_EARR 0xc8 /* Read extended address register */
#define GD55_RSFDP 0x5a /* Read SFDP */
#define GD55_RDSCUR 0x48 /* Read security register */
#define GD55_WRSCUR 0x42 /* Write security register */
#define GD55_ERSCUR 0x44 /* Erase security register */
#define GD55_RSTEN 0x66 /* Reset Enable */
#define GD55_RST 0x99 /* Reset Memory */
#define GD55_GBSL 0x7e /* Global block/sector lock */
#define GD55_GBSUL 0x98 /* Global block/sector unlock */
/* Read ID (RDID) register values */
#define GD55_MANUFACTURER 0xc8 /* GigaSevice manufacturer ID */
/* JEDEC Read ID register values */
#define GD55_JEDEC_MANUFACTURER 0xc8 /* GigaDevice manufacturer ID */
#define GD55B_JEDEC_MEMORY_TYPE 0x47 /* GD55B memory type, 3V */
#define GD55L_JEDEC_MEMORY_TYPE 0x67 /* GD55L memory type, 1.8V */
#define GD55_JEDEC_1G_CAPACITY 0x1b /* 1Gbit memory capacity */
#define GD55_JEDEC_2G_CAPACITY 0x1c /* 2Gbit memory capacity */
/* GD55 devices all have identical sector sizes:
* block protection size: 64KiB
* sector size: 4KiB
* page size: 256B
*/
#define GD55_SECTOR_SHIFT (12)
#define GD55_SECTOR_SIZE (1 << GD55_SECTOR_SHIFT) /* 4KiB */
#define GD55_PAGE_SHIFT (8) /* 256B */
#define GD55_PAGE_SIZE (1 << GD55_PAGE_SHIFT)
#define GD55_BP_SHIFT (16)
#define GD55_BP_SIZE (1 << GD55_BP_SHIFT) /* 64KiB */
#define GD55_MIN_BP_BLKS (GD55_BP_SIZE >> GD55_PAGE_SHIFT)
#define GD55_SECTORS_PER_BP_BLK (GD55_BP_SIZE / GD55_SECTOR_SIZE)
/* GD55B01xx (128 MiB) memory capacity */
#define GD55_NSECTORS_1GBIT (32768)
/* GD55B02xx (256 MiB) memory capacity */
#define GD55_NSECTORS_2GBIT (65536)
/* 512 byte sector support **************************************************/
#define GD55_SECTOR512_SHIFT (9)
#define GD55_SECTOR512_SIZE (1 << GD55_SECTOR512_SHIFT)
/* Status register 1 bit definitions */
#define GD55_SR_WIP (1 << 0) /* Bit 0: Write in progress */
#define GD55_SR_WEL (1 << 1) /* Bit 1: Write enable latch */
#define GD55_SR_BP_SHIFT (2) /* Bits 2-6: Block protect bits */
#define GD55_SR_BP_MASK (31 << GD55_SR_BP_SHIFT)
#define GD55_STATUS_BP_NONE (0 << GD55_SR_BP_SHIFT)
#define GD55_STATUS_BP_ALL (7 << GD55_SR_BP_SHIFT)
#define GD55_STATUS_TB_MASK (1 << 6) /* BP4 Top/Bottom Protect */
#define GD55_STATUS_TB_TOP (0 << 6) /* = 0, BP3..0 protect Top down */
#define GD55_STATUS_TB_BOTTOM (1 << 6) /* = 1, BP3..0 " Bottom up */
#define GD55_SR_BP_TOP(b) (((b + 1) << GD55_SR_BP_SHIFT) | \
GD55_STATUS_TB_TOP)
#define GD55_SR_BP_BOTTOM(b) (((b + 1) << GD55_SR_BP_SHIFT) | \
GD55_STATUS_TB_BOTTOM)
#define GD55_BP_ALL (14 << GD55_SR_BP_SHIFT)
/* GD55B01 needs BP bits = 0xx11xx
* GD55B02 needs BP bits = 0xx111x
*/
#define GD55_SR_SRP0 (1 << 7) /* Bit 7: SR protect bit 0 */
/* Status register 2 bit definitions */
#define GD55_SR_ADS (1 << 0) /* Bit 0: Current Address Mode */
/* Bit 1 - reserved */
#define GD55_SR_SUS2 (1 << 2) /* Bit 2: Program suspend bit 2 */
#define GD55_SR_LB (1 << 3) /* Bit 3: Security Register Lock */
#define GD55_SR_PE (1 << 4) /* Bit 4: Program Error Bit */
#define GD55_SR_EE (1 << 5) /* Bit 5: Erase Error Bit */
#define GD55_SR_SRP1 (1 << 6) /* Bit 6: SR protection bit 1 */
#define GD55_SR_SUS1 (1 << 7) /* Bit 7: Program suspend bit 1 */
/* Non-volatile and volatile config register addresses and bits */
#define GD55_DUMMY_CYCLES_REG 1 /* Dummy Cycle Configuration */
#define GD55_ODT_DS_REG 3 /* On-die termination and driver
* strength configuration
*/
#define GD55_DLP_PROT_REG 4 /* Data Learning and protect mode */
#define GD55_PROT_MODE_MASK (1 << 2) /* Bit 2, BP or WPS mode */
#define GD55_PROT_MODE_WPS (0 << 2) /* 0 = Sector Protect mode */
#define GD55_PROT_MODE_BP (1 << 2) /* 1 = Block Protect mode (def.) */
#define GD55_4BYTE_MODE_REG 5 /* 3 pr 4-byte address mode */
#define GD55_XIP_MODE_REG 6 /* XIP (continuous read) mode */
#define GD55_WRAP_CONFIG_REG 7 /* Wrap mode (none/64/32/16 byte) */
/* Block protection bit */
#define GD55_BLK_PROTECTED (1 << 0) /* lsb set means block is locked */
/* Cache flags **************************************************************/
#define GD55_CACHE_VALID (1 << 0) /* 1=Cache has valid data */
#define GD55_CACHE_DIRTY (1 << 1) /* 1=Cache is dirty */
#define GD55_CACHE_ERASED (1 << 2) /* 1=Backing FLASH is erased */
#define IS_VALID(p) ((((p)->flags) & GD55_CACHE_VALID) != 0)
#define IS_DIRTY(p) ((((p)->flags) & GD55_CACHE_DIRTY) != 0)
#define IS_ERASED(p) ((((p)->flags) & GD55_CACHE_ERASED) != 0)
#define SET_VALID(p) do { (p)->flags |= GD55_CACHE_VALID; } while (0)
#define SET_DIRTY(p) do { (p)->flags |= GD55_CACHE_DIRTY; } while (0)
#define SET_ERASED(p) do { (p)->flags |= GD55_CACHE_ERASED; } while (0)
#define CLR_VALID(p) do { (p)->flags &= ~GD55_CACHE_VALID; } while (0)
#define CLR_DIRTY(p) do { (p)->flags &= ~GD55_CACHE_DIRTY; } while (0)
#define CLR_ERASED(p) do { (p)->flags &= ~GD55_CACHE_ERASED; } while (0)
#define GD55_ERASED_STATE 0xff
/****************************************************************************
* Private Types
****************************************************************************/
/* Internal state of the MTD device */
struct gd55_dev_s
{
struct mtd_dev_s mtd; /* MTD interface */
FAR struct qspi_dev_s *qspi; /* QuadSPI interface */
FAR uint8_t *cmdbuf; /* Allocated command buffer */
FAR uint8_t *readbuf; /* Allocated status read buffer */
uint32_t nsectors; /* Number of erase sectors */
#ifdef CONFIG_MTD_GD55_SECTOR512
uint8_t flags; /* Buffered sector flags */
uint16_t esectno; /* Erase sector number in the cache */
FAR uint8_t *sector; /* Allocated sector data */
#endif
};
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
/* MTD driver methods */
static int gd55_erase(FAR struct mtd_dev_s *dev, off_t startblock,
size_t nblocks);
static ssize_t gd55_bread(FAR struct mtd_dev_s *dev, off_t startblock,
size_t nblocks, FAR uint8_t *buf);
static ssize_t gd55_bwrite(FAR struct mtd_dev_s *dev, off_t startblock,
size_t nblocks, FAR const uint8_t *buf);
static ssize_t gd55_read(FAR struct mtd_dev_s *dev, off_t offset,
size_t nbytes, FAR uint8_t *buffer);
static int gd55_ioctl(FAR struct mtd_dev_s *dev, int cmd,
unsigned long arg);
/* Internal driver methods */
static void gd55_lock(FAR struct gd55_dev_s *priv);
static void gd55_unlock(FAR struct gd55_dev_s *priv);
static int gd55_command_read(FAR struct gd55_dev_s *priv, uint8_t cmd,
FAR void *buffer, size_t buflen);
static int gd55_command(FAR struct gd55_dev_s *priv, uint8_t cmd);
static int gd55_command_address(FAR struct gd55_dev_s *priv,
uint8_t cmd, off_t addr,
uint8_t addrlen);
static int gd55_readid(FAR struct gd55_dev_s *priv);
static int gd55_protect(FAR struct gd55_dev_s *priv, off_t startblock,
size_t nblocks);
static int gd55_unprotect(FAR struct gd55_dev_s *priv, off_t startblock,
size_t nblocks);
static bool gd55_isprotected(FAR struct gd55_dev_s *priv, off_t addr,
uint8_t status);
static int gd55_read_bytes(FAR struct gd55_dev_s *priv,
FAR uint8_t *buffer, off_t address,
size_t buflen);
static uint8_t gd55_read_status1(FAR struct gd55_dev_s *priv);
static uint8_t gd55_read_status2(FAR struct gd55_dev_s *priv);
static void gd55_write_status1(FAR struct gd55_dev_s *priv);
static int gd55_command_write(FAR struct gd55_dev_s *priv, uint8_t cmd,
FAR const void *buffer, size_t buflen);
static void gd55_write_enable(FAR struct gd55_dev_s *priv);
static int gd55_write_page(FAR struct gd55_dev_s *priv,
FAR const uint8_t *buffer, off_t address,
size_t buflen);
static int gd55_erase_sector(FAR struct gd55_dev_s *priv, off_t sector);
static int gd55_erase_chip(FAR struct gd55_dev_s *priv);
#ifdef CONFIG_MTD_GD55_SECTOR512
static int gd55_flush_cache(FAR struct gd55_dev_s *priv);
static FAR uint8_t *gd55_read_cache(FAR struct gd55_dev_s *priv,
off_t sector);
static void gd55_erase_cache(FAR struct gd55_dev_s *priv,
off_t sector);
static int gd55_write_cache(FAR struct gd55_dev_s *priv,
FAR const uint8_t *buffer, off_t sector,
size_t nsectors);
#else
static int gd55_erase_64kblock(FAR struct gd55_dev_s *priv,
off_t sector);
static int gd55_erase_32kblock(FAR struct gd55_dev_s *priv,
off_t sector);
#endif
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: gd55_lock
*
* Description:
* On QSPI buses where there are multiple devices, it will be necessary to
* lock QSPI to have exclusive access to the bus for a sequence of
* transfers. The bus should be locked before the chip is selected.
*
* This is a blocking call and will not return until we have exclusive
* access to the SPI bus. We will retain that exclusive access until the
* bus is unlocked.
*
* Input Parameters:
* priv - a reference to the device structure
*
* Returned Value:
* none
*
****************************************************************************/
static void gd55_lock(FAR struct gd55_dev_s *priv)
{
QSPI_LOCK(priv->qspi, true);
/* After locking the QSPI bus, the we also need call the setfrequency,
* setbits and setmode methods to make sure that the QSPI is properly
* configured for the device. If the QSPI bus is being shared, then it
* may have been left in an incompatible state.
*/
QSPI_SETMODE(priv->qspi, CONFIG_MTD_GD55_QSPIMODE);
QSPI_SETBITS(priv->qspi, 8);
QSPI_SETFREQUENCY(priv->qspi, CONFIG_MTD_GD55_FREQUENCY);
}
/****************************************************************************
* Name: gd55_unlock
*
* Description:
* On QSPI buses where there are multiple devices, it will have been
* necessary to lock QSSPI to have exclusive access to the bus for a sequence
* of transfers. The bus must be unlocked after the transfers to relinquish
* the exclusive access from the call to LOCK the bus.
*
* Input Parameters:
* priv - a reference to the device structure
*
* Returned Value:
* none
*
****************************************************************************/
static void gd55_unlock(FAR struct gd55_dev_s *priv)
{
QSPI_LOCK(priv->qspi, false);
}
/****************************************************************************
* Name: gd55_command_read
*
* Description:
* Read data from the device.
*
* Input Parameters:
* priv - a reference to the device structure
* cmd - the read command to be used
* buffer - pointer to variable to store the read data
* buflen - the number of bytes to be read into the buffer
*
* Returned Value:
* Zero (OK) on SUCCESS, a negated errno on value of failure
*
****************************************************************************/
static int gd55_command_read(FAR struct gd55_dev_s *priv, uint8_t cmd,
FAR void *buffer, size_t buflen)
{
struct qspi_cmdinfo_s cmdinfo;
finfo("CMD: %02x buflen: %lu\n", cmd, (unsigned long)buflen);
cmdinfo.flags = QSPICMD_READDATA;
cmdinfo.addrlen = 0;
cmdinfo.cmd = cmd;
cmdinfo.buflen = buflen;
cmdinfo.addr = 0;
cmdinfo.buffer = buffer;
return QSPI_COMMAND(priv->qspi, &cmdinfo);
}
/****************************************************************************
* Name: gd55_command
*
* Description:
* Send a command to the device.
*
* Input Parameters:
* priv - a reference to the device structure
* cmd - the command to be sent
*
* Returned Value:
* Zero (OK) on SUCCESS, a negated errno on value of failure
*
****************************************************************************/
static int gd55_command(FAR struct gd55_dev_s *priv, uint8_t cmd)
{
struct qspi_cmdinfo_s cmdinfo;
finfo("CMD: %02x\n", cmd);
cmdinfo.flags = 0;
cmdinfo.addrlen = 0;
cmdinfo.cmd = cmd;
cmdinfo.buflen = 0;
cmdinfo.addr = 0;
cmdinfo.buffer = NULL;
return QSPI_COMMAND(priv->qspi, &cmdinfo);
}
/****************************************************************************
* Name: gd55_command_write
*
* Description:
* Send a command to the device with data
*
* Input Parameters:
* priv - a reference to the device structure
* cmd - the command to be sent
* buffer - pointer to variable with the data to write
* buflen - the number of data bytes to be written
*
* Returned Value:
* Zero (OK) on SUCCESS, a negated errno on value of failure
*
****************************************************************************/
static int gd55_command_write(FAR struct gd55_dev_s *priv, uint8_t cmd,
FAR const void *buffer, size_t buflen)
{
struct qspi_cmdinfo_s cmdinfo;
finfo("CMD: %02x buflen: %lu\n", cmd, (unsigned long)buflen);
cmdinfo.flags = QSPICMD_WRITEDATA;
cmdinfo.addrlen = 0;
cmdinfo.cmd = cmd;
cmdinfo.buflen = buflen;
cmdinfo.addr = 0;
cmdinfo.buffer = (FAR void *)buffer;
return QSPI_COMMAND(priv->qspi, &cmdinfo);
}
/****************************************************************************
* Name: gd55_command_address
*
* Description:
* Send a command with an associated address to the device
*
* Input Parameters:
* priv - a reference to the device structure
* cmd - the command to be sent
* addr - address to send
* addrlen - address length
*
* Returned Value:
* Zero (OK) on SUCCESS, a negated errno on value of failure
*
****************************************************************************/
static int gd55_command_address(FAR struct gd55_dev_s *priv, uint8_t cmd,
off_t addr, uint8_t addrlen)
{
struct qspi_cmdinfo_s cmdinfo;
finfo("CMD: %02x Address: %04lx addrlen=%d\n",
cmd, (unsigned long)addr, addrlen);
cmdinfo.flags = QSPICMD_ADDRESS;
cmdinfo.addrlen = addrlen;
cmdinfo.cmd = cmd;
cmdinfo.buflen = 0;
cmdinfo.addr = addr;
cmdinfo.buffer = NULL;
return QSPI_COMMAND(priv->qspi, &cmdinfo);
}
/****************************************************************************
* Name: gd55_read_bytes
*
* Description:
* Read data from the device
*
* Input Parameters:
* priv - a reference to the device structure
* buffer - pointer to buffer to read the data to
* address - address to read from
* buflen - number of bytes to read (buffer length)
*
* Returned Value:
* Zero (OK) on SUCCESS, a negated errno on value of failure
*
****************************************************************************/
static int gd55_read_bytes(FAR struct gd55_dev_s *priv, FAR uint8_t *buffer,
off_t address, size_t buflen)
{
bool mode_4byte_addr = false;
int ret;
struct qspi_meminfo_s meminfo;
/* Check if any address exceeds range of 3 byte addressing */
if ((address + buflen) >= MODE_3BYTE_LIMIT)
{
gd55_command(priv, GD55_EN4B);
mode_4byte_addr = true;
}
finfo("4byte mode: %s\taddress: %08lx\tnbytes: %d\n",
mode_4byte_addr ? "true" : "false", (long)address, (int)buflen);
meminfo.flags = QSPIMEM_READ | QSPIMEM_QUADIO;
meminfo.dummies = GD55_QC_READ_DUMMIES;
meminfo.buflen = buflen;
meminfo.cmd = GD55_QC_READ;
meminfo.addr = address;
meminfo.addrlen = mode_4byte_addr ? 4 : 3;
meminfo.buffer = buffer;
ret = QSPI_MEMORY(priv->qspi, &meminfo);
if (mode_4byte_addr)
{
gd55_command(priv, GD55_DIS4B);
}
return ret;
}
/****************************************************************************
* Name: gd55_write_page
*
* Description:
* Write a page of data to the device
*
* Input Parameters:
* priv - a reference to the device structure
* buffer - pointer to the buffer with the data to write
* address - address to write to
* buflen - number of bytes to write (buffer length)
*
* Returned Value:
* Zero (OK) on SUCCESS, a negated errno on value of failure
*
****************************************************************************/
static int gd55_write_page(FAR struct gd55_dev_s *priv,
FAR const uint8_t *buffer,
off_t address, size_t buflen)
{
struct qspi_meminfo_s meminfo;
uint8_t status;
unsigned int npages;
int ret = OK;
int i;
npages = (buflen >> GD55_PAGE_SHIFT);
/* Check if address exceeds range of 3 byte addressing */
if ((address + buflen) >= MODE_3BYTE_LIMIT)
{
gd55_command(priv, GD55_EN4B);
meminfo.addrlen = 4;
}
else
{
gd55_command(priv, GD55_DIS4B);
meminfo.addrlen = 3;
}
finfo("4byte mode: %s\taddress: %08lx\tbuflen: %u\n",
(meminfo.addrlen == 4) ? "true" : "false", (unsigned long)address,
(unsigned)buflen);
/* Set up non-varying parts of transfer description */
meminfo.flags = QSPIMEM_WRITE | QSPIMEM_QUADIO;
meminfo.cmd = GD55_EQPP;
meminfo.buflen = GD55_PAGE_SIZE;
meminfo.dummies = GD55_EQPP_DUMMIES;
/* Then write each page */
for (i = 0; i < npages; i++)
{
/* Set up varying parts of the transfer description */
meminfo.addr = address;
meminfo.buffer = (FAR void *)buffer;
/* Write one page */
gd55_write_enable(priv);
ret = QSPI_MEMORY(priv->qspi, &meminfo);
if (ret < 0)
{
ferr("ERROR: QSPI_MEMORY failed writing address=%06jx\n",
(intmax_t)address);
goto exit;
}
/* Update for the next time through the loop */
buffer += GD55_PAGE_SIZE;
address += GD55_PAGE_SIZE;
/* Wait for write operation to finish */
do
{
status = gd55_read_status1(priv);
}
while ((status & GD55_SR_WIP) != 0);
}
exit:
if (meminfo.addrlen == 4)
{
gd55_command(priv, GD55_DIS4B);
}
return ret;
}
/****************************************************************************
* Name: gd55_erase_sector
*
* Description:
* Erase a single sector of th device
*
* Input Parameters:
* priv - a reference to the device structure
* sector - the sector to erase
*
* Returned Value:
* Zero (OK) on SUCCESS, a negated errno on value of failure
*
****************************************************************************/
static int gd55_erase_sector(FAR struct gd55_dev_s *priv, off_t sector)
{
uint8_t status;
bool mode_4byte_addr = false;
off_t addr = sector << GD55_SECTOR_SHIFT;
finfo("4byte mode: %s\tsector: %08lx\n", mode_4byte_addr ?
"true" : "false",
(unsigned long)sector);
/* Check that the flash is ready and unprotected */
status = gd55_read_status1(priv);
if ((status & GD55_SR_WIP) == GD55_SR_WIP)
{
ferr("ERROR: Flash busy: %02x", status);
return -EBUSY;
}
if (gd55_isprotected(priv, addr, status))
{
ferr("ERROR: Flash protected at addr: %02" PRIx32, addr);
return -EACCES;
}
/* Check if address exceeds range of 3 byte addressing */
if (addr >= MODE_3BYTE_LIMIT)
{
gd55_command(priv, GD55_EN4B);
mode_4byte_addr = true;
}
/* Send the sector erase command */
gd55_write_enable(priv);
gd55_command_address(priv, GD55_SE, addr, mode_4byte_addr ? 4 : 3);
/* Wait for erasure to finish */
do
{
nxsig_usleep(10 * 1000); /* Typical sector erase time is 30ms */
status = gd55_read_status1(priv);
}
while ((status & GD55_SR_WIP) != 0);
if (mode_4byte_addr)
{
gd55_command(priv, GD55_DIS4B);
}
return OK;
}
#ifndef CONFIG_MTD_GD55_SECTOR512
/****************************************************************************
* Name: gd55_erase_64kblock
*
* Description:
* Erase a 64k block of the device
*
* Input Parameters:
* priv - a reference to the device structure
* sector - an address of a sector within the 64k block to erase
*
* Returned Value:
* Zero (OK) on SUCCESS, a negated errno on value of failure
*
****************************************************************************/
static int gd55_erase_64kblock(FAR struct gd55_dev_s *priv, off_t sector)
{
off_t addr = sector << GD55_SECTOR_SHIFT;
uint8_t status;
bool mode_4byte_addr = false;
finfo("4byte mode: %s\tsector: %08lx\n", mode_4byte_addr ?
"true" : "false",
(unsigned long)sector);
/* Check that the flash is ready and unprotected */
status = gd55_read_status1(priv);
if ((status & GD55_SR_WIP) == GD55_SR_WIP)
{
ferr("ERROR: Flash busy: %02x", status);
return -EBUSY;
}
if (gd55_isprotected(priv, addr, status))
{
ferr("ERROR: Flash protected at addr: %02" PRIx32, addr);
return -EACCES;
}
if (addr >= MODE_3BYTE_LIMIT)
{
gd55_command(priv, GD55_EN4B);
mode_4byte_addr = true;
}
/* Send the 64k block erase command */
gd55_write_enable(priv);
gd55_command_address(priv, GD55_BE64, addr, mode_4byte_addr ?
4 : 3);
/* Wait for erasure to finish */
do
{
nxsig_usleep(50 * 1000); /* typical 64k erase time is 220ms */
status = gd55_read_status1(priv);
}
while ((status & GD55_SR_WIP) != 0);
if (mode_4byte_addr)
{
gd55_command(priv, GD55_DIS4B);
}
return OK;
}
/****************************************************************************
* Name: gd55_erase_32kblock
*
* Description:
* Erase a 32k block of the device
*
* Input Parameters:
* priv - a reference to the device structure
* sector - an address of a sector within the 32k block to erase
*
* Returned Value:
* Zero (OK) on SUCCESS, a negated errno on value of failure
*
****************************************************************************/
static int gd55_erase_32kblock(FAR struct gd55_dev_s *priv, off_t sector)
{
off_t addr = sector << GD55_SECTOR_SHIFT;
uint8_t status;
bool mode_4byte_addr = false;
finfo("4byte mode: %s\tsector: %08lx\n", mode_4byte_addr ?
"true" : "false",
(unsigned long)sector);
/* Check that the flash is ready and unprotected */
status = gd55_read_status1(priv);
if ((status & GD55_SR_WIP) == GD55_SR_WIP)
{
ferr("ERROR: Flash busy: %02x", status);
return -EBUSY;
}
if (gd55_isprotected(priv, addr, status))
{
ferr("ERROR: Flash protected at addr: %02" PRIx32, addr);
return -EACCES;
}
if (addr >= MODE_3BYTE_LIMIT)
{
gd55_command(priv, GD55_EN4B);
mode_4byte_addr = true;
}
/* Send the 32k block erase command */
gd55_write_enable(priv);
gd55_command_address(priv, GD55_BE32, addr, mode_4byte_addr ? 4 : 3);
/* Wait for erasure to finish */
do
{
nxsig_usleep(50 * 1000); /* typical 32k erase time is 150ms */
status = gd55_read_status1(priv);
}
while ((status & GD55_SR_WIP) != 0);
if (mode_4byte_addr)
{
gd55_command(priv, GD55_DIS4B);
}
return OK;
}
#endif
/****************************************************************************
* Name: gd55_erase_chip
*
* Description:
* Erase entire chip
*
* Input Parameters:
* priv - a reference to the device structure
*
* Returned Value:
* Zero (OK) on SUCCESS, a negated errno on value of failure
*
****************************************************************************/
static int gd55_erase_chip(FAR struct gd55_dev_s *priv)
{
uint8_t status;
/* Check if the FLASH is protected */
status = gd55_read_status1(priv);
if ((status & GD55_SR_BP_MASK) != 0)
{
ferr("ERROR: FLASH is Protected: %02x", status);
return -EACCES;
}
/* Erase the whole chip */
gd55_write_enable(priv);
gd55_command(priv, GD55_CE);
/* Wait for the erasure to complete */
status = gd55_read_status1(priv);
while ((status & GD55_SR_WIP) != 0)
{
nxsig_sleep(2);
status = gd55_read_status1(priv);
}
return OK;
}
/****************************************************************************
* Name: gd55_write_enable
*
* Description:
* Enable the device for writing by setting the wriet enable latch bit
*
* Input Parameters:
* priv - a reference to the device structure
*
* Returned Value:
* None
*
****************************************************************************/
static void gd55_write_enable(FAR struct gd55_dev_s *priv)
{
uint8_t status;
gd55_command(priv, GD55_WREN);
do
{
status = gd55_read_status1(priv);
}
while ((status & GD55_SR_WEL) != GD55_SR_WEL);
}
/****************************************************************************
* Name: gd55_read_status1
*
* Description:
* Read status register 1
*
* Input Parameters:
* priv - a reference to the device structure
*
* Returned Value:
* The status register data
*
****************************************************************************/
static uint8_t gd55_read_status1(FAR struct gd55_dev_s *priv)
{
uint8_t status;
gd55_command_read(priv, GD55_RDSR1, &status, 1);
return status;
}
/****************************************************************************
* Name: gd55_read_status2
*
* Description:
* Read status register 2
*
* Input Parameters:
* priv - a reference to the device structure
*
* Returned Value:
* The status register data
*
****************************************************************************/
static uint8_t gd55_read_status2(FAR struct gd55_dev_s *priv)
{
uint8_t status;
gd55_command_read(priv, GD55_RDSR2, &status, 1);
return status;
}
/****************************************************************************
* Name: gd55_write_status1
*
* Description:
* Write data to status register 1
* The data to be written must have been written to the device structures
* command buffer (cmdbuf)
*
* Input Parameters:
* priv - a reference to the device structure
*
* Returned Value:
* None
*
****************************************************************************/
static void gd55_write_status1(FAR struct gd55_dev_s *priv)
{
uint8_t status;
gd55_write_enable(priv);
/* take care to mask of the SRP bit; it is one-time-programmable */
priv->cmdbuf[0] &= ~GD55_SR_SRP0;
gd55_command_write(priv, GD55_WRSR1,
(FAR const void *)priv->cmdbuf, 1);
/* Wait for write operation to finish */
do
{
status = gd55_read_status1(priv);
}
while ((status & GD55_SR_WIP) != 0);
}
/****************************************************************************
* Name: gd55_erase
*
* Description:
* Erase a number of blocks of data.
*
* Input Parameters:
* dev - a reference to the device structure
* startblock - start block of the erase
* nblocks - nblocks to erase
*
* Returned Value:
* Success (OK) or fail (negated error code)
*
****************************************************************************/
static int gd55_erase(FAR struct mtd_dev_s *dev, off_t startblock,
size_t nblocks)
{
FAR struct gd55_dev_s *priv = (FAR struct gd55_dev_s *)dev;
size_t blocksleft = nblocks;
int ret;
#ifndef CONFIG_MTD_GD55_SECTOR512
const size_t sectorsper64kblock = (64 * 1024) >> GD55_SECTOR_SHIFT;
const size_t sectorsper32kblock = (32 * 1024) >> GD55_SECTOR_SHIFT;
#endif
finfo("startblock: %08lx nblocks: %d\n", (long)startblock, (int)nblocks);
/* Lock access to the SPI bus until we complete the erase */
gd55_lock(priv);
#ifdef CONFIG_MTD_GD55_SECTOR512
while (blocksleft-- > 0)
{
/* Erase each sector */
gd55_erase_cache(priv, startblock);
startblock++;
}
/* Flush the last erase block left in the cache */
ret = gd55_flush_cache(priv);
if (ret < 0)
{
nblocks = ret;
}
#else
while (blocksleft > 0)
{
/* Check if block is aligned on 64k or 32k block for faster erase */
if (((startblock & (sectorsper64kblock - 1)) == 0) &&
(blocksleft >= sectorsper64kblock))
{
/* Erase 64k block */
ret = gd55_erase_64kblock(priv, startblock);
if (ret < 0)
{
nblocks = ret;
}
startblock += sectorsper64kblock;
blocksleft -= sectorsper64kblock;
finfo("Erased 64kbytes at address 0x%08" PRIx32 "\n",
startblock << GD55_SECTOR_SHIFT);
}
else if (((startblock & (sectorsper32kblock - 1)) == 0) &&
(blocksleft >= sectorsper32kblock))
{
/* Erase 32k block */
ret = gd55_erase_32kblock(priv, startblock);
if (ret < 0)
{
nblocks = ret;
}
startblock += sectorsper32kblock;
blocksleft -= sectorsper32kblock;
finfo("Erased 32kbytes at address 0x%08" PRIx32 "\n",
startblock << GD55_SECTOR_SHIFT);
}
else
{
/* Erase each sector */
ret = gd55_erase_sector(priv, startblock);
if (ret < 0)
{
nblocks = ret;
}
startblock++;
blocksleft--;
finfo("Erased 4kbytes at address 0x%08" PRIx32 "\n",
startblock << GD55_SECTOR_SHIFT);
}
}
#endif
ret = (int)nblocks;
gd55_unlock(priv);
return ret;
}
/****************************************************************************
* Name: gd55_bread
*
* Description:
* Read a number of blocks of data.
*
* Input Parameters:
* dev - a reference to the device structure
* startblock - start block of the memory to read
* nblocks - nblocks to read
* buf - pointer to the buffer to store the read data
*
* Returned Value:
* Size of the data read
*
****************************************************************************/
static ssize_t gd55_bread(FAR struct mtd_dev_s *dev, off_t startblock,
size_t nblocks, FAR uint8_t *buf)
{
ssize_t nbytes;
finfo("startblock: %08lx nblocks: %d\n", (long)startblock, (int)nblocks);
/* On this device, we can handle the block read just like the
* byte-oriented read
*/
#ifdef CONFIG_MTD_GD55_SECTOR512
nbytes = gd55_read(dev, startblock << GD55_SECTOR512_SHIFT,
nblocks << GD55_SECTOR512_SHIFT, buf);
if (nbytes > 0)
{
nbytes >>= GD55_SECTOR512_SHIFT;
}
#else
nbytes = gd55_read(dev, startblock << GD55_PAGE_SHIFT,
nblocks << GD55_PAGE_SHIFT, buf);
if (nbytes > 0)
{
nbytes >>= GD55_PAGE_SHIFT;
}
#endif
return nbytes;
}
/****************************************************************************
* Name: gd55_bwrite
*
* Description:
* Write a number of blocks of data.
*
* Input Parameters:
* dev - a reference to the device structure
* startblock - start block of the memory to write
* nblocks - nblocks to write
* buf - pointer to the buffer with the data to write
*
* Returned Value:
* Size of the data written
*
****************************************************************************/
static ssize_t gd55_bwrite(FAR struct mtd_dev_s *dev, off_t startblock,
size_t nblocks, FAR const uint8_t *buf)
{
FAR struct gd55_dev_s *priv = (FAR struct gd55_dev_s *)dev;
int ret;
finfo("startblock: %08lx nblocks: %d\n", (long)startblock, (int)nblocks);
/* Lock the QuadSPI bus and write all of the pages to FLASH */
gd55_lock(priv);
#if defined(CONFIG_MTD_GD55_SECTOR512)
ret = gd55_write_cache(priv, buf, startblock, nblocks);
if (ret < 0)
{
ferr("ERROR: gd55_write_cache failed: %d\n", ret);
}
#else
ret = gd55_write_page(priv, buf, startblock << GD55_PAGE_SHIFT,
nblocks << GD55_PAGE_SHIFT);
if (ret < 0)
{
ferr("ERROR: gd55_write_page failed: %d\n", ret);
}
#endif
gd55_unlock(priv);
return ret < 0 ? ret : nblocks;
}
/****************************************************************************
* Name: gd55_read
*
* Description:
* Read a number of bytes of data.
*
* Input Parameters:
* dev - a reference to the device structure
* offset - starting address of the memory to read
* nbytes - nbytes to read
* buf - pointer to the buffer to store the read data
*
* Returned Value:
* Size of the data read
*
****************************************************************************/
static ssize_t gd55_read(FAR struct mtd_dev_s *dev, off_t offset,
size_t nbytes, FAR uint8_t *buffer)
{
int ret;
FAR struct gd55_dev_s *priv = (FAR struct gd55_dev_s *)dev;
finfo("offset: %08lx nbytes: %d\n", (long)offset, (int)nbytes);
/* Lock the QuadSPI bus and select this FLASH part */
gd55_lock(priv);
ret = gd55_read_bytes(priv, buffer, offset, nbytes);
gd55_unlock(priv);
if (ret < 0)
{
ferr("ERROR: gd55_read_bytes returned: %d\n", ret);
return (ssize_t)ret;
}
finfo("return nbytes: %d\n", (int)nbytes);
return (ssize_t)nbytes;
}
/****************************************************************************
* Name: gd55_ioctl
*
* Description:
* IOCTLS relating to the GD55 mtd device
*
* Input Parameters:
* dev - a reference to the device structure
* cmd - ioctl command
* arg - ioctl argument
*
* Returned Value:
* Success (OK) or fail (negated error code)
****************************************************************************/
static int gd55_ioctl(FAR struct mtd_dev_s *dev, int cmd, unsigned long arg)
{
FAR struct gd55_dev_s *priv = (FAR struct gd55_dev_s *)dev;
int ret = -EINVAL;
finfo("cmd: %d\n", cmd);
switch (cmd)
{
case MTDIOC_GEOMETRY:
{
FAR struct mtd_geometry_s *geo =
(FAR struct mtd_geometry_s *)((uintptr_t)arg);
if (geo)
{
memset(geo, 0, sizeof(*geo));
/* Populate the geometry structure with information need to
* know the capacity and how to access the device.
*
* NOTE:
* that the device is treated as though it where just an
* array of fixed size blocks. That is most likely not true,
* but the client will expect the device logic to do whatever
* is necessary to make it appear so.
*/
#ifdef CONFIG_MTD_GD55_SECTOR512
geo->blocksize = GD55_SECTOR512_SIZE;
geo->erasesize = GD55_SECTOR512_SIZE;
geo->neraseblocks = priv->nsectors <<
(GD55_SECTOR_SHIFT -
GD55_SECTOR512_SHIFT);
#else
geo->blocksize = GD55_PAGE_SIZE;
geo->erasesize = GD55_SECTOR_SIZE;
geo->neraseblocks = priv->nsectors;
#endif
ret = OK;
finfo("blocksize: %" PRId32
" erasesize: %" PRId32
" neraseblocks: %" PRId32 "\n",
geo->blocksize, geo->erasesize, geo->neraseblocks);
}
}
break;
case BIOC_PARTINFO:
{
FAR struct partition_info_s *info =
(FAR struct partition_info_s *)arg;
if (info != NULL)
{
#ifdef CONFIG_MTD_GD55_SECTOR512
info->numsectors = priv->nsectors <<
(GD55_SECTOR_SHIFT - GD55_SECTOR512_SHIFT);
info->sectorsize = GD55_SECTOR512_SIZE;
#else
info->numsectors = priv->nsectors <<
(GD55_SECTOR_SHIFT - GD55_PAGE_SHIFT);
info->sectorsize = GD55_PAGE_SIZE;
#endif
info->startsector = 0;
info->parent[0] = '\0';
ret = OK;
}
}
break;
case MTDIOC_PROTECT:
{
FAR const struct mtd_protect_s *prot =
(FAR const struct mtd_protect_s *)((uintptr_t)arg);
DEBUGASSERT(prot);
gd55_lock(priv);
ret = gd55_protect(priv, prot->startblock, prot->nblocks);
gd55_unlock(priv);
}
break;
case MTDIOC_UNPROTECT:
{
FAR const struct mtd_protect_s *prot =
(FAR const struct mtd_protect_s *)((uintptr_t)arg);
DEBUGASSERT(prot);
gd55_lock(priv);
ret = gd55_unprotect(priv, prot->startblock, prot->nblocks);
gd55_unlock(priv);
}
break;
case MTDIOC_BULKERASE:
{
/* Erase the entire device */
gd55_lock(priv);
ret = gd55_erase_chip(priv);
gd55_unlock(priv);
}
break;
case MTDIOC_ERASESTATE:
{
FAR uint8_t *result = (FAR uint8_t *)arg;
*result = GD55_ERASED_STATE;
ret = OK;
}
break;
default:
ret = -ENOTTY; /* Bad/unsupported command */
break;
}
finfo("return %d\n", ret);
return ret;
}
/****************************************************************************
* Name: gd55_readid
*
* Description:
* Read the device ID.
* - the read ID is stored in the cmdbuf variable of the device structure
*
* Input Parameters:
* priv - a reference to the device structure
*
* Returned Value:
* Success (OK) or fail (negated error code)
*
****************************************************************************/
static int gd55_readid(FAR struct gd55_dev_s *priv)
{
/* Lock the QuadSPI bus and configure the bus. */
gd55_lock(priv);
/* Read the JEDEC ID */
gd55_command_read(priv, GD55_RDID, priv->cmdbuf, 4);
/* Unlock the bus */
gd55_unlock(priv);
finfo("Manufacturer: %02x Device Type %02x, Capacity: %02x\n",
priv->cmdbuf[0], priv->cmdbuf[1], priv->cmdbuf[2]);
/* Check for GigaDevices GD55 chip */
if (priv->cmdbuf[0] != GD55_JEDEC_MANUFACTURER &&
(priv->cmdbuf[1] != GD55L_JEDEC_MEMORY_TYPE ||
priv->cmdbuf[1] != GD55B_JEDEC_MEMORY_TYPE))
{
ferr("ERROR: Unrecognized device type: 0x%02x 0x%02x\n",
priv->cmdbuf[0], priv->cmdbuf[1]);
return -ENODEV;
}
/* Check for a supported capacity */
switch (priv->cmdbuf[2])
{
case GD55_JEDEC_1G_CAPACITY:
priv->nsectors = GD55_NSECTORS_1GBIT;
break;
case GD55_JEDEC_2G_CAPACITY:
priv->nsectors = GD55_NSECTORS_2GBIT;
break;
default:
ferr("ERROR: Unsupported memory capacity: %02x\n", priv->cmdbuf[2]);
return -ENODEV;
}
return OK;
}
/****************************************************************************
* Name: gd55_protect
*
* Description:
* The GD55 flash supports sector protection either by individual 64KiB
* blocks, or in a (64KiB * n^2) block from the bottom of the device memory
* OR from the top of the device memory.
*
* Input Parameters:
* priv - a reference to the device structure
* startblock - first block to protect
* nblocks - nblocks to protect
*
* Returned Value:
* Success (OK) or fail (negated error code)
*
****************************************************************************/
static int gd55_protect(FAR struct gd55_dev_s *priv, off_t startblock,
size_t nblocks)
{
uint8_t status[2];
int blkmask;
if (nblocks < GD55_MIN_BP_BLKS)
{
return -EINVAL; /* Too few blocks to protect */
}
/* Check if sector protection registers are locked */
status[0] = gd55_read_status1(priv);
status[1] = gd55_read_status2(priv);
if (status[1] & GD55_SR_SRP1)
{
/* Status register cannot be written to as device is in
* power supply lockdown or is set for OTP.
* If the external HW WP# pin is asserted we won't know until we
* attempt to unlock sectors though, regardless of state of SRP0 bit
* in status register 0.
*/
return -EACCES;
}
if (nblocks == (priv->nsectors * GD55_SECTORS_PER_BP_BLK))
{
if (startblock == 0)
{
blkmask = GD55_BP_ALL; /* protect every block */
}
else
{
return -EINVAL; /* Invalid size and startblock */
}
}
else
{
/* We can only protect in certain increments of size */
blkmask = 0;
while (nblocks > (GD55_MIN_BP_BLKS << blkmask))
{
if ((startblock % (GD55_MIN_BP_BLKS << blkmask)) ||
(nblocks % (GD55_MIN_BP_BLKS << blkmask)))
{
return -EINVAL; /* Not a size we can protect */
}
blkmask++;
}
blkmask = (startblock == 0) ? GD55_SR_BP_BOTTOM(blkmask) :
GD55_SR_BP_TOP(blkmask);
}
/* startblock must be first block, or (memory top - nblocks) */
if ((startblock != 0) &&
(startblock != (((priv->nsectors << GD55_SECTOR_SHIFT) /
GD55_MIN_BP_BLKS) - nblocks)))
{
return -EINVAL;
}
/* Clear the relevant status register bits for the new mask */
priv->cmdbuf[0] = status[0] & ~GD55_SR_BP_MASK;
/* Now set them */
priv->cmdbuf[0] |= blkmask;
if ((priv->cmdbuf[0] & GD55_SR_BP_MASK) == (status[0] & GD55_SR_BP_MASK))
{
return OK; /* this protection is already set */
}
gd55_write_status1(priv);
status[0] = gd55_read_status1(priv);
if ((status[0] & GD55_SR_BP_MASK) != (priv->cmdbuf[0] & GD55_SR_BP_MASK))
{
return -EACCES; /* Likely that the external HW WP# pin is asserted */
}
return OK;
}
/****************************************************************************
* Name: gd55_unprotect
*
* Description:
* The GD55 flash supports sector protection either by individual 64KiB
* blocks, or in a (64KiB * n^2) block from the bottom of the device memory
* OR from the top of the device memory.
*
* This function removes protection from all blocks
*
* REVISIT - there may be benefit from trying to only unprotect a range of
* sectors but this means complex checking of the request range against the
* current range of blocks that are currently protected so is non-trivial
*
* Input Parameters:
* priv - a reference to the device structure
* startblock - first block to unprotect (ignored for now)
* nblocks - nblocks to unprotect (ignored for now)
*
* Returned Value:
* Success (OK) or fail (negated error code)
*
****************************************************************************/
static int gd55_unprotect(FAR struct gd55_dev_s *priv, off_t startblock,
size_t nblocks)
{
uint8_t status[2];
/* Check if sector protection registers are locked */
status[0] = gd55_read_status1(priv);
status[1] = gd55_read_status2(priv);
if (status[1] & GD55_SR_SRP1)
{
/* Status register cannot be written to as device is in
* power supply lockdown or is set for OTP.
* If the external HW WP# pin is asserted we won't know until we
* attempt to unlock sectors though, regardless of state of SRP0 bit
* in status register 0.
*/
return -EACCES;
}
if (!(status[0] & GD55_SR_BP_MASK))
{
return OK; /* all blocks are already unprotected */
}
/* Clear all the status register BP bits */
priv->cmdbuf[0] = status[0] & ~GD55_SR_BP_MASK;
gd55_write_status1(priv);
status[0] = gd55_read_status1(priv);
if ((status[0] & GD55_SR_BP_MASK) != (priv->cmdbuf[0] & GD55_SR_BP_MASK))
{
return -EACCES; /* Likely that the external HW WP# pin is asserted */
}
return OK;
}
/****************************************************************************
* Name: gd55_isprotected
*
* Description:
* Check if an address has been write protected
*
* Input Parameters:
* priv - a reference to the device structure
* addr - address to check
* status - the previously read status register value
*
* Returned Value:
* Protected (true) or unprotected (false)
*
****************************************************************************/
static bool gd55_isprotected(FAR struct gd55_dev_s *priv, off_t addr,
uint8_t status)
{
off_t protstart;
off_t protend;
off_t protsize;
unsigned int bp;
/* the BP field is essentially the power-of-two of the number of 64k
* sectors that are protected, saturated to the device size.
* The msb determines if protection is:
* - top down (msb not set)
* - bottom up (msb set)
*/
bp = (status & GD55_SR_BP_MASK);
bp &= ~GD55_STATUS_TB_MASK; /* Ignore top/bottom for now */
bp >>= GD55_SR_BP_SHIFT;
if (bp == 0)
{
return false;
}
protsize = GD55_BP_SIZE;
protsize <<= (bp - 1);
protend = GD55_SECTOR_SIZE * priv->nsectors;
if (protsize > protend)
{
protsize = protend;
}
/* The final protection range then depends on if the protection region is
* configured top-down or bottom up.
*/
if ((status & GD55_STATUS_TB_BOTTOM))
{
protstart = 0;
protend = protstart + protsize;
}
else
{
protstart = protend - protsize;
/* protend already computed above */
}
return (addr >= protstart && addr < protend);
}
#ifdef CONFIG_MTD_GD55_SECTOR512
/****************************************************************************
* Name: gd55_flush_cache
*
* Description:
* If the cache 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.
*
* Input Parameters:
* priv - a reference to the device structure
*
* Returned Value:
* Success (OK) or fail (negated error code)
*
****************************************************************************/
static int gd55_flush_cache(FAR struct gd55_dev_s *priv)
{
int ret = OK;
if (IS_DIRTY(priv) || IS_ERASED(priv))
{
off_t address;
/* Convert the erase sector number into a FLASH address */
address = (off_t)priv->esectno << GD55_SECTOR_SHIFT;
/* Write entire erase block to FLASH */
ret = gd55_write_page(priv, priv->sector, address, GD55_SECTOR_SIZE);
if (ret < 0)
{
ferr("ERROR: gd55_write_page failed: %d\n", ret);
}
/* The cache is no long dirty and the FLASH is no longer erased */
CLR_DIRTY(priv);
CLR_ERASED(priv);
}
return ret;
}
/****************************************************************************
* Name: gd55_read_cache
*
* Description:
* Read cached data
*
* Input Parameters:
* priv - a reference to the device structure
* sector = sector to read
*
* Returned Value:
* Success (OK) or fail (negated error code)
*
****************************************************************************/
static FAR uint8_t *gd55_read_cache(FAR struct gd55_dev_s *priv,
off_t sector)
{
off_t esectno;
int shift;
int index;
int ret;
/* Convert from the 512 byte sector to the erase sector size of the device.
* For example, if the actual erase sector size is 4Kb (1 << 12), then we
* first shift to the right by 3 to get the sector number in 4096
* increments.
*/
shift = GD55_SECTOR_SHIFT - GD55_SECTOR512_SHIFT;
esectno = sector >> shift;
finfo("sector: %jd esectno: %jd (%d) shift=%d\n",
(intmax_t)sector, (intmax_t)esectno, priv->esectno, shift);
/* Check if the requested erase block is already in the cache */
if (!IS_VALID(priv) || esectno != priv->esectno)
{
/* No.. Flush any dirty erase block currently in the cache */
ret = gd55_flush_cache(priv);
if (ret < 0)
{
ferr("ERROR: gd55_flush_cache failed: %d\n", ret);
return NULL;
}
/* Read the erase block into the cache */
ret = gd55_read_bytes(priv, priv->sector,
(esectno << GD55_SECTOR_SHIFT),
GD55_SECTOR_SIZE);
if (ret < 0)
{
ferr("ERROR: gd55_read_bytes failed: %d\n", ret);
return NULL;
}
/* Mark the sector as cached */
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
*/
index = sector & ((1 << shift) - 1);
/* Return the address in the cache that holds this sector */
return &priv->sector[index << GD55_SECTOR512_SHIFT];
}
/****************************************************************************
* Name: gd55_erase_cache
*
* Description:
* erase cached data
*
* Input Parameters:
* priv - a reference to the device structure
* sector = sector to read
*
* Returned Value:
* Success (OK) or fail (negated error code)
*
****************************************************************************/
static void gd55_erase_cache(FAR struct gd55_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 = gd55_read_cache(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 >>
(GD55_SECTOR_SHIFT - GD55_SECTOR512_SHIFT);
finfo("sector: %jd esectno: %jd\n",
(intmax_t)sector, (intmax_t)esectno);
DEBUGVERIFY(gd55_erase_sector(priv, esectno));
SET_ERASED(priv);
}
/* Put the cached sector data into the erase state and mark the cache as
* dirty (but don't update the FLASH yet. The caller will do that at a
* more optimal time).
*/
memset(dest, GD55_ERASED_STATE, GD55_SECTOR512_SIZE);
SET_DIRTY(priv);
}
/****************************************************************************
* Name: gd55_write_cache
*
* Description:
* write cached data
*
* Input Parameters:
* priv - a reference to the device structure
* sector = sector to read
*
* Returned Value:
* Success (OK) or fail (negated error code)
*
****************************************************************************/
static int gd55_write_cache(FAR struct gd55_dev_s *priv,
FAR const uint8_t *buffer, off_t sector,
size_t nsectors)
{
FAR uint8_t *dest;
int ret;
for (; nsectors > 0; nsectors--)
{
/* First, make sure that the erase block containing 512 byte sector is
* in memory.
*/
dest = gd55_read_cache(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 >>
(GD55_SECTOR_SHIFT - GD55_SECTOR512_SHIFT);
finfo("sector: %jd esectno: %jd\n",
(intmax_t)sector, (intmax_t)esectno);
ret = gd55_erase_sector(priv, esectno);
if (ret < 0)
{
ferr("ERROR: gd55_erase_sector failed: %d\n", ret);
return ret;
}
SET_ERASED(priv);
}
/* Copy the new sector data into cached erase block */
memcpy(dest, buffer, GD55_SECTOR512_SIZE);
SET_DIRTY(priv);
/* Set up for the next 512 byte sector */
finfo("address: %08jx nbytes: %d 0x%04" PRIx32 "\n",
(intmax_t)(sector << GD55_SECTOR512_SHIFT),
GD55_SECTOR512_SIZE,
*(FAR uint32_t *)buffer);
buffer += GD55_SECTOR512_SIZE;
sector++;
}
/* Flush the last erase block left in the cache */
return gd55_flush_cache(priv);
}
#endif /* CONFIG_MTD_GD55_SECTOR512 */
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: gd55_initialize
*
* Description:
* Create an initialize MTD device instance.
*
* MTD devices are not registered in the file system, but are created as
* instances that can be bound to other functions (such as a block or
* character driver front end).
*
* Input Parameters:
* qspi - a reference to the qspi device to initialize
* unprotect - if true, unprotect the device
*
* Returned Value:
* Success (OK) or fail (negated error code)
****************************************************************************/
FAR struct mtd_dev_s *gd55_initialize(FAR struct qspi_dev_s *qspi,
bool unprotect)
{
FAR struct gd55_dev_s *dev;
int ret;
uint8_t status;
DEBUGASSERT(qspi != NULL);
/* Allocate a state structure (we allocate the structure instead of using
* a fixed, static allocation so that we can handle multiple FLASH devices.
* The current implementation would handle only one FLASH part per QuadSPI
* device (only because of the QSPIDEV_FLASH(0) definition) and so would
* have to be extended to handle multiple FLASH parts on the same QuadSPI
* bus.
*/
dev = kmm_zalloc(sizeof(*dev));
if (dev == NULL)
{
ferr("Failed to allocate mtd device\n");
return NULL;
}
dev->mtd.erase = gd55_erase;
dev->mtd.bread = gd55_bread;
dev->mtd.bwrite = gd55_bwrite;
dev->mtd.read = gd55_read;
dev->mtd.ioctl = gd55_ioctl;
dev->mtd.name = "gd55";
dev->qspi = qspi;
/* Allocate a 4-byte buffer to support DMA-able command data */
dev->cmdbuf = (FAR uint8_t *)QSPI_ALLOC(qspi, 4);
if (dev->cmdbuf == NULL)
{
ferr("Failed to allocate command buffer\n");
goto exit_free_dev;
}
dev->readbuf = (FAR uint8_t *)QSPI_ALLOC(qspi, 2);
if (dev->readbuf == NULL)
{
ferr("ERROR Failed to allocate read buffer\n");
goto exit_free_cmdbuf;
}
/* Identify the FLASH chip and get its capacity */
ret = gd55_readid(dev);
if (ret != OK)
{
/* Unrecognized! Discard all of that work we just did and return NULL */
ferr("Unrecognized QSPI device\n");
goto exit_free_readbuf;
}
/* Unprotect all FLASH sectors if so requested. */
if (unprotect)
{
ret = gd55_unprotect(dev, 0, dev->nsectors - 1);
if (ret < 0)
{
ferr("ERROR: Sector unprotect failed\n");
}
}
#ifdef CONFIG_MTD_GD55_SECTOR512 /* Simulate a 512 byte sector */
/* Allocate a buffer for the erase block cache */
dev->sector = (FAR uint8_t *)QSPI_ALLOC(qspi, GD55_SECTOR_SIZE);
if (dev->sector == NULL)
{
/* Allocation failed! Discard all of that work we just did and
* return NULL
*/
ferr("ERROR: Sector allocation failed\n");
goto exit_free_readbuf;
}
#endif
status = gd55_read_status1(dev);
/* Avoid compiler warnings in case info logs are disabled */
UNUSED(status);
finfo("device ready Status = 0x%02x\n", status);
/* Return the implementation-specific state structure as the MTD device */
return &dev->mtd;
exit_free_readbuf:
QSPI_FREE(qspi, dev->readbuf);
exit_free_cmdbuf:
QSPI_FREE(qspi, dev->cmdbuf);
exit_free_dev:
kmm_free(dev);
return NULL;
}