Commit de95c587 authored by Miquel Raynal's avatar Miquel Raynal
Browse files

Merge tag 'spi-nor/for-6.19' of...

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
parents 2158890a ed26bd40
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -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>
+10 −0
Original line number Diff line number Diff line
@@ -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;
	}
}

/**
+6 −0
Original line number Diff line number Diff line
@@ -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);
};
+57 −44
Original line number Diff line number Diff line
@@ -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;
}

+28 −2
Original line number Diff line number Diff line
@@ -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
Loading