diff --git a/drivers/mtd/w25qxxxjv.c b/drivers/mtd/w25qxxxjv.c index e77eeae1b87..7f8e49b344e 100644 --- a/drivers/mtd/w25qxxxjv.c +++ b/drivers/mtd/w25qxxxjv.c @@ -107,12 +107,14 @@ #define W25QXXXJV_EXIT_4BT_MODE 0xE9 /* Exit 4-byte address mode */ /* Read Commands ************************************************************ - * Command Value Description: * - * Data sequence * + * Command Value Description: * + * Data sequence * */ -#define W25QXXXJV_FAST_READ_QUADIO 0xeb /* Fast Read Quad I/O: * - * 0xeb | ADDR | data... */ +#define W25QXXXJV_FAST_READ_QUADIO 0xeb /* Fast Read Quad I/O: * + * 0xeb | ADDR | data... */ +#define W25QXXXJV_FAST_READ_QUADIO_4BT 0xec /* Fast Read Quad I/O 4 bytes * + * 0xec | ADDR | data... */ /* Reset Commands *********************************************************** * Command Value Description: * @@ -130,6 +132,17 @@ * 0x9f | Manufacturer | MemoryType | * * Capacity */ +/* Multiple Die Commands *************************************************** + * Command Value Description: * + * Data sequence * + */ + +#define W25QXXXJV_SW_DIE_SELECT 0xc2 /* SW_DIE_SELECT ID: * + * 0xc2 | Die index */ + +#define W25QXXXJV_DIE0 0x0 /* First die index */ +#define W25QXXXJV_DIE1 0x1 /* Second die index */ + /* Flash Manufacturer JEDEC IDs */ #define W25QXXXJV_JEDEC_ID_WINBOND 0xef /* Winbond Serial Flash */ @@ -274,6 +287,7 @@ #define W25Q01_SECTOR_COUNT (32768) #define W25Q01_PAGE_SIZE (256) #define W25Q01_PAGE_SHIFT (8) +#define W25Q01_DIE_SIZE ((W25Q01_SECTOR_SIZE * W25Q01_SECTOR_COUNT) / 2) /* Cache flags **************************************************************/ @@ -312,12 +326,15 @@ struct w25qxxxjv_dev_s { struct mtd_dev_s mtd; /* MTD interface */ FAR struct qspi_dev_s *qspi; /* Saved QuadSPI interface instance */ + uint32_t diesize; /* Size of a single die. 0 if just one die used */ uint16_t nsectors; /* Number of erase sectors */ uint8_t sectorshift; /* Log2 of sector size */ uint8_t pageshift; /* Log2 of page size */ uint8_t addresslen; /* Length of address 3 or 4 bytes */ uint8_t protectmask; /* Mask for protect bits in status register */ uint8_t tbmask; /* Mask for top/bottom bit in status register */ + uint8_t numofdies; /* Number of dies in flash */ + uint8_t currentdie; /* Number of current active die */ FAR uint8_t *cmdbuf; /* Allocated command buffer */ FAR uint8_t *readbuf; /* Allocated status read buffer */ @@ -360,8 +377,11 @@ static void w25qxxxjv_write_volcfg(FAR struct w25qxxxjv_dev_s *priv); #endif static void w25qxxxjv_write_enable(FAR struct w25qxxxjv_dev_s *priv); static void w25qxxxjv_write_disable(FAR struct w25qxxxjv_dev_s *priv); +static void w25qxxxjv_set_die(FAR struct w25qxxxjv_dev_s *priv, uint8_t die); static void w25qxxxjv_quad_enable(FAR struct w25qxxxjv_dev_s *priv); +static int w25qxxxjv_get_die_from_addr(FAR struct w25qxxxjv_dev_s *priv, + off_t addr); static int w25qxxxjv_readid(FAR struct w25qxxxjv_dev_s *priv); static int w25qxxxjv_protect(FAR struct w25qxxxjv_dev_s *priv, off_t startblock, size_t nblocks); @@ -602,6 +622,25 @@ static void w25qxxxjv_write_disable(FAR struct w25qxxxjv_dev_s *priv) while ((status & STATUS_WEL_MASK) != STATUS_WEL_DISABLED); } +/**************************************************************************** + * Name: w25qxxxjv_set_die + ****************************************************************************/ + +static void w25qxxxjv_set_die(FAR struct w25qxxxjv_dev_s *priv, uint8_t die) +{ + char buff[1] = + { + die, + }; + + w25qxxxjv_write_enable(priv); + w25qxxxjv_command_write(priv->qspi, W25QXXXJV_SW_DIE_SELECT, + (FAR const void *)buff, 1); + w25qxxxjv_write_disable(priv); + + priv->currentdie = die; +} + /**************************************************************************** * Name: w25qxxxjv_quad_enable ****************************************************************************/ @@ -625,6 +664,26 @@ static void w25qxxxjv_quad_enable(FAR struct w25qxxxjv_dev_s *priv) } } +/**************************************************************************** + * Name: w25qxxxjv_get_die_from_addr + ****************************************************************************/ + +static int w25qxxxjv_get_die_from_addr(FAR struct w25qxxxjv_dev_s *priv, + off_t addr) +{ + uint8_t die; + + die = W25QXXXJV_DIE0; + if (addr >= priv->diesize) + { + die = W25QXXXJV_DIE1; + } + + w25qxxxjv_set_die(priv, die); + + return die; +} + /**************************************************************************** * Name: w25qxxxjv_readid ****************************************************************************/ @@ -657,6 +716,10 @@ static inline int w25qxxxjv_readid(struct w25qxxxjv_dev_s *priv) /* Check for a supported capacity */ + priv->numofdies = 0; + priv->currentdie = 0; + priv->diesize = 0; + switch (priv->cmdbuf[2]) { case W25Q016_JEDEC_CAPACITY: @@ -714,12 +777,14 @@ static inline int w25qxxxjv_readid(struct w25qxxxjv_dev_s *priv) break; case W25Q01_JEDEC_CAPACITY: + priv->diesize = W25Q01_DIE_SIZE; priv->sectorshift = W25Q01_SECTOR_SHIFT; priv->pageshift = W25Q01_PAGE_SHIFT; priv->nsectors = W25Q01_SECTOR_COUNT; priv->addresslen = 4; priv->protectmask = STATUS_BP_4_MASK; priv->tbmask = STATUS_TB_6_MASK; + priv->numofdies = 2; break; /* Support for this part is not implemented yet */ @@ -758,11 +823,22 @@ static int w25qxxxjv_protect(FAR struct w25qxxxjv_dev_s *priv, /* Check the new status */ - priv->cmdbuf[0] = w25qxxxjv_read_status(priv); - if ((priv->cmdbuf[0] & priv->protectmask) != - (STATUS_BP_ALL & priv->protectmask)) + for (int i = 0; i < priv->numofdies; i++) { - return -EACCES; + w25qxxxjv_set_die(priv, i); + priv->cmdbuf[0] = w25qxxxjv_read_status(priv); + if ((priv->cmdbuf[0] & priv->protectmask) != + (STATUS_BP_ALL & priv->protectmask)) + { + return -EACCES; + } + } + + /* Set the original die active again if this is multi die flash */ + + if (priv->numofdies != 0) + { + w25qxxxjv_set_die(priv, priv->currentdie); } return OK; @@ -796,10 +872,21 @@ static int w25qxxxjv_unprotect(FAR struct w25qxxxjv_dev_s *priv, /* Check the new status */ - priv->cmdbuf[0] = w25qxxxjv_read_status(priv); - if ((priv->cmdbuf[0] & (STATUS_SRP_MASK | priv->protectmask)) != 0) + for (int i = 0; i < priv->numofdies; i++) { - return -EACCES; + w25qxxxjv_set_die(priv, i); + priv->cmdbuf[0] = w25qxxxjv_read_status(priv); + if ((priv->cmdbuf[0] & priv->protectmask) != 0) + { + return -EACCES; + } + } + + /* Set the original die active again if this is multi die flash */ + + if (priv->numofdies != 0) + { + w25qxxxjv_set_die(priv, priv->currentdie); } return OK; @@ -870,8 +957,17 @@ static int w25qxxxjv_erase_sector(FAR struct w25qxxxjv_dev_s *priv, finfo("sector: %08lx\n", (unsigned long)sector); + /* Get the address associated with the sector */ + + address = (off_t)sector << priv->sectorshift; + /* Check that the flash is ready and unprotected */ + if (priv->numofdies != 0) + { + priv->currentdie = w25qxxxjv_get_die_from_addr(priv, address); + } + status = w25qxxxjv_read_status(priv); if ((status & STATUS_BUSY_MASK) != STATUS_READY) { @@ -879,10 +975,6 @@ static int w25qxxxjv_erase_sector(FAR struct w25qxxxjv_dev_s *priv, return -EBUSY; } - /* Get the address associated with the sector */ - - address = (off_t)sector << priv->sectorshift; - if ((status & priv->protectmask) != 0 && w25qxxxjv_isprotected(priv, status, address)) { @@ -954,7 +1046,8 @@ static int w25qxxxjv_read_byte(FAR struct w25qxxxjv_dev_s *priv, meminfo.addrlen = priv->addresslen; meminfo.dummies = CONFIG_W25QXXXJV_DUMMIES; meminfo.buflen = buflen; - meminfo.cmd = W25QXXXJV_FAST_READ_QUADIO; + meminfo.cmd = (priv->addresslen == 4) ? W25QXXXJV_FAST_READ_QUADIO_4BT + : W25QXXXJV_FAST_READ_QUADIO; meminfo.addr = address; meminfo.buffer = buffer; @@ -1301,6 +1394,12 @@ static ssize_t w25qxxxjv_bread(FAR struct mtd_dev_s *dev, off_t startblock, * read */ + if (priv->numofdies != 0) + { + priv->currentdie = w25qxxxjv_get_die_from_addr(priv, startblock << + priv->pageshift); + } + #ifdef CONFIG_W25QXXXJV_SECTOR512 nbytes = w25qxxxjv_read(dev, startblock << W25QXXXJV_SECTOR512_SHIFT, nblocks << W25QXXXJV_SECTOR512_SHIFT, buffer); @@ -1332,6 +1431,12 @@ static ssize_t w25qxxxjv_bwrite(FAR struct mtd_dev_s *dev, off_t startblock, finfo("startblock: %08lx nblocks: %d\n", (long)startblock, (int)nblocks); + if (priv->numofdies != 0) + { + priv->currentdie = w25qxxxjv_get_die_from_addr(priv, startblock << + priv->pageshift); + } + /* Lock the QuadSPI bus and write all of the pages to FLASH */ w25qxxxjv_lock(priv->qspi); @@ -1374,6 +1479,7 @@ static ssize_t w25qxxxjv_read(FAR struct mtd_dev_s *dev, /* Lock the QuadSPI bus and select this FLASH part */ w25qxxxjv_lock(priv->qspi); + ret = w25qxxxjv_read_byte(priv, buffer, offset, nbytes); w25qxxxjv_unlock(priv->qspi);