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; }