summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMiquel Raynal <miquel.raynal@bootlin.com>2025-11-29 14:06:31 +0100
committerMiquel Raynal <miquel.raynal@bootlin.com>2025-11-29 14:06:31 +0100
commitde95c587981a9870a81e8e74c273aa72397d97ad (patch)
treed3792c1c892d222679726aa6ce8d0f83d88a7020
parent2158890a1af19e13ed222cda3623c8a11d3ec3b4 (diff)
parented26bd40df11ee438d623adc9c6cc2a7bf9c5dd3 (diff)
Merge tag 'spi-nor/for-6.19' of https://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux into mtd/next
SPI NOR changes for 6.19 Notable changes: - Fix SMPT parsing for S25FS-S flash family. They report variable dummy cycles for reads. This results in the default of 0 being used. This works for other Infineon chips, but not for the S25FS-S family. They need 8 dummy cycles. Add fixup hooks to specify that. Also add fixup hooks to fix incorrect map ID data in SFDP. - Add support for a bunch of Winbond flashes. Their block protection information is not discoverable, so they need to have an entry in the flash tables to describe that. - Some cleanups for Micron flash support. - Add support for Micron mt35xu01gbba. - Some SPI controllers like the Intel one on the PCI bus do not support the read CR opcode (0x35). Do not use the opcode if the controller does not support it. # -----BEGIN PGP SIGNATURE----- # # iHUEABYKAB0WIQQTlUWNzXGEo3bFmyIR4drqP028CQUCaSjP+QAKCRAR4drqP028 # CfGsAQC5Vj+FaeQHyY+yywqM5wxE+xj6mMCDNixd2FVYlf5b7wEA2/9bpiHjy3qi # 4MZmFJNcE+XsxReWDTBTZ6VbrjDlqg0= # =M+s4 # -----END PGP SIGNATURE----- # gpg: Signature made jeu. 27 nov. 2025 23:26:01 CET # gpg: using EDDSA key 1395458DCD7184A376C59B2211E1DAEA3F4DBC09 # gpg: Good signature from "Pratyush Yadav <p.yadav@ti.com>" [expired] # gpg: aka "Pratyush Yadav <me@yadavpratyush.com>" [expired] # gpg: p.yadav@ti.com: Verified 5 signatures in the past 3 years. Encrypted 0 messages. # gpg: me@yadavpratyush.com: Verified 5 signatures in the past 3 years. Encrypted # 0 messages. # gpg: Note: This key has expired! # Primary key fingerprint: 805C 3923 2FBE 108C 49E1 663C F650 3556 C11B 1CCD # Subkey fingerprint: 1395 458D CD71 84A3 76C5 9B22 11E1 DAEA 3F4D BC09
-rw-r--r--.mailmap1
-rw-r--r--drivers/mtd/spi-nor/core.c10
-rw-r--r--drivers/mtd/spi-nor/core.h6
-rw-r--r--drivers/mtd/spi-nor/micron-st.c101
-rw-r--r--drivers/mtd/spi-nor/sfdp.c30
-rw-r--r--drivers/mtd/spi-nor/spansion.c38
-rw-r--r--drivers/mtd/spi-nor/winbond.c24
7 files changed, 164 insertions, 46 deletions
diff --git a/.mailmap b/.mailmap
index d2edd256b19d..cf42344098ff 100644
--- a/.mailmap
+++ b/.mailmap
@@ -632,6 +632,7 @@ Peter Oruba <peter.oruba@amd.com>
Peter Oruba <peter@oruba.de>
Pierre-Louis Bossart <pierre-louis.bossart@linux.dev> <pierre-louis.bossart@linux.intel.com>
Pratyush Anand <pratyush.anand@gmail.com> <pratyush.anand@st.com>
+Pratyush Yadav <pratyush@kernel.org> <ptyadav@amazon.de>
Praveen BP <praveenbp@ti.com>
Pradeep Kumar Chitrapu <quic_pradeepc@quicinc.com> <pradeepc@codeaurora.org>
Prasad Sodagudi <quic_psodagud@quicinc.com> <psodagud@codeaurora.org>
diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c
index 20ea80450f22..d3f8a78efd3b 100644
--- a/drivers/mtd/spi-nor/core.c
+++ b/drivers/mtd/spi-nor/core.c
@@ -2459,6 +2459,16 @@ spi_nor_spimem_adjust_hwcaps(struct spi_nor *nor, u32 *hwcaps)
&params->page_programs[ppidx]))
*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);
+
+ spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
+
+ if (spi_nor_spimem_check_op(nor, &op))
+ nor->flags |= SNOR_F_NO_READ_CR;
+ }
}
/**
diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h
index ceff412f7d65..16b382d4f04f 100644
--- a/drivers/mtd/spi-nor/core.h
+++ b/drivers/mtd/spi-nor/core.h
@@ -409,6 +409,10 @@ struct spi_nor_flash_parameter {
* flash parameters when information provided by the flash_info
* table is incomplete or wrong.
* @post_bfpt: called after the BFPT table has been parsed
+ * @smpt_read_dummy: called during SMPT table is being parsed. Used to fix the
+ * number of dummy cycles in read register ops.
+ * @smpt_map_id: called after map ID in SMPT table has been determined for the
+ * case the map ID is wrong and needs to be fixed.
* @post_sfdp: called after SFDP has been parsed (is also called for SPI NORs
* that do not support RDSFDP). Typically used to tweak various
* parameters that could not be extracted by other means (i.e.
@@ -426,6 +430,8 @@ struct spi_nor_fixups {
int (*post_bfpt)(struct spi_nor *nor,
const struct sfdp_parameter_header *bfpt_header,
const struct sfdp_bfpt *bfpt);
+ void (*smpt_read_dummy)(const struct spi_nor *nor, u8 *read_dummy);
+ void (*smpt_map_id)(const struct spi_nor *nor, u8 *map_id);
int (*post_sfdp)(struct spi_nor *nor);
int (*late_init)(struct spi_nor *nor);
};
diff --git a/drivers/mtd/spi-nor/micron-st.c b/drivers/mtd/spi-nor/micron-st.c
index 187239ccd549..88033384a71e 100644
--- a/drivers/mtd/spi-nor/micron-st.c
+++ b/drivers/mtd/spi-nor/micron-st.c
@@ -127,9 +127,36 @@ static int micron_st_nor_set_octal_dtr(struct spi_nor *nor, bool enable)
micron_st_nor_octal_dtr_dis(nor);
}
-static void mt35xu512aba_default_init(struct spi_nor *nor)
+static int micron_st_nor_four_die_late_init(struct spi_nor *nor)
{
- nor->params->set_octal_dtr = micron_st_nor_set_octal_dtr;
+ struct spi_nor_flash_parameter *params = nor->params;
+
+ params->die_erase_opcode = SPINOR_OP_MT_DIE_ERASE;
+ params->n_dice = 4;
+
+ /*
+ * Unfortunately the die erase opcode does not have a 4-byte opcode
+ * correspondent for these flashes. The SFDP 4BAIT table fails to
+ * consider the die erase too. We're forced to enter in the 4 byte
+ * address mode in order to benefit of the die erase.
+ */
+ return spi_nor_set_4byte_addr_mode(nor, true);
+}
+
+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->n_dice = 2;
+
+ /*
+ * Unfortunately the die erase opcode does not have a 4-byte opcode
+ * correspondent for these flashes. The SFDP 4BAIT table fails to
+ * consider the die erase too. We're forced to enter in the 4 byte
+ * address mode in order to benefit of the die erase.
+ */
+ return spi_nor_set_4byte_addr_mode(nor, true);
}
static int mt35xu512aba_post_sfdp_fixup(struct spi_nor *nor)
@@ -155,22 +182,38 @@ static int mt35xu512aba_post_sfdp_fixup(struct spi_nor *nor)
}
static const struct spi_nor_fixups mt35xu512aba_fixups = {
- .default_init = mt35xu512aba_default_init,
.post_sfdp = mt35xu512aba_post_sfdp_fixup,
};
+static const struct spi_nor_fixups mt35xu01gbba_fixups = {
+ .post_sfdp = mt35xu512aba_post_sfdp_fixup,
+ .late_init = micron_st_nor_two_die_late_init,
+};
+
static const struct flash_info micron_nor_parts[] = {
{
+ /* MT35XU512ABA */
.id = SNOR_ID(0x2c, 0x5b, 0x1a),
- .name = "mt35xu512aba",
- .sector_size = SZ_128K,
- .size = SZ_64M,
- .no_sfdp_flags = SECT_4K | SPI_NOR_OCTAL_READ |
- SPI_NOR_OCTAL_DTR_READ | SPI_NOR_OCTAL_DTR_PP,
.mfr_flags = USE_FSR,
- .fixup_flags = SPI_NOR_4B_OPCODES | SPI_NOR_IO_MODE_EN_VOLATILE,
+ .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 = &mt35xu01gbba_fixups,
+ }, {
+ /*
+ * The MT35XU02GCBA flash device does not support chip erase,
+ * according to its datasheet. It supports die erase, which
+ * means the current driver implementation will likely need to
+ * be converted to use die erase. Furthermore, similar to the
+ * MT35XU01GBBA, the SPI_NOR_IO_MODE_EN_VOLATILE flag probably
+ * needs to be enabled.
+ *
+ * TODO: Fix these and test on real hardware.
+ */
.id = SNOR_ID(0x2c, 0x5b, 0x1c),
.name = "mt35xu02g",
.sector_size = SZ_128K,
@@ -193,48 +236,16 @@ static const struct spi_nor_fixups mt25qu512a_fixups = {
.post_bfpt = mt25qu512a_post_bfpt_fixup,
};
-static int 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->n_dice = 4;
-
- /*
- * Unfortunately the die erase opcode does not have a 4-byte opcode
- * correspondent for these flashes. The SFDP 4BAIT table fails to
- * consider the die erase too. We're forced to enter in the 4 byte
- * address mode in order to benefit of the die erase.
- */
- return spi_nor_set_4byte_addr_mode(nor, true);
-}
-
-static int 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->n_dice = 2;
-
- /*
- * Unfortunately the die erase opcode does not have a 4-byte opcode
- * correspondent for these flashes. The SFDP 4BAIT table fails to
- * consider the die erase too. We're forced to enter in the 4 byte
- * address mode in order to benefit of the die erase.
- */
- return spi_nor_set_4byte_addr_mode(nor, true);
-}
-
static const struct spi_nor_fixups n25q00_fixups = {
- .late_init = st_nor_four_die_late_init,
+ .late_init = micron_st_nor_four_die_late_init,
};
static const struct spi_nor_fixups mt25q01_fixups = {
- .late_init = st_nor_two_die_late_init,
+ .late_init = micron_st_nor_two_die_late_init,
};
static const struct spi_nor_fixups mt25q02_fixups = {
- .late_init = st_nor_four_die_late_init,
+ .late_init = micron_st_nor_four_die_late_init,
};
static const struct flash_info st_nor_parts[] = {
@@ -635,6 +646,8 @@ static int micron_st_nor_late_init(struct spi_nor *nor)
if (!params->set_4byte_addr_mode)
params->set_4byte_addr_mode = spi_nor_set_4byte_addr_mode_wren_en4b_ex4b;
+ params->set_octal_dtr = micron_st_nor_set_octal_dtr;
+
return 0;
}
diff --git a/drivers/mtd/spi-nor/sfdp.c b/drivers/mtd/spi-nor/sfdp.c
index 21727f9a4ac6..a8324c2da0ac 100644
--- a/drivers/mtd/spi-nor/sfdp.c
+++ b/drivers/mtd/spi-nor/sfdp.c
@@ -699,6 +699,17 @@ 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);
+
+ if (nor->info->fixups && nor->info->fixups->smpt_read_dummy)
+ nor->info->fixups->smpt_read_dummy(nor, read_dummy);
+}
+
/**
* spi_nor_smpt_read_dummy() - return the configuration detection command read
* latency, in clock cycles.
@@ -711,11 +722,24 @@ static u8 spi_nor_smpt_read_dummy(const struct spi_nor *nor, const u32 settings)
{
u8 read_dummy = SMPT_CMD_READ_DUMMY(settings);
- if (read_dummy == SMPT_CMD_READ_DUMMY_IS_VARIABLE)
- return nor->read_dummy;
+ if (read_dummy == SMPT_CMD_READ_DUMMY_IS_VARIABLE) {
+ read_dummy = nor->read_dummy;
+ spi_nor_smpt_read_dummy_fixups(nor, &read_dummy);
+ }
+
return read_dummy;
}
+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);
+
+ if (nor->info->fixups && nor->info->fixups->smpt_map_id)
+ nor->info->fixups->smpt_map_id(nor, map_id);
+}
+
/**
* spi_nor_get_map_in_use() - get the configuration map in use
* @nor: pointer to a 'struct spi_nor'
@@ -769,6 +793,8 @@ static const u32 *spi_nor_get_map_in_use(struct spi_nor *nor, const u32 *smpt,
map_id = map_id << 1 | !!(*buf & read_data_mask);
}
+ spi_nor_smpt_map_id_fixups(nor, &map_id);
+
/*
* If command descriptors are provided, they always precede map
* descriptors in the table. There is no need to start the iteration
diff --git a/drivers/mtd/spi-nor/spansion.c b/drivers/mtd/spi-nor/spansion.c
index a0296c871634..8498c7003d88 100644
--- a/drivers/mtd/spi-nor/spansion.c
+++ b/drivers/mtd/spi-nor/spansion.c
@@ -785,8 +785,46 @@ s25fs_s_nor_post_bfpt_fixups(struct spi_nor *nor,
return 0;
}
+static void s25fs_s_nor_smpt_read_dummy(const struct spi_nor *nor,
+ u8 *read_dummy)
+{
+ /*
+ * The configuration detection dwords in S25FS-S SMPT has 65h as
+ * command instruction and 'variable' as configuration detection command
+ * latency. Set 8 dummy cycles as it is factory default for 65h (read
+ * any register) op.
+ */
+ *read_dummy = 8;
+}
+
+static void s25fs_s_nor_smpt_map_id_dummy(const struct spi_nor *nor, u8 *map_id)
+{
+ /*
+ * The S25FS512S chip supports:
+ * - Hybrid sector option which has physical set of eight 4-KB sectors
+ * and one 224-KB sector at the top or bottom of address space with
+ * all remaining sectors of 256-KB
+ * - Uniform sector option which has uniform 256-KB sectors
+ *
+ * On the other hand, the datasheet rev.O Table 71 on page 153 JEDEC
+ * Sector Map Parameter Dword-6 Config. Detect-3 does use CR3NV[1] to
+ * discern 64-KB(CR3NV[1]=0) and 256-KB(CR3NV[1]=1) uniform sectors
+ * device configuration. And in section 7.5.5.1 Configuration Register 3
+ * Non-volatile (CR3NV) page 61, the CR3NV[1] is RFU Reserved for Future
+ * Use and set to 0, which means 64-KB uniform. Since the device does
+ * not support 64-KB uniform sectors in any configuration, parsing SMPT
+ * table cannot find a valid sector map entry and fails. Fix this up by
+ * setting SMPT by overwriting the CR3NV[1] value to 1, as the table
+ * expects.
+ */
+ if (nor->params->size == SZ_64M)
+ *map_id |= BIT(0);
+}
+
static const struct spi_nor_fixups s25fs_s_nor_fixups = {
.post_bfpt = s25fs_s_nor_post_bfpt_fixups,
+ .smpt_read_dummy = s25fs_s_nor_smpt_read_dummy,
+ .smpt_map_id = s25fs_s_nor_smpt_map_id_dummy,
};
static const struct flash_info spansion_nor_parts[] = {
diff --git a/drivers/mtd/spi-nor/winbond.c b/drivers/mtd/spi-nor/winbond.c
index 63a93c9eb917..fb855fe44733 100644
--- a/drivers/mtd/spi-nor/winbond.c
+++ b/drivers/mtd/spi-nor/winbond.c
@@ -343,6 +343,30 @@ static const struct flash_info winbond_nor_parts[] = {
.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,
+ }, {
+ /* W25Q01NWxxIM */
+ .id = SNOR_ID(0xef, 0x80, 0x21),
+ .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 | SPI_NOR_4BIT_BP,
+ }, {
+ /* W25Q02NWxxIM */
+ .id = SNOR_ID(0xef, 0x80, 0x22),
+ .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 | SPI_NOR_4BIT_BP,
+ }, {
+ /* W25H512NWxxAM */
+ .id = SNOR_ID(0xef, 0xa0, 0x20),
+ .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 | SPI_NOR_4BIT_BP,
+ }, {
+ /* W25H01NWxxAM */
+ .id = SNOR_ID(0xef, 0xa0, 0x21),
+ .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 | SPI_NOR_4BIT_BP,
+ }, {
+ /* W25H02NWxxAM */
+ .id = SNOR_ID(0xef, 0xa0, 0x22),
+ .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 | SPI_NOR_4BIT_BP,
},
};