summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Brown <broonie@kernel.org>2026-09-07 13:26:32 +0100
committerMark Brown <broonie@kernel.org>2026-09-07 13:26:32 +0100
commit96e31f7b776524d8a1e9940e53fe97763523f145 (patch)
tree053786f6dca2a120f8c2c7bdd10d9fdbdee57d19
parent058d195055dc1b84eff3203c927148c459928709 (diff)
parent700bf34058ca7cd792236b1ba5caad3770d66208 (diff)
downloadlinux-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.rst2
-rw-r--r--drivers/mtd/spi-nor/atmel.c73
-rw-r--r--drivers/mtd/spi-nor/core.c864
-rw-r--r--drivers/mtd/spi-nor/core.h144
-rw-r--r--drivers/mtd/spi-nor/debugfs.c33
-rw-r--r--drivers/mtd/spi-nor/everspin.c7
-rw-r--r--drivers/mtd/spi-nor/gigadevice.c15
-rw-r--r--drivers/mtd/spi-nor/issi.c29
-rw-r--r--drivers/mtd/spi-nor/macronix.c73
-rw-r--r--drivers/mtd/spi-nor/micron-st.c70
-rw-r--r--drivers/mtd/spi-nor/otp.c16
-rw-r--r--drivers/mtd/spi-nor/sfdp.c230
-rw-r--r--drivers/mtd/spi-nor/sfdp.h23
-rw-r--r--drivers/mtd/spi-nor/spansion.c103
-rw-r--r--drivers/mtd/spi-nor/sst.c16
-rw-r--r--drivers/mtd/spi-nor/swp.c133
-rw-r--r--drivers/mtd/spi-nor/sysfs.c4
-rw-r--r--drivers/mtd/spi-nor/winbond.c241
-rw-r--r--drivers/mtd/spi-nor/xmc.c1
-rw-r--r--include/linux/mtd/spi-nor.h10
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 = &params->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(&params->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 = &macronix_qpp4b_fixups,
}, {
/* MX66L1G45G */
.id = SNOR_ID(0xc2, 0x20, 0x1b),
- .fixups = &macronix_qpp4b_fixups,
}, {
/* MX66L2G45G */
.id = SNOR_ID(0xc2, 0x20, 0x1c),
- .fixups = &macronix_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 = &macronix_qpp4b_fixups,
}, {
- /* MX66U1G45G */
+ /* MX66U1G45G, MX66U1G93G */
.id = SNOR_ID(0xc2, 0x25, 0x3b),
- .fixups = &macronix_qpp4b_fixups,
}, {
/* MX66U2G45G */
.id = SNOR_ID(0xc2, 0x25, 0x3c),
- .fixups = &macronix_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 = &macronix_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 = &macronix_qpp4b_fixups,
+ .fixup_flags = SPI_NOR_4B_OPCODES },
+ { .id = SNOR_ID(0xc2, 0x20, 0x1b), .fixups = &macronix_qpp4b_fixups },
+ { .id = SNOR_ID(0xc2, 0x20, 0x1c), .fixups = &macronix_qpp4b_fixups },
+ { .id = SNOR_ID(0xc2, 0x25, 0x3a), .fixups = &macronix_qpp4b_fixups },
+ { .id = SNOR_ID(0xc2, 0x25, 0x3b), .fixups = &macronix_qpp4b_fixups },
+ { .id = SNOR_ID(0xc2, 0x25, 0x3c), .fixups = &macronix_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 = &macronix_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 = &micron_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 = &micron_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 = &micron_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 = &micron_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(&params, 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(&params2, nor->params, sizeof(params2));
param_header = &param_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, &params2, 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, &params, 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];