From 2c5947cffd81ac8181346efacdca3c777ab330ba Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Tue, 7 Jun 2022 20:59:18 +0200 Subject: Revert "mtd: rawnand: add support for Toshiba TC58NVG0S3HTA00 NAND flash" This reverts commit 3380557fc7e28d9bce7607e16d98f123d36da4ca. It turned out this "4-byte" ID might have been an honest mistake. Regrettably, the chip Andreas has might be a counterfeit or is damaged in some other way and shouldn't have ended up in a router. Andreas reported his chip is returning just four bytes: "98 f1 80 15 00 00 00 00". However, according to Kioxia/Toshiba's datasheet, there should have been at least another byte that would have contained the correct OOB size that Andreas needed. Miquel and Andreas are both favoring reverting the patch over further, possibly hacky modifications: "[Reverting] is the safest option here. Apart from this device, we do not know how many devices have these damaged/counterfeit chips. If it is just a couple and only on Fritzboxes, as suggested in the Github issue the patch could be carried through OpenWrt[...]" Thanks to several users on the openwrt forum and github issue, who stayed along for the ride: - Peter-vdL for reporting the issue and testing patches. - neg2led and Hannu Nyman who did all the datasheet digging and debugging. Cc: Andreas Boehler Suggested-by: Andreas Boehler Suggested-by: Miquel Raynal Link: https://github.com/openwrt/openwrt/issues/9962 Signed-off-by: Christian Lamparter Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20220607185918.1048204-1-chunkeey@gmail.com --- drivers/mtd/nand/raw/nand_ids.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/raw/nand_ids.c b/drivers/mtd/nand/raw/nand_ids.c index 88c2440b47d8..dacc5529b3df 100644 --- a/drivers/mtd/nand/raw/nand_ids.c +++ b/drivers/mtd/nand/raw/nand_ids.c @@ -29,9 +29,6 @@ struct nand_flash_dev nand_flash_ids[] = { {"TC58NVG0S3E 1G 3.3V 8-bit", { .id = {0x98, 0xd1, 0x90, 0x15, 0x76, 0x14, 0x01, 0x00} }, SZ_2K, SZ_128, SZ_128K, 0, 8, 64, NAND_ECC_INFO(1, SZ_512), }, - {"TC58NVG0S3HTA00 1G 3.3V 8-bit", - { .id = {0x98, 0xf1, 0x80, 0x15} }, - SZ_2K, SZ_128, SZ_128K, 0, 4, 128, NAND_ECC_INFO(8, SZ_512), }, {"TC58NVG2S0F 4G 3.3V 8-bit", { .id = {0x98, 0xdc, 0x90, 0x26, 0x76, 0x15, 0x01, 0x08} }, SZ_4K, SZ_512, SZ_256K, 0, 8, 224, NAND_ECC_INFO(4, SZ_512) }, -- cgit v1.2.3 From 06781a5026350cde699d2d10c9914a25c1524f45 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Tue, 14 Jun 2022 10:31:38 +0200 Subject: mtd: rawnand: gpmi: Fix setting busy timeout setting The DEVICE_BUSY_TIMEOUT value is described in the Reference Manual as: | Timeout waiting for NAND Ready/Busy or ATA IRQ. Used in WAIT_FOR_READY | mode. This value is the number of GPMI_CLK cycles multiplied by 4096. So instead of multiplying the value in cycles with 4096, we have to divide it by that value. Use DIV_ROUND_UP to make sure we are on the safe side, especially when the calculated value in cycles is smaller than 4096 as typically the case. This bug likely never triggered because any timeout != 0 usually will do. In my case the busy timeout in cycles was originally calculated as 2408, which multiplied with 4096 is 0x968000. The lower 16 bits were taken for the 16 bit wide register field, so the register value was 0x8000. With 2970bf5a32f0 ("mtd: rawnand: gpmi: fix controller timings setting") however the value in cycles became 2384, which multiplied with 4096 is 0x950000. The lower 16 bit are 0x0 now resulting in an intermediate timeout when reading from NAND. Fixes: b1206122069aa ("mtd: rawnand: gpmi: use core timings instead of an empirical derivation") Cc: stable@vger.kernel.org Signed-off-by: Sascha Hauer Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20220614083138.3455683-1-s.hauer@pengutronix.de --- drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c index 0b68d05846e1..889e40329956 100644 --- a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c @@ -890,7 +890,7 @@ static int gpmi_nfc_compute_timings(struct gpmi_nand_data *this, hw->timing0 = BF_GPMI_TIMING0_ADDRESS_SETUP(addr_setup_cycles) | BF_GPMI_TIMING0_DATA_HOLD(data_hold_cycles) | BF_GPMI_TIMING0_DATA_SETUP(data_setup_cycles); - hw->timing1 = BF_GPMI_TIMING1_BUSY_TIMEOUT(busy_timeout_cycles * 4096); + hw->timing1 = BF_GPMI_TIMING1_BUSY_TIMEOUT(DIV_ROUND_UP(busy_timeout_cycles, 4096)); /* * Derive NFC ideal delay from {3}: -- cgit v1.2.3 From 77d4ac6d38487c2ab2688335b2a904469f5b16a4 Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Mon, 18 Apr 2022 13:26:50 +0200 Subject: mtd: spi-nor: move SECT_4K_PMC special handling The SECT_4K_PMC flag will set a device specific opcode for the 4k sector erase. Instead of handling it in the core, we can move it to a late_init(). In that late init, loop over all erase types, look for the 4k size and replace the opcode. Signed-off-by: Michael Walle Signed-off-by: Pratyush Yadav Link: https://lore.kernel.org/r/20220418112650.2791459-1-michael@walle.cc --- drivers/mtd/spi-nor/core.c | 7 +------ drivers/mtd/spi-nor/core.h | 2 -- drivers/mtd/spi-nor/issi.c | 23 +++++++++++++++++++++-- 3 files changed, 22 insertions(+), 10 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index 502967c76c5f..ce5d69317d46 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -2382,12 +2382,7 @@ static void spi_nor_no_sfdp_init_params(struct spi_nor *nor) */ erase_mask = 0; i = 0; - if (no_sfdp_flags & SECT_4K_PMC) { - erase_mask |= BIT(i); - spi_nor_set_erase_type(&map->erase_type[i], 4096u, - SPINOR_OP_BE_4K_PMC); - i++; - } else if (no_sfdp_flags & SECT_4K) { + if (no_sfdp_flags & SECT_4K) { erase_mask |= BIT(i); spi_nor_set_erase_type(&map->erase_type[i], 4096u, SPINOR_OP_BE_4K); diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h index 3f841ec36e56..61886868cd02 100644 --- a/drivers/mtd/spi-nor/core.h +++ b/drivers/mtd/spi-nor/core.h @@ -457,7 +457,6 @@ struct spi_nor_fixups { * flags are used together with the SPI_NOR_SKIP_SFDP flag. * SPI_NOR_SKIP_SFDP: skip parsing of SFDP tables. * SECT_4K: SPINOR_OP_BE_4K works uniformly. - * SECT_4K_PMC: SPINOR_OP_BE_4K_PMC works uniformly. * SPI_NOR_DUAL_READ: flash supports Dual Read. * SPI_NOR_QUAD_READ: flash supports Quad Read. * SPI_NOR_OCTAL_READ: flash supports Octal Read. @@ -505,7 +504,6 @@ struct flash_info { u8 no_sfdp_flags; #define SPI_NOR_SKIP_SFDP BIT(0) #define SECT_4K BIT(1) -#define SECT_4K_PMC BIT(2) #define SPI_NOR_DUAL_READ BIT(3) #define SPI_NOR_QUAD_READ BIT(4) #define SPI_NOR_OCTAL_READ BIT(5) diff --git a/drivers/mtd/spi-nor/issi.c b/drivers/mtd/spi-nor/issi.c index c012bc2486e1..3c7d51d2b050 100644 --- a/drivers/mtd/spi-nor/issi.c +++ b/drivers/mtd/spi-nor/issi.c @@ -29,6 +29,21 @@ static const struct spi_nor_fixups is25lp256_fixups = { .post_bfpt = is25lp256_post_bfpt_fixups, }; +static void pm25lv_nor_late_init(struct spi_nor *nor) +{ + struct spi_nor_erase_map *map = &nor->params->erase_map; + int i; + + /* The PM25LV series has a different 4k sector erase opcode */ + for (i = 0; i < SNOR_ERASE_TYPE_MAX; i++) + if (map->erase_type[i].size == 4096) + map->erase_type[i].opcode = SPINOR_OP_BE_4K_PMC; +} + +static const struct spi_nor_fixups pm25lv_nor_fixups = { + .late_init = pm25lv_nor_late_init, +}; + static const struct flash_info issi_nor_parts[] = { /* ISSI */ { "is25cd512", INFO(0x7f9d20, 0, 32 * 1024, 2) @@ -62,9 +77,13 @@ static const struct flash_info issi_nor_parts[] = { /* PMC */ { "pm25lv512", INFO(0, 0, 32 * 1024, 2) - NO_SFDP_FLAGS(SECT_4K_PMC) }, + NO_SFDP_FLAGS(SECT_4K) + .fixups = &pm25lv_nor_fixups + }, { "pm25lv010", INFO(0, 0, 32 * 1024, 4) - NO_SFDP_FLAGS(SECT_4K_PMC) }, + NO_SFDP_FLAGS(SECT_4K) + .fixups = &pm25lv_nor_fixups + }, { "pm25lq032", INFO(0x7f9d46, 0, 64 * 1024, 64) NO_SFDP_FLAGS(SECT_4K) }, }; -- cgit v1.2.3 From 90c517f435a9e469cceb7b1985ba92011c2e26aa Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Fri, 6 May 2022 13:51:58 +0300 Subject: mtd: spi-nor: micron-st: Skip FSR reading if SPI controller does not support it The Intel SPI controller does not support low level operations, like reading the flag status register (FSR). It only exposes a set of high level operations for software to use. For this reason check the return value of micron_st_nor_read_fsr() and if the operation was not supported, use the status register value only. This allows the chip to work even when attached to Intel SPI controller (there are such systems out there). Signed-off-by: Mika Westerberg Signed-off-by: Tudor Ambarus Reviewed-by: Michael Walle Link: https://lore.kernel.org/r/20220506105158.43613-1-mika.westerberg@linux.intel.com --- drivers/mtd/spi-nor/micron-st.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/spi-nor/micron-st.c b/drivers/mtd/spi-nor/micron-st.c index a96f74e0f568..3c9681a3f7a3 100644 --- a/drivers/mtd/spi-nor/micron-st.c +++ b/drivers/mtd/spi-nor/micron-st.c @@ -399,8 +399,16 @@ static int micron_st_nor_ready(struct spi_nor *nor) return sr_ready; ret = micron_st_nor_read_fsr(nor, nor->bouncebuf); - if (ret) - return ret; + if (ret) { + /* + * Some controllers, such as Intel SPI, do not support low + * level operations such as reading the flag status + * register. They only expose small amount of high level + * operations to the software. If this is the case we use + * only the status register value. + */ + return ret == -EOPNOTSUPP ? sr_ready : ret; + } if (nor->bouncebuf[0] & (FSR_E_ERR | FSR_P_ERR)) { if (nor->bouncebuf[0] & FSR_E_ERR) -- cgit v1.2.3 From 41e4f15f02af67fbcd6fec243ef52627f51760b4 Mon Sep 17 00:00:00 2001 From: Sungbo Eo Date: Fri, 23 Jul 2021 23:12:32 +0900 Subject: mtd: spi-nor: esmt: Use correct name of f25l32qa The flash ID of F25L32QA is 0x8c4016, whereas that of F25L32QA(2S) is 0x8c4116. F25L32QA(2S) is the newer version of F25L32QA and its BPn bits are non-volatile, unlike its older version. Signed-off-by: Sungbo Eo Signed-off-by: Tudor Ambarus Reviewed-by: Michael Walle Link: https://lore.kernel.org/r/20210723141232.15659-1-mans0n@gorani.run Datasheet: https://www.esmt.com.tw/upload/pdf/ESMT/datasheets/F25L32QA.pdf Datasheet: https://www.esmt.com.tw/upload/pdf/ESMT/datasheets/F25L32QA_1(2S).pdf --- drivers/mtd/spi-nor/esmt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/spi-nor/esmt.c b/drivers/mtd/spi-nor/esmt.c index 79e2408f4998..fcc3b0e7cda9 100644 --- a/drivers/mtd/spi-nor/esmt.c +++ b/drivers/mtd/spi-nor/esmt.c @@ -13,7 +13,7 @@ static const struct flash_info esmt_nor_parts[] = { { "f25l32pa", INFO(0x8c2016, 0, 64 * 1024, 64) FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) NO_SFDP_FLAGS(SECT_4K) }, - { "f25l32qa", INFO(0x8c4116, 0, 64 * 1024, 64) + { "f25l32qa-2s", INFO(0x8c4116, 0, 64 * 1024, 64) FLAGS(SPI_NOR_HAS_LOCK) NO_SFDP_FLAGS(SECT_4K) }, { "f25l64qa", INFO(0x8c4117, 0, 64 * 1024, 128) -- cgit v1.2.3 From c452d49849d48bd37ae97fc2bc92c6435707c35f Mon Sep 17 00:00:00 2001 From: Tudor Ambarus Date: Mon, 25 Jul 2022 12:24:59 +0300 Subject: mtd: spi-nor: s/addr_width/addr_nbytes Address width was an unfortunate name, as it means the number of IO lines used for the address, whereas in the code it is used as the number of address bytes. s/addr_width/addr_nbytes throughout the entire SPI NOR framework. Signed-off-by: Tudor Ambarus Reviewed-by: Michael Walle Acked-by: Pratyush Yadav Link: https://lore.kernel.org/r/20220725092505.446315-2-tudor.ambarus@microchip.com --- drivers/mtd/spi-nor/controllers/hisi-sfc.c | 2 +- drivers/mtd/spi-nor/controllers/nxp-spifi.c | 8 ++--- drivers/mtd/spi-nor/core.c | 54 ++++++++++++++--------------- drivers/mtd/spi-nor/core.h | 12 +++---- drivers/mtd/spi-nor/debugfs.c | 2 +- drivers/mtd/spi-nor/issi.c | 8 ++--- drivers/mtd/spi-nor/otp.c | 12 +++---- drivers/mtd/spi-nor/sfdp.c | 32 ++++++++--------- drivers/mtd/spi-nor/xilinx.c | 2 +- include/linux/mtd/spi-nor.h | 4 +-- 10 files changed, 68 insertions(+), 68 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/spi-nor/controllers/hisi-sfc.c b/drivers/mtd/spi-nor/controllers/hisi-sfc.c index 94a969185ceb..5070d72835ec 100644 --- a/drivers/mtd/spi-nor/controllers/hisi-sfc.c +++ b/drivers/mtd/spi-nor/controllers/hisi-sfc.c @@ -237,7 +237,7 @@ static int hisi_spi_nor_dma_transfer(struct spi_nor *nor, loff_t start_off, reg = readl(host->regbase + FMC_CFG); reg &= ~(FMC_CFG_OP_MODE_MASK | SPI_NOR_ADDR_MODE_MASK); reg |= FMC_CFG_OP_MODE_NORMAL; - reg |= (nor->addr_width == 4) ? SPI_NOR_ADDR_MODE_4BYTES + reg |= (nor->addr_nbytes == 4) ? SPI_NOR_ADDR_MODE_4BYTES : SPI_NOR_ADDR_MODE_3BYTES; writel(reg, host->regbase + FMC_CFG); diff --git a/drivers/mtd/spi-nor/controllers/nxp-spifi.c b/drivers/mtd/spi-nor/controllers/nxp-spifi.c index 9032b9ab2eaf..ab3990e6ac25 100644 --- a/drivers/mtd/spi-nor/controllers/nxp-spifi.c +++ b/drivers/mtd/spi-nor/controllers/nxp-spifi.c @@ -203,7 +203,7 @@ static ssize_t nxp_spifi_write(struct spi_nor *nor, loff_t to, size_t len, SPIFI_CMD_DATALEN(len) | SPIFI_CMD_FIELDFORM_ALL_SERIAL | SPIFI_CMD_OPCODE(nor->program_opcode) | - SPIFI_CMD_FRAMEFORM(spifi->nor.addr_width + 1); + SPIFI_CMD_FRAMEFORM(spifi->nor.addr_nbytes + 1); writel(cmd, spifi->io_base + SPIFI_CMD); for (i = 0; i < len; i++) @@ -230,7 +230,7 @@ static int nxp_spifi_erase(struct spi_nor *nor, loff_t offs) cmd = SPIFI_CMD_FIELDFORM_ALL_SERIAL | SPIFI_CMD_OPCODE(nor->erase_opcode) | - SPIFI_CMD_FRAMEFORM(spifi->nor.addr_width + 1); + SPIFI_CMD_FRAMEFORM(spifi->nor.addr_nbytes + 1); writel(cmd, spifi->io_base + SPIFI_CMD); return nxp_spifi_wait_for_cmd(spifi); @@ -252,12 +252,12 @@ static int nxp_spifi_setup_memory_cmd(struct nxp_spifi *spifi) } /* Memory mode supports address length between 1 and 4 */ - if (spifi->nor.addr_width < 1 || spifi->nor.addr_width > 4) + if (spifi->nor.addr_nbytes < 1 || spifi->nor.addr_nbytes > 4) return -EINVAL; spifi->mcmd |= SPIFI_CMD_OPCODE(spifi->nor.read_opcode) | SPIFI_CMD_INTLEN(spifi->nor.read_dummy / 8) | - SPIFI_CMD_FRAMEFORM(spifi->nor.addr_width + 1); + SPIFI_CMD_FRAMEFORM(spifi->nor.addr_nbytes + 1); return 0; } diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index ce5d69317d46..31604188ee59 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -38,7 +38,7 @@ */ #define CHIP_ERASE_2MB_READY_WAIT_JIFFIES (40UL * HZ) -#define SPI_NOR_MAX_ADDR_WIDTH 4 +#define SPI_NOR_MAX_ADDR_NBYTES 4 #define SPI_NOR_SRST_SLEEP_MIN 200 #define SPI_NOR_SRST_SLEEP_MAX 400 @@ -198,7 +198,7 @@ static ssize_t spi_nor_spimem_read_data(struct spi_nor *nor, loff_t from, { struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(nor->read_opcode, 0), - SPI_MEM_OP_ADDR(nor->addr_width, from, 0), + SPI_MEM_OP_ADDR(nor->addr_nbytes, from, 0), SPI_MEM_OP_DUMMY(nor->read_dummy, 0), SPI_MEM_OP_DATA_IN(len, buf, 0)); bool usebouncebuf; @@ -262,7 +262,7 @@ static ssize_t spi_nor_spimem_write_data(struct spi_nor *nor, loff_t to, { struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(nor->program_opcode, 0), - SPI_MEM_OP_ADDR(nor->addr_width, to, 0), + SPI_MEM_OP_ADDR(nor->addr_nbytes, to, 0), SPI_MEM_OP_NO_DUMMY, SPI_MEM_OP_DATA_OUT(len, buf, 0)); ssize_t nbytes; @@ -1113,7 +1113,7 @@ int spi_nor_erase_sector(struct spi_nor *nor, u32 addr) if (nor->spimem) { struct spi_mem_op op = SPI_NOR_SECTOR_ERASE_OP(nor->erase_opcode, - nor->addr_width, addr); + nor->addr_nbytes, addr); spi_nor_spimem_setup_op(nor, &op, nor->write_proto); @@ -1126,13 +1126,13 @@ int spi_nor_erase_sector(struct spi_nor *nor, u32 addr) * Default implementation, if driver doesn't have a specialized HW * control */ - for (i = nor->addr_width - 1; i >= 0; i--) { + for (i = nor->addr_nbytes - 1; i >= 0; i--) { nor->bouncebuf[i] = addr & 0xff; addr >>= 8; } return spi_nor_controller_ops_write_reg(nor, nor->erase_opcode, - nor->bouncebuf, nor->addr_width); + nor->bouncebuf, nor->addr_nbytes); } /** @@ -2249,43 +2249,43 @@ static int spi_nor_default_setup(struct spi_nor *nor, return 0; } -static int spi_nor_set_addr_width(struct spi_nor *nor) +static int spi_nor_set_addr_nbytes(struct spi_nor *nor) { - if (nor->addr_width) { + if (nor->addr_nbytes) { /* already configured from SFDP */ } else if (nor->read_proto == SNOR_PROTO_8_8_8_DTR) { /* * In 8D-8D-8D mode, one byte takes half a cycle to transfer. So - * in this protocol an odd address width cannot be used because + * in this protocol an odd addr_nbytes cannot be used because * then the address phase would only span a cycle and a half. * Half a cycle would be left over. We would then have to start * the dummy phase in the middle of a cycle and so too the data * phase, and we will end the transaction with half a cycle left * over. * - * Force all 8D-8D-8D flashes to use an address width of 4 to + * Force all 8D-8D-8D flashes to use an addr_nbytes of 4 to * avoid this situation. */ - nor->addr_width = 4; - } else if (nor->info->addr_width) { - nor->addr_width = nor->info->addr_width; + nor->addr_nbytes = 4; + } else if (nor->info->addr_nbytes) { + nor->addr_nbytes = nor->info->addr_nbytes; } else { - nor->addr_width = 3; + nor->addr_nbytes = 3; } - if (nor->addr_width == 3 && nor->params->size > 0x1000000) { + if (nor->addr_nbytes == 3 && nor->params->size > 0x1000000) { /* enable 4-byte addressing if the device exceeds 16MiB */ - nor->addr_width = 4; + nor->addr_nbytes = 4; } - if (nor->addr_width > SPI_NOR_MAX_ADDR_WIDTH) { - dev_dbg(nor->dev, "address width is too large: %u\n", - nor->addr_width); + if (nor->addr_nbytes > SPI_NOR_MAX_ADDR_NBYTES) { + dev_dbg(nor->dev, "The number of address bytes is too large: %u\n", + nor->addr_nbytes); return -EINVAL; } /* Set 4byte opcodes when possible. */ - if (nor->addr_width == 4 && nor->flags & SNOR_F_4B_OPCODES && + if (nor->addr_nbytes == 4 && nor->flags & SNOR_F_4B_OPCODES && !(nor->flags & SNOR_F_HAS_4BAIT)) spi_nor_set_4byte_opcodes(nor); @@ -2304,7 +2304,7 @@ static int spi_nor_setup(struct spi_nor *nor, if (ret) return ret; - return spi_nor_set_addr_width(nor); + return spi_nor_set_addr_nbytes(nor); } /** @@ -2492,7 +2492,7 @@ static void spi_nor_sfdp_init_params_deprecated(struct spi_nor *nor) if (spi_nor_parse_sfdp(nor)) { memcpy(nor->params, &sfdp_params, sizeof(*nor->params)); - nor->addr_width = 0; + nor->addr_nbytes = 0; nor->flags &= ~SNOR_F_4B_OPCODES; } } @@ -2713,7 +2713,7 @@ static int spi_nor_init(struct spi_nor *nor) nor->flags & SNOR_F_SWP_IS_VOLATILE)) spi_nor_try_unlock_all(nor); - if (nor->addr_width == 4 && + if (nor->addr_nbytes == 4 && nor->read_proto != SNOR_PROTO_8_8_8_DTR && !(nor->flags & SNOR_F_4B_OPCODES)) { /* @@ -2840,7 +2840,7 @@ static void spi_nor_put_device(struct mtd_info *mtd) void spi_nor_restore(struct spi_nor *nor) { /* restore the addressing mode */ - if (nor->addr_width == 4 && !(nor->flags & SNOR_F_4B_OPCODES) && + if (nor->addr_nbytes == 4 && !(nor->flags & SNOR_F_4B_OPCODES) && nor->flags & SNOR_F_BROKEN_RESET) nor->params->set_4byte_addr_mode(nor, false); @@ -2984,7 +2984,7 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, * - select op codes for (Fast) Read, Page Program and Sector Erase. * - set the number of dummy cycles (mode cycles + wait states). * - set the SPI protocols for register and memory accesses. - * - set the address width. + * - set the number of address bytes. */ ret = spi_nor_setup(nor, hwcaps); if (ret) @@ -3025,7 +3025,7 @@ static int spi_nor_create_read_dirmap(struct spi_nor *nor) { struct spi_mem_dirmap_info info = { .op_tmpl = SPI_MEM_OP(SPI_MEM_OP_CMD(nor->read_opcode, 0), - SPI_MEM_OP_ADDR(nor->addr_width, 0, 0), + SPI_MEM_OP_ADDR(nor->addr_nbytes, 0, 0), SPI_MEM_OP_DUMMY(nor->read_dummy, 0), SPI_MEM_OP_DATA_IN(0, NULL, 0)), .offset = 0, @@ -3056,7 +3056,7 @@ static int spi_nor_create_write_dirmap(struct spi_nor *nor) { struct spi_mem_dirmap_info info = { .op_tmpl = SPI_MEM_OP(SPI_MEM_OP_CMD(nor->program_opcode, 0), - SPI_MEM_OP_ADDR(nor->addr_width, 0, 0), + SPI_MEM_OP_ADDR(nor->addr_nbytes, 0, 0), SPI_MEM_OP_NO_DUMMY, SPI_MEM_OP_DATA_OUT(0, NULL, 0)), .offset = 0, diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h index 61886868cd02..14921a507b0f 100644 --- a/drivers/mtd/spi-nor/core.h +++ b/drivers/mtd/spi-nor/core.h @@ -84,9 +84,9 @@ SPI_MEM_OP_NO_DUMMY, \ SPI_MEM_OP_NO_DATA) -#define SPI_NOR_SECTOR_ERASE_OP(opcode, addr_width, addr) \ +#define SPI_NOR_SECTOR_ERASE_OP(opcode, addr_nbytes, addr) \ SPI_MEM_OP(SPI_MEM_OP_CMD(opcode, 0), \ - SPI_MEM_OP_ADDR(addr_width, addr, 0), \ + SPI_MEM_OP_ADDR(addr_nbytes, addr, 0), \ SPI_MEM_OP_NO_DUMMY, \ SPI_MEM_OP_NO_DATA) @@ -429,7 +429,7 @@ struct spi_nor_fixups { * isn't necessarily called a "sector" by the vendor. * @n_sectors: the number of sectors. * @page_size: the flash's page size. - * @addr_width: the flash's address width. + * @addr_nbytes: number of address bytes to send. * * @parse_sfdp: true when flash supports SFDP tables. The false value has no * meaning. If one wants to skip the SFDP tables, one should @@ -487,7 +487,7 @@ struct flash_info { unsigned sector_size; u16 n_sectors; u16 page_size; - u16 addr_width; + u16 addr_nbytes; bool parse_sfdp; u16 flags; @@ -548,11 +548,11 @@ struct flash_info { .n_sectors = (_n_sectors), \ .page_size = 256, \ -#define CAT25_INFO(_sector_size, _n_sectors, _page_size, _addr_width) \ +#define CAT25_INFO(_sector_size, _n_sectors, _page_size, _addr_nbytes) \ .sector_size = (_sector_size), \ .n_sectors = (_n_sectors), \ .page_size = (_page_size), \ - .addr_width = (_addr_width), \ + .addr_nbytes = (_addr_nbytes), \ .flags = SPI_NOR_NO_ERASE | SPI_NOR_NO_FR, \ #define OTP_INFO(_len, _n_regions, _base, _offset) \ diff --git a/drivers/mtd/spi-nor/debugfs.c b/drivers/mtd/spi-nor/debugfs.c index eaf84f7a0676..df76cb5de3f9 100644 --- a/drivers/mtd/spi-nor/debugfs.c +++ b/drivers/mtd/spi-nor/debugfs.c @@ -86,7 +86,7 @@ static int spi_nor_params_show(struct seq_file *s, void *data) seq_printf(s, "size\t\t%s\n", buf); seq_printf(s, "write size\t%u\n", params->writesize); seq_printf(s, "page size\t%u\n", params->page_size); - seq_printf(s, "address width\t%u\n", nor->addr_width); + seq_printf(s, "address nbytes\t%u\n", nor->addr_nbytes); seq_puts(s, "flags\t\t"); spi_nor_print_flags(s, nor->flags, snor_f_names, sizeof(snor_f_names)); diff --git a/drivers/mtd/spi-nor/issi.c b/drivers/mtd/spi-nor/issi.c index 3c7d51d2b050..71687e5babdc 100644 --- a/drivers/mtd/spi-nor/issi.c +++ b/drivers/mtd/spi-nor/issi.c @@ -14,13 +14,13 @@ is25lp256_post_bfpt_fixups(struct spi_nor *nor, const struct sfdp_bfpt *bfpt) { /* - * IS25LP256 supports 4B opcodes, but the BFPT advertises a - * BFPT_DWORD1_ADDRESS_BYTES_3_ONLY address width. - * Overwrite the address width advertised by the BFPT. + * IS25LP256 supports 4B opcodes, but the BFPT advertises + * BFPT_DWORD1_ADDRESS_BYTES_3_ONLY. + * Overwrite the number of address bytes advertised by the BFPT. */ if ((bfpt->dwords[BFPT_DWORD(1)] & BFPT_DWORD1_ADDRESS_BYTES_MASK) == BFPT_DWORD1_ADDRESS_BYTES_3_ONLY) - nor->addr_width = 4; + nor->addr_nbytes = 4; return 0; } diff --git a/drivers/mtd/spi-nor/otp.c b/drivers/mtd/spi-nor/otp.c index fa63d8571218..00ab0d2d6d2f 100644 --- a/drivers/mtd/spi-nor/otp.c +++ b/drivers/mtd/spi-nor/otp.c @@ -35,13 +35,13 @@ */ int spi_nor_otp_read_secr(struct spi_nor *nor, loff_t addr, size_t len, u8 *buf) { - u8 addr_width, read_opcode, read_dummy; + u8 addr_nbytes, read_opcode, read_dummy; struct spi_mem_dirmap_desc *rdesc; enum spi_nor_protocol read_proto; int ret; read_opcode = nor->read_opcode; - addr_width = nor->addr_width; + addr_nbytes = nor->addr_nbytes; read_dummy = nor->read_dummy; read_proto = nor->read_proto; rdesc = nor->dirmap.rdesc; @@ -54,7 +54,7 @@ int spi_nor_otp_read_secr(struct spi_nor *nor, loff_t addr, size_t len, u8 *buf) ret = spi_nor_read_data(nor, addr, len, buf); nor->read_opcode = read_opcode; - nor->addr_width = addr_width; + nor->addr_nbytes = addr_nbytes; nor->read_dummy = read_dummy; nor->read_proto = read_proto; nor->dirmap.rdesc = rdesc; @@ -85,11 +85,11 @@ int spi_nor_otp_write_secr(struct spi_nor *nor, loff_t addr, size_t len, { enum spi_nor_protocol write_proto; struct spi_mem_dirmap_desc *wdesc; - u8 addr_width, program_opcode; + u8 addr_nbytes, program_opcode; int ret, written; program_opcode = nor->program_opcode; - addr_width = nor->addr_width; + addr_nbytes = nor->addr_nbytes; write_proto = nor->write_proto; wdesc = nor->dirmap.wdesc; @@ -113,7 +113,7 @@ int spi_nor_otp_write_secr(struct spi_nor *nor, loff_t addr, size_t len, out: nor->program_opcode = program_opcode; - nor->addr_width = addr_width; + nor->addr_nbytes = addr_nbytes; nor->write_proto = write_proto; nor->dirmap.wdesc = wdesc; diff --git a/drivers/mtd/spi-nor/sfdp.c b/drivers/mtd/spi-nor/sfdp.c index a5211543d30d..61ae8c8c5237 100644 --- a/drivers/mtd/spi-nor/sfdp.c +++ b/drivers/mtd/spi-nor/sfdp.c @@ -134,7 +134,7 @@ struct sfdp_4bait { /** * spi_nor_read_raw() - raw read of serial flash memory. read_opcode, - * addr_width and read_dummy members of the struct spi_nor + * addr_nbytes and read_dummy members of the struct spi_nor * should be previously * set. * @nor: pointer to a 'struct spi_nor' @@ -178,21 +178,21 @@ static int spi_nor_read_raw(struct spi_nor *nor, u32 addr, size_t len, u8 *buf) static int spi_nor_read_sfdp(struct spi_nor *nor, u32 addr, size_t len, void *buf) { - u8 addr_width, read_opcode, read_dummy; + u8 addr_nbytes, read_opcode, read_dummy; int ret; read_opcode = nor->read_opcode; - addr_width = nor->addr_width; + addr_nbytes = nor->addr_nbytes; read_dummy = nor->read_dummy; nor->read_opcode = SPINOR_OP_RDSFDP; - nor->addr_width = 3; + nor->addr_nbytes = 3; nor->read_dummy = 8; ret = spi_nor_read_raw(nor, addr, len, buf); nor->read_opcode = read_opcode; - nor->addr_width = addr_width; + nor->addr_nbytes = addr_nbytes; nor->read_dummy = read_dummy; return ret; @@ -462,11 +462,11 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor, switch (bfpt.dwords[BFPT_DWORD(1)] & BFPT_DWORD1_ADDRESS_BYTES_MASK) { case BFPT_DWORD1_ADDRESS_BYTES_3_ONLY: case BFPT_DWORD1_ADDRESS_BYTES_3_OR_4: - nor->addr_width = 3; + nor->addr_nbytes = 3; break; case BFPT_DWORD1_ADDRESS_BYTES_4_ONLY: - nor->addr_width = 4; + nor->addr_nbytes = 4; break; default: @@ -637,12 +637,12 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor, } /** - * spi_nor_smpt_addr_width() - return the address width used in the + * spi_nor_smpt_addr_nbytes() - return the number of address bytes used in the * configuration detection command. * @nor: pointer to a 'struct spi_nor' * @settings: configuration detection command descriptor, dword1 */ -static u8 spi_nor_smpt_addr_width(const struct spi_nor *nor, const u32 settings) +static u8 spi_nor_smpt_addr_nbytes(const struct spi_nor *nor, const u32 settings) { switch (settings & SMPT_CMD_ADDRESS_LEN_MASK) { case SMPT_CMD_ADDRESS_LEN_0: @@ -653,7 +653,7 @@ static u8 spi_nor_smpt_addr_width(const struct spi_nor *nor, const u32 settings) return 4; case SMPT_CMD_ADDRESS_LEN_USE_CURRENT: default: - return nor->addr_width; + return nor->addr_nbytes; } } @@ -690,7 +690,7 @@ static const u32 *spi_nor_get_map_in_use(struct spi_nor *nor, const u32 *smpt, u32 addr; int err; u8 i; - u8 addr_width, read_opcode, read_dummy; + u8 addr_nbytes, read_opcode, read_dummy; u8 read_data_mask, map_id; /* Use a kmalloc'ed bounce buffer to guarantee it is DMA-able. */ @@ -698,7 +698,7 @@ static const u32 *spi_nor_get_map_in_use(struct spi_nor *nor, const u32 *smpt, if (!buf) return ERR_PTR(-ENOMEM); - addr_width = nor->addr_width; + addr_nbytes = nor->addr_nbytes; read_dummy = nor->read_dummy; read_opcode = nor->read_opcode; @@ -709,7 +709,7 @@ static const u32 *spi_nor_get_map_in_use(struct spi_nor *nor, const u32 *smpt, break; read_data_mask = SMPT_CMD_READ_DATA(smpt[i]); - nor->addr_width = spi_nor_smpt_addr_width(nor, smpt[i]); + nor->addr_nbytes = spi_nor_smpt_addr_nbytes(nor, smpt[i]); nor->read_dummy = spi_nor_smpt_read_dummy(nor, smpt[i]); nor->read_opcode = SMPT_CMD_OPCODE(smpt[i]); addr = smpt[i + 1]; @@ -756,7 +756,7 @@ static const u32 *spi_nor_get_map_in_use(struct spi_nor *nor, const u32 *smpt, /* fall through */ out: kfree(buf); - nor->addr_width = addr_width; + nor->addr_nbytes = addr_nbytes; nor->read_dummy = read_dummy; nor->read_opcode = read_opcode; return ret; @@ -1044,7 +1044,7 @@ static int spi_nor_parse_4bait(struct spi_nor *nor, /* * We need at least one 4-byte op code per read, program and erase * operation; the .read(), .write() and .erase() hooks share the - * nor->addr_width value. + * nor->addr_nbytes value. */ if (!read_hwcaps || !pp_hwcaps || !erase_mask) goto out; @@ -1098,7 +1098,7 @@ static int spi_nor_parse_4bait(struct spi_nor *nor, * Spansion memory. However this quirk is no longer needed with new * SFDP compliant memories. */ - nor->addr_width = 4; + nor->addr_nbytes = 4; nor->flags |= SNOR_F_4B_OPCODES | SNOR_F_HAS_4BAIT; /* fall through */ diff --git a/drivers/mtd/spi-nor/xilinx.c b/drivers/mtd/spi-nor/xilinx.c index 1d2f5db047bd..5723157739fc 100644 --- a/drivers/mtd/spi-nor/xilinx.c +++ b/drivers/mtd/spi-nor/xilinx.c @@ -31,7 +31,7 @@ .sector_size = (8 * (_page_size)), \ .n_sectors = (_n_sectors), \ .page_size = (_page_size), \ - .addr_width = 3, \ + .addr_nbytes = 3, \ .flags = SPI_NOR_NO_FR /* Xilinx S3AN share MFR with Atmel SPI NOR */ diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index 1ede4c89805a..42218a1164f6 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -351,7 +351,7 @@ struct spi_nor_flash_parameter; * @bouncebuf_size: size of the bounce buffer * @info: SPI NOR part JEDEC MFR ID and other info * @manufacturer: SPI NOR manufacturer - * @addr_width: number of address bytes + * @addr_nbytes: number of address bytes * @erase_opcode: the opcode for erasing a sector * @read_opcode: the read opcode * @read_dummy: the dummy needed by the read operation @@ -381,7 +381,7 @@ struct spi_nor { size_t bouncebuf_size; const struct flash_info *info; const struct spi_nor_manufacturer *manufacturer; - u8 addr_width; + u8 addr_nbytes; u8 erase_opcode; u8 read_opcode; u8 read_dummy; -- cgit v1.2.3 From aa5d980a144cd0bf717eb16609c29ff276f8bd47 Mon Sep 17 00:00:00 2001 From: Tudor Ambarus Date: Mon, 25 Jul 2022 12:25:00 +0300 Subject: mtd: spi-nor: core: Shrink the storage size of the flash_info's addr_nbytes The maximum number of address bytes in SPI NOR is 4. Shrink the storage size of the flash_info's addr_nbytes. Signed-off-by: Tudor Ambarus Reviewed-by: Michael Walle Reviewed-by: Pratyush Yadav Link: https://lore.kernel.org/r/20220725092505.446315-3-tudor.ambarus@microchip.com --- drivers/mtd/spi-nor/core.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h index 14921a507b0f..19a692e27c92 100644 --- a/drivers/mtd/spi-nor/core.h +++ b/drivers/mtd/spi-nor/core.h @@ -487,7 +487,7 @@ struct flash_info { unsigned sector_size; u16 n_sectors; u16 page_size; - u16 addr_nbytes; + u8 addr_nbytes; bool parse_sfdp; u16 flags; -- cgit v1.2.3 From 47c6f8a67f2ce1b95202c16fa126411d6f5c7d5c Mon Sep 17 00:00:00 2001 From: Tudor Ambarus Date: Mon, 25 Jul 2022 12:25:01 +0300 Subject: mtd: spi-nor: Do not change nor->addr_nbytes at SFDP parsing time At the SFDP parsing time we should not change members of struct spi_nor, but instead fill members of struct spi_nor_flash_parameters which could later on be used by callers. The caller will then decide if SFDP params should be used and more importantly when they should be used. Clean the code flow and don't initialize nor->addr_nbytes at SFDP parsing time. Signed-off-by: Tudor Ambarus Tested-by: Takahiro Kuwano Reviewed-by: Michael Walle Reviewed-by: Pratyush Yadav Link: https://lore.kernel.org/r/20220725092505.446315-4-tudor.ambarus@microchip.com --- drivers/mtd/spi-nor/core.c | 5 ++--- drivers/mtd/spi-nor/core.h | 2 ++ drivers/mtd/spi-nor/issi.c | 2 +- drivers/mtd/spi-nor/sfdp.c | 8 ++++---- 4 files changed, 9 insertions(+), 8 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index 31604188ee59..9f07f1036151 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -2251,8 +2251,8 @@ static int spi_nor_default_setup(struct spi_nor *nor, static int spi_nor_set_addr_nbytes(struct spi_nor *nor) { - if (nor->addr_nbytes) { - /* already configured from SFDP */ + if (nor->params->addr_nbytes) { + nor->addr_nbytes = nor->params->addr_nbytes; } else if (nor->read_proto == SNOR_PROTO_8_8_8_DTR) { /* * In 8D-8D-8D mode, one byte takes half a cycle to transfer. So @@ -2492,7 +2492,6 @@ static void spi_nor_sfdp_init_params_deprecated(struct spi_nor *nor) if (spi_nor_parse_sfdp(nor)) { memcpy(nor->params, &sfdp_params, sizeof(*nor->params)); - nor->addr_nbytes = 0; nor->flags &= ~SNOR_F_4B_OPCODES; } } diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h index 19a692e27c92..7dc4cda41db3 100644 --- a/drivers/mtd/spi-nor/core.h +++ b/drivers/mtd/spi-nor/core.h @@ -340,6 +340,7 @@ struct spi_nor_otp { * @writesize Minimal writable flash unit size. Defaults to 1. Set to * ECC unit size for ECC-ed flashes. * @page_size: the page size of the SPI NOR flash memory. + * @addr_nbytes: number of address bytes to send. * @rdsr_dummy: dummy cycles needed for Read Status Register command * in octal DTR mode. * @rdsr_addr_nbytes: dummy address bytes needed for Read Status Register @@ -372,6 +373,7 @@ struct spi_nor_flash_parameter { u64 size; u32 writesize; u32 page_size; + u8 addr_nbytes; u8 rdsr_dummy; u8 rdsr_addr_nbytes; diff --git a/drivers/mtd/spi-nor/issi.c b/drivers/mtd/spi-nor/issi.c index 71687e5babdc..89a66a19d754 100644 --- a/drivers/mtd/spi-nor/issi.c +++ b/drivers/mtd/spi-nor/issi.c @@ -20,7 +20,7 @@ is25lp256_post_bfpt_fixups(struct spi_nor *nor, */ if ((bfpt->dwords[BFPT_DWORD(1)] & BFPT_DWORD1_ADDRESS_BYTES_MASK) == BFPT_DWORD1_ADDRESS_BYTES_3_ONLY) - nor->addr_nbytes = 4; + nor->params->addr_nbytes = 4; return 0; } diff --git a/drivers/mtd/spi-nor/sfdp.c b/drivers/mtd/spi-nor/sfdp.c index 61ae8c8c5237..3a48173a2d78 100644 --- a/drivers/mtd/spi-nor/sfdp.c +++ b/drivers/mtd/spi-nor/sfdp.c @@ -462,11 +462,11 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor, switch (bfpt.dwords[BFPT_DWORD(1)] & BFPT_DWORD1_ADDRESS_BYTES_MASK) { case BFPT_DWORD1_ADDRESS_BYTES_3_ONLY: case BFPT_DWORD1_ADDRESS_BYTES_3_OR_4: - nor->addr_nbytes = 3; + params->addr_nbytes = 3; break; case BFPT_DWORD1_ADDRESS_BYTES_4_ONLY: - nor->addr_nbytes = 4; + params->addr_nbytes = 4; break; default: @@ -653,7 +653,7 @@ static u8 spi_nor_smpt_addr_nbytes(const struct spi_nor *nor, const u32 settings return 4; case SMPT_CMD_ADDRESS_LEN_USE_CURRENT: default: - return nor->addr_nbytes; + return nor->params->addr_nbytes; } } @@ -1098,7 +1098,7 @@ static int spi_nor_parse_4bait(struct spi_nor *nor, * Spansion memory. However this quirk is no longer needed with new * SFDP compliant memories. */ - nor->addr_nbytes = 4; + params->addr_nbytes = 4; nor->flags |= SNOR_F_4B_OPCODES | SNOR_F_HAS_4BAIT; /* fall through */ -- cgit v1.2.3 From 08412e72afba3a2daef3e7f3378c3753255a0017 Mon Sep 17 00:00:00 2001 From: Takahiro Kuwano Date: Mon, 25 Jul 2022 12:25:02 +0300 Subject: mtd: spi-nor: core: Return error code from set_4byte_addr_mode() The prams->set_4byte_addr_mode returns error code but is not handled in spi_nor_init(). Handle the return code from set_4byte_addr_mode(). Suggested-by: Michael Walle Signed-off-by: Takahiro Kuwano Signed-off-by: Tudor Ambarus Reviewed-by: Pratyush Yadav Reviewed-by: Michael Walle Link: https://lore.kernel.org/r/20220725092505.446315-5-tudor.ambarus@microchip.com --- drivers/mtd/spi-nor/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index 9f07f1036151..ec4c368b4ba6 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -2724,7 +2724,7 @@ static int spi_nor_init(struct spi_nor *nor) */ WARN_ONCE(nor->flags & SNOR_F_BROKEN_RESET, "enabling reset hack; may not recover from unexpected reboots\n"); - nor->params->set_4byte_addr_mode(nor, true); + return nor->params->set_4byte_addr_mode(nor, true); } return 0; -- cgit v1.2.3 From d7931a21506321327351f68fdebf74a56a58e675 Mon Sep 17 00:00:00 2001 From: Tudor Ambarus Date: Mon, 25 Jul 2022 12:25:03 +0300 Subject: mtd: spi-nor: core: Track flash's internal address mode We need to track the flash's internal address mode as there are flashes that can operate with 4B opcodes but unfortunately do not have a 4B opcode correspondent for all the 3B opcodes. Such an example is the Infineon Semper chips which provide 4B opcodes for read/program/erase but do not provide 4B opcodes for Read/Write Any Register. These registers are indexed by address and require the internal address mode of the flash before Read/Write Any Register opcodes are issued. 4B opcodes are preferred over changing the flash's address mode to 4byte, as set_4byte_addr_mode could be done in a non-volatile way and could break the boot sequence. Thus we need to track the flash's internal address mode so that we can use 4B opcodes together with opcodes that don't have a 4B opcode correspondent. Track flash's internal address mode. addr_mode_nbytes is discovered when parsing BFPT. For the BFPT_DWORD1_ADDRESS_BYTES_3_OR_4 case, one could introduce a method that queries the flash's internal address mode at run-time (works for Winbond). If a run-time querying can not be accomplished or if SFDP is not defined at all, but the address mode is volatile and resets to a default known value at boot, one can change the default addr_mode_nbytes value of 3 by introducing a flash_info flag. If the address mode can not be queried, discovered and it is configured via a non-volatile register, we may introduce a dt property, but it will harm the generic approach of the jedec,spi-nor compatible. All this complexity is not needed now, so let it for future development. Signed-off-by: Tudor Ambarus Reviewed-by: Takahiro Kuwano Reviewed-by: Michael Walle Link: https://lore.kernel.org/r/20220725092505.446315-6-tudor.ambarus@microchip.com --- drivers/mtd/spi-nor/core.h | 5 +++++ drivers/mtd/spi-nor/sfdp.c | 4 +++- 2 files changed, 8 insertions(+), 1 deletion(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h index 7dc4cda41db3..85b0cf254e97 100644 --- a/drivers/mtd/spi-nor/core.h +++ b/drivers/mtd/spi-nor/core.h @@ -341,6 +341,10 @@ struct spi_nor_otp { * ECC unit size for ECC-ed flashes. * @page_size: the page size of the SPI NOR flash memory. * @addr_nbytes: number of address bytes to send. + * @addr_mode_nbytes: number of address bytes of current address mode. Useful + * when the flash operates with 4B opcodes but needs the + * internal address mode for opcodes that don't have a 4B + * opcode correspondent. * @rdsr_dummy: dummy cycles needed for Read Status Register command * in octal DTR mode. * @rdsr_addr_nbytes: dummy address bytes needed for Read Status Register @@ -374,6 +378,7 @@ struct spi_nor_flash_parameter { u32 writesize; u32 page_size; u8 addr_nbytes; + u8 addr_mode_nbytes; u8 rdsr_dummy; u8 rdsr_addr_nbytes; diff --git a/drivers/mtd/spi-nor/sfdp.c b/drivers/mtd/spi-nor/sfdp.c index 3a48173a2d78..2257f1b4c2e2 100644 --- a/drivers/mtd/spi-nor/sfdp.c +++ b/drivers/mtd/spi-nor/sfdp.c @@ -463,10 +463,12 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor, case BFPT_DWORD1_ADDRESS_BYTES_3_ONLY: case BFPT_DWORD1_ADDRESS_BYTES_3_OR_4: params->addr_nbytes = 3; + params->addr_mode_nbytes = 3; break; case BFPT_DWORD1_ADDRESS_BYTES_4_ONLY: params->addr_nbytes = 4; + params->addr_mode_nbytes = 4; break; default: @@ -653,7 +655,7 @@ static u8 spi_nor_smpt_addr_nbytes(const struct spi_nor *nor, const u32 settings return 4; case SMPT_CMD_ADDRESS_LEN_USE_CURRENT: default: - return nor->params->addr_nbytes; + return nor->params->addr_mode_nbytes; } } -- cgit v1.2.3 From a6b50aa1279614df7033f8b57d6854fce0334e27 Mon Sep 17 00:00:00 2001 From: Takahiro Kuwano Date: Mon, 25 Jul 2022 12:25:04 +0300 Subject: mtd: spi-nor: spansion: Add local function to discover page size The page size check in s28hs512t fixup can be used for s25hs/hl-t as well. Move that to a newly created local function. Signed-off-by: Takahiro Kuwano Signed-off-by: Tudor Ambarus Reviewed-by: Michael Walle Link: https://lore.kernel.org/r/20220725092505.446315-7-tudor.ambarus@microchip.com --- drivers/mtd/spi-nor/spansion.c | 53 ++++++++++++++++++++++++------------------ 1 file changed, 31 insertions(+), 22 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/spi-nor/spansion.c b/drivers/mtd/spi-nor/spansion.c index 43cd6cd92537..60e41e1a9a92 100644 --- a/drivers/mtd/spi-nor/spansion.c +++ b/drivers/mtd/spi-nor/spansion.c @@ -113,6 +113,36 @@ static int cypress_nor_octal_dtr_dis(struct spi_nor *nor) return 0; } +/** + * cypress_nor_set_page_size() - Set page size which corresponds to the flash + * configuration. + * @nor: pointer to a 'struct spi_nor' + * + * The BFPT table advertises a 512B or 256B page size depending on part but the + * page size is actually configurable (with the default being 256B). Read from + * CFR3V[4] and set the correct size. + * + * Return: 0 on success, -errno otherwise. + */ +static int cypress_nor_set_page_size(struct spi_nor *nor) +{ + struct spi_mem_op op = + CYPRESS_NOR_RD_ANY_REG_OP(3, SPINOR_REG_CYPRESS_CFR3V, + nor->bouncebuf); + int ret; + + ret = spi_nor_read_any_reg(nor, &op, nor->reg_proto); + if (ret) + return ret; + + if (nor->bouncebuf[0] & SPINOR_REG_CYPRESS_CFR3V_PGSZ) + nor->params->page_size = 512; + else + nor->params->page_size = 256; + + return 0; +} + /** * cypress_nor_octal_dtr_enable() - Enable octal DTR on Cypress flashes. * @nor: pointer to a 'struct spi_nor' @@ -167,28 +197,7 @@ static int s28hs512t_post_bfpt_fixup(struct spi_nor *nor, const struct sfdp_parameter_header *bfpt_header, const struct sfdp_bfpt *bfpt) { - /* - * The BFPT table advertises a 512B page size but the page size is - * actually configurable (with the default being 256B). Read from - * CFR3V[4] and set the correct size. - */ - struct spi_mem_op op = - CYPRESS_NOR_RD_ANY_REG_OP(3, SPINOR_REG_CYPRESS_CFR3V, - nor->bouncebuf); - int ret; - - spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); - - ret = spi_mem_exec_op(nor->spimem, &op); - if (ret) - return ret; - - if (nor->bouncebuf[0] & SPINOR_REG_CYPRESS_CFR3V_PGSZ) - nor->params->page_size = 512; - else - nor->params->page_size = 256; - - return 0; + return cypress_nor_set_page_size(nor); } static const struct spi_nor_fixups s28hs512t_fixups = { -- cgit v1.2.3 From b6b23833fc42a10ceed00006cb0a6184f9b9bbde Mon Sep 17 00:00:00 2001 From: Takahiro Kuwano Date: Mon, 25 Jul 2022 12:25:05 +0300 Subject: mtd: spi-nor: spansion: Add s25hl-t/s25hs-t IDs and fixups The S25HL-T/S25HS-T family is the Infineon SEMPER Flash with Quad SPI. These Infineon chips support volatile version of configuration registers and it is recommended to update volatile registers in the field application due to a risk of the non-volatile registers corruption by power interrupt. Add support for volatile QE bit. For the single-die package parts (512Mb and 1Gb), only bottom 4KB and uniform sector sizes are supported. This is due to missing or incorrect entries in SMPT. Fixup for other sector sizes configurations will be followed up as needed. Tested on Xilinx Zynq-7000 FPGA board. Signed-off-by: Takahiro Kuwano Signed-off-by: Tudor Ambarus Acked-by: Michael Walle Link: https://lore.kernel.org/r/20220725092505.446315-8-tudor.ambarus@microchip.com --- drivers/mtd/spi-nor/spansion.c | 132 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 132 insertions(+) (limited to 'drivers/mtd') diff --git a/drivers/mtd/spi-nor/spansion.c b/drivers/mtd/spi-nor/spansion.c index 60e41e1a9a92..0150049007be 100644 --- a/drivers/mtd/spi-nor/spansion.c +++ b/drivers/mtd/spi-nor/spansion.c @@ -14,6 +14,8 @@ #define SPINOR_OP_CLSR 0x30 /* Clear status register 1 */ #define SPINOR_OP_RD_ANY_REG 0x65 /* Read any register */ #define SPINOR_OP_WR_ANY_REG 0x71 /* Write any register */ +#define SPINOR_REG_CYPRESS_CFR1V 0x00800002 +#define SPINOR_REG_CYPRESS_CFR1V_QUAD_EN BIT(1) /* Quad Enable */ #define SPINOR_REG_CYPRESS_CFR2V 0x00800003 #define SPINOR_REG_CYPRESS_CFR2V_MEMLAT_11_24 0xb #define SPINOR_REG_CYPRESS_CFR3V 0x00800004 @@ -113,6 +115,68 @@ static int cypress_nor_octal_dtr_dis(struct spi_nor *nor) return 0; } +/** + * cypress_nor_quad_enable_volatile() - enable Quad I/O mode in volatile + * register. + * @nor: pointer to a 'struct spi_nor' + * + * It is recommended to update volatile registers in the field application due + * to a risk of the non-volatile registers corruption by power interrupt. This + * function sets Quad Enable bit in CFR1 volatile. If users set the Quad Enable + * bit in the CFR1 non-volatile in advance (typically by a Flash programmer + * before mounting Flash on PCB), the Quad Enable bit in the CFR1 volatile is + * also set during Flash power-up. + * + * Return: 0 on success, -errno otherwise. + */ +static int cypress_nor_quad_enable_volatile(struct spi_nor *nor) +{ + struct spi_mem_op op; + u8 addr_mode_nbytes = nor->params->addr_mode_nbytes; + u8 cfr1v_written; + int ret; + + op = (struct spi_mem_op) + CYPRESS_NOR_RD_ANY_REG_OP(addr_mode_nbytes, + SPINOR_REG_CYPRESS_CFR1V, + nor->bouncebuf); + + ret = spi_nor_read_any_reg(nor, &op, nor->reg_proto); + if (ret) + return ret; + + if (nor->bouncebuf[0] & SPINOR_REG_CYPRESS_CFR1V_QUAD_EN) + return 0; + + /* Update the Quad Enable bit. */ + nor->bouncebuf[0] |= SPINOR_REG_CYPRESS_CFR1V_QUAD_EN; + op = (struct spi_mem_op) + CYPRESS_NOR_WR_ANY_REG_OP(addr_mode_nbytes, + SPINOR_REG_CYPRESS_CFR1V, 1, + nor->bouncebuf); + ret = spi_nor_write_any_volatile_reg(nor, &op, nor->reg_proto); + if (ret) + return ret; + + cfr1v_written = nor->bouncebuf[0]; + + /* Read back and check it. */ + op = (struct spi_mem_op) + CYPRESS_NOR_RD_ANY_REG_OP(addr_mode_nbytes, + SPINOR_REG_CYPRESS_CFR1V, + nor->bouncebuf); + ret = spi_nor_read_any_reg(nor, &op, nor->reg_proto); + if (ret) + return ret; + + if (nor->bouncebuf[0] != cfr1v_written) { + dev_err(nor->dev, "CFR1: Read back test failed\n"); + return -EIO; + } + + return 0; +} + /** * cypress_nor_set_page_size() - Set page size which corresponds to the flash * configuration. @@ -143,6 +207,58 @@ static int cypress_nor_set_page_size(struct spi_nor *nor) return 0; } +static int +s25hx_t_post_bfpt_fixup(struct spi_nor *nor, + const struct sfdp_parameter_header *bfpt_header, + const struct sfdp_bfpt *bfpt) +{ + /* Replace Quad Enable with volatile version */ + nor->params->quad_enable = cypress_nor_quad_enable_volatile; + + return cypress_nor_set_page_size(nor); +} + +static void s25hx_t_post_sfdp_fixup(struct spi_nor *nor) +{ + struct spi_nor_erase_type *erase_type = + nor->params->erase_map.erase_type; + unsigned int i; + + /* + * In some parts, 3byte erase opcodes are advertised by 4BAIT. + * Convert them to 4byte erase opcodes. + */ + for (i = 0; i < SNOR_ERASE_TYPE_MAX; i++) { + switch (erase_type[i].opcode) { + case SPINOR_OP_SE: + erase_type[i].opcode = SPINOR_OP_SE_4B; + break; + case SPINOR_OP_BE_4K: + erase_type[i].opcode = SPINOR_OP_BE_4K_4B; + break; + default: + break; + } + } +} + +static void s25hx_t_late_init(struct spi_nor *nor) +{ + struct spi_nor_flash_parameter *params = nor->params; + + /* Fast Read 4B requires mode cycles */ + params->reads[SNOR_CMD_READ_FAST].num_mode_clocks = 8; + + /* The writesize should be ECC data unit size */ + params->writesize = 16; +} + +static struct spi_nor_fixups s25hx_t_fixups = { + .post_bfpt = s25hx_t_post_bfpt_fixup, + .post_sfdp = s25hx_t_post_sfdp_fixup, + .late_init = s25hx_t_late_init, +}; + /** * cypress_nor_octal_dtr_enable() - Enable octal DTR on Cypress flashes. * @nor: pointer to a 'struct spi_nor' @@ -319,6 +435,22 @@ static const struct flash_info spansion_nor_parts[] = { { "s25fl256l", INFO(0x016019, 0, 64 * 1024, 512) NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) FIXUP_FLAGS(SPI_NOR_4B_OPCODES) }, + { "s25hl512t", INFO6(0x342a1a, 0x0f0390, 256 * 1024, 256) + PARSE_SFDP + MFR_FLAGS(USE_CLSR) + .fixups = &s25hx_t_fixups }, + { "s25hl01gt", INFO6(0x342a1b, 0x0f0390, 256 * 1024, 512) + PARSE_SFDP + MFR_FLAGS(USE_CLSR) + .fixups = &s25hx_t_fixups }, + { "s25hs512t", INFO6(0x342b1a, 0x0f0390, 256 * 1024, 256) + PARSE_SFDP + MFR_FLAGS(USE_CLSR) + .fixups = &s25hx_t_fixups }, + { "s25hs01gt", INFO6(0x342b1b, 0x0f0390, 256 * 1024, 512) + PARSE_SFDP + MFR_FLAGS(USE_CLSR) + .fixups = &s25hx_t_fixups }, { "cy15x104q", INFO6(0x042cc2, 0x7f7f7f, 512 * 1024, 1) FLAGS(SPI_NOR_NO_ERASE) }, { "s28hs512t", INFO(0x345b1a, 0, 256 * 1024, 256) -- cgit v1.2.3 From f8cd9f632f4415b1e8838bdca8ab42cfb37a6584 Mon Sep 17 00:00:00 2001 From: Patrice Chotard Date: Wed, 29 Jun 2022 15:30:13 +0200 Subject: mtd: spi-nor: fix spi_nor_spimem_setup_op() call in spi_nor_erase_{sector,chip}() For erase operations, reg_proto must be used as indicated in struct spi_nor description in spi-nor.h. This issue was found when DT property spi-tx-bus-width is set to 4. In this case the spi_mem_op->addr.buswidth is set to 4 for erase command which is not correct. Tested on stm32mp157c-ev1 board with mx66l51235f spi-nor. Fixes: 0e30f47232ab ("mtd: spi-nor: add support for DTR protocol") Signed-off-by: Patrice Chotard [ta: use nor->reg_proto in spi_nor_controller_ops_erase()] Signed-off-by: Tudor Ambarus Tested-by: Alexander Sverdlin Reviewed-by: Pratyush Yadav Link: https://lore.kernel.org/r/20220629133013.3382393-1-patrice.chotard@foss.st.com --- drivers/mtd/spi-nor/core.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index ec4c368b4ba6..f2c64006f8d7 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -177,7 +177,7 @@ int spi_nor_controller_ops_write_reg(struct spi_nor *nor, u8 opcode, static int spi_nor_controller_ops_erase(struct spi_nor *nor, loff_t offs) { - if (spi_nor_protocol_is_dtr(nor->write_proto)) + if (spi_nor_protocol_is_dtr(nor->reg_proto)) return -EOPNOTSUPP; return nor->controller_ops->erase(nor, offs); @@ -972,7 +972,7 @@ static int spi_nor_erase_chip(struct spi_nor *nor) if (nor->spimem) { struct spi_mem_op op = SPI_NOR_CHIP_ERASE_OP; - spi_nor_spimem_setup_op(nor, &op, nor->write_proto); + spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); ret = spi_mem_exec_op(nor->spimem, &op); } else { @@ -1115,7 +1115,7 @@ int spi_nor_erase_sector(struct spi_nor *nor, u32 addr) SPI_NOR_SECTOR_ERASE_OP(nor->erase_opcode, nor->addr_nbytes, addr); - spi_nor_spimem_setup_op(nor, &op, nor->write_proto); + spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); return spi_mem_exec_op(nor->spimem, &op); } else if (nor->controller_ops->erase) { -- cgit v1.2.3