TimJTi commented on code in PR #15523: URL: https://github.com/apache/nuttx/pull/15523#discussion_r1914620744
########## drivers/mtd/gd55.c: ########## @@ -0,0 +1,2065 @@ +/**************************************************************************** + * 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/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-BP0 protect Top down */ +#define GD55_STATUS_TB_BOTTOM (1 << 6) /* = 1, BP3-BP0 protect 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 (default) */ +#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 + * + ****************************************************************************/ + +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 + * + ****************************************************************************/ + +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 + * + ****************************************************************************/ + +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 + * + ****************************************************************************/ + +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 + * + ****************************************************************************/ + +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 + * + ****************************************************************************/ + +int gd55_read_bytes(FAR struct gd55_dev_s *priv, FAR uint8_t *buffer, + off_t address, size_t buflen) +{ + bool mode_4byte_addr; + 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 + * + ****************************************************************************/ + +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; + 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); + return ret; + } + + /* 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); + } + + if (meminfo.addrlen == 4) + { + gd55_command(priv, GD55_DIS4B); + } + + return OK; +} + +/**************************************************************************** + * 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 + * + ****************************************************************************/ + +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 + * + ****************************************************************************/ + +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 + * + ****************************************************************************/ + +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 + * + ****************************************************************************/ + +int gd55_erase_chip(FAR struct gd55_dev_s *priv) +{ + uint8_t status; + + /* 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 + * + ****************************************************************************/ + +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 + * + ****************************************************************************/ + +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) + * + ****************************************************************************/ + +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) + { + return ret; Review Comment: Good spot. Thanks. -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
