SPI NOR changes for 6.17

Notable changes:
 
 - Fix exiting 4-byte addressing on Infineon SEMPER flashes. These
   flashes do not support the standard EX4B opcode (E9h), and use a
   vendor-specific opcode (B8h) instead.
 
 - Fix unlocking of flashes that are write-protected at power-on. This
   was caused by using an uninitialized mtd_info in
   spi_nor_try_unlock_all().
 -----BEGIN PGP SIGNATURE-----
 
 iHUEABYKAB0WIQQTlUWNzXGEo3bFmyIR4drqP028CQUCaIIcbAAKCRAR4drqP028
 CcnDAQDj6YyRac/AqiKa+W0KL5n0AneymQtb/Pjkme2DaV++cwD/Slfk2ljsO5zN
 cAhKd6ZKOuu1BhKgHAFg577UNAxs2A8=
 =mXJz
 -----END PGP SIGNATURE-----

Merge tag 'spi-nor/for-6.17' into mtd/next

SPI NOR changes for 6.17

Notable changes:

- Fix exiting 4-byte addressing on Infineon SEMPER flashes. These
  flashes do not support the standard EX4B opcode (E9h), and use a
  vendor-specific opcode (B8h) instead.

- Fix unlocking of flashes that are write-protected at power-on. This
  was caused by using an uninitialized mtd_info in
  spi_nor_try_unlock_all().

Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
This commit is contained in:
Miquel Raynal 2025-07-31 18:52:04 +02:00
commit 3dd8aa0ef7
4 changed files with 46 additions and 18 deletions

View File

@ -20,7 +20,7 @@ properties:
- pattern: "^((((micron|spansion|st),)?\
(m25p(40|80|16|32|64|128)|\
n25q(32b|064|128a11|128a13|256a|512a|164k)))|\
atmel,at25df(321a|641|081a)|\
atmel,at(25|26)df(321a|641|081a)|\
everspin,mr25h(10|40|128|256)|\
(mxicy|macronix),mx25l(4005a|1606e|6405d|8005|12805d|25635e)|\
(mxicy|macronix),mx25u(4033|4035)|\

View File

@ -189,7 +189,7 @@ static int mt25qu512a_post_bfpt_fixup(struct spi_nor *nor,
return 0;
}
static struct spi_nor_fixups mt25qu512a_fixups = {
static const struct spi_nor_fixups mt25qu512a_fixups = {
.post_bfpt = mt25qu512a_post_bfpt_fixup,
};
@ -225,15 +225,15 @@ static int st_nor_two_die_late_init(struct spi_nor *nor)
return spi_nor_set_4byte_addr_mode(nor, true);
}
static struct spi_nor_fixups n25q00_fixups = {
static const struct spi_nor_fixups n25q00_fixups = {
.late_init = st_nor_four_die_late_init,
};
static struct spi_nor_fixups mt25q01_fixups = {
static const struct spi_nor_fixups mt25q01_fixups = {
.late_init = st_nor_two_die_late_init,
};
static struct spi_nor_fixups mt25q02_fixups = {
static const struct spi_nor_fixups mt25q02_fixups = {
.late_init = st_nor_four_die_late_init,
};

View File

@ -17,6 +17,7 @@
#define SPINOR_OP_CLSR 0x30 /* Clear status register 1 */
#define SPINOR_OP_CLPEF 0x82 /* Clear program/erase failure flags */
#define SPINOR_OP_CYPRESS_EX4B 0xB8 /* Exit 4-byte address mode */
#define SPINOR_OP_CYPRESS_DIE_ERASE 0x61 /* Chip (die) erase */
#define SPINOR_OP_RD_ANY_REG 0x65 /* Read any register */
#define SPINOR_OP_WR_ANY_REG 0x71 /* Write any register */
@ -58,6 +59,13 @@
SPI_MEM_OP_DUMMY(ndummy, 0), \
SPI_MEM_OP_DATA_IN(1, buf, 0))
#define CYPRESS_NOR_EN4B_EX4B_OP(enable) \
SPI_MEM_OP(SPI_MEM_OP_CMD(enable ? SPINOR_OP_EN4B : \
SPINOR_OP_CYPRESS_EX4B, 0), \
SPI_MEM_OP_NO_ADDR, \
SPI_MEM_OP_NO_DUMMY, \
SPI_MEM_OP_NO_DATA)
#define SPANSION_OP(opcode) \
SPI_MEM_OP(SPI_MEM_OP_CMD(opcode, 0), \
SPI_MEM_OP_NO_ADDR, \
@ -356,6 +364,20 @@ static int cypress_nor_quad_enable_volatile(struct spi_nor *nor)
return 0;
}
static int cypress_nor_set_4byte_addr_mode(struct spi_nor *nor, bool enable)
{
int ret;
struct spi_mem_op op = CYPRESS_NOR_EN4B_EX4B_OP(enable);
spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
ret = spi_mem_exec_op(nor->spimem, &op);
if (ret)
dev_dbg(nor->dev, "error %d setting 4-byte mode\n", ret);
return ret;
}
/**
* cypress_nor_determine_addr_mode_by_sr1() - Determine current address mode
* (3 or 4-byte) by querying status
@ -526,6 +548,9 @@ s25fs256t_post_bfpt_fixup(struct spi_nor *nor,
struct spi_mem_op op;
int ret;
/* Assign 4-byte address mode method that is not determined in BFPT */
nor->params->set_4byte_addr_mode = cypress_nor_set_4byte_addr_mode;
ret = cypress_nor_set_addr_mode_nbytes(nor);
if (ret)
return ret;
@ -578,7 +603,7 @@ static int s25fs256t_late_init(struct spi_nor *nor)
return 0;
}
static struct spi_nor_fixups s25fs256t_fixups = {
static const struct spi_nor_fixups s25fs256t_fixups = {
.post_bfpt = s25fs256t_post_bfpt_fixup,
.post_sfdp = s25fs256t_post_sfdp_fixup,
.late_init = s25fs256t_late_init,
@ -591,6 +616,9 @@ s25hx_t_post_bfpt_fixup(struct spi_nor *nor,
{
int ret;
/* Assign 4-byte address mode method that is not determined in BFPT */
nor->params->set_4byte_addr_mode = cypress_nor_set_4byte_addr_mode;
ret = cypress_nor_set_addr_mode_nbytes(nor);
if (ret)
return ret;
@ -650,7 +678,7 @@ static int s25hx_t_late_init(struct spi_nor *nor)
return 0;
}
static struct spi_nor_fixups s25hx_t_fixups = {
static const struct spi_nor_fixups s25hx_t_fixups = {
.post_bfpt = s25hx_t_post_bfpt_fixup,
.post_sfdp = s25hx_t_post_sfdp_fixup,
.late_init = s25hx_t_late_init,
@ -718,6 +746,9 @@ static int s28hx_t_post_bfpt_fixup(struct spi_nor *nor,
const struct sfdp_parameter_header *bfpt_header,
const struct sfdp_bfpt *bfpt)
{
/* Assign 4-byte address mode method that is not determined in BFPT */
nor->params->set_4byte_addr_mode = cypress_nor_set_4byte_addr_mode;
return cypress_nor_set_addr_mode_nbytes(nor);
}

View File

@ -56,7 +56,6 @@ static u64 spi_nor_get_min_prot_length_sr(struct spi_nor *nor)
static void spi_nor_get_locked_range_sr(struct spi_nor *nor, u8 sr, loff_t *ofs,
u64 *len)
{
struct mtd_info *mtd = &nor->mtd;
u64 min_prot_len;
u8 mask = spi_nor_get_sr_bp_mask(nor);
u8 tb_mask = spi_nor_get_sr_tb_mask(nor);
@ -77,13 +76,13 @@ static void spi_nor_get_locked_range_sr(struct spi_nor *nor, u8 sr, loff_t *ofs,
min_prot_len = spi_nor_get_min_prot_length_sr(nor);
*len = min_prot_len << (bp - 1);
if (*len > mtd->size)
*len = mtd->size;
if (*len > nor->params->size)
*len = nor->params->size;
if (nor->flags & SNOR_F_HAS_SR_TB && sr & tb_mask)
*ofs = 0;
else
*ofs = mtd->size - *len;
*ofs = nor->params->size - *len;
}
/*
@ -158,7 +157,6 @@ static bool spi_nor_is_unlocked_sr(struct spi_nor *nor, loff_t ofs, u64 len,
*/
static int spi_nor_sr_lock(struct spi_nor *nor, loff_t ofs, u64 len)
{
struct mtd_info *mtd = &nor->mtd;
u64 min_prot_len;
int ret, status_old, status_new;
u8 mask = spi_nor_get_sr_bp_mask(nor);
@ -183,7 +181,7 @@ static int spi_nor_sr_lock(struct spi_nor *nor, loff_t ofs, u64 len)
can_be_bottom = false;
/* If anything above us is unlocked, we can't use 'top' protection */
if (!spi_nor_is_locked_sr(nor, ofs + len, mtd->size - (ofs + len),
if (!spi_nor_is_locked_sr(nor, ofs + len, nor->params->size - (ofs + len),
status_old))
can_be_top = false;
@ -195,11 +193,11 @@ static int spi_nor_sr_lock(struct spi_nor *nor, loff_t ofs, u64 len)
/* lock_len: length of region that should end up locked */
if (use_top)
lock_len = mtd->size - ofs;
lock_len = nor->params->size - ofs;
else
lock_len = ofs + len;
if (lock_len == mtd->size) {
if (lock_len == nor->params->size) {
val = mask;
} else {
min_prot_len = spi_nor_get_min_prot_length_sr(nor);
@ -248,7 +246,6 @@ 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 mtd_info *mtd = &nor->mtd;
u64 min_prot_len;
int ret, status_old, status_new;
u8 mask = spi_nor_get_sr_bp_mask(nor);
@ -273,7 +270,7 @@ static int spi_nor_sr_unlock(struct spi_nor *nor, loff_t ofs, u64 len)
can_be_top = false;
/* If anything above us is locked, we can't use 'bottom' protection */
if (!spi_nor_is_unlocked_sr(nor, ofs + len, mtd->size - (ofs + len),
if (!spi_nor_is_unlocked_sr(nor, ofs + len, nor->params->size - (ofs + len),
status_old))
can_be_bottom = false;
@ -285,7 +282,7 @@ static int spi_nor_sr_unlock(struct spi_nor *nor, loff_t ofs, u64 len)
/* lock_len: length of region that should remain locked */
if (use_top)
lock_len = mtd->size - (ofs + len);
lock_len = nor->params->size - (ofs + len);
else
lock_len = ofs;