diff options
| author | Mark Brown <broonie@kernel.org> | 2026-09-07 13:26:32 +0100 |
|---|---|---|
| committer | Mark Brown <broonie@kernel.org> | 2026-09-07 13:26:32 +0100 |
| commit | 96e31f7b776524d8a1e9940e53fe97763523f145 (patch) | |
| tree | 053786f6dca2a120f8c2c7bdd10d9fdbdee57d19 | |
| parent | 058d195055dc1b84eff3203c927148c459928709 (diff) | |
| parent | 700bf34058ca7cd792236b1ba5caad3770d66208 (diff) | |
| download | linux-next-96e31f7b776524d8a1e9940e53fe97763523f145.tar.gz linux-next-96e31f7b776524d8a1e9940e53fe97763523f145.zip | |
Merge branch 'spi-nor/next' of https://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux.git
| -rw-r--r-- | Documentation/driver-api/mtd/spi-nor.rst | 2 | ||||
| -rw-r--r-- | drivers/mtd/spi-nor/atmel.c | 73 | ||||
| -rw-r--r-- | drivers/mtd/spi-nor/core.c | 864 | ||||
| -rw-r--r-- | drivers/mtd/spi-nor/core.h | 144 | ||||
| -rw-r--r-- | drivers/mtd/spi-nor/debugfs.c | 33 | ||||
| -rw-r--r-- | drivers/mtd/spi-nor/everspin.c | 7 | ||||
| -rw-r--r-- | drivers/mtd/spi-nor/gigadevice.c | 15 | ||||
| -rw-r--r-- | drivers/mtd/spi-nor/issi.c | 29 | ||||
| -rw-r--r-- | drivers/mtd/spi-nor/macronix.c | 73 | ||||
| -rw-r--r-- | drivers/mtd/spi-nor/micron-st.c | 70 | ||||
| -rw-r--r-- | drivers/mtd/spi-nor/otp.c | 16 | ||||
| -rw-r--r-- | drivers/mtd/spi-nor/sfdp.c | 230 | ||||
| -rw-r--r-- | drivers/mtd/spi-nor/sfdp.h | 23 | ||||
| -rw-r--r-- | drivers/mtd/spi-nor/spansion.c | 103 | ||||
| -rw-r--r-- | drivers/mtd/spi-nor/sst.c | 16 | ||||
| -rw-r--r-- | drivers/mtd/spi-nor/swp.c | 133 | ||||
| -rw-r--r-- | drivers/mtd/spi-nor/sysfs.c | 4 | ||||
| -rw-r--r-- | drivers/mtd/spi-nor/winbond.c | 241 | ||||
| -rw-r--r-- | drivers/mtd/spi-nor/xmc.c | 1 | ||||
| -rw-r--r-- | include/linux/mtd/spi-nor.h | 10 |
20 files changed, 1130 insertions, 957 deletions
diff --git a/Documentation/driver-api/mtd/spi-nor.rst b/Documentation/driver-api/mtd/spi-nor.rst index 747a326fb6c0..5441fa7d4f04 100644 --- a/Documentation/driver-api/mtd/spi-nor.rst +++ b/Documentation/driver-api/mtd/spi-nor.rst @@ -118,7 +118,7 @@ section, after the ``---`` marker. write size 1 page size 256 address nbytes 3 - flags HAS_LOCK | HAS_16BIT_SR | SOFT_RESET | SWP_IS_VOLATILE + flags HAS_LOCK | SOFT_RESET | SWP_IS_VOLATILE opcodes read 0xeb diff --git a/drivers/mtd/spi-nor/atmel.c b/drivers/mtd/spi-nor/atmel.c index 82c592f0a1e1..bccc702a2c4a 100644 --- a/drivers/mtd/spi-nor/atmel.c +++ b/drivers/mtd/spi-nor/atmel.c @@ -23,18 +23,28 @@ static int at25fs_nor_lock(struct spi_nor *nor, loff_t ofs, u64 len) static int at25fs_nor_unlock(struct spi_nor *nor, loff_t ofs, u64 len) { + /* Write 0x00 to the status register to disable write protection */ + u8 sr = 0; int ret; /* We only support unlocking the whole flash array */ if (ofs || len != nor->params->size) return -EINVAL; - /* Write 0x00 to the status register to disable write protection */ - ret = spi_nor_write_sr_and_check(nor, 0); + ret = spi_nor_write_sr1(nor, &sr); + if (ret) + return ret; + + ret = spi_nor_read_sr1(nor, &sr); if (ret) + return ret; + + if (sr) { dev_dbg(nor->dev, "unable to clear BP bits, WP# asserted?\n"); + return -EIO; + } - return ret; + return 0; } static int at25fs_nor_is_locked(struct spi_nor *nor, loff_t ofs, u64 len) @@ -50,6 +60,7 @@ static const struct spi_nor_locking_ops at25fs_nor_locking_ops = { static int at25fs_nor_late_init(struct spi_nor *nor) { + nor->params->opcodes.write_sr1 = SPINOR_OP_WRSR; nor->params->locking_ops = &at25fs_nor_locking_ops; return 0; @@ -69,6 +80,7 @@ static const struct spi_nor_fixups at25fs_nor_fixups = { * Return: 0 on success, -error otherwise. */ static int atmel_nor_set_global_protection(struct spi_nor *nor, loff_t ofs, + u64 len, bool is_protect) { int ret; @@ -78,19 +90,24 @@ static int atmel_nor_set_global_protection(struct spi_nor *nor, loff_t ofs, if (ofs || len != nor->params->size) return -EINVAL; - ret = spi_nor_read_sr(nor, nor->bouncebuf); + ret = spi_nor_read_sr1(nor, &sr); if (ret) return ret; - sr = nor->bouncebuf[0]; - /* SRWD bit needs to be cleared, otherwise the protection doesn't change */ if (sr & SR_SRWD) { sr &= ~SR_SRWD; - ret = spi_nor_write_sr_and_check(nor, sr); - if (ret) { - dev_dbg(nor->dev, "unable to clear SRWD bit, WP# asserted?\n"); + ret = spi_nor_write_sr1(nor, &sr); + if (ret) return ret; + + ret = spi_nor_read_sr1(nor, &sr); + if (ret) + return ret; + + if (sr & SR_SRWD) { + dev_dbg(nor->dev, "unable to clear SRWD bit, WP# asserted?\n"); + return -EIO; } } @@ -108,14 +125,7 @@ static int atmel_nor_set_global_protection(struct spi_nor *nor, loff_t ofs, sr &= ~ATMEL_SR_GLOBAL_PROTECT_MASK; } - nor->bouncebuf[0] = sr; - - /* - * We cannot use the spi_nor_write_sr_and_check() because this command - * isn't really setting any bits, instead it is an pseudo command for - * "Global Unprotect" or "Global Protect" - */ - return spi_nor_write_sr(nor, nor->bouncebuf, 1); + return spi_nor_write_sr1(nor, &sr); } static int atmel_nor_global_protect(struct spi_nor *nor, loff_t ofs, u64 len) @@ -131,16 +141,17 @@ static int atmel_nor_global_unprotect(struct spi_nor *nor, loff_t ofs, u64 len) static int atmel_nor_is_global_protected(struct spi_nor *nor, loff_t ofs, u64 len) { + u8 sr; int ret; if (ofs >= nor->params->size || (ofs + len) > nor->params->size) return -EINVAL; - ret = spi_nor_read_sr(nor, nor->bouncebuf); + ret = spi_nor_read_sr1(nor, &sr); if (ret) return ret; - return ((nor->bouncebuf[0] & ATMEL_SR_GLOBAL_PROTECT_MASK) == ATMEL_SR_GLOBAL_PROTECT_MASK); + return ((sr & ATMEL_SR_GLOBAL_PROTECT_MASK) == ATMEL_SR_GLOBAL_PROTECT_MASK); } static const struct spi_nor_locking_ops atmel_nor_global_protection_ops = { @@ -151,6 +162,7 @@ static const struct spi_nor_locking_ops atmel_nor_global_protection_ops = { static int atmel_nor_global_protection_late_init(struct spi_nor *nor) { + nor->params->opcodes.write_sr1 = SPINOR_OP_WRSR; nor->params->locking_ops = &atmel_nor_global_protection_ops; return 0; @@ -182,47 +194,40 @@ static const struct flash_info atmel_nor_parts[] = { .size = SZ_512K, .flags = SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE, .no_sfdp_flags = SECT_4K, - .fixups = &atmel_nor_global_protection_fixups, }, { .id = SNOR_ID(0x1f, 0x45, 0x01), .name = "at26df081a", .size = SZ_1M, .flags = SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE, .no_sfdp_flags = SECT_4K, - .fixups = &atmel_nor_global_protection_fixups }, { .id = SNOR_ID(0x1f, 0x46, 0x01), .name = "at26df161a", .size = SZ_2M, .flags = SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE, .no_sfdp_flags = SECT_4K, - .fixups = &atmel_nor_global_protection_fixups }, { .id = SNOR_ID(0x1f, 0x47, 0x00), .name = "at25df321", .size = SZ_4M, .flags = SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE, .no_sfdp_flags = SECT_4K, - .fixups = &atmel_nor_global_protection_fixups }, { .id = SNOR_ID(0x1f, 0x47, 0x01), .name = "at25df321a", .size = SZ_4M, .flags = SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE, .no_sfdp_flags = SECT_4K, - .fixups = &atmel_nor_global_protection_fixups }, { .id = SNOR_ID(0x1f, 0x47, 0x08), .name = "at25ff321a", .flags = SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE, - .fixups = &atmel_nor_global_protection_fixups }, { .id = SNOR_ID(0x1f, 0x48, 0x00), .name = "at25df641", .size = SZ_8M, .flags = SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE, .no_sfdp_flags = SECT_4K, - .fixups = &atmel_nor_global_protection_fixups }, { .id = SNOR_ID(0x1f, 0x66, 0x01), .name = "at25fs010", @@ -230,14 +235,12 @@ static const struct flash_info atmel_nor_parts[] = { .size = SZ_128K, .flags = SPI_NOR_HAS_LOCK, .no_sfdp_flags = SECT_4K, - .fixups = &at25fs_nor_fixups }, { .id = SNOR_ID(0x1f, 0x66, 0x04), .name = "at25fs040", .size = SZ_512K, .flags = SPI_NOR_HAS_LOCK, .no_sfdp_flags = SECT_4K, - .fixups = &at25fs_nor_fixups }, { .id = SNOR_ID(0x1f, 0x87, 0x01), .size = SZ_4M, @@ -245,8 +248,22 @@ static const struct flash_info atmel_nor_parts[] = { }, }; +static const struct spi_nor_fixup atmel_fixups[] = { + { .id = SNOR_ID(0x1f, 0x44, 0x01), .fixups = &atmel_nor_global_protection_fixups }, + { .id = SNOR_ID(0x1f, 0x45, 0x01), .fixups = &atmel_nor_global_protection_fixups }, + { .id = SNOR_ID(0x1f, 0x46, 0x01), .fixups = &atmel_nor_global_protection_fixups }, + { .id = SNOR_ID(0x1f, 0x47, 0x00), .fixups = &atmel_nor_global_protection_fixups }, + { .id = SNOR_ID(0x1f, 0x47, 0x01), .fixups = &atmel_nor_global_protection_fixups }, + { .id = SNOR_ID(0x1f, 0x47, 0x08), .fixups = &atmel_nor_global_protection_fixups }, + { .id = SNOR_ID(0x1f, 0x48, 0x00), .fixups = &atmel_nor_global_protection_fixups }, + { .id = SNOR_ID(0x1f, 0x66, 0x01), .fixups = &at25fs_nor_fixups }, + { .id = SNOR_ID(0x1f, 0x66, 0x04), .fixups = &at25fs_nor_fixups }, +}; + const struct spi_nor_manufacturer spi_nor_atmel = { .name = "atmel", .parts = atmel_nor_parts, .nparts = ARRAY_SIZE(atmel_nor_parts), + .fixups = atmel_fixups, + .nfixups = ARRAY_SIZE(atmel_fixups), }; diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index ccf4396cdcd0..e2b6efafdd8d 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -59,7 +59,7 @@ static u8 spi_nor_get_cmd_ext(const struct spi_nor *nor, const struct spi_mem_op *op) { - switch (nor->cmd_ext_type) { + switch (nor->params->cmd_ext_type) { case SPI_NOR_EXT_INVERT: return ~op->cmd.opcode; @@ -83,6 +83,7 @@ void spi_nor_spimem_setup_op(const struct spi_nor *nor, struct spi_mem_op *op, const enum spi_nor_protocol proto) { + struct spi_nor_flash_parameter *params = nor->params; u8 ext; op->cmd.buswidth = spi_nor_get_protocol_inst_nbits(proto); @@ -116,7 +117,7 @@ void spi_nor_spimem_setup_op(const struct spi_nor *nor, op->cmd.nbytes = 2; } - if (proto == SNOR_PROTO_8_8_8_DTR && nor->flags & SNOR_F_SWAP16) + if (proto == SNOR_PROTO_8_8_8_DTR && params->flags & SNOR_F_SWAP16) op->data.swap16 = true; } @@ -444,75 +445,6 @@ int spi_nor_read_id(struct spi_nor *nor, u8 naddr, u8 ndummy, u8 *id, } /** - * spi_nor_read_sr() - Read the Status Register. - * @nor: pointer to 'struct spi_nor'. - * @sr: pointer to a DMA-able buffer where the value of the - * Status Register will be written. Should be at least 2 bytes. - * - * Return: 0 on success, -errno otherwise. - */ -int spi_nor_read_sr(struct spi_nor *nor, u8 *sr) -{ - int ret; - - if (nor->spimem) { - struct spi_mem_op op = SPI_NOR_RDSR_OP(sr); - - if (nor->reg_proto == SNOR_PROTO_8_8_8_DTR) { - op.addr.nbytes = nor->params->rdsr_addr_nbytes; - op.dummy.nbytes = nor->params->rdsr_dummy; - /* - * We don't want to read only one byte in DTR mode. So, - * read 2 and then discard the second byte. - */ - op.data.nbytes = 2; - } - - spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); - - ret = spi_mem_exec_op(nor->spimem, &op); - } else { - ret = spi_nor_controller_ops_read_reg(nor, SPINOR_OP_RDSR, sr, - 1); - } - - if (ret) - dev_dbg(nor->dev, "error %d reading SR\n", ret); - - return ret; -} - -/** - * spi_nor_read_cr() - Read the Configuration Register using the - * SPINOR_OP_RDCR (35h) command. - * @nor: pointer to 'struct spi_nor' - * @cr: pointer to a DMA-able buffer where the value of the - * Configuration Register will be written. - * - * Return: 0 on success, -errno otherwise. - */ -int spi_nor_read_cr(struct spi_nor *nor, u8 *cr) -{ - int ret; - - if (nor->spimem) { - struct spi_mem_op op = SPI_NOR_RDCR_OP(cr); - - spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); - - ret = spi_mem_exec_op(nor->spimem, &op); - } else { - ret = spi_nor_controller_ops_read_reg(nor, SPINOR_OP_RDCR, cr, - 1); - } - - if (ret) - dev_dbg(nor->dev, "error %d reading CR\n", ret); - - return ret; -} - -/** * spi_nor_set_4byte_addr_mode_en4b_ex4b() - Enter/Exit 4-byte address mode * using SPINOR_OP_EN4B/SPINOR_OP_EX4B. Typically used by * Winbond and Macronix. @@ -617,12 +549,13 @@ int spi_nor_set_4byte_addr_mode_brwr(struct spi_nor *nor, bool enable) int spi_nor_sr_ready(struct spi_nor *nor) { int ret; + u8 sr; - ret = spi_nor_read_sr(nor, nor->bouncebuf); + ret = spi_nor_read_sr1(nor, &sr); if (ret) return ret; - return !(nor->bouncebuf[0] & SR_WIP); + return !(sr & SR_WIP); } /** @@ -633,7 +566,7 @@ int spi_nor_sr_ready(struct spi_nor *nor) */ static bool spi_nor_use_parallel_locking(struct spi_nor *nor) { - return nor->flags & SNOR_F_RWW; + return nor->params->flags & SNOR_F_RWW; } /* Locking helpers for status read operations */ @@ -784,343 +717,285 @@ int spi_nor_global_block_unlock(struct spi_nor *nor) } /** - * spi_nor_write_sr() - Write the Status Register. + * spi_nor_read_sr_ll() - Low-level Status Registers read. * @nor: pointer to 'struct spi_nor'. - * @sr: pointer to DMA-able buffer to write to the Status Register. - * @len: number of bytes to write to the Status Register. + * @opcode: opcode for the status register read operation. + * @sr: pointer to buffer for storing the content of the status registers. + * @len: number of status registers to read (1 or 2). * * Return: 0 on success, -errno otherwise. */ -int spi_nor_write_sr(struct spi_nor *nor, const u8 *sr, size_t len) +int spi_nor_read_sr_ll(struct spi_nor *nor, u8 opcode, u8 *sr, + unsigned int len) { - int ret; + int ret, i; - ret = spi_nor_write_enable(nor); - if (ret) - return ret; + if (len > 2) + return -EINVAL; + + for (i = 0; i < len; i++) + nor->bouncebuf[i] = 0; if (nor->spimem) { - struct spi_mem_op op = SPI_NOR_WRSR_OP(sr, len); + struct spi_mem_op op = SPI_NOR_RDSR_OP(opcode, nor->bouncebuf, len); + + if (nor->reg_proto == SNOR_PROTO_8_8_8_DTR) { + op.addr.nbytes = nor->params->rdsr_addr_nbytes; + op.dummy.nbytes = nor->params->rdsr_dummy; + /* + * We don't want to read only one byte in DTR mode. So, + * read 2 and then discard the second byte. + */ + op.data.nbytes = 2; + } spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); ret = spi_mem_exec_op(nor->spimem, &op); } else { - ret = spi_nor_controller_ops_write_reg(nor, SPINOR_OP_WRSR, sr, - len); - } - - if (ret) { - dev_dbg(nor->dev, "error %d writing SR\n", ret); - return ret; + ret = spi_nor_controller_ops_read_reg(nor, opcode, nor->bouncebuf, len); } - return spi_nor_wait_till_ready(nor); -} + memcpy(sr, nor->bouncebuf, len); -/** - * spi_nor_write_sr1_and_check() - Write one byte to the Status Register 1 and - * ensure that the byte written match the received value. - * @nor: pointer to a 'struct spi_nor'. - * @sr1: byte value to be written to the Status Register. - * - * Return: 0 on success, -errno otherwise. - */ -static int spi_nor_write_sr1_and_check(struct spi_nor *nor, u8 sr1) -{ - int ret; - - nor->bouncebuf[0] = sr1; - - ret = spi_nor_write_sr(nor, nor->bouncebuf, 1); - if (ret) - return ret; - - ret = spi_nor_read_sr(nor, nor->bouncebuf); if (ret) - return ret; + dev_dbg(nor->dev, "Error %d reading Status Registers\n", ret); - if (nor->bouncebuf[0] != sr1) { - dev_dbg(nor->dev, "SR1: read back test failed\n"); - return -EIO; - } - - return 0; + return ret; } /** - * spi_nor_write_16bit_sr_and_check() - Write the Status Register 1 and the - * Status Register 2 in one shot. Ensure that the byte written in the Status - * Register 1 match the received value, and that the 16-bit Write did not - * affect what was already in the Status Register 2. - * @nor: pointer to a 'struct spi_nor'. - * @sr1: byte value to be written to the Status Register 1. + * spi_nor_write_sr_ll() - Low-level Status Registers write. + * @nor: pointer to 'struct spi_nor'. + * @opcode: opcode for the status register write operation. + * @sr: pointer to status registers buffer to write. + * @len: number of status registers to write. * * Return: 0 on success, -errno otherwise. */ -static int spi_nor_write_16bit_sr_and_check(struct spi_nor *nor, u8 sr1) +static int spi_nor_write_sr_ll(struct spi_nor *nor, u8 opcode, const u8 *sr, + unsigned int len) { - int ret; - u8 *sr_cr = nor->bouncebuf; - u8 cr_written; - - /* Make sure we don't overwrite the contents of Status Register 2. */ - if (!(nor->flags & SNOR_F_NO_READ_CR)) { - ret = spi_nor_read_cr(nor, &sr_cr[1]); - if (ret) - return ret; - } else if ((spi_nor_get_protocol_width(nor->read_proto) == 4 || - spi_nor_get_protocol_width(nor->write_proto) == 4) && - nor->params->quad_enable) { - /* - * If the Status Register 2 Read command (35h) is not - * supported, we should at least be sure we don't - * change the value of the SR2 Quad Enable bit. - * - * When the Quad Enable method is set and the buswidth is 4, we - * can safely assume that the value of the QE bit is one, as a - * consequence of the nor->params->quad_enable() call. - * - * According to the JESD216 revB standard, BFPT DWORDS[15], - * bits 22:20, the 16-bit Write Status (01h) command is - * available just for the cases in which the QE bit is - * described in SR2 at BIT(1). - */ - sr_cr[1] = SR2_QUAD_EN_BIT1; - } else { - sr_cr[1] = 0; - } + int ret, i; - sr_cr[0] = sr1; + if (len > 2) + return -EINVAL; - ret = spi_nor_write_sr(nor, sr_cr, 2); + ret = spi_nor_write_enable(nor); if (ret) return ret; - ret = spi_nor_read_sr(nor, sr_cr); - if (ret) - return ret; + for (i = 0; i < len; i++) + nor->bouncebuf[i] = sr[i]; - if (sr1 != sr_cr[0]) { - dev_dbg(nor->dev, "SR: Read back test failed\n"); - return -EIO; - } + if (nor->spimem) { + struct spi_mem_op op = SPI_NOR_WRSR_OP(opcode, + nor->bouncebuf, len); - if (nor->flags & SNOR_F_NO_READ_CR) - return 0; + spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); - cr_written = sr_cr[1]; + ret = spi_mem_exec_op(nor->spimem, &op); + } else { + ret = spi_nor_controller_ops_write_reg(nor, opcode, + nor->bouncebuf, len); + } - ret = spi_nor_read_cr(nor, &sr_cr[1]); - if (ret) + if (ret) { + dev_dbg(nor->dev, "Error %d writing Status Registers\n", ret); return ret; - - if (cr_written != sr_cr[1]) { - dev_dbg(nor->dev, "CR: read back test failed\n"); - return -EIO; } - return 0; + return spi_nor_wait_till_ready(nor); } /** - * spi_nor_write_16bit_cr_and_check() - Write the Status Register 1 and the - * Configuration Register in one shot. Ensure that the byte written in the - * Configuration Register match the received value, and that the 16-bit Write - * did not affect what was already in the Status Register 1. - * @nor: pointer to a 'struct spi_nor'. - * @cr: byte value to be written to the Configuration Register. + * spi_nor_read_sr1() - Read SR1 only + * Useful for: + * - The core to offer a generic "read SR1 and SR2" capability + * - Manufacturer drivers (since they know the chip SR layout) + * - Polling the BUSY bit in the core * - * Return: 0 on success, -errno otherwise. + * @nor: the spi_nor structure + * @sr1: pointer to a valid SR1 buffer + * + * Return 0 or errno. */ -int spi_nor_write_16bit_cr_and_check(struct spi_nor *nor, u8 cr) +int spi_nor_read_sr1(struct spi_nor *nor, u8 *sr1) { - int ret; - u8 *sr_cr = nor->bouncebuf; - u8 sr_written; - - /* Keep the current value of the Status Register 1. */ - ret = spi_nor_read_sr(nor, sr_cr); - if (ret) - return ret; - - sr_cr[1] = cr; - - ret = spi_nor_write_sr(nor, sr_cr, 2); - if (ret) - return ret; - - sr_written = sr_cr[0]; - - ret = spi_nor_read_sr(nor, sr_cr); - if (ret) - return ret; - - if (sr_written != sr_cr[0]) { - dev_dbg(nor->dev, "SR: Read back test failed\n"); - return -EIO; - } - - if (nor->flags & SNOR_F_NO_READ_CR) - return 0; + return spi_nor_read_sr_ll(nor, nor->params->opcodes.read_sr1, sr1, 1); +} - ret = spi_nor_read_cr(nor, &sr_cr[1]); - if (ret) - return ret; +/** + * spi_nor_read_sr2() - Read SR2 only + * Useful for: + * - The core to offer a generic "read SR1 and SR2" capability + * - Manufacturer drivers (since they know the chip SR layout) + * - SR2 based OTP configuration + * + * @nor: the spi_nor structure + * @sr2: pointer to a valid SR2 buffer + * + * Return 0 or errno. + */ +int spi_nor_read_sr2(struct spi_nor *nor, u8 *sr2) +{ + struct spi_nor_flash_parameter *params = nor->params; - if (cr != sr_cr[1]) { - dev_dbg(nor->dev, "CR: read back test failed\n"); - return -EIO; - } + if (!params->opcodes.read_sr2) + return -EINVAL; - return 0; + return spi_nor_read_sr_ll(nor, params->opcodes.read_sr2, sr2, 1); } /** - * spi_nor_write_16bit_sr_cr_and_check() - Write the Status Register 1 and the - * Configuration Register in one shot. Ensure that the bytes written in both - * registers match the received value. - * @nor: pointer to a 'struct spi_nor'. - * @regs: two-byte array with values to be written to the status and - * configuration registers. + * spi_nor_read_sr1_and_sr2() - Read SR1 then SR2 + * General purpose helper. * - * Return: 0 on success, -errno otherwise. + * @nor: the spi_nor structure + * @sr: pointer to a valid 2-byte array + * + * Return 0 or errno. */ -static int spi_nor_write_16bit_sr_cr_and_check(struct spi_nor *nor, const u8 *regs) +int spi_nor_read_sr1_and_sr2(struct spi_nor *nor, u8 *sr) { - u8 written_regs[2]; int ret; - written_regs[0] = regs[0]; - written_regs[1] = regs[1]; - nor->bouncebuf[0] = regs[0]; - nor->bouncebuf[1] = regs[1]; - - ret = spi_nor_write_sr(nor, nor->bouncebuf, 2); - if (ret) - return ret; - - ret = spi_nor_read_sr(nor, &nor->bouncebuf[0]); + ret = spi_nor_read_sr1(nor, &sr[0]); if (ret) return ret; - if (written_regs[0] != nor->bouncebuf[0]) { - dev_dbg(nor->dev, "SR: Read back test failed\n"); - return -EIO; - } - - if (nor->flags & SNOR_F_NO_READ_CR) - return 0; - - ret = spi_nor_read_cr(nor, &nor->bouncebuf[1]); - if (ret) - return ret; - - if (written_regs[1] != nor->bouncebuf[1]) { - dev_dbg(nor->dev, "CR: read back test failed\n"); - return -EIO; - } - - return 0; + return spi_nor_read_sr2(nor, &sr[1]); } /** - * spi_nor_write_sr_and_check() - Write the Status Register 1 and ensure that - * the byte written match the received value without affecting other bits in the - * Status Register 1 and 2. - * @nor: pointer to a 'struct spi_nor'. - * @sr1: byte value to be written to the Status Register. + * spi_nor_write_sr1() - Write SR1 only + * Useful for: + * - Manufacturer drivers (since they know the chip SR layout) * - * Return: 0 on success, -errno otherwise. + * @nor: the spi_nor structure + * @sr1: pointer to a valid SR1 buffer + * + * Return 0 or errno. */ -int spi_nor_write_sr_and_check(struct spi_nor *nor, u8 sr1) +int spi_nor_write_sr1(struct spi_nor *nor, const u8 *sr1) { - if (nor->flags & SNOR_F_HAS_16BIT_SR) - return spi_nor_write_16bit_sr_and_check(nor, sr1); + if (WARN_ONCE(!nor->params->opcodes.write_sr1, + "Restricted helper use, write SR1 not supported")) + return -EIO; - return spi_nor_write_sr1_and_check(nor, sr1); + return spi_nor_write_sr_ll(nor, nor->params->opcodes.write_sr1, sr1, 1); } /** - * spi_nor_write_sr_cr_and_check() - Write the Status Register 1 and ensure that - * the byte written match the received value. Same for the Control Register if - * available. - * @nor: pointer to a 'struct spi_nor'. - * @regs: byte array to be written to the registers. + * spi_nor_write_sr2() - Write SR2 only + * Useful for: + * - Manufacturer drivers (since they know the chip SR layout) + * - SR2 based OTP configuration * - * Return: 0 on success, -errno otherwise. + * @nor: the spi_nor structure + * @sr2: pointer to a valid SR2 buffer + * + * Return 0 or errno. */ -int spi_nor_write_sr_cr_and_check(struct spi_nor *nor, const u8 *regs) +int spi_nor_write_sr2(struct spi_nor *nor, const u8 *sr2) { - if (nor->flags & SNOR_F_HAS_16BIT_SR) - return spi_nor_write_16bit_sr_cr_and_check(nor, regs); + if (WARN_ONCE(!nor->params->opcodes.write_sr2, + "Restricted helper use, write SR2 not supported")) + return -EIO; - return spi_nor_write_sr1_and_check(nor, regs[0]); + return spi_nor_write_sr_ll(nor, nor->params->opcodes.write_sr2, sr2, 1); } /** - * spi_nor_write_sr2() - Write the Status Register 2 using the - * SPINOR_OP_WRSR2 (3eh) command. - * @nor: pointer to 'struct spi_nor'. - * @sr2: pointer to DMA-able buffer to write to the Status Register 2. + * spi_nor_write_sr1_and_sr2() - Write SR1 then SR2 + * General purpose helper, always safe to call. Will expectedly ignore + * SR2 on certain chips. * - * Return: 0 on success, -errno otherwise. + * @nor: the spi_nor structure + * @sr: pointer to a valid 2-byte array + * + * Return 0 or errno. */ -static int spi_nor_write_sr2(struct spi_nor *nor, const u8 *sr2) +static int spi_nor_write_sr1_and_sr2(struct spi_nor *nor, const u8 *sr) { + struct spi_nor_flash_parameter *params = nor->params; int ret; - ret = spi_nor_write_enable(nor); + if (params->opcodes.write_sr1_and_sr2) + return spi_nor_write_sr_ll(nor, + params->opcodes.write_sr1_and_sr2, + sr, 2); + + ret = spi_nor_write_sr1(nor, &sr[0]); if (ret) return ret; - if (nor->spimem) { - struct spi_mem_op op = SPI_NOR_WRSR2_OP(sr2); + if (params->opcodes.write_sr2) + return spi_nor_write_sr2(nor, &sr[1]); - spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); + return 0; +} - ret = spi_mem_exec_op(nor->spimem, &op); - } else { - ret = spi_nor_controller_ops_write_reg(nor, SPINOR_OP_WRSR2, - sr2, 1); - } +/** + * spi_nor_write_sr1_and_sr2_and_check() - Write SR1 then SR2, then read + * them back and verifies + * General purpose helper, always safe to call. Will expectedly ignore + * SR2 on certain chips. + * + * @nor: the spi_nor structure + * @sr: pointer to a valid 2-byte array + * + * Return 0 or errno. + */ +int spi_nor_write_sr1_and_sr2_and_check(struct spi_nor *nor, const u8 *sr) +{ + u8 tmp[2]; + int ret; - if (ret) { - dev_dbg(nor->dev, "error %d writing SR2\n", ret); + ret = spi_nor_write_sr1_and_sr2(nor, sr); + if (ret) return ret; - } - return spi_nor_wait_till_ready(nor); + ret = spi_nor_read_sr1_and_sr2(nor, tmp); + if (ret) + return ret; + + if (sr[0] != tmp[0] || sr[1] != tmp[1]) + return -EIO; + + return 0; } /** - * spi_nor_read_sr2() - Read the Status Register 2 using the - * SPINOR_OP_RDSR2 (3fh) command. - * @nor: pointer to 'struct spi_nor'. - * @sr2: pointer to DMA-able buffer where the value of the - * Status Register 2 will be written. + * spi_nor_generic_quad_enable() - Read the status registers, + * apply the QE bit mask, + * write the status registers, + * read them back and check the content. + * + * @nor: pointer to 'struct spi_nor' * * Return: 0 on success, -errno otherwise. */ -static int spi_nor_read_sr2(struct spi_nor *nor, u8 *sr2) +static int spi_nor_generic_quad_enable(struct spi_nor *nor) { + u8 *qe_mask = nor->params->qe_mask; + u8 sr[2] = {}; int ret; - if (nor->spimem) { - struct spi_mem_op op = SPI_NOR_RDSR2_OP(sr2); + if (!qe_mask[0] && !qe_mask[1]) + return 0; - spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); + ret = spi_nor_read_sr1_and_sr2(nor, sr); + if (ret) + return ret; - ret = spi_mem_exec_op(nor->spimem, &op); - } else { - ret = spi_nor_controller_ops_read_reg(nor, SPINOR_OP_RDSR2, sr2, - 1); - } + if (sr[0] & qe_mask[0] || sr[1] & qe_mask[1]) + return 0; - if (ret) - dev_dbg(nor->dev, "error %d reading SR2\n", ret); + sr[0] |= qe_mask[0]; + sr[1] |= qe_mask[1]; - return ret; + return spi_nor_write_sr1_and_sr2_and_check(nor, sr); } /** @@ -1140,7 +1015,7 @@ static int spi_nor_erase_die(struct spi_nor *nor, loff_t addr, size_t die_size) if (nor->spimem) { struct spi_mem_op op = - SPI_NOR_DIE_ERASE_OP(nor->params->die_erase_opcode, + SPI_NOR_DIE_ERASE_OP(nor->params->opcodes.die_erase, nor->addr_nbytes, addr, multi_die); spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); @@ -1346,6 +1221,9 @@ int spi_nor_prep_and_lock(struct spi_nor *nor) ret = wait_event_killable(nor->rww.wait, spi_nor_rww_start_exclusive(nor)); + if (ret) + spi_nor_unprep(nor); + return ret; } @@ -1417,6 +1295,9 @@ static int spi_nor_prep_and_lock_pe(struct spi_nor *nor, loff_t start, size_t le ret = wait_event_killable(nor->rww.wait, spi_nor_rww_start_pe(nor, start, len)); + if (ret) + spi_nor_unprep(nor); + return ret; } @@ -1490,6 +1371,9 @@ static int spi_nor_prep_and_lock_rd(struct spi_nor *nor, loff_t start, size_t le ret = wait_event_killable(nor->rww.wait, spi_nor_rww_start_rd(nor, start, len)); + if (ret) + spi_nor_unprep(nor); + return ret; } @@ -1852,8 +1736,7 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr) return ret; /* chip (die) erase? */ - if ((len == mtd->size && !(nor->flags & SNOR_F_NO_OP_CHIP_ERASE)) || - multi_die_erase) { + if (len == mtd->size || multi_die_erase) { ret = spi_nor_erase_dice(nor, addr, len, die_size); if (ret) goto erase_err; @@ -1904,106 +1787,6 @@ erase_err: return ret; } -/** - * spi_nor_sr1_bit6_quad_enable() - Set the Quad Enable BIT(6) in the Status - * Register 1. - * @nor: pointer to a 'struct spi_nor' - * - * Bit 6 of the Status Register 1 is the QE bit for Macronix like QSPI memories. - * - * Return: 0 on success, -errno otherwise. - */ -int spi_nor_sr1_bit6_quad_enable(struct spi_nor *nor) -{ - int ret; - - ret = spi_nor_read_sr(nor, nor->bouncebuf); - if (ret) - return ret; - - if (nor->bouncebuf[0] & SR1_QUAD_EN_BIT6) - return 0; - - nor->bouncebuf[0] |= SR1_QUAD_EN_BIT6; - - return spi_nor_write_sr1_and_check(nor, nor->bouncebuf[0]); -} - -/** - * spi_nor_sr2_bit1_quad_enable() - set the Quad Enable BIT(1) in the Status - * Register 2. - * @nor: pointer to a 'struct spi_nor'. - * - * Bit 1 of the Status Register 2 is the QE bit for Spansion like QSPI memories. - * - * Return: 0 on success, -errno otherwise. - */ -int spi_nor_sr2_bit1_quad_enable(struct spi_nor *nor) -{ - int ret; - - if (nor->flags & SNOR_F_NO_READ_CR) - return spi_nor_write_16bit_cr_and_check(nor, SR2_QUAD_EN_BIT1); - - ret = spi_nor_read_cr(nor, nor->bouncebuf); - if (ret) - return ret; - - if (nor->bouncebuf[0] & SR2_QUAD_EN_BIT1) - return 0; - - nor->bouncebuf[0] |= SR2_QUAD_EN_BIT1; - - return spi_nor_write_16bit_cr_and_check(nor, nor->bouncebuf[0]); -} - -/** - * spi_nor_sr2_bit7_quad_enable() - set QE bit in Status Register 2. - * @nor: pointer to a 'struct spi_nor' - * - * Set the Quad Enable (QE) bit in the Status Register 2. - * - * This is one of the procedures to set the QE bit described in the SFDP - * (JESD216 rev B) specification but no manufacturer using this procedure has - * been identified yet, hence the name of the function. - * - * Return: 0 on success, -errno otherwise. - */ -int spi_nor_sr2_bit7_quad_enable(struct spi_nor *nor) -{ - u8 *sr2 = nor->bouncebuf; - int ret; - u8 sr2_written; - - /* Check current Quad Enable bit value. */ - ret = spi_nor_read_sr2(nor, sr2); - if (ret) - return ret; - if (*sr2 & SR2_QUAD_EN_BIT7) - return 0; - - /* Update the Quad Enable bit. */ - *sr2 |= SR2_QUAD_EN_BIT7; - - ret = spi_nor_write_sr2(nor, sr2); - if (ret) - return ret; - - sr2_written = *sr2; - - /* Read back and check it. */ - ret = spi_nor_read_sr2(nor, sr2); - if (ret) - return ret; - - if (*sr2 != sr2_written) { - dev_dbg(nor->dev, "SR2: Read back test failed\n"); - return -EIO; - } - - return 0; -} - static const struct spi_nor_manufacturer *manufacturers[] = { &spi_nor_atmel, &spi_nor_eon, @@ -2494,6 +2277,7 @@ spi_nor_spimem_adjust_hwcaps(struct spi_nor *nor, u32 *hwcaps) { struct spi_nor_flash_parameter *params = nor->params; unsigned int cap; + u8 opcode; /* X-X-X modes are not supported yet, mask them all. */ *hwcaps &= ~SNOR_HWCAPS_X_X_X; @@ -2502,7 +2286,7 @@ spi_nor_spimem_adjust_hwcaps(struct spi_nor *nor, u32 *hwcaps) * If the reset line is broken, we do not want to enter a stateful * mode. */ - if (nor->flags & SNOR_F_BROKEN_RESET) + if (params->flags & SNOR_F_BROKEN_RESET) *hwcaps &= ~(SNOR_HWCAPS_X_X_X | SNOR_HWCAPS_X_X_X_DTR); for (cap = 0; cap < sizeof(*hwcaps) * BITS_PER_BYTE; cap++) { @@ -2525,14 +2309,15 @@ spi_nor_spimem_adjust_hwcaps(struct spi_nor *nor, u32 *hwcaps) *hwcaps &= ~BIT(cap); } - /* Some SPI controllers might not support CR read opcode. */ - if (!(nor->flags & SNOR_F_NO_READ_CR)) { - struct spi_mem_op op = SPI_NOR_RDCR_OP(nor->bouncebuf); + /* Some SPI controllers might not support reading SR2 */ + opcode = nor->params->opcodes.read_sr2; + if (opcode) { + struct spi_mem_op op = SPI_NOR_RDSR_OP(opcode, nor->bouncebuf, 1); spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); if (!spi_mem_supports_op(nor->spimem, &op)) - nor->flags |= SNOR_F_NO_READ_CR; + nor->params->opcodes.read_sr2 = 0; } } @@ -2578,26 +2363,6 @@ void spi_nor_init_uniform_erase_map(struct spi_nor_erase_map *map, map->n_regions = 1; } -int spi_nor_post_bfpt_fixups(struct spi_nor *nor, - const struct sfdp_parameter_header *bfpt_header, - const struct sfdp_bfpt *bfpt) -{ - int ret; - - if (nor->manufacturer && nor->manufacturer->fixups && - nor->manufacturer->fixups->post_bfpt) { - ret = nor->manufacturer->fixups->post_bfpt(nor, bfpt_header, - bfpt); - if (ret) - return ret; - } - - if (nor->info->fixups && nor->info->fixups->post_bfpt) - return nor->info->fixups->post_bfpt(nor, bfpt_header, bfpt); - - return 0; -} - static int spi_nor_select_read(struct spi_nor *nor, u32 shared_hwcaps) { @@ -2749,6 +2514,8 @@ static int spi_nor_select_erase(struct spi_nor *nor) static int spi_nor_set_addr_nbytes(struct spi_nor *nor) { + struct spi_nor_flash_parameter *params = nor->params; + if (nor->params->addr_nbytes) { nor->addr_nbytes = nor->params->addr_nbytes; } else if (nor->read_proto == SNOR_PROTO_8_8_8_DTR) { @@ -2783,8 +2550,8 @@ static int spi_nor_set_addr_nbytes(struct spi_nor *nor) } /* Set 4byte opcodes when possible. */ - if (nor->addr_nbytes == 4 && nor->flags & SNOR_F_4B_OPCODES && - !(nor->flags & SNOR_F_HAS_4BAIT)) + if (nor->addr_nbytes == 4 && params->flags & SNOR_F_4B_OPCODES && + !(params->flags & SNOR_F_HAS_4BAIT)) spi_nor_set_4byte_opcodes(nor); return 0; @@ -2851,6 +2618,26 @@ static int spi_nor_setup(struct spi_nor *nor, return spi_nor_set_addr_nbytes(nor); } +bool spi_nor_fixup_match(const struct spi_nor *nor, + const struct spi_nor_fixup *fixup) +{ + const struct spi_nor_id *id = nor->info ? nor->info->id : NULL; + + /* Filter by ID first, if available */ + if (fixup->id) { + if (!id || fixup->id->len > id->len || + memcmp(id->bytes, fixup->id->bytes, fixup->id->len)) + return false; + } + + /* Further filter with the match callback, if provided */ + if (fixup->match) + return fixup->match(nor); + + /* Either there was an ID and it matched, or it is a catch-all entry */ + return true; +} + /** * spi_nor_manufacturer_init_params() - Initialize the flash's parameters and * settings based on MFR register and ->default_init() hook. @@ -2858,21 +2645,27 @@ static int spi_nor_setup(struct spi_nor *nor, */ static void spi_nor_manufacturer_init_params(struct spi_nor *nor) { - if (nor->manufacturer && nor->manufacturer->fixups && - nor->manufacturer->fixups->default_init) - nor->manufacturer->fixups->default_init(nor); + const struct spi_nor_fixup *fixups; + unsigned int i; + + if (!nor->manufacturer || !nor->manufacturer->fixups) + return; + + fixups = nor->manufacturer->fixups; - if (nor->info->fixups && nor->info->fixups->default_init) - nor->info->fixups->default_init(nor); + for (i = 0; i < nor->manufacturer->nfixups; i++) { + if (fixups[i].fixups && fixups[i].fixups->default_init && + spi_nor_fixup_match(nor, &fixups[i])) + fixups[i].fixups->default_init(nor); + } } /** * spi_nor_no_sfdp_init_params() - Initialize the flash's parameters and - * settings based on nor->info->sfdp_flags. This method should be called only by - * flashes that do not define SFDP tables. If the flash supports SFDP but the - * information is wrong and the settings from this function can not be retrieved - * by parsing SFDP, one should instead use the fixup hooks and update the wrong - * bits. + * settings based on nor->info->sfdp_flags. + * If the flash supports SFDP but the information is wrong and the settings from + * this function can not be retrieved by parsing SFDP, one should instead use + * the fixup hooks and update the wrong bits. * @nor: pointer to a 'struct spi_nor'. */ static void spi_nor_no_sfdp_init_params(struct spi_nor *nor) @@ -2947,39 +2740,40 @@ static void spi_nor_no_sfdp_init_params(struct spi_nor *nor) */ static void spi_nor_init_flags(struct spi_nor *nor) { + struct spi_nor_flash_parameter *params = nor->params; struct device_node *np = spi_nor_get_flash_node(nor); const u16 flags = nor->info->flags; if (of_property_read_bool(np, "broken-flash-reset")) - nor->flags |= SNOR_F_BROKEN_RESET; + params->flags |= SNOR_F_BROKEN_RESET; if (of_property_read_bool(np, "no-wp")) - nor->flags |= SNOR_F_NO_WP; + params->flags |= SNOR_F_NO_WP; if (flags & SPI_NOR_SWP_IS_VOLATILE) - nor->flags |= SNOR_F_SWP_IS_VOLATILE; + params->flags |= SNOR_F_SWP_IS_VOLATILE; if (flags & SPI_NOR_HAS_LOCK) - nor->flags |= SNOR_F_HAS_LOCK; + params->flags |= SNOR_F_HAS_LOCK; if (flags & SPI_NOR_HAS_TB) { - nor->flags |= SNOR_F_HAS_SR_TB; + params->flags |= SNOR_F_HAS_SR_TB; if (flags & SPI_NOR_TB_SR_BIT6) - nor->flags |= SNOR_F_HAS_SR_TB_BIT6; + params->flags |= SNOR_F_HAS_SR_TB_BIT6; } if (flags & SPI_NOR_4BIT_BP) { - nor->flags |= SNOR_F_HAS_4BIT_BP; + params->flags |= SNOR_F_HAS_4BIT_BP; if (flags & SPI_NOR_BP3_SR_BIT6) - nor->flags |= SNOR_F_HAS_SR_BP3_BIT6; + params->flags |= SNOR_F_HAS_SR_BP3_BIT6; } if (flags & SPI_NOR_HAS_CMP) - nor->flags |= SNOR_F_HAS_SR2_CMP_BIT6; + params->flags |= SNOR_F_HAS_SR2_CMP_BIT6; if (flags & SPI_NOR_RWW && nor->params->n_banks > 1 && !nor->controller_ops) - nor->flags |= SNOR_F_RWW; + params->flags |= SNOR_F_RWW; } /** @@ -2992,13 +2786,25 @@ static void spi_nor_init_flags(struct spi_nor *nor) */ static void spi_nor_init_fixup_flags(struct spi_nor *nor) { - const u8 fixup_flags = nor->info->fixup_flags; + struct spi_nor_flash_parameter *params = nor->params; + const struct spi_nor_fixup *fixups; + unsigned int i; + + if (!nor->manufacturer || !nor->manufacturer->fixups) + return; + + fixups = nor->manufacturer->fixups; - if (fixup_flags & SPI_NOR_4B_OPCODES) - nor->flags |= SNOR_F_4B_OPCODES; + for (i = 0; i < nor->manufacturer->nfixups; i++) { + if (!fixups[i].fixup_flags || + !spi_nor_fixup_match(nor, &fixups[i])) + continue; - if (fixup_flags & SPI_NOR_IO_MODE_EN_VOLATILE) - nor->flags |= SNOR_F_IO_MODE_EN_VOLATILE; + if (fixups[i].fixup_flags & SPI_NOR_4B_OPCODES) + params->flags |= SNOR_F_4B_OPCODES; + if (fixups[i].fixup_flags & SPI_NOR_IO_MODE_EN_VOLATILE) + params->flags |= SNOR_F_IO_MODE_EN_VOLATILE; + } } /** @@ -3012,26 +2818,28 @@ static void spi_nor_init_fixup_flags(struct spi_nor *nor) static int spi_nor_late_init_params(struct spi_nor *nor) { struct spi_nor_flash_parameter *params = nor->params; + const struct spi_nor_fixup *fixups; + unsigned int i; int ret; - if (nor->manufacturer && nor->manufacturer->fixups && - nor->manufacturer->fixups->late_init) { - ret = nor->manufacturer->fixups->late_init(nor); - if (ret) - return ret; - } - - /* Needed by some flashes late_init hooks. */ + /* Needed by some late_init hooks */ spi_nor_init_flags(nor); - if (nor->info->fixups && nor->info->fixups->late_init) { - ret = nor->info->fixups->late_init(nor); - if (ret) - return ret; + if (nor->manufacturer && nor->manufacturer->fixups) { + fixups = nor->manufacturer->fixups; + + for (i = 0; i < nor->manufacturer->nfixups; i++) { + if (fixups[i].fixups && fixups[i].fixups->late_init && + spi_nor_fixup_match(nor, &fixups[i])) { + ret = fixups[i].fixups->late_init(nor); + if (ret) + return ret; + } + } } - if (!nor->params->die_erase_opcode) - nor->params->die_erase_opcode = SPINOR_OP_CHIP_ERASE; + if (!nor->params->opcodes.die_erase) + nor->params->opcodes.die_erase = SPINOR_OP_CHIP_ERASE; /* Default method kept for backward compatibility. */ if (!params->set_4byte_addr_mode) @@ -3043,7 +2851,7 @@ static int spi_nor_late_init_params(struct spi_nor *nor) * NOR protection support. When locking_ops are not provided, we pick * the default ones. */ - if (nor->flags & SNOR_F_HAS_LOCK && !nor->params->locking_ops) + if (params->flags & SNOR_F_HAS_LOCK && !nor->params->locking_ops) spi_nor_init_default_locking_ops(nor); if (params->n_banks > 1) @@ -3053,48 +2861,6 @@ static int spi_nor_late_init_params(struct spi_nor *nor) } /** - * spi_nor_sfdp_init_params_deprecated() - Deprecated way of initializing flash - * parameters and settings based on JESD216 SFDP standard. - * @nor: pointer to a 'struct spi_nor'. - * - * The method has a roll-back mechanism: in case the SFDP parsing fails, the - * legacy flash parameters and settings will be restored. - */ -static void spi_nor_sfdp_init_params_deprecated(struct spi_nor *nor) -{ - struct spi_nor_flash_parameter sfdp_params; - - memcpy(&sfdp_params, nor->params, sizeof(sfdp_params)); - - if (spi_nor_parse_sfdp(nor)) { - memcpy(nor->params, &sfdp_params, sizeof(*nor->params)); - nor->flags &= ~SNOR_F_4B_OPCODES; - } -} - -/** - * spi_nor_init_params_deprecated() - Deprecated way of initializing flash - * parameters and settings. - * @nor: pointer to a 'struct spi_nor'. - * - * The method assumes that flash doesn't support SFDP so it initializes flash - * parameters in spi_nor_no_sfdp_init_params() which later on can be overwritten - * when parsing SFDP, if supported. - */ -static void spi_nor_init_params_deprecated(struct spi_nor *nor) -{ - spi_nor_no_sfdp_init_params(nor); - - spi_nor_manufacturer_init_params(nor); - - if (nor->info->no_sfdp_flags & (SPI_NOR_DUAL_READ | - SPI_NOR_QUAD_READ | - SPI_NOR_OCTAL_READ | - SPI_NOR_OCTAL_DTR_READ)) - spi_nor_sfdp_init_params_deprecated(nor); -} - -/** * spi_nor_init_default_params() - Default initialization of flash parameters * and settings. Done for all flashes, regardless is they define SFDP tables * or not. @@ -3106,11 +2872,14 @@ static void spi_nor_init_default_params(struct spi_nor *nor) const struct flash_info *info = nor->info; struct device_node *np = spi_nor_get_flash_node(nor); - params->quad_enable = spi_nor_sr2_bit1_quad_enable; - params->otp.org = info->otp; + /* Default to 16-bit Read/Write Status commands */ + params->opcodes.read_sr1 = SPINOR_OP_RDSR; + params->opcodes.read_sr2 = SPINOR_OP_RDCR; + params->opcodes.write_sr1_and_sr2 = SPINOR_OP_WRSR; + params->quad_enable = spi_nor_generic_quad_enable; + params->qe_mask[1] = BIT(1); - /* Default to 16-bit Write Status (01h) Command */ - nor->flags |= SNOR_F_HAS_16BIT_SR; + params->otp.org = info->otp; /* Set SPI NOR sizes. */ params->writesize = 1; @@ -3154,7 +2923,8 @@ static void spi_nor_init_default_params(struct spi_nor *nor) * * 1/ Default flash parameters initialization. The initializations are done * based on nor->info data: - * spi_nor_info_init_params() + * spi_nor_init_default_params() + * spi_nor_no_sfdp_init_params() * * which can be overwritten by: * 2/ Manufacturer flash parameters initialization. The initializations are @@ -3165,7 +2935,7 @@ static void spi_nor_init_default_params(struct spi_nor *nor) * which can be overwritten by: * 3/ SFDP flash parameters initialization. JESD216 SFDP is a standard and * should be more accurate that the above. - * spi_nor_parse_sfdp() or spi_nor_no_sfdp_init_params() + * spi_nor_parse_sfdp() * * Please note that there is a ->post_bfpt() fixup hook that can overwrite * the flash parameters and settings immediately after parsing the Basic @@ -3191,17 +2961,14 @@ static int spi_nor_init_params(struct spi_nor *nor) return -ENOMEM; spi_nor_init_default_params(nor); + spi_nor_no_sfdp_init_params(nor); + spi_nor_manufacturer_init_params(nor); - if (spi_nor_needs_sfdp(nor)) { - ret = spi_nor_parse_sfdp(nor); - if (ret) { - dev_err(nor->dev, "BFPT parsing failed. Please consider using SPI_NOR_SKIP_SFDP when declaring the flash\n"); - return ret; - } - } else if (nor->info->no_sfdp_flags & SPI_NOR_SKIP_SFDP) { - spi_nor_no_sfdp_init_params(nor); - } else { - spi_nor_init_params_deprecated(nor); + ret = spi_nor_parse_sfdp(nor); + if (ret && spi_nor_needs_sfdp(nor)) { + dev_err(nor->dev, + "SFDP parsing failed. You need to manually declare the flash parameters.\n"); + return ret; } ret = spi_nor_late_init_params(nor); @@ -3231,7 +2998,7 @@ static int spi_nor_set_octal_dtr(struct spi_nor *nor, bool enable) nor->write_proto == SNOR_PROTO_8_8_8_DTR)) return 0; - if (!(nor->flags & SNOR_F_IO_MODE_EN_VOLATILE)) + if (!(nor->params->flags & SNOR_F_IO_MODE_EN_VOLATILE)) return 0; ret = nor->params->set_octal_dtr(nor, enable); @@ -3284,7 +3051,7 @@ int spi_nor_set_4byte_addr_mode(struct spi_nor *nor, bool enable) * reboots (e.g., crashes). Warn the user (or hopefully, system * designer) that this is bad. */ - WARN_ONCE(nor->flags & SNOR_F_BROKEN_RESET, + WARN_ONCE(nor->params->flags & SNOR_F_BROKEN_RESET, "enabling reset hack; may not recover from unexpected reboots\n"); } @@ -3305,6 +3072,7 @@ int spi_nor_set_4byte_addr_mode(struct spi_nor *nor, bool enable) static int spi_nor_init(struct spi_nor *nor) { + struct spi_nor_flash_parameter *params = nor->params; int err; err = spi_nor_set_octal_dtr(nor, true); @@ -3332,13 +3100,13 @@ static int spi_nor_init(struct spi_nor *nor) spi_nor_cache_sr_lock_bits(nor, NULL); if (IS_ENABLED(CONFIG_MTD_SPI_NOR_SWP_DISABLE) || (IS_ENABLED(CONFIG_MTD_SPI_NOR_SWP_DISABLE_ON_VOLATILE) && - nor->flags & SNOR_F_SWP_IS_VOLATILE)) { + params->flags & SNOR_F_SWP_IS_VOLATILE)) { spi_nor_try_unlock_all(nor); } if (nor->addr_nbytes == 4 && nor->read_proto != SNOR_PROTO_8_8_8_DTR && - !(nor->flags & SNOR_F_4B_OPCODES)) + !(params->flags & SNOR_F_4B_OPCODES)) return spi_nor_set_4byte_addr_mode(nor, true); return 0; @@ -3453,11 +3221,12 @@ static void spi_nor_put_device(struct mtd_info *mtd) static void spi_nor_restore(struct spi_nor *nor) { + struct spi_nor_flash_parameter *params = nor->params; int ret; /* restore the addressing mode */ - if (nor->addr_nbytes == 4 && !(nor->flags & SNOR_F_4B_OPCODES) && - nor->flags & SNOR_F_BROKEN_RESET) { + if (nor->addr_nbytes == 4 && !(params->flags & SNOR_F_4B_OPCODES) && + params->flags & SNOR_F_BROKEN_RESET) { ret = spi_nor_set_4byte_addr_mode(nor, false); if (ret) /* @@ -3468,7 +3237,7 @@ static void spi_nor_restore(struct spi_nor *nor) dev_err(nor->dev, "Failed to exit 4-byte address mode, err = %d\n", ret); } - if (nor->flags & SNOR_F_SOFT_RESET) + if (params->flags & SNOR_F_SOFT_RESET) spi_nor_soft_reset(nor); } @@ -3585,7 +3354,7 @@ static int spi_nor_set_mtd_info(struct spi_nor *nor) mtd->type = MTD_NORFLASH; mtd->flags = MTD_CAP_NORFLASH; /* Unset BIT_WRITEABLE to enable JFFS2 write buffer for ECC'd NOR */ - if (nor->flags & SNOR_F_ECC) + if (nor->params->flags & SNOR_F_ECC) mtd->flags &= ~MTD_BIT_WRITEABLE; if (nor->info->flags & SPI_NOR_NO_ERASE) mtd->flags |= MTD_NO_ERASE; @@ -3669,6 +3438,7 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, return PTR_ERR(info); nor->info = info; + nor->partname = info->name; mutex_init(&nor->lock); diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h index ba2d1a862c9d..670182b3c2ad 100644 --- a/drivers/mtd/spi-nor/core.h +++ b/drivers/mtd/spi-nor/core.h @@ -37,36 +37,18 @@ SPI_MEM_OP_NO_DUMMY, \ SPI_MEM_OP_NO_DATA) -#define SPI_NOR_RDSR_OP(buf) \ - SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDSR, 0), \ +#define SPI_NOR_RDSR_OP(opcode, buf, len) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(opcode, 0), \ SPI_MEM_OP_NO_ADDR, \ SPI_MEM_OP_NO_DUMMY, \ - SPI_MEM_OP_DATA_IN(1, buf, 0)) + SPI_MEM_OP_DATA_IN(len, buf, 0)) -#define SPI_NOR_WRSR_OP(buf, len) \ - SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WRSR, 0), \ +#define SPI_NOR_WRSR_OP(opcode, buf, len) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(opcode, 0), \ SPI_MEM_OP_NO_ADDR, \ SPI_MEM_OP_NO_DUMMY, \ SPI_MEM_OP_DATA_OUT(len, buf, 0)) -#define SPI_NOR_RDSR2_OP(buf) \ - SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDSR2, 0), \ - SPI_MEM_OP_NO_ADDR, \ - SPI_MEM_OP_NO_DUMMY, \ - SPI_MEM_OP_DATA_OUT(1, buf, 0)) - -#define SPI_NOR_WRSR2_OP(buf) \ - SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WRSR2, 0), \ - SPI_MEM_OP_NO_ADDR, \ - SPI_MEM_OP_NO_DUMMY, \ - SPI_MEM_OP_DATA_OUT(1, buf, 0)) - -#define SPI_NOR_RDCR_OP(buf) \ - SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDCR, 0), \ - SPI_MEM_OP_NO_ADDR, \ - SPI_MEM_OP_NO_DUMMY, \ - SPI_MEM_OP_DATA_IN(1, buf, 0)) - #define SPI_NOR_EN4B_EX4B_OP(enable) \ SPI_MEM_OP(SPI_MEM_OP_CMD(enable ? SPINOR_OP_EN4B : SPINOR_OP_EX4B, 0), \ SPI_MEM_OP_NO_ADDR, \ @@ -121,16 +103,16 @@ SPI_MEM_OP_NO_ADDR, \ SPI_MEM_OP_NO_DATA) -/* Keep these in sync with the list in debugfs.c */ +/* + * This could contain holes, if adding a new flag use the first free spot. + * Keep the flags in sync with the list in debugfs.c + */ enum spi_nor_option_flags { SNOR_F_HAS_SR_TB = BIT(0), - SNOR_F_NO_OP_CHIP_ERASE = BIT(1), SNOR_F_BROKEN_RESET = BIT(2), SNOR_F_4B_OPCODES = BIT(3), SNOR_F_HAS_4BAIT = BIT(4), SNOR_F_HAS_LOCK = BIT(5), - SNOR_F_HAS_16BIT_SR = BIT(6), - SNOR_F_NO_READ_CR = BIT(7), SNOR_F_HAS_SR_TB_BIT6 = BIT(8), SNOR_F_HAS_4BIT_BP = BIT(9), SNOR_F_HAS_SR_BP3_BIT6 = BIT(10), @@ -341,6 +323,26 @@ struct spi_nor_otp { }; /** + * struct spi_nor_opcodes - SPI NOR flash specific opcodes. + * List of variable opcodes used by the chip. + * + * @die_erase: opcode for erasing a die, defaults to SPINOR_OP_CHIP_ERASE + * @read_sr1: opcode for reading SR1 alone + * @read_sr2: opcode for reading SR2 alone + * @write_sr1: opcode for writing SR1 alone + * @write_sr2: opcode for writing SR2 alone + * @write_sr1_and_sr2: opcode for writing SR1 then SR2 in one operation + */ +struct spi_nor_opcodes { + u8 die_erase; + u8 read_sr1; + u8 read_sr2; + u8 write_sr1; + u8 write_sr2; + u8 write_sr1_and_sr2; +}; + +/** * struct spi_nor_flash_parameter - SPI NOR flash parameters and settings. * Includes legacy flash parameters and settings that can be overwritten * by the spi_nor_fixups hooks, or dynamically when parsing the JESD216 @@ -348,6 +350,7 @@ struct spi_nor_otp { * * @bank_size: the flash memory bank density in bytes. * @size: the total flash memory density in bytes. + * @flags: flag options for the current SPI NOR (SNOR_F_*) * @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. @@ -362,7 +365,6 @@ struct spi_nor_otp { * command in octal DTR mode. * @n_banks: number of banks. * @n_dice: number of dice in the flash memory. - * @die_erase_opcode: die erase opcode. Defaults to SPINOR_OP_CHIP_ERASE. * @vreg_offset: volatile register offset for each die. * @hwcaps: describes the read and page program hardware * capabilities. @@ -370,11 +372,13 @@ struct spi_nor_otp { * in the array, the higher priority. * @page_programs: page program capabilities ordered by priority: the * higher index in the array, the higher priority. + * @cmd_ext_type: the command opcode extension type for DTR mode. * @erase_map: the erase map parsed from the SFDP Sector Map Parameter * Table. * @otp: SPI NOR OTP info. * @set_octal_dtr: enables or disables SPI NOR octal DTR mode. * @quad_enable: enables SPI NOR quad mode. + * @qe_mask: two bytes mask used to set/clear the QE bit * @set_4byte_addr_mode: puts the SPI NOR in 4 byte addressing mode. * @ready: (optional) flashes might use a different mechanism * than reading the status register to indicate they @@ -385,6 +389,7 @@ struct spi_nor_otp { struct spi_nor_flash_parameter { u64 bank_size; u64 size; + u32 flags; u32 writesize; u32 page_size; u8 addr_nbytes; @@ -393,18 +398,20 @@ struct spi_nor_flash_parameter { u8 rdsr_addr_nbytes; u8 n_banks; u8 n_dice; - u8 die_erase_opcode; u32 *vreg_offset; struct spi_nor_hwcaps hwcaps; struct spi_nor_read_command reads[SNOR_CMD_READ_MAX]; struct spi_nor_pp_command page_programs[SNOR_CMD_PP_MAX]; + enum spi_nor_cmd_ext cmd_ext_type; + struct spi_nor_opcodes opcodes; struct spi_nor_erase_map erase_map; struct spi_nor_otp otp; int (*set_octal_dtr)(struct spi_nor *nor, bool enable); int (*quad_enable)(struct spi_nor *nor); + u8 qe_mask[2]; int (*set_4byte_addr_mode)(struct spi_nor *nor, bool enable); int (*ready)(struct spi_nor *nor); @@ -446,6 +453,33 @@ struct spi_nor_fixups { }; /** + * struct spi_nor_fixup - SPI NOR fixup registration. + * @id: (optional) flash ID this fixup applies to, may only match the + * ID prefix, eg. just the first few bytes to match a whole family + * @match: (optional) custom match function (can be used together with @id) + * @fixup_flags: flags that indicate support that can be discovered via SFDP + * ideally, but can not be discovered for this particular flash + * because the SFDP table that indicates this support is not + * defined by the flash. In case the table for this support is + * defined but has wrong values, one should instead use a + * post_sfdp() hook to set the SNOR_F equivalent flag. + * + * SPI_NOR_4B_OPCODES: use dedicated 4byte address op codes to support + * memory size above 128Mib. + * SPI_NOR_IO_MODE_EN_VOLATILE: flash enables the best available I/O mode + * via a volatile bit. + * @fixups: the fixup hooks to apply when this entry matches + */ +struct spi_nor_fixup { + const struct spi_nor_id *id; + bool (*match)(const struct spi_nor *nor); + u8 fixup_flags; +#define SPI_NOR_4B_OPCODES BIT(0) +#define SPI_NOR_IO_MODE_EN_VOLATILE BIT(1) + const struct spi_nor_fixups *fixups; +}; + +/** * struct spi_nor_id - SPI NOR flash ID. * * @bytes: the bytes returned by the flash when issuing command 9F. Typically, @@ -496,9 +530,7 @@ struct spi_nor_id { * be used with SPI_NOR_HAS_LOCK. * * @no_sfdp_flags: flags that indicate support that can be discovered via SFDP. - * Used when SFDP tables are not defined in the flash. These - * flags are used together with the SPI_NOR_SKIP_SFDP flag. - * SPI_NOR_SKIP_SFDP: skip parsing of SFDP tables. + * Used when SFDP tables are not defined in the flash. * SECT_4K: SPINOR_OP_BE_4K works uniformly. * SPI_NOR_DUAL_READ: flash supports Dual Read. * SPI_NOR_QUAD_READ: flash supports Quad Read. @@ -506,22 +538,10 @@ struct spi_nor_id { * SPI_NOR_OCTAL_DTR_READ: flash supports octal DTR Read. * SPI_NOR_OCTAL_DTR_PP: flash supports Octal DTR Page Program. * - * @fixup_flags: flags that indicate support that can be discovered via SFDP - * ideally, but can not be discovered for this particular flash - * because the SFDP table that indicates this support is not - * defined by the flash. In case the table for this support is - * defined but has wrong values, one should instead use a - * post_sfdp() hook to set the SNOR_F equivalent flag. - * - * SPI_NOR_4B_OPCODES: use dedicated 4byte address op codes to support - * memory size above 128Mib. - * SPI_NOR_IO_MODE_EN_VOLATILE: flash enables the best available I/O mode - * via a volatile bit. * @mfr_flags: manufacturer private flags. Used in the manufacturer fixup * hooks to differentiate support between flashes of the same * manufacturer. * @otp_org: flash's OTP organization. - * @fixups: part specific fixup hooks. */ struct flash_info { char *name; @@ -545,7 +565,6 @@ struct flash_info { #define SPI_NOR_HAS_CMP BIT(10) u8 no_sfdp_flags; -#define SPI_NOR_SKIP_SFDP BIT(0) #define SECT_4K BIT(1) #define SPI_NOR_DUAL_READ BIT(3) #define SPI_NOR_QUAD_READ BIT(4) @@ -553,14 +572,9 @@ struct flash_info { #define SPI_NOR_OCTAL_DTR_READ BIT(6) #define SPI_NOR_OCTAL_DTR_PP BIT(7) - u8 fixup_flags; -#define SPI_NOR_4B_OPCODES BIT(0) -#define SPI_NOR_IO_MODE_EN_VOLATILE BIT(1) - u8 mfr_flags; const struct spi_nor_otp_organization *otp; - const struct spi_nor_fixups *fixups; }; #define SNOR_ID(...) \ @@ -582,13 +596,16 @@ struct flash_info { * @name: manufacturer name * @parts: array of parts supported by this manufacturer * @nparts: number of entries in the parts array - * @fixups: hooks called at various points in time during spi_nor_scan() + * @fixups: list of fixups, each matched by ID (or a custom match function), + * applied to any part of this manufacturer. + * @nfixups: number of entries in the fixups array */ struct spi_nor_manufacturer { const char *name; const struct flash_info *parts; unsigned int nparts; - const struct spi_nor_fixups *fixups; + const struct spi_nor_fixup *fixups; + unsigned int nfixups; }; /** @@ -619,6 +636,8 @@ extern const struct spi_nor_manufacturer spi_nor_xmc; extern const struct attribute_group *spi_nor_sysfs_groups[]; +bool spi_nor_fixup_match(const struct spi_nor *nor, + const struct spi_nor_fixup *fixup); void spi_nor_spimem_setup_op(const struct spi_nor *nor, struct spi_mem_op *op, const enum spi_nor_protocol proto); @@ -633,18 +652,17 @@ int spi_nor_wait_till_ready(struct spi_nor *nor); int spi_nor_global_block_unlock(struct spi_nor *nor); int spi_nor_prep_and_lock(struct spi_nor *nor); void spi_nor_unlock_and_unprep(struct spi_nor *nor); -int spi_nor_sr1_bit6_quad_enable(struct spi_nor *nor); -int spi_nor_sr2_bit1_quad_enable(struct spi_nor *nor); -int spi_nor_sr2_bit7_quad_enable(struct spi_nor *nor); int spi_nor_read_id(struct spi_nor *nor, u8 naddr, u8 ndummy, u8 *id, enum spi_nor_protocol reg_proto); -int spi_nor_read_sr(struct spi_nor *nor, u8 *sr); int spi_nor_sr_ready(struct spi_nor *nor); -int spi_nor_read_cr(struct spi_nor *nor, u8 *cr); -int spi_nor_write_sr(struct spi_nor *nor, const u8 *sr, size_t len); -int spi_nor_write_sr_and_check(struct spi_nor *nor, u8 sr1); -int spi_nor_write_16bit_cr_and_check(struct spi_nor *nor, u8 cr); -int spi_nor_write_sr_cr_and_check(struct spi_nor *nor, const u8 *regs); +int spi_nor_read_sr_ll(struct spi_nor *nor, u8 opcode, u8 *sr, + unsigned int len); +int spi_nor_read_sr1(struct spi_nor *nor, u8 *sr1); +int spi_nor_read_sr2(struct spi_nor *nor, u8 *sr2); +int spi_nor_read_sr1_and_sr2(struct spi_nor *nor, u8 *sr); +int spi_nor_write_sr1(struct spi_nor *nor, const u8 *sr1); +int spi_nor_write_sr2(struct spi_nor *nor, const u8 *sr2); +int spi_nor_write_sr1_and_sr2_and_check(struct spi_nor *nor, const u8 *sr); ssize_t spi_nor_read_data(struct spi_nor *nor, loff_t from, size_t len, u8 *buf); @@ -680,10 +698,6 @@ void spi_nor_mask_erase_type(struct spi_nor_erase_type *erase); void spi_nor_init_uniform_erase_map(struct spi_nor_erase_map *map, u8 erase_mask, u64 flash_size); -int spi_nor_post_bfpt_fixups(struct spi_nor *nor, - const struct sfdp_parameter_header *bfpt_header, - const struct sfdp_bfpt *bfpt); - void spi_nor_init_default_locking_ops(struct spi_nor *nor); bool spi_nor_has_default_locking_ops(struct spi_nor *nor); void spi_nor_try_unlock_all(struct spi_nor *nor); diff --git a/drivers/mtd/spi-nor/debugfs.c b/drivers/mtd/spi-nor/debugfs.c index 288e2866daed..3f8d586a7622 100644 --- a/drivers/mtd/spi-nor/debugfs.c +++ b/drivers/mtd/spi-nor/debugfs.c @@ -14,13 +14,10 @@ #define SNOR_F_NAME(name) [ilog2(SNOR_F_##name)] = #name static const char *const snor_f_names[] = { SNOR_F_NAME(HAS_SR_TB), - SNOR_F_NAME(NO_OP_CHIP_ERASE), SNOR_F_NAME(BROKEN_RESET), SNOR_F_NAME(4B_OPCODES), SNOR_F_NAME(HAS_4BAIT), SNOR_F_NAME(HAS_LOCK), - SNOR_F_NAME(HAS_16BIT_SR), - SNOR_F_NAME(NO_READ_CR), SNOR_F_NAME(HAS_SR_TB_BIT6), SNOR_F_NAME(HAS_4BIT_BP), SNOR_F_NAME(HAS_SR_BP3_BIT6), @@ -84,13 +81,12 @@ static int spi_nor_params_show(struct seq_file *s, void *data) struct spi_nor_flash_parameter *params = nor->params; struct spi_nor_erase_map *erase_map = ¶ms->erase_map; struct spi_nor_erase_region *region = erase_map->regions; - const struct flash_info *info = nor->info; char buf[16], *str; loff_t lock_start; u64 lock_length; unsigned int i; - seq_printf(s, "name\t\t%s\n", info->name); + seq_printf(s, "name\t\t%s\n", nor->partname); seq_printf(s, "id\t\t%*ph\n", SPI_NOR_MAX_ID_LEN, nor->id); string_get_size(params->size, 1, STRING_UNITS_2, buf, sizeof(buf)); seq_printf(s, "size\t\t%s\n", buf); @@ -99,7 +95,7 @@ static int spi_nor_params_show(struct seq_file *s, void *data) 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, + spi_nor_print_flags(s, params->flags, snor_f_names, ARRAY_SIZE(snor_f_names)); seq_puts(s, "\n"); @@ -108,8 +104,23 @@ static int spi_nor_params_show(struct seq_file *s, void *data) seq_printf(s, " dummy cycles\t%u\n", nor->read_dummy); seq_printf(s, " erase\t\t0x%02x\n", nor->erase_opcode); seq_printf(s, " program\t0x%02x\n", nor->program_opcode); - - switch (nor->cmd_ext_type) { + seq_printf(s, " SR1 read\t0x%02x\n", params->opcodes.read_sr1); + if (params->opcodes.read_sr2) + seq_printf(s, " SR2 read\t0x%02x\n", params->opcodes.read_sr2); + if (params->opcodes.write_sr1) + seq_printf(s, " SR1 write\t0x%02x\n", params->opcodes.write_sr1); + if (params->opcodes.write_sr2) + seq_printf(s, " SR2 write\t0x%02x\n", params->opcodes.write_sr2); + if (params->opcodes.write_sr1_and_sr2) + seq_printf(s, " SR1+SR2 write\t0x%02x\n", + params->opcodes.write_sr1_and_sr2); + + if (params->qe_mask[0] || params->qe_mask[1]) + seq_printf(s, " QE\t\tSR%d bit %d\n", + params->qe_mask[0] ? 1 : 2, + ffs(params->qe_mask[0] | params->qe_mask[1]) - 1); + + switch (params->cmd_ext_type) { case SPI_NOR_EXT_NONE: str = "none"; break; @@ -144,10 +155,8 @@ static int spi_nor_params_show(struct seq_file *s, void *data) } } - if (!(nor->flags & SNOR_F_NO_OP_CHIP_ERASE)) { - string_get_size(params->size, 1, STRING_UNITS_2, buf, sizeof(buf)); - seq_printf(s, " %02x (%s)\n", params->die_erase_opcode, buf); - } + string_get_size(params->size, 1, STRING_UNITS_2, buf, sizeof(buf)); + seq_printf(s, " %02x (%s)\n", params->opcodes.die_erase, buf); seq_puts(s, "\nsector map\n"); seq_puts(s, " region (in hex) | erase mask | overlaid\n"); diff --git a/drivers/mtd/spi-nor/everspin.c b/drivers/mtd/spi-nor/everspin.c index add37104d673..864dffba0e9c 100644 --- a/drivers/mtd/spi-nor/everspin.c +++ b/drivers/mtd/spi-nor/everspin.c @@ -44,9 +44,14 @@ static const struct spi_nor_fixups everspin_nor_fixups = { .default_init = everspin_nor_default_init, }; +static const struct spi_nor_fixup everspin_fixups[] = { + { .fixups = &everspin_nor_fixups }, +}; + const struct spi_nor_manufacturer spi_nor_everspin = { .name = "everspin", .parts = everspin_nor_parts, .nparts = ARRAY_SIZE(everspin_nor_parts), - .fixups = &everspin_nor_fixups, + .fixups = everspin_fixups, + .nfixups = ARRAY_SIZE(everspin_fixups), }; diff --git a/drivers/mtd/spi-nor/gigadevice.c b/drivers/mtd/spi-nor/gigadevice.c index ef1edd0add70..6a2e6ebda148 100644 --- a/drivers/mtd/spi-nor/gigadevice.c +++ b/drivers/mtd/spi-nor/gigadevice.c @@ -23,8 +23,10 @@ gd25q256_post_bfpt(struct spi_nor *nor, * GD25Q256E | SFDP_JESD216_MAJOR | SFDP_JESD216B_MINOR */ if (bfpt_header->major == SFDP_JESD216_MAJOR && - bfpt_header->minor == SFDP_JESD216_MINOR) - nor->params->quad_enable = spi_nor_sr1_bit6_quad_enable; + bfpt_header->minor == SFDP_JESD216_MINOR) { + nor->params->qe_mask[0] = BIT(6); + nor->params->qe_mask[1] = 0; + } return 0; } @@ -62,8 +64,6 @@ static const struct flash_info gigadevice_nor_parts[] = { .id = SNOR_ID(0xc8, 0x40, 0x19), .name = "gd25q256", .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6, - .fixups = &gd25q256_fixups, - .fixup_flags = SPI_NOR_4B_OPCODES, }, { .id = SNOR_ID(0xc8, 0x60, 0x16), .name = "gd25lq32", @@ -85,8 +85,15 @@ static const struct flash_info gigadevice_nor_parts[] = { }, }; +static const struct spi_nor_fixup gigadevice_fixups[] = { + { .id = SNOR_ID(0xc8, 0x40, 0x19), .fixups = &gd25q256_fixups, + .fixup_flags = SPI_NOR_4B_OPCODES }, +}; + const struct spi_nor_manufacturer spi_nor_gigadevice = { .name = "gigadevice", .parts = gigadevice_nor_parts, .nparts = ARRAY_SIZE(gigadevice_nor_parts), + .fixups = gigadevice_fixups, + .nfixups = ARRAY_SIZE(gigadevice_fixups), }; diff --git a/drivers/mtd/spi-nor/issi.c b/drivers/mtd/spi-nor/issi.c index 18d9a00aa22e..2f057d731df2 100644 --- a/drivers/mtd/spi-nor/issi.c +++ b/drivers/mtd/spi-nor/issi.c @@ -52,13 +52,11 @@ static const struct flash_info issi_nor_parts[] = { .sector_size = SZ_32K, .size = SZ_64K, .no_sfdp_flags = SECT_4K, - .fixups = &pm25lv_nor_fixups }, { .name = "pm25lv010", .sector_size = SZ_32K, .size = SZ_128K, .no_sfdp_flags = SECT_4K, - .fixups = &pm25lv_nor_fixups }, { .id = SNOR_ID(0x7f, 0x9d, 0x20), .name = "is25cd512", @@ -103,8 +101,6 @@ static const struct flash_info issi_nor_parts[] = { }, { .id = SNOR_ID(0x9d, 0x60, 0x19), .name = "is25lp256", - .fixups = &is25lp256_fixups, - .fixup_flags = SPI_NOR_4B_OPCODES, }, { .id = SNOR_ID(0x9d, 0x70, 0x16), .name = "is25wp032", @@ -124,23 +120,40 @@ static const struct flash_info issi_nor_parts[] = { .id = SNOR_ID(0x9d, 0x70, 0x19), .name = "is25wp256", .flags = SPI_NOR_QUAD_PP, - .fixups = &is25lp256_fixups, - .fixup_flags = SPI_NOR_4B_OPCODES, } }; static void issi_nor_default_init(struct spi_nor *nor) { - nor->params->quad_enable = spi_nor_sr1_bit6_quad_enable; + nor->params->qe_mask[0] = BIT(6); + nor->params->qe_mask[1] = 0; } static const struct spi_nor_fixups issi_fixups = { .default_init = issi_nor_default_init, }; +/* PM25LV parts have no JEDEC ID and are likely matched by name */ +static bool issi_pm25lv_match(const struct spi_nor *nor) +{ + const char *name = nor->info ? nor->info->name : NULL; + + return name && strstarts(name, "pm25lv"); +} + +static const struct spi_nor_fixup issi_fixup_list[] = { + { .fixups = &issi_fixups }, + { .match = issi_pm25lv_match, .fixups = &pm25lv_nor_fixups }, + { .id = SNOR_ID(0x9d, 0x60, 0x19), .fixups = &is25lp256_fixups, + .fixup_flags = SPI_NOR_4B_OPCODES }, + { .id = SNOR_ID(0x9d, 0x70, 0x19), .fixups = &is25lp256_fixups, + .fixup_flags = SPI_NOR_4B_OPCODES }, +}; + const struct spi_nor_manufacturer spi_nor_issi = { .name = "issi", .parts = issi_nor_parts, .nparts = ARRAY_SIZE(issi_nor_parts), - .fixups = &issi_fixups, + .fixups = issi_fixup_list, + .nfixups = ARRAY_SIZE(issi_fixup_list), }; diff --git a/drivers/mtd/spi-nor/macronix.c b/drivers/mtd/spi-nor/macronix.c index e97f5cbd9aad..b3fdb4b2fa1c 100644 --- a/drivers/mtd/spi-nor/macronix.c +++ b/drivers/mtd/spi-nor/macronix.c @@ -40,7 +40,7 @@ mx25l25635_post_bfpt_fixups(struct spi_nor *nor, * its BFPT table. */ if (bfpt->dwords[SFDP_DWORD(5)] & BFPT_DWORD5_FAST_READ_4_4_4) - nor->flags |= SNOR_F_4B_OPCODES; + nor->params->flags |= SNOR_F_4B_OPCODES; return 0; } @@ -65,11 +65,12 @@ mx25l3255e_late_init_fixups(struct spi_nor *nor) /* * SFDP of MX25L3255E is JESD216, which does not include the Quad - * Enable bit Requirement in BFPT. As a result, during BFPT parsing, - * the quad_enable method is not set to spi_nor_sr1_bit6_quad_enable. - * Therefore, it is necessary to correct this setting by late_init. + * Enable bit Requirement in BFPT. As a result, during BFPT parsing + * the quad_enable mask is reset. Therefore, it is necessary to + * correct this setting by late_init. */ - params->quad_enable = spi_nor_sr1_bit6_quad_enable; + params->qe_mask[0] = BIT(6); + params->qe_mask[1] = 0; /* * In addition, MX25L3255E also supports 1-4-4 page program in 3-byte @@ -83,6 +84,25 @@ mx25l3255e_late_init_fixups(struct spi_nor *nor) return 0; } +static int +mx25l12805d_4pp3b_post_sfdp_fixups(struct spi_nor *nor) +{ + struct spi_nor_flash_parameter *params = nor->params; + + /* + * JEDEC ID 0xc22018 is shared by MX25L12805D (no SFDP, no 4PP) and + * MX25L12833F/MX25L12845G (support SFDP and 4PP in 3-byte mode). + * The legacy 05D lacks SFDP and will not execute this hook. For + * the newer flashes, 3-byte 1-4-4 PP is not defined in SFDP, so + * we safely enable it here. + */ + params->hwcaps.mask |= SNOR_HWCAPS_PP_1_4_4; + spi_nor_set_pp_settings(¶ms->page_programs[SNOR_CMD_PP_1_4_4], + SPINOR_OP_PP_1_4_4, SNOR_PROTO_1_4_4); + + return 0; +} + static const struct spi_nor_fixups mx25l25635_fixups = { .post_bfpt = mx25l25635_post_bfpt_fixups, .post_sfdp = macronix_qpp4b_post_sfdp_fixups, @@ -96,6 +116,10 @@ static const struct spi_nor_fixups mx25l3255e_fixups = { .late_init = mx25l3255e_late_init_fixups, }; +static const struct spi_nor_fixups mx25l12805d_4pp3b_fixups = { + .post_sfdp = mx25l12805d_4pp3b_post_sfdp_fixups, +}; + static const struct flash_info macronix_nor_parts[] = { { .id = SNOR_ID(0xc2, 0x20, 0x10), @@ -130,26 +154,23 @@ static const struct flash_info macronix_nor_parts[] = { .size = SZ_8M, .no_sfdp_flags = SECT_4K, }, { - /* MX25L12805D */ + /* MX25L12805D, MX25L12833F, MX25L12845G */ .id = SNOR_ID(0xc2, 0x20, 0x18), + .size = SZ_16M, .flags = SPI_NOR_HAS_LOCK | SPI_NOR_4BIT_BP, + .no_sfdp_flags = SECT_4K, }, { /* MX25L25635E, MX25L25645G */ .id = SNOR_ID(0xc2, 0x20, 0x19), - .fixups = &mx25l25635_fixups }, { - /* MX66L51235F */ + /* MX25L51245G, MX25L51273G, MX66L51235F */ .id = SNOR_ID(0xc2, 0x20, 0x1a), - .fixup_flags = SPI_NOR_4B_OPCODES, - .fixups = ¯onix_qpp4b_fixups, }, { /* MX66L1G45G */ .id = SNOR_ID(0xc2, 0x20, 0x1b), - .fixups = ¯onix_qpp4b_fixups, }, { /* MX66L2G45G */ .id = SNOR_ID(0xc2, 0x20, 0x1c), - .fixups = ¯onix_qpp4b_fixups, }, { .id = SNOR_ID(0xc2, 0x23, 0x14), .name = "mx25v8035f", @@ -186,17 +207,14 @@ static const struct flash_info macronix_nor_parts[] = { .size = SZ_16M, .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, }, { - /* MX25U51245G */ + /* MX25U51245G, MX25U51293G */ .id = SNOR_ID(0xc2, 0x25, 0x3a), - .fixups = ¯onix_qpp4b_fixups, }, { - /* MX66U1G45G */ + /* MX66U1G45G, MX66U1G93G */ .id = SNOR_ID(0xc2, 0x25, 0x3b), - .fixups = ¯onix_qpp4b_fixups, }, { /* MX66U2G45G */ .id = SNOR_ID(0xc2, 0x25, 0x3c), - .fixups = ¯onix_qpp4b_fixups, }, { .id = SNOR_ID(0xc2, 0x26, 0x18), .name = "mx25l12855e", @@ -228,7 +246,6 @@ static const struct flash_info macronix_nor_parts[] = { }, { /* MX25L3255E */ .id = SNOR_ID(0xc2, 0x9e, 0x16), - .fixups = &mx25l3255e_fixups, }, /* * This spares us of adding new flash entries for flashes that can be @@ -314,7 +331,8 @@ static int macronix_nor_set_octal_dtr(struct spi_nor *nor, bool enable) static void macronix_nor_default_init(struct spi_nor *nor) { - nor->params->quad_enable = spi_nor_sr1_bit6_quad_enable; + nor->params->qe_mask[0] = BIT(6); + nor->params->qe_mask[1] = 0; } static int macronix_nor_late_init(struct spi_nor *nor) @@ -331,9 +349,24 @@ static const struct spi_nor_fixups macronix_nor_fixups = { .late_init = macronix_nor_late_init, }; +static const struct spi_nor_fixup macronix_fixups[] = { + { .fixups = ¯onix_nor_fixups }, + { .id = SNOR_ID(0xc2, 0x20, 0x18), .fixups = &mx25l12805d_4pp3b_fixups }, + { .id = SNOR_ID(0xc2, 0x20, 0x19), .fixups = &mx25l25635_fixups }, + { .id = SNOR_ID(0xc2, 0x20, 0x1a), .fixups = ¯onix_qpp4b_fixups, + .fixup_flags = SPI_NOR_4B_OPCODES }, + { .id = SNOR_ID(0xc2, 0x20, 0x1b), .fixups = ¯onix_qpp4b_fixups }, + { .id = SNOR_ID(0xc2, 0x20, 0x1c), .fixups = ¯onix_qpp4b_fixups }, + { .id = SNOR_ID(0xc2, 0x25, 0x3a), .fixups = ¯onix_qpp4b_fixups }, + { .id = SNOR_ID(0xc2, 0x25, 0x3b), .fixups = ¯onix_qpp4b_fixups }, + { .id = SNOR_ID(0xc2, 0x25, 0x3c), .fixups = ¯onix_qpp4b_fixups }, + { .id = SNOR_ID(0xc2, 0x9e, 0x16), .fixups = &mx25l3255e_fixups }, +}; + const struct spi_nor_manufacturer spi_nor_macronix = { .name = "macronix", .parts = macronix_nor_parts, .nparts = ARRAY_SIZE(macronix_nor_parts), - .fixups = ¯onix_nor_fixups, + .fixups = macronix_fixups, + .nfixups = ARRAY_SIZE(macronix_fixups), }; diff --git a/drivers/mtd/spi-nor/micron-st.c b/drivers/mtd/spi-nor/micron-st.c index c75b0a1cd567..f97f2dead8c2 100644 --- a/drivers/mtd/spi-nor/micron-st.c +++ b/drivers/mtd/spi-nor/micron-st.c @@ -131,7 +131,7 @@ static int micron_st_nor_four_die_late_init(struct spi_nor *nor) { struct spi_nor_flash_parameter *params = nor->params; - params->die_erase_opcode = SPINOR_OP_MT_DIE_ERASE; + params->opcodes.die_erase = SPINOR_OP_MT_DIE_ERASE; params->n_dice = 4; /* @@ -147,7 +147,7 @@ static int micron_st_nor_two_die_late_init(struct spi_nor *nor) { struct spi_nor_flash_parameter *params = nor->params; - params->die_erase_opcode = SPINOR_OP_MT_DIE_ERASE; + params->opcodes.die_erase = SPINOR_OP_MT_DIE_ERASE; params->n_dice = 2; /* @@ -177,7 +177,7 @@ static int mt35xu512aba_post_sfdp_fixup(struct spi_nor *nor) spi_nor_set_pp_settings(&nor->params->page_programs[SNOR_CMD_PP_8_8_8_DTR], SPINOR_OP_PP_4B, SNOR_PROTO_8_8_8_DTR); - nor->cmd_ext_type = SPI_NOR_EXT_REPEAT; + nor->params->cmd_ext_type = SPI_NOR_EXT_REPEAT; nor->params->rdsr_dummy = 8; nor->params->rdsr_addr_nbytes = 0; @@ -205,14 +205,10 @@ static const struct flash_info micron_nor_parts[] = { /* MT35XU512ABA */ .id = SNOR_ID(0x2c, 0x5b, 0x1a), .mfr_flags = USE_FSR, - .fixup_flags = SPI_NOR_IO_MODE_EN_VOLATILE, - .fixups = &mt35xu512aba_fixups, }, { /* MT35XU01GBBA */ .id = SNOR_ID(0x2c, 0x5b, 0x1b), .mfr_flags = USE_FSR, - .fixup_flags = SPI_NOR_IO_MODE_EN_VOLATILE, - .fixups = &mt35_two_die_fixups, }, { .id = SNOR_ID(0x2c, 0x5b, 0x1c), .name = "mt35xu02g", @@ -220,8 +216,6 @@ static const struct flash_info micron_nor_parts[] = { .size = SZ_256M, .no_sfdp_flags = SECT_4K | SPI_NOR_OCTAL_READ, .mfr_flags = USE_FSR, - .fixup_flags = SPI_NOR_4B_OPCODES | SPI_NOR_IO_MODE_EN_VOLATILE, - .fixups = &mt35_two_die_fixups, }, }; @@ -229,10 +223,21 @@ static int mt25qu512a_post_bfpt_fixup(struct spi_nor *nor, const struct sfdp_parameter_header *bfpt_header, const struct sfdp_bfpt *bfpt) { - nor->flags &= ~SNOR_F_HAS_16BIT_SR; + nor->params->opcodes.write_sr1 = 0; return 0; } +/* + * n25q00a parts share the first same 3 ID bytes with mt25qu01g. + * In order to not mix the fixups, further filter out using the part name. + */ +static bool n25q00a_match(const struct spi_nor *nor) +{ + const char *name = nor->info ? nor->info->name : NULL; + + return name && !strncmp(name, "n25q00a", 7); +} + static const struct spi_nor_fixups mt25qu512a_fixups = { .post_bfpt = mt25qu512a_post_bfpt_fixup, }; @@ -395,7 +400,6 @@ static const struct flash_info st_nor_parts[] = { .name = "mt25ql256a", .size = SZ_32M, .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, - .fixup_flags = SPI_NOR_4B_OPCODES, .mfr_flags = USE_FSR, }, { .id = SNOR_ID(0x20, 0xba, 0x19), @@ -408,7 +412,6 @@ static const struct flash_info st_nor_parts[] = { .name = "mt25ql512a", .size = SZ_64M, .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, - .fixup_flags = SPI_NOR_4B_OPCODES, .mfr_flags = USE_FSR, }, { .id = SNOR_ID(0x20, 0xba, 0x20), @@ -426,14 +429,12 @@ static const struct flash_info st_nor_parts[] = { SPI_NOR_BP3_SR_BIT6, .no_sfdp_flags = SECT_4K | SPI_NOR_QUAD_READ, .mfr_flags = USE_FSR, - .fixups = &n25q00_fixups, }, { .id = SNOR_ID(0x20, 0xba, 0x22), .name = "mt25ql02g", .size = SZ_256M, .no_sfdp_flags = SECT_4K | SPI_NOR_QUAD_READ, .mfr_flags = USE_FSR, - .fixups = &mt25q02_fixups, }, { .id = SNOR_ID(0x20, 0xbb, 0x15), .name = "n25q016a", @@ -466,7 +467,6 @@ static const struct flash_info st_nor_parts[] = { .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_4BIT_BP | SPI_NOR_BP3_SR_BIT6, .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, - .fixup_flags = SPI_NOR_4B_OPCODES, .mfr_flags = USE_FSR, }, { .id = SNOR_ID(0x20, 0xbb, 0x19), @@ -480,7 +480,6 @@ static const struct flash_info st_nor_parts[] = { .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_4BIT_BP | SPI_NOR_BP3_SR_BIT6, .mfr_flags = USE_FSR, - .fixups = &mt25qu512a_fixups, }, { .id = SNOR_ID(0x20, 0xbb, 0x20), .name = "n25q512a", @@ -493,21 +492,18 @@ static const struct flash_info st_nor_parts[] = { .id = SNOR_ID(0x20, 0xbb, 0x21, 0x10, 0x44, 0x00), .name = "mt25qu01g", .mfr_flags = USE_FSR, - .fixups = &mt25q01_fixups, }, { .id = SNOR_ID(0x20, 0xbb, 0x21), .name = "n25q00a", .size = SZ_128M, .no_sfdp_flags = SECT_4K | SPI_NOR_QUAD_READ, .mfr_flags = USE_FSR, - .fixups = &n25q00_fixups, }, { .id = SNOR_ID(0x20, 0xbb, 0x22), .name = "mt25qu02g", .size = SZ_256M, .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, .mfr_flags = USE_FSR, - .fixups = &mt25q02_fixups, } }; @@ -632,8 +628,8 @@ static int micron_st_nor_ready(struct spi_nor *nor) static void micron_st_nor_default_init(struct spi_nor *nor) { - nor->flags |= SNOR_F_HAS_LOCK; - nor->flags &= ~SNOR_F_HAS_16BIT_SR; + nor->params->flags |= SNOR_F_HAS_LOCK; + nor->params->opcodes.write_sr1 = 0; nor->params->quad_enable = NULL; } @@ -657,16 +653,44 @@ static const struct spi_nor_fixups micron_st_nor_fixups = { .late_init = micron_st_nor_late_init, }; +static const struct spi_nor_fixup micron_fixups[] = { + { .fixups = µn_st_nor_fixups }, + { .id = SNOR_ID(0x2c, 0x5b, 0x1a), .fixups = &mt35xu512aba_fixups, + .fixup_flags = SPI_NOR_IO_MODE_EN_VOLATILE }, + { .id = SNOR_ID(0x2c, 0x5b, 0x1b), .fixups = &mt35_two_die_fixups, + .fixup_flags = SPI_NOR_IO_MODE_EN_VOLATILE }, + { .id = SNOR_ID(0x2c, 0x5b, 0x1c), .fixups = &mt35_two_die_fixups, + .fixup_flags = SPI_NOR_4B_OPCODES | SPI_NOR_IO_MODE_EN_VOLATILE }, +}; + +static const struct spi_nor_fixup st_fixups[] = { + { .fixups = µn_st_nor_fixups }, + { .id = SNOR_ID(0x20, 0xba, 0x19, 0x10, 0x44, 0x00), + .fixup_flags = SPI_NOR_4B_OPCODES }, + { .id = SNOR_ID(0x20, 0xba, 0x20, 0x10, 0x44, 0x00), + .fixup_flags = SPI_NOR_4B_OPCODES }, + { .id = SNOR_ID(0x20, 0xbb, 0x19, 0x10, 0x44, 0x00), + .fixup_flags = SPI_NOR_4B_OPCODES }, + { .id = SNOR_ID(0x20, 0xba, 0x21), .fixups = &n25q00_fixups }, + { .id = SNOR_ID(0x20, 0xba, 0x22), .fixups = &mt25q02_fixups }, + { .id = SNOR_ID(0x20, 0xbb, 0x20, 0x10, 0x44, 0x00), .fixups = &mt25qu512a_fixups }, + { .id = SNOR_ID(0x20, 0xbb, 0x21, 0x10, 0x44, 0x00), .fixups = &mt25q01_fixups }, + { .id = SNOR_ID(0x20, 0xbb, 0x21), .match = n25q00a_match, .fixups = &n25q00_fixups }, + { .id = SNOR_ID(0x20, 0xbb, 0x22), .fixups = &mt25q02_fixups }, +}; + const struct spi_nor_manufacturer spi_nor_micron = { .name = "micron", .parts = micron_nor_parts, .nparts = ARRAY_SIZE(micron_nor_parts), - .fixups = µn_st_nor_fixups, + .fixups = micron_fixups, + .nfixups = ARRAY_SIZE(micron_fixups), }; const struct spi_nor_manufacturer spi_nor_st = { .name = "st", .parts = st_nor_parts, .nparts = ARRAY_SIZE(st_nor_parts), - .fixups = µn_st_nor_fixups, + .fixups = st_fixups, + .nfixups = ARRAY_SIZE(st_fixups), }; diff --git a/drivers/mtd/spi-nor/otp.c b/drivers/mtd/spi-nor/otp.c index 61c55227e15d..bb9a801b3ceb 100644 --- a/drivers/mtd/spi-nor/otp.c +++ b/drivers/mtd/spi-nor/otp.c @@ -175,24 +175,24 @@ static int spi_nor_otp_lock_bit_cr(unsigned int region) */ int spi_nor_otp_lock_sr2(struct spi_nor *nor, unsigned int region) { - u8 *cr = nor->bouncebuf; int ret, lock_bit; + u8 sr[2]; lock_bit = spi_nor_otp_lock_bit_cr(region); if (lock_bit < 0) return lock_bit; - ret = spi_nor_read_cr(nor, cr); + ret = spi_nor_read_sr1_and_sr2(nor, sr); if (ret) return ret; /* no need to write the register if region is already locked */ - if (cr[0] & lock_bit) + if (sr[1] & lock_bit) return 0; - cr[0] |= lock_bit; + sr[1] |= lock_bit; - return spi_nor_write_16bit_cr_and_check(nor, cr[0]); + return spi_nor_write_sr1_and_sr2_and_check(nor, sr); } /** @@ -207,18 +207,18 @@ int spi_nor_otp_lock_sr2(struct spi_nor *nor, unsigned int region) */ int spi_nor_otp_is_locked_sr2(struct spi_nor *nor, unsigned int region) { - u8 *cr = nor->bouncebuf; int ret, lock_bit; + u8 sr2; lock_bit = spi_nor_otp_lock_bit_cr(region); if (lock_bit < 0) return lock_bit; - ret = spi_nor_read_cr(nor, cr); + ret = spi_nor_read_sr2(nor, &sr2); if (ret) return ret; - return cr[0] & lock_bit; + return sr2 & lock_bit; } static loff_t spi_nor_otp_region_start(const struct spi_nor *nor, unsigned int region) diff --git a/drivers/mtd/spi-nor/sfdp.c b/drivers/mtd/spi-nor/sfdp.c index 4600983cb579..c21a6953db96 100644 --- a/drivers/mtd/spi-nor/sfdp.c +++ b/drivers/mtd/spi-nor/sfdp.c @@ -34,17 +34,6 @@ #define SFDP_SIGNATURE 0x50444653U -struct sfdp_header { - u32 signature; /* Ox50444653U <=> "SFDP" */ - u8 minor; - u8 major; - u8 nph; /* 0-base number of parameter headers */ - u8 unused; - - /* Basic Flash Parameter Table. */ - struct sfdp_parameter_header bfpt_header; -}; - /* Fast Read settings. */ struct sfdp_bfpt_read { /* The Fast Read x-y-z hardware capability in params->hwcaps.mask. */ @@ -402,6 +391,42 @@ static void spi_nor_regions_sort_erase_types(struct spi_nor_erase_map *map) } /** + * spi_nor_post_bfpt_fixups() - Updates the flash's parameters and settings + * after BFPT has been parsed. Called only for flashes that define JESD216 SFDP + * tables. + * @nor: pointer to a 'struct spi_nor' + * @bfpt_header: pointer to the 'struct sfdp_parameter_header' describing + * the Basic Flash Parameter Table length and version + * + * Used to tweak various flash parameters when information provided by the SFDP + * tables are wrong. + */ +static int spi_nor_post_bfpt_fixups(struct spi_nor *nor, + const struct sfdp_parameter_header *bfpt_header, + const struct sfdp_bfpt *bfpt) +{ + const struct spi_nor_fixup *fixups; + int ret; + unsigned int i; + + if (!nor->manufacturer || !nor->manufacturer->fixups) + return 0; + + fixups = nor->manufacturer->fixups; + + for (i = 0; i < nor->manufacturer->nfixups; i++) { + if (fixups[i].fixups && fixups[i].fixups->post_bfpt && + spi_nor_fixup_match(nor, &fixups[i])) { + ret = fixups[i].fixups->post_bfpt(nor, bfpt_header, bfpt); + if (ret) + return ret; + } + } + + return 0; +} + +/** * spi_nor_parse_bfpt() - read and parse the Basic Flash Parameter Table. * @nor: pointer to a 'struct spi_nor' * @bfpt_header: pointer to the 'struct sfdp_parameter_header' describing @@ -453,7 +478,7 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor, bfpt_header->length * sizeof(u32)); addr = SFDP_PARAM_HEADER_PTP(bfpt_header); memset(&bfpt, 0, sizeof(bfpt)); - err = spi_nor_read_sfdp_dma_unsafe(nor, addr, len, &bfpt); + err = spi_nor_read_sfdp_dma_unsafe(nor, addr, len, &bfpt); if (err < 0) return err; @@ -561,34 +586,50 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor, val >>= BFPT_DWORD11_PAGE_SIZE_SHIFT; params->page_size = 1U << val; + /* + * The standard declares various read and write status methods, some of + * them will be overloaded based on the QER field. + */ + params->opcodes.read_sr1 = SPINOR_OP_RDSR; + params->opcodes.read_sr2 = SPINOR_OP_RDCR; + params->opcodes.write_sr1 = SPINOR_OP_WRSR; + params->opcodes.write_sr1_and_sr2 = SPINOR_OP_WRSR; + + params->qe_mask[0] = 0; + params->qe_mask[1] = 0; + /* Quad Enable Requirements. */ switch (bfpt.dwords[SFDP_DWORD(15)] & BFPT_DWORD15_QER_MASK) { case BFPT_DWORD15_QER_NONE: - params->quad_enable = NULL; break; - case BFPT_DWORD15_QER_SR2_BIT1_BUGGY: + case BFPT_DWORD15_QER_SR2_BIT1_NO_1B_WR: /* * Writing only one byte to the Status Register has the * side-effect of clearing Status Register 2. */ + fallthrough; case BFPT_DWORD15_QER_SR2_BIT1_NO_RD: /* * Read Configuration Register (35h) instruction is not - * supported. + * supported. 16-bit writes expected. */ - nor->flags |= SNOR_F_HAS_16BIT_SR | SNOR_F_NO_READ_CR; - params->quad_enable = spi_nor_sr2_bit1_quad_enable; + params->opcodes.read_sr2 = 0; + params->opcodes.write_sr1 = 0; + params->qe_mask[1] = BIT(1); break; case BFPT_DWORD15_QER_SR1_BIT6: - nor->flags &= ~SNOR_F_HAS_16BIT_SR; - params->quad_enable = spi_nor_sr1_bit6_quad_enable; + params->opcodes.read_sr2 = 0; + params->opcodes.write_sr1_and_sr2 = 0; + params->qe_mask[0] = BIT(6); break; case BFPT_DWORD15_QER_SR2_BIT7: - nor->flags &= ~SNOR_F_HAS_16BIT_SR; - params->quad_enable = spi_nor_sr2_bit7_quad_enable; + params->opcodes.read_sr2 = SPINOR_OP_RDSR2; + params->opcodes.write_sr1_and_sr2 = 0; + params->opcodes.write_sr2 = SPINOR_OP_WRSR2; + params->qe_mask[1] = BIT(7); break; case BFPT_DWORD15_QER_SR2_BIT1: @@ -598,9 +639,14 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor, * Register 2, so let's be cautious and keep the default * assumption of a 16-bit Write Status (01h) command. */ - nor->flags |= SNOR_F_HAS_16BIT_SR; + params->opcodes.write_sr1 = 0; + params->qe_mask[1] = BIT(1); + break; - params->quad_enable = spi_nor_sr2_bit1_quad_enable; + case BFPT_DWORD15_QER_SR2_BIT1_1B: + params->qe_mask[1] = BIT(1); + params->opcodes.write_sr1_and_sr2 = 0; + params->opcodes.write_sr2 = SPINOR_OP_WRSR2_ALT; break; default: @@ -608,6 +654,10 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor, break; } + /* opcodes sanity check */ + WARN_ON(!params->opcodes.read_sr1 || + (!params->opcodes.write_sr1 && !params->opcodes.write_sr1_and_sr2)); + dword = bfpt.dwords[SFDP_DWORD(16)] & BFPT_DWORD16_4B_ADDR_MODE_MASK; if (SFDP_MASK_CHECK(dword, BFPT_DWORD16_4B_ADDR_MODE_BRWR)) params->set_4byte_addr_mode = spi_nor_set_4byte_addr_mode_brwr; @@ -620,7 +670,7 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor, /* Soft Reset support. */ if (bfpt.dwords[SFDP_DWORD(16)] & BFPT_DWORD16_SWRST_EN_RST) - nor->flags |= SNOR_F_SOFT_RESET; + params->flags |= SNOR_F_SOFT_RESET; /* Stop here if not JESD216 rev C or later. */ if (bfpt_header->length == BFPT_DWORD_MAX_JESD216B) @@ -655,11 +705,11 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor, /* 8D-8D-8D command extension. */ switch (bfpt.dwords[SFDP_DWORD(18)] & BFPT_DWORD18_CMD_EXT_MASK) { case BFPT_DWORD18_CMD_EXT_REP: - nor->cmd_ext_type = SPI_NOR_EXT_REPEAT; + params->cmd_ext_type = SPI_NOR_EXT_REPEAT; break; case BFPT_DWORD18_CMD_EXT_INV: - nor->cmd_ext_type = SPI_NOR_EXT_INVERT; + params->cmd_ext_type = SPI_NOR_EXT_INVERT; break; case BFPT_DWORD18_CMD_EXT_RES: @@ -673,7 +723,7 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor, /* Byte order in 8D-8D-8D mode */ if (bfpt.dwords[SFDP_DWORD(18)] & BFPT_DWORD18_BYTE_ORDER_SWAPPED) - nor->flags |= SNOR_F_SWAP16; + params->flags |= SNOR_F_SWAP16; return spi_nor_post_bfpt_fixups(nor, bfpt_header, &bfpt); } @@ -702,12 +752,19 @@ static u8 spi_nor_smpt_addr_nbytes(const struct spi_nor *nor, const u32 settings static void spi_nor_smpt_read_dummy_fixups(const struct spi_nor *nor, u8 *read_dummy) { - if (nor->manufacturer && nor->manufacturer->fixups && - nor->manufacturer->fixups->smpt_read_dummy) - nor->manufacturer->fixups->smpt_read_dummy(nor, read_dummy); + const struct spi_nor_fixup *fixups; + unsigned int i; + + if (!nor->manufacturer || !nor->manufacturer->fixups) + return; - if (nor->info->fixups && nor->info->fixups->smpt_read_dummy) - nor->info->fixups->smpt_read_dummy(nor, read_dummy); + fixups = nor->manufacturer->fixups; + + for (i = 0; i < nor->manufacturer->nfixups; i++) { + if (fixups[i].fixups && fixups[i].fixups->smpt_read_dummy && + spi_nor_fixup_match(nor, &fixups[i])) + fixups[i].fixups->smpt_read_dummy(nor, read_dummy); + } } /** @@ -732,12 +789,19 @@ static u8 spi_nor_smpt_read_dummy(const struct spi_nor *nor, const u32 settings) static void spi_nor_smpt_map_id_fixups(const struct spi_nor *nor, u8 *map_id) { - if (nor->manufacturer && nor->manufacturer->fixups && - nor->manufacturer->fixups->smpt_map_id) - nor->manufacturer->fixups->smpt_map_id(nor, map_id); + const struct spi_nor_fixup *fixups; + unsigned int i; + + if (!nor->manufacturer || !nor->manufacturer->fixups) + return; + + fixups = nor->manufacturer->fixups; - if (nor->info->fixups && nor->info->fixups->smpt_map_id) - nor->info->fixups->smpt_map_id(nor, map_id); + for (i = 0; i < nor->manufacturer->nfixups; i++) { + if (fixups[i].fixups && fixups[i].fixups->smpt_map_id && + spi_nor_fixup_match(nor, &fixups[i])) + fixups[i].fixups->smpt_map_id(nor, map_id); + } } /** @@ -1160,7 +1224,7 @@ static int spi_nor_parse_4bait(struct spi_nor *nor, * SFDP compliant memories. */ params->addr_nbytes = 4; - nor->flags |= SNOR_F_4B_OPCODES | SNOR_F_HAS_4BAIT; + params->flags |= SNOR_F_4B_OPCODES | SNOR_F_HAS_4BAIT; /* fall through */ out: @@ -1175,6 +1239,7 @@ out: #define PROFILE1_DWORD5_DUMMY_166MHZ GENMASK(31, 27) #define PROFILE1_DWORD5_DUMMY_133MHZ GENMASK(21, 17) #define PROFILE1_DWORD5_DUMMY_100MHZ GENMASK(11, 7) +#define SFDP_PROFILE1_DWORD_MIN 5 /** * spi_nor_parse_profile1() - parse the xSPI Profile 1.0 table @@ -1192,6 +1257,9 @@ static int spi_nor_parse_profile1(struct spi_nor *nor, int ret; u8 dummy, opcode; + if (profile1_header->length < SFDP_PROFILE1_DWORD_MIN) + return -EINVAL; + len = profile1_header->length * sizeof(*dwords); dwords = kmalloc(len, GFP_KERNEL); if (!dwords) @@ -1262,6 +1330,7 @@ out: } #define SCCR_DWORD22_OCTAL_DTR_EN_VOLATILE BIT(31) +#define SFDP_SCCR_DWORD_MIN 22 /** * spi_nor_parse_sccr() - Parse the Status, Control and Configuration Register @@ -1280,6 +1349,9 @@ static int spi_nor_parse_sccr(struct spi_nor *nor, size_t len; int ret; + if (sccr_header->length < SFDP_SCCR_DWORD_MIN) + return -EINVAL; + len = sccr_header->length * sizeof(*dwords); dwords = kmalloc(len, GFP_KERNEL); if (!dwords) @@ -1306,7 +1378,7 @@ static int spi_nor_parse_sccr(struct spi_nor *nor, if (FIELD_GET(SCCR_DWORD22_OCTAL_DTR_EN_VOLATILE, dwords[SFDP_DWORD(22)])) - nor->flags |= SNOR_F_IO_MODE_EN_VOLATILE; + params->flags |= SNOR_F_IO_MODE_EN_VOLATILE; out: kfree(dwords); @@ -1381,17 +1453,23 @@ out: */ static int spi_nor_post_sfdp_fixups(struct spi_nor *nor) { + const struct spi_nor_fixup *fixups; + unsigned int i; int ret; - if (nor->manufacturer && nor->manufacturer->fixups && - nor->manufacturer->fixups->post_sfdp) { - ret = nor->manufacturer->fixups->post_sfdp(nor); - if (ret) - return ret; - } + if (!nor->manufacturer || !nor->manufacturer->fixups) + return 0; + + fixups = nor->manufacturer->fixups; - if (nor->info->fixups && nor->info->fixups->post_sfdp) - return nor->info->fixups->post_sfdp(nor); + for (i = 0; i < nor->manufacturer->nfixups; i++) { + if (fixups[i].fixups && fixups[i].fixups->post_sfdp && + spi_nor_fixup_match(nor, &fixups[i])) { + ret = fixups[i].fixups->post_sfdp(nor); + if (ret) + return ret; + } + } return 0; } @@ -1433,12 +1511,16 @@ int spi_nor_check_sfdp_signature(struct spi_nor *nor) * runtime the main parameters needed to perform basic SPI flash operations such * as Fast Read, Page Program or Sector Erase commands. * + * Because the parsing is optional, all the settings have to be reverted. IOW, + * nothing of struct spi_nor shall be changed. + * * Return: 0 on success, -errno otherwise. */ int spi_nor_parse_sfdp(struct spi_nor *nor) { const struct sfdp_parameter_header *param_header, *bfpt_header; struct sfdp_parameter_header *param_headers = NULL; + struct spi_nor_flash_parameter params, params2; struct sfdp_header header; struct device *dev = nor->dev; struct sfdp *sfdp; @@ -1446,6 +1528,12 @@ int spi_nor_parse_sfdp(struct spi_nor *nor) size_t psize; int i, err; + /* + * Get a backup of all the parameter to roll back to in case of an + * error. + */ + memcpy(¶ms, nor->params, sizeof(params)); + /* Get the SFDP header. */ err = spi_nor_read_sfdp_dma_unsafe(nor, 0, sizeof(header), &header); if (err < 0) @@ -1490,7 +1578,7 @@ int spi_nor_parse_sfdp(struct spi_nor *nor) psize, param_headers); if (err < 0) { dev_dbg(dev, "failed to read SFDP parameter headers\n"); - goto exit; + goto free_param_headers; } } @@ -1518,7 +1606,7 @@ int spi_nor_parse_sfdp(struct spi_nor *nor) sfdp = devm_kzalloc(dev, sizeof(*sfdp), GFP_KERNEL); if (!sfdp) { err = -ENOMEM; - goto exit; + goto free_param_headers; } /* @@ -1532,16 +1620,13 @@ int spi_nor_parse_sfdp(struct spi_nor *nor) sizeof(*sfdp->dwords), GFP_KERNEL); if (!sfdp->dwords) { err = -ENOMEM; - devm_kfree(dev, sfdp); - goto exit; + goto free_sfdp; } err = spi_nor_read_sfdp(nor, 0, sfdp_size, sfdp->dwords); if (err < 0) { dev_dbg(dev, "failed to read SFDP data\n"); - devm_kfree(dev, sfdp->dwords); - devm_kfree(dev, sfdp); - goto exit; + goto free_dwords; } nor->sfdp = sfdp; @@ -1563,10 +1648,11 @@ int spi_nor_parse_sfdp(struct spi_nor *nor) err = spi_nor_parse_bfpt(nor, bfpt_header); if (err) - goto exit; + goto clear_sfdp_ptr; /* Parse optional parameter tables. */ for (i = 0; i < header.nph; i++) { + memcpy(¶ms2, nor->params, sizeof(params2)); param_header = ¶m_headers[i]; switch (SFDP_PARAM_HEADER_ID(param_header)) { @@ -1600,15 +1686,43 @@ int spi_nor_parse_sfdp(struct spi_nor *nor) /* * Let's not drop all information we extracted so far * if optional table parsers fail. In case of failing, - * each optional parser is responsible to roll back to - * the previously known spi_nor data. + * roll back to the previously known + * spi_nor_flash_parameter data. */ err = 0; + memcpy(nor->params, ¶ms2, sizeof(*nor->params)); } } err = spi_nor_post_sfdp_fixups(nor); -exit: + if (err) + goto clear_sfdp_ptr; + + kfree(param_headers); + + return 0; + +clear_sfdp_ptr: + nor->sfdp = NULL; +free_dwords: + devm_kfree(dev, sfdp->dwords); +free_sfdp: + devm_kfree(dev, sfdp); +free_param_headers: kfree(param_headers); + if (err) + memcpy(nor->params, ¶ms, sizeof(*nor->params)); + return err; } + +/** + * spi_nor_sfdp_get_header() - retrieves the SFDP header + * @nor: pointer to a 'struct spi_nor' with a valid SFDP field + * + * Return: the cached SFDP header. + */ +struct sfdp_header *spi_nor_sfdp_get_header(const struct spi_nor *nor) +{ + return (struct sfdp_header *)nor->sfdp->dwords; +} diff --git a/drivers/mtd/spi-nor/sfdp.h b/drivers/mtd/spi-nor/sfdp.h index f74a0eb339ea..1e49ed548a7e 100644 --- a/drivers/mtd/spi-nor/sfdp.h +++ b/drivers/mtd/spi-nor/sfdp.h @@ -12,6 +12,7 @@ #define SFDP_JESD216_MINOR 0 #define SFDP_JESD216A_MINOR 5 #define SFDP_JESD216B_MINOR 6 +#define SFDP_JESD216F_MINOR 10 /* SFDP DWORDS are indexed from 1 but C arrays are indexed from 0. */ #define SFDP_DWORD(i) ((i) - 1) @@ -81,14 +82,21 @@ struct sfdp_bfpt { * instruction 35h. QE is set via Write Status instruction 01h with * two data bytes where bit 1 of the second byte is one. * [...] + * (from JESD216 rev F) + * - 110b: QE is bit 1 of status register 2. Status register 1 is read using + * Read Status instruction 05h. Status register2 is read using + * instruction 35h, and status register 3 is read using instruction 15h. + * QE is set via Write Status Register instruction 31h with one data + * byte. */ #define BFPT_DWORD15_QER_MASK GENMASK(22, 20) #define BFPT_DWORD15_QER_NONE (0x0UL << 20) /* Micron */ -#define BFPT_DWORD15_QER_SR2_BIT1_BUGGY (0x1UL << 20) +#define BFPT_DWORD15_QER_SR2_BIT1_NO_1B_WR (0x1UL << 20) #define BFPT_DWORD15_QER_SR1_BIT6 (0x2UL << 20) /* Macronix */ #define BFPT_DWORD15_QER_SR2_BIT7 (0x3UL << 20) #define BFPT_DWORD15_QER_SR2_BIT1_NO_RD (0x4UL << 20) #define BFPT_DWORD15_QER_SR2_BIT1 (0x5UL << 20) /* Spansion */ +#define BFPT_DWORD15_QER_SR2_BIT1_1B (0x6UL << 20) /* Winbond */ #define BFPT_DWORD16_EN4B_MASK GENMASK(31, 24) #define BFPT_DWORD16_EN4B_ALWAYS_4B BIT(30) @@ -141,4 +149,17 @@ struct sfdp_parameter_header { u8 id_msb; }; +struct sfdp_header { + u32 signature; /* Ox50444653U <=> "SFDP" */ + u8 minor; + u8 major; + u8 nph; /* 0-base number of parameter headers */ + u8 unused; + + /* Basic Flash Parameter Table. */ + struct sfdp_parameter_header bfpt_header; +}; + +struct sfdp_header *spi_nor_sfdp_get_header(const struct spi_nor *nor); + #endif /* __LINUX_MTD_SFDP_H */ diff --git a/drivers/mtd/spi-nor/spansion.c b/drivers/mtd/spi-nor/spansion.c index 65227d989de1..6938fe26969c 100644 --- a/drivers/mtd/spi-nor/spansion.c +++ b/drivers/mtd/spi-nor/spansion.c @@ -399,8 +399,9 @@ static int cypress_nor_determine_addr_mode_by_sr1(struct spi_nor *nor, nor->bouncebuf); bool is3byte, is4byte; int ret; + u8 sr; - ret = spi_nor_read_sr(nor, &nor->bouncebuf[1]); + ret = spi_nor_read_sr1(nor, &sr); if (ret) return ret; @@ -408,7 +409,7 @@ static int cypress_nor_determine_addr_mode_by_sr1(struct spi_nor *nor, if (ret) return ret; - is3byte = (nor->bouncebuf[0] == nor->bouncebuf[1]); + is3byte = (nor->bouncebuf[0] == sr); op = (struct spi_mem_op) CYPRESS_NOR_RD_ANY_REG_OP(4, SPINOR_REG_CYPRESS_STR1V, 0, @@ -417,7 +418,7 @@ static int cypress_nor_determine_addr_mode_by_sr1(struct spi_nor *nor, if (ret) return ret; - is4byte = (nor->bouncebuf[0] == nor->bouncebuf[1]); + is4byte = (nor->bouncebuf[0] == sr); if (is3byte == is4byte) return -EIO; @@ -537,7 +538,7 @@ static void cypress_nor_ecc_init(struct spi_nor *nor) * same ECC data unit without an erase are not allowed. */ nor->params->writesize = 16; - nor->flags |= SNOR_F_ECC; + nor->params->flags |= SNOR_F_ECC; } static int @@ -545,7 +546,6 @@ s25fs256t_post_bfpt_fixup(struct spi_nor *nor, const struct sfdp_parameter_header *bfpt_header, const struct sfdp_bfpt *bfpt) { - struct spi_mem_op op; int ret; /* Assign 4-byte address mode method that is not determined in BFPT */ @@ -555,19 +555,6 @@ s25fs256t_post_bfpt_fixup(struct spi_nor *nor, if (ret) return ret; - /* Read Architecture Configuration Register (ARCFN) */ - op = (struct spi_mem_op) - CYPRESS_NOR_RD_ANY_REG_OP(nor->params->addr_mode_nbytes, - SPINOR_REG_CYPRESS_ARCFN, 1, - nor->bouncebuf); - ret = spi_nor_read_any_reg(nor, &op, nor->reg_proto); - if (ret) - return ret; - - /* ARCFN value must be 0 if uniform sector is selected */ - if (nor->bouncebuf[0]) - return -ENODEV; - return 0; } @@ -598,6 +585,22 @@ static int s25fs256t_post_sfdp_fixup(struct spi_nor *nor) static int s25fs256t_late_init(struct spi_nor *nor) { + struct spi_mem_op op; + int ret; + + /* Read Architecture Configuration Register (ARCFN) */ + op = (struct spi_mem_op) + CYPRESS_NOR_RD_ANY_REG_OP(nor->params->addr_mode_nbytes, + SPINOR_REG_CYPRESS_ARCFN, 1, + nor->bouncebuf); + ret = spi_nor_read_any_reg(nor, &op, nor->reg_proto); + if (ret) + return ret; + + /* ARCFN value must be 0 if uniform sector is selected */ + if (nor->bouncebuf[0]) + return -ENODEV; + cypress_nor_ecc_init(nor); return 0; @@ -675,7 +678,7 @@ static int s25hx_t_late_init(struct spi_nor *nor) cypress_nor_ecc_init(nor); if (params->n_dice > 1) - params->die_erase_opcode = SPINOR_OP_CYPRESS_DIE_ERASE; + params->opcodes.die_erase = SPINOR_OP_CYPRESS_DIE_ERASE; return 0; } @@ -763,7 +766,7 @@ static int s28hx_t_late_init(struct spi_nor *nor) cypress_nor_ecc_init(nor); if (params->n_dice > 1) - params->die_erase_opcode = SPINOR_OP_CYPRESS_DIE_ERASE; + params->opcodes.die_erase = SPINOR_OP_CYPRESS_DIE_ERASE; return 0; } @@ -868,7 +871,7 @@ static const struct flash_info spansion_nor_parts[] = { .name = "s25fl256s0", .size = SZ_32M, .sector_size = SZ_256K, - .no_sfdp_flags = SPI_NOR_SKIP_SFDP | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, + .no_sfdp_flags = SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, .mfr_flags = USE_CLSR, }, { .id = SNOR_ID(0x01, 0x02, 0x19, 0x4d, 0x00, 0x81), @@ -904,7 +907,6 @@ static const struct flash_info spansion_nor_parts[] = { .sector_size = SZ_256K, .no_sfdp_flags = SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, .mfr_flags = USE_CLSR, - .fixups = &s25fs_s_nor_fixups, }, { .id = SNOR_ID(0x01, 0x20, 0x18, 0x03, 0x00), .name = "s25sl12800", @@ -940,7 +942,6 @@ static const struct flash_info spansion_nor_parts[] = { .size = SZ_16M, .no_sfdp_flags = SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, .mfr_flags = USE_CLSR, - .fixups = &s25fs_s_nor_fixups, }, { .id = SNOR_ID(0x01, 0x20, 0x18, 0x4d, 0x01), .name = "s25fl129p1", @@ -977,19 +978,16 @@ static const struct flash_info spansion_nor_parts[] = { .name = "s25fl064l", .size = SZ_8M, .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, - .fixup_flags = SPI_NOR_4B_OPCODES, }, { .id = SNOR_ID(0x01, 0x60, 0x18), .name = "s25fl128l", .size = SZ_16M, .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, - .fixup_flags = SPI_NOR_4B_OPCODES, }, { .id = SNOR_ID(0x01, 0x60, 0x19), .name = "s25fl256l", .size = SZ_32M, .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, - .fixup_flags = SPI_NOR_4B_OPCODES, }, { .id = SNOR_ID(0x04, 0x2c, 0xc2, 0x7f, 0x7f, 0x7f), .name = "cy15x104q", @@ -1000,76 +998,61 @@ static const struct flash_info spansion_nor_parts[] = { .id = SNOR_ID(0x34, 0x2a, 0x1a, 0x0f, 0x03, 0x90), .name = "s25hl512t", .mfr_flags = USE_CLPEF, - .fixups = &s25hx_t_fixups }, { .id = SNOR_ID(0x34, 0x2a, 0x1b, 0x0f, 0x03, 0x90), .name = "s25hl01gt", .mfr_flags = USE_CLPEF, - .fixups = &s25hx_t_fixups }, { .id = SNOR_ID(0x34, 0x2a, 0x1c, 0x0f, 0x00, 0x90), .name = "s25hl02gt", .mfr_flags = USE_CLPEF, - .fixups = &s25hx_t_fixups }, { .id = SNOR_ID(0x34, 0x2b, 0x19, 0x0f, 0x08, 0x90), .name = "s25fs256t", .mfr_flags = USE_CLPEF, - .fixups = &s25fs256t_fixups }, { .id = SNOR_ID(0x34, 0x2b, 0x1a, 0x0f, 0x03, 0x90), .name = "s25hs512t", .mfr_flags = USE_CLPEF, - .fixups = &s25hx_t_fixups }, { .id = SNOR_ID(0x34, 0x2b, 0x1b, 0x0f, 0x03, 0x90), .name = "s25hs01gt", .mfr_flags = USE_CLPEF, - .fixups = &s25hx_t_fixups }, { .id = SNOR_ID(0x34, 0x2b, 0x1c, 0x0f, 0x00, 0x90), .name = "s25hs02gt", .mfr_flags = USE_CLPEF, - .fixups = &s25hx_t_fixups }, { /* S28HL256T */ .id = SNOR_ID(0x34, 0x5a, 0x19), .mfr_flags = USE_CLPEF, - .fixups = &s28hx_t_fixups, }, { .id = SNOR_ID(0x34, 0x5a, 0x1a), .name = "s28hl512t", .mfr_flags = USE_CLPEF, - .fixups = &s28hx_t_fixups, }, { .id = SNOR_ID(0x34, 0x5a, 0x1b), .name = "s28hl01gt", .mfr_flags = USE_CLPEF, - .fixups = &s28hx_t_fixups, }, { /* S28HL02GT */ .id = SNOR_ID(0x34, 0x5a, 0x1c), .mfr_flags = USE_CLPEF, - .fixups = &s28hx_t_fixups, }, { .id = SNOR_ID(0x34, 0x5b, 0x19), .mfr_flags = USE_CLPEF, - .fixups = &s28hx_t_fixups, }, { .id = SNOR_ID(0x34, 0x5b, 0x1a), .name = "s28hs512t", .mfr_flags = USE_CLPEF, - .fixups = &s28hx_t_fixups, }, { .id = SNOR_ID(0x34, 0x5b, 0x1b), .name = "s28hs01gt", .mfr_flags = USE_CLPEF, - .fixups = &s28hx_t_fixups, }, { .id = SNOR_ID(0x34, 0x5b, 0x1c), .name = "s28hs02gt", .mfr_flags = USE_CLPEF, - .fixups = &s28hx_t_fixups, }, { .id = SNOR_ID(0xef, 0x40, 0x13), .name = "s25fl004k", @@ -1103,13 +1086,14 @@ static const struct flash_info spansion_nor_parts[] = { static int spansion_nor_sr_ready_and_clear(struct spi_nor *nor) { int ret; + u8 sr; - ret = spi_nor_read_sr(nor, nor->bouncebuf); + ret = spi_nor_read_sr1(nor, &sr); if (ret) return ret; - if (nor->bouncebuf[0] & (SR_E_ERR | SR_P_ERR)) { - if (nor->bouncebuf[0] & SR_E_ERR) + if (sr & (SR_E_ERR | SR_P_ERR)) { + if (sr & SR_E_ERR) dev_err(nor->dev, "Erase Error occurred\n"); else dev_err(nor->dev, "Programming Error occurred\n"); @@ -1129,7 +1113,7 @@ static int spansion_nor_sr_ready_and_clear(struct spi_nor *nor) return -EIO; } - return !(nor->bouncebuf[0] & SR_WIP); + return !(sr & SR_WIP); } static int spansion_nor_late_init(struct spi_nor *nor) @@ -1139,7 +1123,7 @@ static int spansion_nor_late_init(struct spi_nor *nor) u8 mfr_flags = nor->info->mfr_flags; if (params->size > SZ_16M) { - nor->flags |= SNOR_F_4B_OPCODES; + params->flags |= SNOR_F_4B_OPCODES; /* No small sector erase for 4-byte command set */ nor->erase_opcode = SPINOR_OP_SE; nor->mtd.erasesize = nor->info->sector_size ?: @@ -1168,9 +1152,34 @@ static const struct spi_nor_fixups spansion_nor_fixups = { .late_init = spansion_nor_late_init, }; +static const struct spi_nor_fixup spansion_fixups[] = { + { .fixups = &spansion_nor_fixups }, + { .id = SNOR_ID(0x01, 0x02, 0x20, 0x4d, 0x00, 0x81), .fixups = &s25fs_s_nor_fixups }, + { .id = SNOR_ID(0x01, 0x20, 0x18, 0x4d, 0x01, 0x81), .fixups = &s25fs_s_nor_fixups }, + { .id = SNOR_ID(0x01, 0x60, 0x17), .fixup_flags = SPI_NOR_4B_OPCODES }, + { .id = SNOR_ID(0x01, 0x60, 0x18), .fixup_flags = SPI_NOR_4B_OPCODES }, + { .id = SNOR_ID(0x01, 0x60, 0x19), .fixup_flags = SPI_NOR_4B_OPCODES }, + { .id = SNOR_ID(0x34, 0x2a, 0x1a, 0x0f, 0x03, 0x90), .fixups = &s25hx_t_fixups }, + { .id = SNOR_ID(0x34, 0x2a, 0x1b, 0x0f, 0x03, 0x90), .fixups = &s25hx_t_fixups }, + { .id = SNOR_ID(0x34, 0x2a, 0x1c, 0x0f, 0x00, 0x90), .fixups = &s25hx_t_fixups }, + { .id = SNOR_ID(0x34, 0x2b, 0x19, 0x0f, 0x08, 0x90), .fixups = &s25fs256t_fixups }, + { .id = SNOR_ID(0x34, 0x2b, 0x1a, 0x0f, 0x03, 0x90), .fixups = &s25hx_t_fixups }, + { .id = SNOR_ID(0x34, 0x2b, 0x1b, 0x0f, 0x03, 0x90), .fixups = &s25hx_t_fixups }, + { .id = SNOR_ID(0x34, 0x2b, 0x1c, 0x0f, 0x00, 0x90), .fixups = &s25hx_t_fixups }, + { .id = SNOR_ID(0x34, 0x5a, 0x19), .fixups = &s28hx_t_fixups }, + { .id = SNOR_ID(0x34, 0x5a, 0x1a), .fixups = &s28hx_t_fixups }, + { .id = SNOR_ID(0x34, 0x5a, 0x1b), .fixups = &s28hx_t_fixups }, + { .id = SNOR_ID(0x34, 0x5a, 0x1c), .fixups = &s28hx_t_fixups }, + { .id = SNOR_ID(0x34, 0x5b, 0x19), .fixups = &s28hx_t_fixups }, + { .id = SNOR_ID(0x34, 0x5b, 0x1a), .fixups = &s28hx_t_fixups }, + { .id = SNOR_ID(0x34, 0x5b, 0x1b), .fixups = &s28hx_t_fixups }, + { .id = SNOR_ID(0x34, 0x5b, 0x1c), .fixups = &s28hx_t_fixups }, +}; + const struct spi_nor_manufacturer spi_nor_spansion = { .name = "spansion", .parts = spansion_nor_parts, .nparts = ARRAY_SIZE(spansion_nor_parts), - .fixups = &spansion_nor_fixups, + .fixups = spansion_fixups, + .nfixups = ARRAY_SIZE(spansion_fixups), }; diff --git a/drivers/mtd/spi-nor/sst.c b/drivers/mtd/spi-nor/sst.c index db02c14ba16f..c460803b278b 100644 --- a/drivers/mtd/spi-nor/sst.c +++ b/drivers/mtd/spi-nor/sst.c @@ -21,16 +21,17 @@ static int sst26vf_nor_lock(struct spi_nor *nor, loff_t ofs, u64 len) static int sst26vf_nor_unlock(struct spi_nor *nor, loff_t ofs, u64 len) { int ret; + u8 cr; /* We only support unlocking the entire flash array. */ if (ofs != 0 || len != nor->params->size) return -EINVAL; - ret = spi_nor_read_cr(nor, nor->bouncebuf); + ret = spi_nor_read_sr2(nor, &cr); if (ret) return ret; - if (!(nor->bouncebuf[0] & SST26VF_CR_BPNV)) { + if (!(cr & SST26VF_CR_BPNV)) { dev_dbg(nor->dev, "Any block has been permanently locked\n"); return -EINVAL; } @@ -151,14 +152,12 @@ static const struct flash_info sst_nor_parts[] = { .id = SNOR_ID(0xbf, 0x26, 0x42), .name = "sst26vf032b", .flags = SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE, - .fixups = &sst26vf_nor_fixups, }, { .id = SNOR_ID(0xbf, 0x26, 0x43), .name = "sst26vf064b", .size = SZ_8M, .flags = SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE, .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, - .fixups = &sst26vf_nor_fixups, }, { .id = SNOR_ID(0xbf, 0x26, 0x51), .name = "sst26wf016b", @@ -277,9 +276,16 @@ static const struct spi_nor_fixups sst_nor_fixups = { .late_init = sst_nor_late_init, }; +static const struct spi_nor_fixup sst_fixups[] = { + { .fixups = &sst_nor_fixups }, + { .id = SNOR_ID(0xbf, 0x26, 0x42), .fixups = &sst26vf_nor_fixups }, + { .id = SNOR_ID(0xbf, 0x26, 0x43), .fixups = &sst26vf_nor_fixups }, +}; + const struct spi_nor_manufacturer spi_nor_sst = { .name = "sst", .parts = sst_nor_parts, .nparts = ARRAY_SIZE(sst_nor_parts), - .fixups = &sst_nor_fixups, + .fixups = sst_fixups, + .nfixups = ARRAY_SIZE(sst_fixups), }; diff --git a/drivers/mtd/spi-nor/swp.c b/drivers/mtd/spi-nor/swp.c index 235070b215d1..7e667e4ca84d 100644 --- a/drivers/mtd/spi-nor/swp.c +++ b/drivers/mtd/spi-nor/swp.c @@ -13,12 +13,13 @@ static u8 spi_nor_get_sr_bp_mask(struct spi_nor *nor) { + struct spi_nor_flash_parameter *params = nor->params; u8 mask = SR_BP2 | SR_BP1 | SR_BP0; - if (nor->flags & SNOR_F_HAS_SR_BP3_BIT6) + if (params->flags & SNOR_F_HAS_SR_BP3_BIT6) return mask | SR_BP3_BIT6; - if (nor->flags & SNOR_F_HAS_4BIT_BP) + if (params->flags & SNOR_F_HAS_4BIT_BP) return mask | SR_BP3; return mask; @@ -26,9 +27,11 @@ static u8 spi_nor_get_sr_bp_mask(struct spi_nor *nor) static u8 spi_nor_get_sr_tb_mask(struct spi_nor *nor) { - if (nor->flags & SNOR_F_HAS_SR_TB_BIT6) + struct spi_nor_flash_parameter *params = nor->params; + + if (params->flags & SNOR_F_HAS_SR_TB_BIT6) return SR_TB_BIT6; - else if (nor->flags & SNOR_F_HAS_SR_TB) + else if (params->flags & SNOR_F_HAS_SR_TB) return SR_TB_BIT5; else return 0; @@ -36,8 +39,10 @@ static u8 spi_nor_get_sr_tb_mask(struct spi_nor *nor) static u8 spi_nor_get_sr_cmp_mask(struct spi_nor *nor) { - if (!(nor->flags & SNOR_F_NO_READ_CR) && - nor->flags & SNOR_F_HAS_SR2_CMP_BIT6) + struct spi_nor_flash_parameter *params = nor->params; + + if (params->opcodes.read_sr2 && + params->flags & SNOR_F_HAS_SR2_CMP_BIT6) return SR2_CMP_BIT6; else return 0; @@ -67,15 +72,16 @@ u64 spi_nor_get_min_prot_length_sr(struct spi_nor *nor) void spi_nor_get_locked_range_sr(struct spi_nor *nor, const u8 *sr, loff_t *ofs, u64 *len) { + struct spi_nor_flash_parameter *params = nor->params; u64 min_prot_len; u8 bp_mask = spi_nor_get_sr_bp_mask(nor); u8 tb_mask = spi_nor_get_sr_tb_mask(nor); u8 cmp_mask = spi_nor_get_sr_cmp_mask(nor); u8 bp, val = sr[0] & bp_mask; - bool tb = (nor->flags & SNOR_F_HAS_SR_TB) ? sr[0] & tb_mask : 0; + bool tb = (params->flags & SNOR_F_HAS_SR_TB) ? sr[0] & tb_mask : 0; bool cmp = sr[1] & cmp_mask; - if (nor->flags & SNOR_F_HAS_SR_BP3_BIT6 && val & SR_BP3_BIT6) + if (params->flags & SNOR_F_HAS_SR_BP3_BIT6 && val & SR_BP3_BIT6) val = (val & ~SR_BP3_BIT6) | SR_BP3; bp = val >> SR_BP_SHIFT; @@ -153,10 +159,11 @@ static bool spi_nor_is_unlocked_sr(struct spi_nor *nor, loff_t ofs, u64 len, static int spi_nor_sr_set_bp_mask(struct spi_nor *nor, u8 *sr, u8 pow) { + struct spi_nor_flash_parameter *params = nor->params; u8 mask = spi_nor_get_sr_bp_mask(nor); u8 val = pow << SR_BP_SHIFT; - if (nor->flags & SNOR_F_HAS_SR_BP3_BIT6 && val & SR_BP3) + if (params->flags & SNOR_F_HAS_SR_BP3_BIT6 && val & SR_BP3) val = (val & ~SR_BP3) | SR_BP3_BIT6; if (val & ~mask) @@ -195,6 +202,45 @@ static int spi_nor_build_sr(struct spi_nor *nor, const u8 *old_sr, u8 *new_sr, } /* + * Make sure we do our best to guess SR2. This has historically only be needed + * for swp.c, so let's keep this extra carefulness in this file. + */ +static int spi_nor_read_sr2_careful(struct spi_nor *nor, u8 *sr2) +{ + struct spi_nor_flash_parameter *params = nor->params; + int ret; + + if (params->opcodes.read_sr2) { + ret = spi_nor_read_sr_ll(nor, params->opcodes.read_sr2, sr2, 1); + if (ret) + return ret; + } else if ((spi_nor_get_protocol_width(nor->read_proto) == 4 || + spi_nor_get_protocol_width(nor->write_proto) == 4) && + nor->params->quad_enable) { + /* + * Make sure the QE bit is persistently kept. qe_mask[1] will be + * 0 if the QE bit is in SR1. + */ + *sr2 = params->qe_mask[1]; + } else { + return 0; + } + + return 0; +} + +static int spi_nor_read_sr1_and_sr2_careful(struct spi_nor *nor, u8 *sr) +{ + int ret; + + ret = spi_nor_read_sr1(nor, &sr[0]); + if (ret) + return ret; + + return spi_nor_read_sr2_careful(nor, &sr[1]); +} + +/* * Keep a local cache containing all lock-related bits for debugfs use only. * This way, debugfs never needs to access the flash directly. */ @@ -207,17 +253,9 @@ void spi_nor_cache_sr_lock_bits(struct spi_nor *nor, u8 *sr) if (!sr) { - if (spi_nor_read_sr(nor, nor->bouncebuf)) + if (spi_nor_read_sr1_and_sr2_careful(nor, sr_cr)) return; - sr_cr[0] = nor->bouncebuf[0]; - - if (!(nor->flags & SNOR_F_NO_READ_CR)) { - if (spi_nor_read_cr(nor, nor->bouncebuf)) - return; - } - - sr_cr[1] = nor->bouncebuf[0]; sr = sr_cr; } @@ -261,32 +299,23 @@ void spi_nor_cache_sr_lock_bits(struct spi_nor *nor, u8 *sr) */ static int spi_nor_sr_lock(struct spi_nor *nor, loff_t ofs, u64 len) { + struct spi_nor_flash_parameter *params = nor->params; u64 min_prot_len = spi_nor_get_min_prot_length_sr(nor); u8 status_old[2] = {}, status_new[2] = {}, status_new_cmp[2] = {}; u8 *best_status_new = status_new; loff_t ofs_old, ofs_new, ofs_new_cmp; u64 len_old, len_new, len_new_cmp; loff_t lock_len; - bool can_be_top = true, can_be_bottom = nor->flags & SNOR_F_HAS_SR_TB, + bool can_be_top = true, can_be_bottom = params->flags & SNOR_F_HAS_SR_TB, can_be_cmp = spi_nor_get_sr_cmp_mask(nor); bool use_top; int ret; u8 pow; - ret = spi_nor_read_sr(nor, nor->bouncebuf); + ret = spi_nor_read_sr1_and_sr2_careful(nor, status_old); if (ret) return ret; - status_old[0] = nor->bouncebuf[0]; - - if (!(nor->flags & SNOR_F_NO_READ_CR)) { - ret = spi_nor_read_cr(nor, nor->bouncebuf); - if (ret) - return ret; - - status_old[1] = nor->bouncebuf[0]; - } - /* If nothing in our range is unlocked, we don't need to do anything */ if (spi_nor_is_locked_sr(nor, ofs, len, status_old)) return 0; @@ -313,7 +342,7 @@ static int spi_nor_sr_lock(struct spi_nor *nor, loff_t ofs, u64 len) lock_len = ofs + len; if (lock_len == nor->params->size) - pow = (nor->flags & SNOR_F_HAS_4BIT_BP) ? GENMASK(3, 0) : GENMASK(2, 0); + pow = (params->flags & SNOR_F_HAS_4BIT_BP) ? GENMASK(3, 0) : GENMASK(2, 0); else pow = ilog2(lock_len) - ilog2(min_prot_len) + 1; @@ -359,7 +388,7 @@ static int spi_nor_sr_lock(struct spi_nor *nor, loff_t ofs, u64 len) * wrongly tied to GND (that includes internal pull-downs). * WP# pin hard strapped to GND can be a valid use case. */ - if (!(nor->flags & SNOR_F_NO_WP)) + if (!(params->flags & SNOR_F_NO_WP)) best_status_new[0] |= SR_SRWD; spi_nor_get_locked_range_sr(nor, status_old, &ofs_old, &len_old); @@ -378,10 +407,7 @@ static int spi_nor_sr_lock(struct spi_nor *nor, loff_t ofs, u64 len) (ofs_old < ofs_new || (ofs_new + len_new) < (ofs_old + len_old))) return -EINVAL; - if (nor->flags & SNOR_F_NO_READ_CR) - ret = spi_nor_write_sr_and_check(nor, best_status_new[0]); - else - ret = spi_nor_write_sr_cr_and_check(nor, best_status_new); + ret = spi_nor_write_sr1_and_sr2_and_check(nor, best_status_new); if (ret) return ret; @@ -397,32 +423,23 @@ static int spi_nor_sr_lock(struct spi_nor *nor, loff_t ofs, u64 len) */ static int spi_nor_sr_unlock(struct spi_nor *nor, loff_t ofs, u64 len) { + struct spi_nor_flash_parameter *params = nor->params; u64 min_prot_len = spi_nor_get_min_prot_length_sr(nor); u8 status_old[2] = {}, status_new[2] = {}, status_new_cmp[2] = {}; u8 *best_status_new = status_new; loff_t ofs_old, ofs_new, ofs_new_cmp; u64 len_old, len_new, len_new_cmp; loff_t lock_len; - bool can_be_top = true, can_be_bottom = nor->flags & SNOR_F_HAS_SR_TB, + bool can_be_top = true, can_be_bottom = params->flags & SNOR_F_HAS_SR_TB, can_be_cmp = spi_nor_get_sr_cmp_mask(nor); bool use_top; int ret; u8 pow; - ret = spi_nor_read_sr(nor, nor->bouncebuf); + ret = spi_nor_read_sr1_and_sr2_careful(nor, status_old); if (ret) return ret; - status_old[0] = nor->bouncebuf[0]; - - if (!(nor->flags & SNOR_F_NO_READ_CR)) { - ret = spi_nor_read_cr(nor, nor->bouncebuf); - if (ret) - return ret; - - status_old[1] = nor->bouncebuf[0]; - } - /* If nothing in our range is locked, we don't need to do anything */ if (spi_nor_is_unlocked_sr(nor, ofs, len, status_old)) return 0; @@ -512,10 +529,7 @@ static int spi_nor_sr_unlock(struct spi_nor *nor, loff_t ofs, u64 len) (ofs_new < ofs_old || (ofs_old + len_old) < (ofs_new + len_new))) return -EINVAL; - if (nor->flags & SNOR_F_NO_READ_CR) - ret = spi_nor_write_sr_and_check(nor, best_status_new[0]); - else - ret = spi_nor_write_sr_cr_and_check(nor, best_status_new); + ret = spi_nor_write_sr1_and_sr2_and_check(nor, best_status_new); if (ret) return ret; @@ -536,20 +550,10 @@ static int spi_nor_sr_is_locked(struct spi_nor *nor, loff_t ofs, u64 len) u8 sr_cr[2] = {}; int ret; - ret = spi_nor_read_sr(nor, nor->bouncebuf); + ret = spi_nor_read_sr1_and_sr2_careful(nor, sr_cr); if (ret) return ret; - sr_cr[0] = nor->bouncebuf[0]; - - if (!(nor->flags & SNOR_F_NO_READ_CR)) { - ret = spi_nor_read_cr(nor, nor->bouncebuf); - if (ret) - return ret; - - sr_cr[1] = nor->bouncebuf[0]; - } - return spi_nor_is_locked_sr(nor, ofs, len, sr_cr); } @@ -628,9 +632,10 @@ static int spi_nor_is_locked(struct mtd_info *mtd, loff_t ofs, u64 len) */ void spi_nor_try_unlock_all(struct spi_nor *nor) { + struct spi_nor_flash_parameter *params = nor->params; int ret; - if (!(nor->flags & SNOR_F_HAS_LOCK)) + if (!(params->flags & SNOR_F_HAS_LOCK)) return; dev_dbg(nor->dev, "Unprotecting entire flash array\n"); diff --git a/drivers/mtd/spi-nor/sysfs.c b/drivers/mtd/spi-nor/sysfs.c index 643513ee891b..a2dabe4c6422 100644 --- a/drivers/mtd/spi-nor/sysfs.c +++ b/drivers/mtd/spi-nor/sysfs.c @@ -25,7 +25,7 @@ static ssize_t partname_show(struct device *dev, struct spi_mem *spimem = spi_get_drvdata(spi); struct spi_nor *nor = spi_mem_get_drvdata(spimem); - return sysfs_emit(buf, "%s\n", nor->info->name); + return sysfs_emit(buf, "%s\n", nor->partname); } static DEVICE_ATTR_RO(partname); @@ -78,7 +78,7 @@ static umode_t spi_nor_sysfs_is_visible(struct kobject *kobj, if (attr == &dev_attr_manufacturer.attr && !nor->manufacturer) return 0; - if (attr == &dev_attr_partname.attr && !nor->info->name) + if (attr == &dev_attr_partname.attr && !nor->partname) return 0; if (attr == &dev_attr_jedec_id.attr && !nor->info->id && !nor->id) return 0; diff --git a/drivers/mtd/spi-nor/winbond.c b/drivers/mtd/spi-nor/winbond.c index 8ebdbcec0b3f..f6321c1dc7dc 100644 --- a/drivers/mtd/spi-nor/winbond.c +++ b/drivers/mtd/spi-nor/winbond.c @@ -24,20 +24,59 @@ SPI_MEM_OP_NO_DUMMY, \ SPI_MEM_OP_DATA_OUT(1, buf, 0)) -static int -w25q128_post_bfpt_fixups(struct spi_nor *nor, - const struct sfdp_parameter_header *bfpt_header, - const struct sfdp_bfpt *bfpt) +static bool is_w25qxxrv(const struct spi_nor *nor) +{ + struct sfdp_header *sfdp_h = spi_nor_sfdp_get_header(nor); + + /* + * W25QxxRV chips re-use the same ID as the W25QxxJV family. + * + * Chips are very similar, W25QxxRV brings mostly performance and power + * consumption improvements. The RV family does not require the multi + * die fixup. + * + * They can be distinguished based on their SFDP minor revision: + * W25QxxJV: JESD216A, minor revision == 05h + * W25Q512/01/02JV: JESD216B, minor revision == 06h + * W25QxxRV: JESD216F, minor revision >= 0Ah + */ + return sfdp_h->minor >= SFDP_JESD216F_MINOR; +} + +static bool is_zd25q128c(const struct spi_nor *nor, + const struct sfdp_parameter_header *bfpt_header) { /* * Zetta ZD25Q128C is a clone of the Winbond device. But the encoded * size is really wrong. It seems that they confused Mbit with MiB. * Thus the flash is discovered as a 2MiB device. */ - if (bfpt_header->major == SFDP_JESD216_MAJOR && - bfpt_header->minor == SFDP_JESD216_MINOR && - nor->params->size == SZ_2M && - nor->params->erase_map.regions[0].size == SZ_2M) { + return bfpt_header->major == SFDP_JESD216_MAJOR && + bfpt_header->minor == SFDP_JESD216_MINOR && + nor->params->size == SZ_2M && + nor->params->erase_map.regions[0].size == SZ_2M; +} + +/* + * Since SFDP is populated after ->default_init(), the match functions using + * nor->sfdp as discriminant cannot be used for this specific early fixup. + */ +static bool winbond_jv_match(const struct spi_nor *nor) +{ + return !nor->sfdp || !is_w25qxxrv(nor); +} + +static bool winbond_rv_match(const struct spi_nor *nor) +{ + return nor->sfdp && is_w25qxxrv(nor); +} + +static int +w25q128_post_bfpt_fixups(struct spi_nor *nor, + const struct sfdp_parameter_header *bfpt_header, + const struct sfdp_bfpt *bfpt) +{ + if (is_zd25q128c(nor, bfpt_header) || winbond_rv_match(nor)) { nor->params->size = SZ_16M; nor->params->erase_map.regions[0].size = SZ_16M; } @@ -64,7 +103,7 @@ w25q256_post_bfpt_fixups(struct spi_nor *nor, */ if (bfpt_header->major == SFDP_JESD216_MAJOR && bfpt_header->minor == SFDP_JESD216A_MINOR) - nor->flags |= SNOR_F_4B_OPCODES; + nor->params->flags |= SNOR_F_4B_OPCODES; return 0; } @@ -73,26 +112,6 @@ static const struct spi_nor_fixups w25q256_fixups = { .post_bfpt = w25q256_post_bfpt_fixups, }; -static int -winbond_rdcr_post_bfpt_fixup(struct spi_nor *nor, - const struct sfdp_parameter_header *bfpt_header, - const struct sfdp_bfpt *bfpt) -{ - /* - * W25H02NW, unlike its W25H512NW nor W25H01NW cousins, improperly sets - * the QE BFPT configuration bits, indicating a non readable CR. This is - * both incorrect and impractical, as the chip features a CMP bit for its - * locking scheme that lays in the Control Register, and needs to be read. - */ - nor->flags &= ~SNOR_F_NO_READ_CR; - - return 0; -} - -static const struct spi_nor_fixups winbond_rdcr_fixup = { - .post_bfpt = winbond_rdcr_post_bfpt_fixup, -}; - /** * winbond_nor_select_die() - Set active die. * @nor: pointer to 'struct spi_nor'. @@ -166,6 +185,22 @@ static const struct spi_nor_fixups winbond_nor_multi_die_fixups = { .post_sfdp = winbond_nor_multi_die_post_sfdp_fixups, }; +static int winbond_nor_partname_post_sfdp_fixups(struct spi_nor *nor) +{ + /* + * W25QxxRV parts re-use the JEDEC IDs of the JV family. Their name + * being a legacy field, it is kept for the already established JV parts + * but must not be exposed by the newer RV ones. + */ + nor->partname = NULL; + + return 0; +} + +static const struct spi_nor_fixups winbond_nor_partname_fixups = { + .post_sfdp = winbond_nor_partname_post_sfdp_fixups, +}; + static const struct flash_info winbond_nor_parts[] = { { .id = SNOR_ID(0xef, 0x30, 0x10), @@ -218,38 +253,53 @@ static const struct flash_info winbond_nor_parts[] = { .size = SZ_1M, .no_sfdp_flags = SECT_4K, }, { + /* W25Q32JV-Q/N, W25Q32RV-Q/N */ .id = SNOR_ID(0xef, 0x40, 0x16), .name = "w25q32", .size = SZ_4M, .no_sfdp_flags = SECT_4K, + .flags = SPI_NOR_QUAD_PP | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_HAS_CMP, }, { + /* W25Q64JV-Q/N, W25Q64RV-Q/N */ .id = SNOR_ID(0xef, 0x40, 0x17), .name = "w25q64", .size = SZ_8M, .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, + .flags = SPI_NOR_QUAD_PP | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_HAS_CMP, }, { + /* W25Q128JV-Q/N, W25Q12RV-Q/N */ .id = SNOR_ID(0xef, 0x40, 0x18), /* Flavors w/ and w/o SFDP. */ .name = "w25q128", .size = SZ_16M, - .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB, .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, - .fixups = &w25q128_fixups, + .flags = SPI_NOR_QUAD_PP | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_HAS_CMP, }, { + /* W25Q256JV-Q/N, W25Q25RV-Q/N */ .id = SNOR_ID(0xef, 0x40, 0x19), .name = "w25q256", .size = SZ_32M, .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, - .fixups = &w25q256_fixups, + .flags = SPI_NOR_QUAD_PP | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | + SPI_NOR_TB_SR_BIT6 | SPI_NOR_4BIT_BP | SPI_NOR_HAS_CMP, }, { + /* W25Q512JV-Q/N, W25Q51RV-Q/N */ .id = SNOR_ID(0xef, 0x40, 0x20), .name = "w25q512jvq", .size = SZ_64M, .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, + .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 | + SPI_NOR_4BIT_BP | SPI_NOR_HAS_CMP, }, { - /* W25Q01JV */ + /* W25Q01JV-Q/N, W25Q01RV-Q/N */ .id = SNOR_ID(0xef, 0x40, 0x21), - .fixups = &winbond_nor_multi_die_fixups, + .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 | + SPI_NOR_4BIT_BP | SPI_NOR_HAS_CMP, + }, { + /* W25Q02RV-Q/N */ + .id = SNOR_ID(0xef, 0x40, 0x22), + .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 | + SPI_NOR_4BIT_BP | SPI_NOR_HAS_CMP, }, { .id = SNOR_ID(0xef, 0x50, 0x12), .name = "w25q20bw", @@ -285,58 +335,85 @@ static const struct flash_info winbond_nor_parts[] = { .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB, .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, }, { + /* W25Q128FW-G/Q, W25Q128JW-Q/N */ .id = SNOR_ID(0xef, 0x60, 0x18), .name = "w25q128fw", .size = SZ_16M, .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB, .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, }, { + /* W25Q256JW-Q/N */ .id = SNOR_ID(0xef, 0x60, 0x19), .name = "w25q256jw", .size = SZ_32M, .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 | SPI_NOR_4BIT_BP, .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, }, { + /* W25Q512NW-Q/N */ .id = SNOR_ID(0xef, 0x60, 0x20), .name = "w25q512nwq", .otp = SNOR_OTP(256, 3, 0x1000, 0x1000), }, { + /* W25Q01NW-Q/N */ + .id = SNOR_ID(0xef, 0x60, 0x21), + .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 | + SPI_NOR_4BIT_BP | SPI_NOR_HAS_CMP, + }, { + /* W25Q16JV-M */ .id = SNOR_ID(0xef, 0x70, 0x15), .name = "w25q16jv-im/jm", .size = SZ_2M, - .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB, .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, + .flags = SPI_NOR_QUAD_PP | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_HAS_CMP, }, { + /* W25Q32JV-M, W25Q32RV-M */ .id = SNOR_ID(0xef, 0x70, 0x16), .name = "w25q32jv", .size = SZ_4M, - .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB, .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, + .flags = SPI_NOR_QUAD_PP | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_HAS_CMP, }, { + /* W25Q64JV-M, W25Q64RV-M */ .id = SNOR_ID(0xef, 0x70, 0x17), .name = "w25q64jvm", .size = SZ_8M, - .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB, .no_sfdp_flags = SECT_4K, + .flags = SPI_NOR_QUAD_PP | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_HAS_CMP, }, { + /* W25Q128JV-M, W25Q12RV-M */ .id = SNOR_ID(0xef, 0x70, 0x18), .name = "w25q128jv", .size = SZ_16M, - .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB, .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, + .flags = SPI_NOR_QUAD_PP | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_HAS_CMP, }, { + /* W25Q256JV-M, W25Q25RV-M */ .id = SNOR_ID(0xef, 0x70, 0x19), .name = "w25q256jvm", + .flags = SPI_NOR_QUAD_PP | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | + SPI_NOR_TB_SR_BIT6 | SPI_NOR_4BIT_BP | SPI_NOR_HAS_CMP, }, { - /* W25Q02JV */ + /* W25Q512JV-M, W25Q51RV-M */ + .id = SNOR_ID(0xef, 0x70, 0x20), + .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 | + SPI_NOR_4BIT_BP | SPI_NOR_HAS_CMP, + }, { + /* W25Q01JV-M, W25Q01RV-M */ + .id = SNOR_ID(0xef, 0x70, 0x21), + .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 | + SPI_NOR_4BIT_BP | SPI_NOR_HAS_CMP, + }, { + /* W25Q02JV-M, W25Q02RV-M */ .id = SNOR_ID(0xef, 0x70, 0x22), - .fixups = &winbond_nor_multi_die_fixups, + .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 | + SPI_NOR_4BIT_BP | SPI_NOR_HAS_CMP, }, { .id = SNOR_ID(0xef, 0x71, 0x19), .name = "w25m512jv", .size = SZ_64M, .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, }, { + /* W25Q32JW-M */ .id = SNOR_ID(0xef, 0x80, 0x16), .name = "w25q32jwm", .size = SZ_4M, @@ -344,61 +421,63 @@ static const struct flash_info winbond_nor_parts[] = { .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, .otp = SNOR_OTP(256, 3, 0x1000, 0x1000), }, { + /* W25Q64JW-M */ .id = SNOR_ID(0xef, 0x80, 0x17), .name = "w25q64jwm", .size = SZ_8M, .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB, .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, }, { + /* W25Q128JW-M */ .id = SNOR_ID(0xef, 0x80, 0x18), .name = "w25q128jwm", .size = SZ_16M, .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB, .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, }, { + /* W25Q256JW-M */ .id = SNOR_ID(0xef, 0x80, 0x19), .name = "w25q256jwm", .size = SZ_32M, .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 | SPI_NOR_4BIT_BP, .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, }, { + /* W25Q512NW-M */ .id = SNOR_ID(0xef, 0x80, 0x20), .name = "w25q512nwm", .otp = SNOR_OTP(256, 3, 0x1000, 0x1000), }, { - /* W25Q01NWxxIQ */ - .id = SNOR_ID(0xef, 0x60, 0x21), - .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 | - SPI_NOR_4BIT_BP | SPI_NOR_HAS_CMP, - .fixups = &winbond_rdcr_fixup, - }, { - /* W25Q01NWxxIM */ + /* W25Q01NW-M */ .id = SNOR_ID(0xef, 0x80, 0x21), .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 | SPI_NOR_4BIT_BP | SPI_NOR_HAS_CMP, }, { - /* W25Q02NWxxIM */ + /* W25Q02NW-M */ .id = SNOR_ID(0xef, 0x80, 0x22), .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 | SPI_NOR_4BIT_BP | SPI_NOR_HAS_CMP, - .fixups = &winbond_rdcr_fixup, }, { - /* W25H512NWxxAM */ + /* W25H512NW-M */ .id = SNOR_ID(0xef, 0xa0, 0x20), .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 | SPI_NOR_4BIT_BP | SPI_NOR_HAS_CMP, }, { - /* W25H01NWxxAM */ + /* W25H01NW-M */ .id = SNOR_ID(0xef, 0xa0, 0x21), .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 | SPI_NOR_4BIT_BP | SPI_NOR_HAS_CMP, }, { - /* W25H02NWxxAM */ + /* W25H02NW-M */ .id = SNOR_ID(0xef, 0xa0, 0x22), .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 | SPI_NOR_4BIT_BP | SPI_NOR_HAS_CMP, - .fixups = &winbond_rdcr_fixup, - }, + }, { + /* + * Catch all entry to make sure all chips solely relying + * on SFDP still go through the manufacturer hooks. + */ + .id = SNOR_ID(0xef), + } }; /** @@ -490,6 +569,38 @@ static int winbond_nor_late_init(struct spi_nor *nor) */ params->set_4byte_addr_mode = winbond_nor_set_4byte_addr_mode; + /* + * All W25Q/W25H chips do set the BFPT_DWORD15_QER_SR2_BIT1_NO_RD bit in + * their SFDP tables. The historical spi-nor assumption in this case has + * been to declare CR reads as unsupported, whereas the Jedec + * specification doesn't clearly state that. In practice, all these + * chips do support reading back the CR, which is needed for SWP support, + * so make sure that capability remains enabled. + * In practice, only exclude the old W25X family (JEDEC ID: EF 30 xx) + * which actually does not support this feature. + */ + if (nor->id[1] > 0x30) + params->opcodes.read_sr2 = SPINOR_OP_RDCR; + + /* + * Winbond has reused many IDs, up to four times at this + * stage. In general, most of the chips with SFDP support are + * correctly described by the ID table, but the non-SFDP chips, + * however, are known to not feature as many capabilities. Make + * sure we filter out those capabilities to keep backward + * compatibility with these devices manufactured until ~2016. + */ + if (!nor->sfdp) { + struct spi_nor_flash_parameter *p = nor->params; + + /* SPI_NOR_QUAD_PP was unsupported */ + p->hwcaps.mask &= ~SNOR_HWCAPS_PP_1_1_4; + spi_nor_set_pp_settings(&p->page_programs[SNOR_CMD_PP_1_1_4], 0, 0); + + /* SPI_NOR_HAS_CMP was unsupported */ + nor->params->flags &= ~SNOR_F_HAS_SR2_CMP_BIT6; + } + return 0; } @@ -497,9 +608,29 @@ static const struct spi_nor_fixups winbond_nor_fixups = { .late_init = winbond_nor_late_init, }; +static const struct spi_nor_fixup winbond_fixups[] = { + { .fixups = &winbond_nor_fixups }, + { .id = SNOR_ID(0xef, 0x40, 0x18), .fixups = &w25q128_fixups }, + { .id = SNOR_ID(0xef, 0x40, 0x19), .fixups = &w25q256_fixups }, + { .id = SNOR_ID(0xef, 0x40), .match = winbond_rv_match, + .fixups = &winbond_nor_partname_fixups }, + { .id = SNOR_ID(0xef, 0x40, 0x21), .match = winbond_jv_match, + .fixups = &winbond_nor_multi_die_fixups }, + { .id = SNOR_ID(0xef, 0x40, 0x22), .match = winbond_jv_match, + .fixups = &winbond_nor_multi_die_fixups }, + { .id = SNOR_ID(0xef, 0x70), .match = winbond_rv_match, + .fixups = &winbond_nor_partname_fixups }, + { .id = SNOR_ID(0xef, 0x70, 0x18), .fixups = &w25q128_fixups }, + { .id = SNOR_ID(0xef, 0x70, 0x21), .match = winbond_jv_match, + .fixups = &winbond_nor_multi_die_fixups }, + { .id = SNOR_ID(0xef, 0x70, 0x22), .match = winbond_jv_match, + .fixups = &winbond_nor_multi_die_fixups }, +}; + const struct spi_nor_manufacturer spi_nor_winbond = { .name = "winbond", .parts = winbond_nor_parts, .nparts = ARRAY_SIZE(winbond_nor_parts), - .fixups = &winbond_nor_fixups, + .fixups = winbond_fixups, + .nfixups = ARRAY_SIZE(winbond_fixups), }; diff --git a/drivers/mtd/spi-nor/xmc.c b/drivers/mtd/spi-nor/xmc.c index d5a06054b0dd..d3b52eb93e2c 100644 --- a/drivers/mtd/spi-nor/xmc.c +++ b/drivers/mtd/spi-nor/xmc.c @@ -19,6 +19,7 @@ static const struct flash_info xmc_nor_parts[] = { .name = "XM25QH128A", .size = SZ_16M, .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, + .flags = SPI_NOR_HAS_LOCK | SPI_NOR_4BIT_BP | SPI_NOR_HAS_TB, }, }; diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index 4b92494827b1..b3e3c6b10186 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -25,6 +25,7 @@ #define SPINOR_OP_WRSR 0x01 /* Write status register 1 */ #define SPINOR_OP_RDSR2 0x3f /* Read status register 2 */ #define SPINOR_OP_WRSR2 0x3e /* Write status register 2 */ +#define SPINOR_OP_WRSR2_ALT 0x31 /* Write status register 2 (alternative) */ #define SPINOR_OP_READ 0x03 /* Read data bytes (low frequency) */ #define SPINOR_OP_READ_FAST 0x0b /* Read data bytes (high frequency) */ #define SPINOR_OP_READ_1_1_2 0x3b /* Read data bytes (Dual Output SPI) */ @@ -113,20 +114,16 @@ #define SR_E_ERR BIT(5) #define SR_P_ERR BIT(6) -#define SR1_QUAD_EN_BIT6 BIT(6) - #define SR_BP_SHIFT 2 /* Enhanced Volatile Configuration Register bits */ #define EVCR_QUAD_EN_MICRON BIT(7) /* Micron Quad I/O */ /* Status Register 2 bits. */ -#define SR2_QUAD_EN_BIT1 BIT(1) #define SR2_LB1 BIT(3) /* Security Register Lock Bit 1 */ #define SR2_LB2 BIT(4) /* Security Register Lock Bit 2 */ #define SR2_LB3 BIT(5) /* Security Register Lock Bit 3 */ #define SR2_CMP_BIT6 BIT(6) -#define SR2_QUAD_EN_BIT7 BIT(7) /* Supported SPI protocols */ #define SNOR_PROTO_INST_MASK GENMASK(23, 16) @@ -365,8 +362,6 @@ struct spi_nor_flash_parameter; * @read_dummy: the dummy needed by the read operation * @program_opcode: the program opcode * @sst_write_second: used by the SST write operation - * @flags: flag options for the current SPI NOR (SNOR_F_*) - * @cmd_ext_type: the command opcode extension type for DTR mode. * @read_proto: the SPI protocol for read operations * @write_proto: the SPI protocol for write operations * @reg_proto: the SPI protocol for read_reg/write_reg/erase operations @@ -398,6 +393,7 @@ struct spi_nor { u8 *id; const struct flash_info *info; const struct spi_nor_manufacturer *manufacturer; + const char *partname; u8 addr_nbytes; u8 erase_opcode; u8 read_opcode; @@ -407,8 +403,6 @@ struct spi_nor { enum spi_nor_protocol write_proto; enum spi_nor_protocol reg_proto; bool sst_write_second; - u32 flags; - enum spi_nor_cmd_ext cmd_ext_type; struct sfdp *sfdp; struct dentry *debugfs_root; u8 dfs_sr_cache[2]; |
