summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMiquel Raynal <miquel.raynal@bootlin.com>2026-10-23 18:50:09 +0200
committerMiquel Raynal <miquel.raynal@bootlin.com>2026-10-23 18:50:09 +0200
commit0ef71adafaa9b36a06dd5b823aebcd2fa9b9b9c2 (patch)
tree29da1e0ecbafefa2e05db502eeda3cd66b6b6b94
parent76ebe529a48518d0fa671091a8c2f1a01069aa49 (diff)
parentd7040c620d8b22f839af7750860b96e39cc58b7c (diff)
Merge tag 'spi-nor/for-7.4' into mtd/next
SPI NOR changes for 7.4 - Support for new Macronix and (lots of) Winbond flash devices - The addition of the Winbond flashes was accompanied by a big rewrite of the status register handling. There were many small helper functions to enable quad I/O mode in many different ways. That's now consolidated in one generic function. - The SFPD handling was reworked. It used to be only parsed for flashes present in the in-kernel database which had any of the multi-I/O bit set - or with the addition of generic flash driver for any unknown flashes. This left a gap where SFDP wasn't handled and it turns out newer flashes (with SFDP support) will reused IDs of just these old flashed. With the rework, SFDP is tried to be parsed for any flashes and will amend the information which is already present in the in-kernel database. - SFDP size limit was increased to 16KiB. There are flashes which have some larger gaps between tables and thus exceeds the former 4KiB limit. - Flash fixups are getting more and more common. Due to this, the fixups are now a separate list (per manufacturer) instead of being attached to a specific flash. A new .match() callback makes them more flexible, when one is applied. - 1-1-8 and 1-8-8 modes are now supported with larger flashes which needs 4byte addresses.
-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.c869
-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.c284
-rw-r--r--drivers/mtd/spi-nor/sfdp.h23
-rw-r--r--drivers/mtd/spi-nor/spansion.c109
-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.c320
-rw-r--r--drivers/mtd/spi-nor/xmc.c1
-rw-r--r--include/linux/mtd/spi-nor.h10
20 files changed, 1249 insertions, 982 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..4377b73e57fb 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,294 @@ 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, 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_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;
+ if (nor->params->opcodes.read_sr2)
+ ret = spi_nor_read_sr2(nor, &sr[1]);
+ else
+ sr[1] = 0;
- ret = spi_nor_read_cr(nor, &nor->bouncebuf[1]);
- if (ret)
- return ret;
+ return ret;
+}
- if (written_regs[1] != nor->bouncebuf[1]) {
- dev_dbg(nor->dev, "CR: read back test failed\n");
+/**
+ * spi_nor_write_sr1() - Write SR1 only
+ * Useful for:
+ * - Manufacturer drivers (since they know the chip SR layout)
+ *
+ * @nor: the spi_nor structure
+ * @sr1: pointer to a valid SR1 buffer
+ *
+ * Return 0 or errno.
+ */
+int spi_nor_write_sr1(struct spi_nor *nor, const u8 *sr1)
+{
+ if (WARN_ONCE(!nor->params->opcodes.write_sr1,
+ "Restricted helper use, write SR1 not supported"))
return -EIO;
- }
- return 0;
+ return spi_nor_write_sr_ll(nor, nor->params->opcodes.write_sr1, sr1, 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_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_and_check(struct spi_nor *nor, u8 sr1)
+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_and_check(nor, sr1);
+ 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, sr1);
+ return spi_nor_write_sr_ll(nor, nor->params->opcodes.write_sr2, sr2, 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_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.
*/
-int spi_nor_write_sr_cr_and_check(struct spi_nor *nor, const u8 *regs)
+static int spi_nor_write_sr1_and_sr2(struct spi_nor *nor, const u8 *sr)
{
- if (nor->flags & SNOR_F_HAS_16BIT_SR)
- return spi_nor_write_16bit_sr_cr_and_check(nor, regs);
+ struct spi_nor_flash_parameter *params = nor->params;
+ int ret;
+
+ 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;
- return spi_nor_write_sr1_and_check(nor, regs[0]);
+ if (params->opcodes.write_sr2)
+ return spi_nor_write_sr2(nor, &sr[1]);
+
+ return 0;
}
/**
- * 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_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.
*
- * 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)
+int spi_nor_write_sr1_and_sr2_and_check(struct spi_nor *nor, const u8 *sr)
{
+ u8 tmp[2];
int ret;
- ret = spi_nor_write_enable(nor);
+ ret = spi_nor_write_sr1_and_sr2(nor, sr);
if (ret)
return ret;
- if (nor->spimem) {
- struct spi_mem_op op = SPI_NOR_WRSR2_OP(sr2);
-
- spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
+ ret = spi_nor_read_sr1_and_sr2(nor, tmp);
+ if (ret)
+ return ret;
- ret = spi_mem_exec_op(nor->spimem, &op);
- } else {
- ret = spi_nor_controller_ops_write_reg(nor, SPINOR_OP_WRSR2,
- sr2, 1);
- }
+ if (sr[0] != tmp[0])
+ return -EIO;
- if (ret) {
- dev_dbg(nor->dev, "error %d writing SR2\n", ret);
- return ret;
- }
+ if (nor->params->opcodes.read_sr2 && sr[1] != tmp[1])
+ return -EIO;
- return spi_nor_wait_till_ready(nor);
+ 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 +1024,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 +1230,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 +1304,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 +1380,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 +1745,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 +1796,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 +2286,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 +2295,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 +2318,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 +2372,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 +2523,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 +2559,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 +2627,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 +2654,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 +2749,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 +2795,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 (fixup_flags & SPI_NOR_4B_OPCODES)
- nor->flags |= SNOR_F_4B_OPCODES;
+ if (!nor->manufacturer || !nor->manufacturer->fixups)
+ return;
- if (fixup_flags & SPI_NOR_IO_MODE_EN_VOLATILE)
- nor->flags |= SNOR_F_IO_MODE_EN_VOLATILE;
+ fixups = nor->manufacturer->fixups;
+
+ for (i = 0; i < nor->manufacturer->nfixups; i++) {
+ if (!fixups[i].fixup_flags ||
+ !spi_nor_fixup_match(nor, &fixups[i]))
+ continue;
+
+ 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 +2827,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 +2860,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 +2870,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 +2881,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 +2932,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 +2944,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 +2970,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 +3007,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 +3060,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 +3081,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 +3109,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 +3230,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 +3246,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 +3363,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 +3447,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..4a3f82e97cef 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. */
@@ -126,6 +115,12 @@ struct sfdp_bfpt_erase {
#define SFDP_4BAIT_DWORD_MAX 2
+/*
+ * Limit the total size of SFDP to a reasonable value to avoid allocating too
+ * much memory just of because the flash returned some insane values.
+ */
+#define SFDP_MAX_SIZE SZ_16K
+
struct sfdp_4bait {
/* The hardware capability. */
u32 hwcaps;
@@ -402,6 +397,45 @@ 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
+ * @bfpt: pointer to the 'struct sfdp_bfpt' to be fixed up
+ *
+ * Used to tweak various flash parameters when information provided by the SFDP
+ * tables are wrong.
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+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 +487,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 +595,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 +648,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 +663,11 @@ 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) ||
+ (!params->opcodes.read_sr2 && params->opcodes.write_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 +680,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 +715,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 +733,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 +762,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->info->fixups && nor->info->fixups->smpt_read_dummy)
- nor->info->fixups->smpt_read_dummy(nor, read_dummy);
+ if (!nor->manufacturer || !nor->manufacturer->fixups)
+ return;
+
+ 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 +799,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;
- if (nor->info->fixups && nor->info->fixups->smpt_map_id)
- nor->info->fixups->smpt_map_id(nor, map_id);
+ fixups = nor->manufacturer->fixups;
+
+ 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);
+ }
}
/**
@@ -1017,6 +1091,8 @@ static int spi_nor_parse_4bait(struct spi_nor *nor,
{ SNOR_HWCAPS_PP, BIT(6) },
{ SNOR_HWCAPS_PP_1_1_4, BIT(7) },
{ SNOR_HWCAPS_PP_1_4_4, BIT(8) },
+ { SNOR_HWCAPS_PP_1_1_8, BIT(23) },
+ { SNOR_HWCAPS_PP_1_8_8, BIT(24) },
};
static const struct sfdp_4bait erases[SNOR_ERASE_TYPE_MAX] = {
{ 0u /* not used */, BIT(9) },
@@ -1143,6 +1219,14 @@ static int spi_nor_parse_4bait(struct spi_nor *nor,
spi_nor_set_pp_settings(&params_pp[SNOR_CMD_PP_1_4_4],
SPINOR_OP_PP_1_4_4_4B,
SNOR_PROTO_1_4_4);
+ if (pp_hwcaps & SNOR_HWCAPS_PP_1_1_8)
+ spi_nor_set_pp_settings(&params_pp[SNOR_CMD_PP_1_1_8],
+ SPINOR_OP_PP_1_1_8_4B,
+ SNOR_PROTO_1_1_8);
+ if (pp_hwcaps & SNOR_HWCAPS_PP_1_8_8)
+ spi_nor_set_pp_settings(&params_pp[SNOR_CMD_PP_1_8_8],
+ SPINOR_OP_PP_1_8_8_4B,
+ SNOR_PROTO_1_8_8);
for (i = 0; i < SNOR_ERASE_TYPE_MAX; i++) {
if (erase_mask & BIT(i))
@@ -1160,7 +1244,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 +1259,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 +1277,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 +1350,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 +1369,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 +1398,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 +1473,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;
- if (nor->info->fixups && nor->info->fixups->post_sfdp)
- return nor->info->fixups->post_sfdp(nor);
+ fixups = nor->manufacturer->fixups;
+
+ 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 +1531,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 *save_params;
struct sfdp_header header;
struct device *dev = nor->dev;
struct sfdp *sfdp;
@@ -1446,15 +1548,26 @@ 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.
+ */
+ save_params = kmalloc_objs(*save_params, 2);
+ if (!save_params)
+ return -ENOMEM;
+ save_params[0] = *nor->params;
+
/* Get the SFDP header. */
err = spi_nor_read_sfdp_dma_unsafe(nor, 0, sizeof(header), &header);
if (err < 0)
- return err;
+ goto free_save_params;
/* Check the SFDP header version. */
if (le32_to_cpu(header.signature) != SFDP_SIGNATURE ||
- header.major != SFDP_JESD216_MAJOR)
- return -EINVAL;
+ header.major != SFDP_JESD216_MAJOR) {
+ err = -EINVAL;
+ goto free_save_params;
+ }
/*
* Verify that the first and only mandatory parameter header is a
@@ -1462,8 +1575,10 @@ int spi_nor_parse_sfdp(struct spi_nor *nor)
*/
bfpt_header = &header.bfpt_header;
if (SFDP_PARAM_HEADER_ID(bfpt_header) != SFDP_BFPT_ID ||
- bfpt_header->major != SFDP_JESD216_MAJOR)
- return -EINVAL;
+ bfpt_header->major != SFDP_JESD216_MAJOR) {
+ err = -EINVAL;
+ goto free_save_params;
+ }
sfdp_size = SFDP_PARAM_HEADER_PTP(bfpt_header) +
SFDP_PARAM_HEADER_PARAM_LEN(bfpt_header);
@@ -1483,14 +1598,16 @@ int spi_nor_parse_sfdp(struct spi_nor *nor)
psize = header.nph * sizeof(*param_headers);
param_headers = kmalloc(psize, GFP_KERNEL);
- if (!param_headers)
- return -ENOMEM;
+ if (!param_headers) {
+ err = -ENOMEM;
+ goto free_save_params;
+ }
err = spi_nor_read_sfdp(nor, sizeof(header),
psize, param_headers);
if (err < 0) {
dev_dbg(dev, "failed to read SFDP parameter headers\n");
- goto exit;
+ goto free_param_headers;
}
}
@@ -1505,20 +1622,16 @@ int spi_nor_parse_sfdp(struct spi_nor *nor)
SFDP_PARAM_HEADER_PARAM_LEN(param_header));
}
- /*
- * Limit the total size to a reasonable value to avoid allocating too
- * much memory just of because the flash returned some insane values.
- */
- if (sfdp_size > PAGE_SIZE) {
+ if (sfdp_size > SFDP_MAX_SIZE) {
dev_dbg(dev, "SFDP data (%zu) too big, truncating\n",
sfdp_size);
- sfdp_size = PAGE_SIZE;
+ sfdp_size = SFDP_MAX_SIZE;
}
sfdp = devm_kzalloc(dev, sizeof(*sfdp), GFP_KERNEL);
if (!sfdp) {
err = -ENOMEM;
- goto exit;
+ goto free_param_headers;
}
/*
@@ -1532,16 +1645,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 +1673,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++) {
+ save_params[1] = *nor->params;
param_header = &param_headers[i];
switch (SFDP_PARAM_HEADER_ID(param_header)) {
@@ -1600,15 +1711,46 @@ 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;
+ *nor->params = save_params[1];
}
}
err = spi_nor_post_sfdp_fixups(nor);
-exit:
+ if (err)
+ goto clear_sfdp_ptr;
+
+ kfree(param_headers);
+ kfree(save_params);
+
+ 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)
+ *nor->params = save_params[0];
+free_save_params:
+ kfree(save_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..bc50881d5648 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,13 +871,6 @@ 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,
- .mfr_flags = USE_CLSR,
- }, {
- .id = SNOR_ID(0x01, 0x02, 0x19, 0x4d, 0x00, 0x81),
- .name = "s25fs256s0",
- .size = SZ_32M,
- .sector_size = SZ_256K,
.no_sfdp_flags = SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
.mfr_flags = USE_CLSR,
}, {
@@ -904,7 +900,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 +935,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 +971,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 +991,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 +1079,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 +1106,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 +1116,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 +1145,35 @@ 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, 0x02, 0x19, 0x4d, 0x01, 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..51f6bcd3f8e5 100644
--- a/drivers/mtd/spi-nor/winbond.c
+++ b/drivers/mtd/spi-nor/winbond.c
@@ -24,20 +24,94 @@
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 bool is_w25qxxpw(const struct spi_nor *nor)
+{
+ struct sfdp_header *sfdp_h = spi_nor_sfdp_get_header(nor);
+
+ /*
+ * W25QxxPW chips re-use the same ID as the W25QxxJW/NW family.
+ *
+ * Chips are very similar, W25QxxPW brings mostly performance and power
+ * consumption improvements. One key difference in behaviour is the
+ * automatic 16-byte based error correction.
+ *
+ * They can be distinguished based on their SFDP minor revision:
+ * W25QxxJW: JESD216B, minor revision == 06h
+ * W25Q51/01/02NW: JESD216B, minor revision == 06h
+ * W25QxxPW: JESD216F, minor revision >= 0Ah
+ */
+ return sfdp_h->minor >= SFDP_JESD216F_MINOR;
+}
+
+static bool winbond_pw_match(const struct spi_nor *nor)
+{
+ return nor->sfdp && is_w25qxxpw(nor);
+}
+
+static bool winbond_pw_with_ecc_match(const struct spi_nor *nor)
+{
+ const struct spi_nor_id *id = nor->info->id;
+
+ if (!winbond_pw_match(nor))
+ return false;
+
+ /* W25QxxPW densities <= 32 Mbit (id[2] <= 0x16) do not have built-in ECC support */
+ return id->len == 3 && id->bytes[2] >= 0x17;
+}
+
+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 +138,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 +147,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 +220,38 @@ 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 int winbond_nor_ecc_configuration_post_sfdp_fixups(struct spi_nor *nor)
+{
+ /*
+ * PW chips feature automatic error correction. Non 16-byte aligned
+ * writes work, but disable error correction on the region until next erase.
+ */
+ nor->params->writesize = 16;
+ nor->params->flags |= SNOR_F_ECC;
+
+ return 0;
+}
+
+static const struct spi_nor_fixups winbond_nor_ecc_configuration_fixups = {
+ .post_sfdp = winbond_nor_ecc_configuration_post_sfdp_fixups,
+};
+
static const struct flash_info winbond_nor_parts[] = {
{
.id = SNOR_ID(0xef, 0x30, 0x10),
@@ -218,38 +304,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",
@@ -272,133 +373,166 @@ 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,
}, {
+ /* W25Q32DW-P/G, W25Q32FW-G/Q, W25Q32JW-Q/N, W25Q33PW-Q/N */
.id = SNOR_ID(0xef, 0x60, 0x16),
.name = "w25q32dw",
.size = SZ_4M,
- .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB,
+ .flags = SPI_NOR_QUAD_PP | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_HAS_CMP,
.no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
.otp = SNOR_OTP(256, 3, 0x1000, 0x1000),
}, {
+ /* W25Q64DW-P/G, W25Q64FW-G/Q, W25Q64JW-Q/N, W25Q64PW-Q/N */
.id = SNOR_ID(0xef, 0x60, 0x17),
.name = "w25q64dw",
.size = SZ_8M,
- .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB,
+ .flags = SPI_NOR_QUAD_PP | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_HAS_CMP,
.no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
}, {
+ /* W25Q128FW-G/Q, W25Q128JW-Q/N, W25Q12PW-Q/N */
.id = SNOR_ID(0xef, 0x60, 0x18),
.name = "w25q128fw",
.size = SZ_16M,
- .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB,
+ .flags = SPI_NOR_QUAD_PP | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_HAS_CMP,
.no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
}, {
+ /* W25Q256JW-Q/N, W25Q25PW-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,
+ .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 |
+ SPI_NOR_4BIT_BP | SPI_NOR_HAS_CMP,
.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, W25Q33PW-M */
.id = SNOR_ID(0xef, 0x80, 0x16),
.name = "w25q32jwm",
.size = SZ_4M,
- .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB,
+ .flags = SPI_NOR_QUAD_PP | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_HAS_CMP,
.no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
.otp = SNOR_OTP(256, 3, 0x1000, 0x1000),
}, {
+ /* W25Q64JW-M, W25Q64PW-M */
.id = SNOR_ID(0xef, 0x80, 0x17),
.name = "w25q64jwm",
.size = SZ_8M,
- .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB,
+ .flags = SPI_NOR_QUAD_PP | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_HAS_CMP,
.no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
}, {
+ /* W25Q128JW-M, W25Q12PW-M */
.id = SNOR_ID(0xef, 0x80, 0x18),
.name = "w25q128jwm",
.size = SZ_16M,
- .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB,
+ .flags = SPI_NOR_QUAD_PP | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_HAS_CMP,
.no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
}, {
+ /* W25Q256JW-M, W25Q25PW-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,
+ .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 |
+ SPI_NOR_4BIT_BP | SPI_NOR_HAS_CMP,
.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 +624,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 +663,37 @@ 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, 0x60), .match = winbond_pw_with_ecc_match,
+ .fixups = &winbond_nor_ecc_configuration_fixups },
+ { .id = SNOR_ID(0xef, 0x60), .match = winbond_pw_match,
+ .fixups = &winbond_nor_partname_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 },
+ { .id = SNOR_ID(0xef, 0x80), .match = winbond_pw_with_ecc_match,
+ .fixups = &winbond_nor_ecc_configuration_fixups },
+ { .id = SNOR_ID(0xef, 0x80), .match = winbond_pw_match,
+ .fixups = &winbond_nor_partname_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];